Time Machine Encryption

xDunes
New Contributor

Is there a way to prevent unencrypted time machine backups?

If possible, is there a way to standardize encryption key similar to FileVault2?

13 REPLIES 13

charliwest
Contributor II

@xDunes did you ever get anywhere with this idea?

PeterClarke
Contributor II

I Think it's one that we have to wait for Apple to resolve..

As far as I can see, trying to 'hack' a solution would raise too many problems..

If Apple does come up with a solution to this - then I think it would not be included until at least OS X 10.10..
- and maybe not even then..

Bauer
New Contributor

We contacted Apple about this at one point as well, and they said the best they had to offer was tmutil and diskutil corestorage. Using those tools, I built a POC for verifying employees are backing up to approved drives and their Time Machine drives are encrypted. The script is triggered by a launchdaemon that's activated by drives being mounted or changes to /Library/Preferences/com.appleTimeMachine.plist. This POC was built around using a specific company approved USB drive, so you'll see that in the script below.

The script forces encryption on the Time Machine drive and ejects and removes unapproved drives listed in Time Machine. I found it consistently works within a matter of seconds after plugging in an external drive or setting up a drive in Time Machine, and it seemed to be pretty quiet too, running without blowing up the system logs. This was a POC, and I think I tweaked the script a little more in testing, but this is pretty close to a final version:

#!/bin/bash

####### Time Machine drive detection and encryption script. 
####### Written by Matt Bauer with contributions by Nick Kalister, 2013

############################## Variables Defined Here ##############################

## Cocoa Dialog Detection
CocoaDialog=`detect cocoa dialog here`

## Get names of drives being used with Time Machine and assign them to TMDestCheck array variable
IFS=$'
'
TMDestCheck=(`tmutil destinationinfo | grep Name | cut -d ':' -f 2 | sed -e 's/^[ 	]*//'`)
echo --- Drives being used with Time Machine ---
echo ${TMDestCheck[@]}
echo ${TMDestCheck[@]} > /private/tmp/tmdestcheck.txt

############################## End Variable Definitions ##############################

############################## Functions Defined Here ##############################

## Quit backup and notify user of unsupported drive
function DriveKill {
VerbotenDrive=`cat /private/tmp/verbotendrive.txt`
tmutil stopbackup
VerbotenDriveTMID=`tmutil destinationinfo | sed "1,/$VerbotenDrive/d" | grep -m 1 "ID" | cut -d ":" -f 2 | sed -e 's/^[ 	]*//'`
tmutil removedestination $VerbotenDriveTMID
echo $VerbotenDrive removed from Time Machine
WarningDialog=`$CocoaDialog msgbox --title "Unsupported Time Machine Drive" --x-placement "center" --y-placement "center" 
--button1 "Ok" 
--text "Backing up to '$VerbotenDrive' is prohibited." 
--informative-text "Please connect a company provided USB drive and try again. Contact the Help Desk 
at 866-555-1212 if you have questions or need help ordering an approved drive."`
if [ "$WarningDialog" = "1" ]; then
    echo User accepted prohibited drive dialogue
fi
}

## Check the encryption status of the Company Time Machine drive
function DriveCheck {
tmutil stopbackup
GutenDrive=`cat /private/tmp/gutendrive.txt`
EncryptionCheck=`diskutil cs list | grep -s -om 1 "$GutenDrive"`
if [[ "$EncryptionCheck" == "" ]]; then
    echo Drive needs to be encrypted
    EncryptionDialog=`$CocoaDialog msgbox --title "Encrypt Your Company Drive" --x-placement "center" --y-placement "center" 
    --button1 "Ok" 
    --text "Encrypt '$GutenDrive' to continue backing up." 
    --informative-text "You must encrypt '$GutenDrive' to proceed. Please choose a strong password to use for encrypting your drive. 
Only you will know this password, and it will be unrecoverable if your boot drive fails, so make note of this password in a secure location."`
        if [ "$EncryptionDialog" = "1" ]; then
            echo User accepted encryption dialogue
            PasswordEntry
        fi
    else
    echo Drive '$GutenDrive' is already encrypted, exiting script.
fi
}

## Dialogue to warn of mismatched passwords
function TryAgain {
TryAgainDialogue=`$CocoaDialog msgbox --title "Password Error" --x-placement "center" --y-placement "center" 
    --button1 "Retry" 
    --text "Passwords do not match." 
    --informative-text "The passwords you entered for '$GutenDrive' do not match. Please try again."`
if [ "$TryAgainDialogue" = "1" ]; then
    echo User trying again
    PasswordEntry
fi
}

## Password entry dialogues here
function PasswordEntry {
PasswordDialogue=`$CocoaDialog secure-inputbox --title "Enter Your Password" --x-placement "center" --y-placement "center" 
--informative-text "Please enter a password for '$GutenDrive'." --button1 "Ok"`
FirstPass=`echo $PasswordDialogue | cut -d " " -f 2`
SecondPasswordDialogue=`$CocoaDialog secure-inputbox --title "Enter Your Password" --x-placement "center" --y-placement "center" 
--informative-text "Please re-enter your password for '$GutenDrive'." --button1 "Ok"`
SecondPass=`echo $SecondPasswordDialogue | cut -d " " -f 2`
if [[ $FirstPass == $SecondPass ]]; then
    DriveEncryption
        elif [[ $FirstPass != $SecondPass ]]; then
        TryAgain
fi
}

## Encrypt the Company Time Machine drive and resume the Time Machine backup
function DriveEncryption {
GutenDrive=`cat /private/tmp/gutendrive.txt`
EncryptionDialogue=`$CocoaDialog msgbox --title "Encrypting Drive" --x-placement "center" --y-placement "center" 
    --button1 "Ok" 
    --text "'$GutenDrive' is ready to be encrypted." 
    --informative-text "Please click 'Ok' to convert '$GutenDrive' to an encrypted volume and complete this process."`
diskutil cs convert $GutenDrive -passphrase $SecondPass
CompletionDialogue=`$CocoaDialog msgbox --title "Drive Encrypted" --x-placement "center" --y-placement "center" 
    --button1 "Ok" 
    --text "'$GutenDrive' is ready to be used." 
    --informative-text "Please click 'Ok' to enable this drive for Time Machine."`
tmutil startbackup
}

## Check the type of drive connected and write out to temp files
function DriveType {
for i in "${TMDestCheck[@]}"; do
    DriveName=(`system_profiler SPStorageDataType | grep -A 33 "$i" | grep -m 1 "Media Name:" | cut -d ":" -f 2 | sed -e 's/^[ 	]*//'`)
    for n in "${DriveName[@]}"; do
        if [[ "${DriveName[@]}" != "Name of approved drive" ]]; then
            echo Verboten drive connected, shutting down Time Machine
            echo "$i" > /private/tmp/verbotendrive.txt
            DriveKill
            elif [[ "${DriveName[@]}" == "Name of approved drive" ]]; then
            echo "$i" > /private/tmp/gutendrive.txt
            DriveCheck
        fi
    done
done
}

############################## End Function Definitions ##############################

################################ Script Begins Here ################################

DriveType

echo Removing temporary files

rm -f /private/tmp/tmdestcheck.txt
rm -f /private/tmp/verbotendrive.txt
rm -f /private/tmp/gutendrive.txt

echo Script complete. Exiting now

exit

Here's the launchdaemon from /Library/LaunchDaemons too. Really straightforward, and easy to create with Lingon or LaunchControl.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict> <key>Label</key> <string>com.company.TMChecker_1.0</string> <key>Program</key> <string>/path/to/script</string> <key>StartOnMount</key> <true/> <key>WatchPaths</key> <array> <string>/Library/Preferences/com.apple.TimeMachine.plist</string> </array>
</dict>
</plist>

-Matt

justingrigg
New Contributor II

@Bauer, I have to admit, this scripting is above me. Could you possibly post the script without the Approved drive section? I just need to force encryption on time machine drives.

Truly appreciated.

Bauer
New Contributor

@justingrigg][/url

Here's a retooled version that doesn't require a specific drive, and I also added in some icons and tidied things up a bit to make it easier to parse out for editing. I tested it out this AM, but definitely proof and test thoroughly to make sure it works in your environment.

#!/bin/bash

####### Time Machine drive detection and encryption script. 
####### Written by Matt Bauer with contributions by Nick Kalister, 2013

############################## Variables Defined Here ##############################

## Cocoa Dialog Detection goes here. Cocoa Dialog v3 is recommended.
## Set the variable $CocoaDialog as the path to CocoaDialog.app/Contents/MacOS/CocoaDialog on your workstations.

## Get names of drives being used with Time Machine and assign them to TMDestCheck array variable.
IFS=$'
'
TMDestCheck=(`tmutil destinationinfo | grep Name | cut -d ':' -f 2 | sed -e 's/^[ 	]*//'`)
echo --- Drives being used with Time Machine ---
echo ${TMDestCheck[@]}
echo ${TMDestCheck[@]} > /private/tmp/tmdestcheck.txt

############################## End Variable Definitions ##############################

############################## Functions Defined Here ##############################

## 1 - Check the encryption status of the  Time Machine drive.
function DriveCheck {
for i in "${TMDestCheck[@]}"; do
DiskAttached=`diskutil info "$i" | grep "Could not find"`
if [ "$DiskAttached" == "" ]; then
    EncryptionCheck=`diskutil cs list | grep -s -om 1 "$i"`
    if [[ "$EncryptionCheck" == "" ]]; then
        echo "$i" needs to be encrypted.
        tmutil stopbackup
        echo "$i" > /private/tmp/timemachinedrive.txt
        EncryptionDialog=`$CocoaDialog msgbox --title "Encrypt Your Time Machine Drive" --x-placement "center" --y-placement "center" 
        --icon-file "/System/Library/PreferencePanes/TimeMachine.prefPane/Contents/Resources/TimeMachineGraphic@2x.png" 
        --icon-height 72 
        --icon-width 72 
        --button1 "Ok" 
        --text "Encrypt '$i' to continue backing up." 
        --informative-text "You must encrypt '$i' to proceed. Please choose a strong password to use for encrypting your drive. 
Only you will know this password, and it will be unrecoverable if your boot drive fails, so make note of this password in a secure location."`
            if [ "$EncryptionDialog" = "1" ]; then
                echo User accepted encryption dialogue
                PasswordEntry
            fi
        else
        echo ""$i" is already encrypted, exiting script."
    fi
else echo ""$i" not attached."
fi
done
}

## 2 - Password entry dialogues here.
function PasswordEntry {
AttachedDrive=`cat /private/tmp/timemachinedrive.txt`
PasswordDialogue=`$CocoaDialog secure-inputbox --title "Enter Your Password" --x-placement "center" --y-placement "center" 
--icon "notice" 
--informative-text "Please enter a password for '$AttachedDrive'." --button1 "Ok"`
FirstPass=`echo $PasswordDialogue | cut -d " " -f 2`
SecondPasswordDialogue=`$CocoaDialog secure-inputbox --title "Enter Your Password" --x-placement "center" --y-placement "center" 
--icon "notice" 
--informative-text "Please re-enter your password for '$AttachedDrive'." --button1 "Ok"`
SecondPass=`echo $SecondPasswordDialogue | cut -d " " -f 2`
if [[ "$FirstPass" == "1" ]] || [[ "$SecondPass" == "1" ]]; then
    BlankPass
elif [[ "$FirstPass" != "$SecondPass" ]]; then
    TryAgain
elif [[ "$FirstPass" == "" ]] || [[ "$SecondPass" == "" ]]; then
    echo "User attempted to bypass password entry. Exiting."
elif [[ "$FirstPass" == "$SecondPass" ]]; then
    DriveEncryption
fi
}

## 3 - Dialogue to warn of mismatched passwords.
function TryAgain {
TryAgainDialogue=`$CocoaDialog msgbox --title "Password Error" --x-placement "center" --y-placement "center" 
    --icon "stop" 
    --button1 "Retry" 
    --text "Passwords do not match." 
    --informative-text "The passwords you entered for '$AttachedDrive' do not match. Please try again."`
if [ "$TryAgainDialogue" = "1" ]; then
    echo User trying again
    PasswordEntry
fi
}

## 4 - Function to warn of blank password.
function BlankPass {
BlankPassword=`$CocoaDialog msgbox --title "Password Error" --x-placement "center" --y-placement "center" 
    --icon "stop" 
    --button1 "Retry" 
    --text "Passwords cannot be blank." 
    --informative-text "The passwords you entered for '"$i"' are blank. Please try again."`
PasswordEntry
}

## 5 - Encrypt the Time Machine drive and resume the Time Machine backup.
function DriveEncryption {
AttachedDrive=`cat /private/tmp/timemachinedrive.txt`
EncryptionDialogue=`$CocoaDialog msgbox --title "Encrypting Drive" --x-placement "center" --y-placement "center" 
    --icon-file "/System/Library/PreferencePanes/TimeMachine.prefPane/Contents/Resources/TimeMachineGraphic@2x.png" 
    --icon-height 72 
    --icon-width 72 
    --button1 "Ok" 
    --text "'$AttachedDrive' is ready to be encrypted." 
    --informative-text "Please click 'Ok' to convert '$AttachedDrive' to an encrypted volume and complete this process."`
diskutil cs convert $AttachedDrive -passphrase $SecondPass
CompletionDialogue=`$CocoaDialog msgbox --title "Drive Encrypted" --x-placement "center" --y-placement "center" 
    --icon-file "/System/Library/CoreServices/Installer.app/Contents/PlugIns/Summary.bundle/Contents/Resources/Success.tiff" 
    --button1 "Ok" 
    --text "'$AttachedDrive' is ready to be used." 
    --informative-text "Please click 'Ok' to enable this drive for Time Machine and begin backing up."`
tmutil startbackup
}

############################## End Function Definitions ##############################

################################ Script Begins Here ################################

DriveCheck

echo Removing temporary files

rm -f /private/tmp/tmdestcheck.txt
rm -f /private/tmp/timemachinedrive.txt

echo Script complete. Exiting now

exit

rblaas
Contributor II

@Bauer Do you have any idea how to modify this script for checking/enabling encryption on a Network volume (Like Time Capsule / Synology)?

Regards,

Ronald

Bauer
New Contributor

@rblaas If I remember correctly, Time Capsule stores the backups in sparsebundle disk images, and it's the disk image that's encrypted on Time Capsule, not the whole disk. If that's the case, hdiutil can verify if a sparsebundle or plain disk image is encrypted, but I'm not sure that you can "see" the disk image itself when the Time Machine backup runs, as the OS will probably only mount and display the disk image's volume, without exposing the disk image itself.

I don't have this setup to test on my own right now, but I would recommend testing with diskutil and hdiutil to see if either of them will report back encryption information on that sparsebundle. If not, try looking in the sparsebundle itself to see if maybe there's a hidden preference file at the root of the volume that indicates whether or not encryption has been enabled.

If I find some time to set up a test, I'll report back if I figure something out, and if you figure something out, please let me know what you found.

rblaas
Contributor II

@Bauer

I have found a way.. I have rebuild your script.

The thing you need to do is add a password in the system keychain. When the first Time Machine backup starts it will use this password for encryption.

and although the GUI is not saying that the backup is encrypted it still is. (you can try mounting the sparsebundle on a machine. It will ask for a password. )

I have included my (your rebuild) script. Sorry for the dialogs to be in Dutch.. But I think you can guess what it's saying :)

I do want to say that the script is not completely done. Some tuning is still in place. But that's just how you want it to be.

Things to keep in mind:

  • If there is already a encryption password in the keychain I have no idea (not yet tested) if the new or the old given encryption pass is being used.
  • if there is already an (encrypted) image on the network location it will not change it.
  • Still have to find a way to limit the sparsebundle (for lets say 500GB)

  • still need to create some escapes in case of loops :)

  • there may be better ways for doing this script.

  • I have not yet tested this script with Casper. I just ran the script as root on a client. (logged on with a simple user)

thanks for the help!

#!/bin/bash

## Set all Variables

CocoaDialog="/Library/Application Support/JAMF/bin/CocoaDialog.app/Contents/MacOS/CocoaDialog"
username=`stat -f '%u %Su' /dev/console | awk '{print $2}'`
tmserver="IP Address"
servervol="TimeMachine"
Retry=0
TMPass=1
Encpass=1

#### Functions


## 1 - Function to get Userpass for TimeMachine Server

function TMvolPass {
PasswordDialogue=`"$CocoaDialog" secure-inputbox --title "Voer uw inlog wachtwoord in" --x-placement "center" --y-placement "center" 
--icon "notice" 
--informative-text "Geef uw wachtwoord op voor de TimeMachine Server" --button1 "Ok"`
TMPass=`echo $PasswordDialogue | cut -d " " -f 2`
if [[ "$TMPass" == "1" ]]; then
    BlankPass1
elif [[ "$TMPass" != "1" ]]; then
    SetTMDest
    fi
fi
}

## 2 - Function for Blank Password (TMvolPass)

function BlankPass1 {
BlankPassword=`"$CocoaDialog" msgbox --title "Password fout" --x-placement "center" --y-placement "center" 
    --icon "stop" 
    --button1 "Retry" 
    --text "Wachtwoord kan niet leeg zijn!" 
    --informative-text "Je hebt geen wachtwoord ingevuld. Probeer het opnieuw"`
TMvolPass
}

## 3 - Function for Password MisMatch

function TryAgain {
TryAgainDialogue=`"$CocoaDialog" msgbox --title "Password fout" --x-placement "center" --y-placement "center" 
    --icon "stop" 
    --button1 "Retry" 
    --text "Wachtwoorden niet gelijk" 
    --informative-text "De wachtwoorden komen niet overeen. Probeer het opnieuw."`
EncryptPass
}

## 4 - Set Time Machine Destination

function SetTMDest {
tmutil setdestination afp://$username:$TMPass@$tmserver/$servervol
if [[ $? -gt 0 ]]; then PassIncorrect=`"$CocoaDialog" msgbox --title "Wachtwoord Fout" --x-placement "center" --y-placement "center" 
        --icon "stop" 
        --button1 "Retry" 
        --text "Wachtwoord is niet correct!" 
        --informative-text "Het wachtwoord voor de TimeMachine Server is niet correct. Probeer het opnieuw."`
    TMvolPass
    fi
CompletionDialogue=`"$CocoaDialog" msgbox --title "Time Machine Server verbinding gelukt" --x-placement "center" --y-placement "center" 
    --icon-file "/System/Library/CoreServices/Installer.app/Contents/PlugIns/Summary.bundle/Contents/Resources/Success.tiff" 
    --button1 "Ok" 
    --text "Server verbinding gelukt" 
    --informative-text "Klik op OK om door te gaan."`
}

## 5 - Function to get Time Machine Encryption Password

function EncryptPass {
EncryptPassDialog=`"$CocoaDialog" secure-inputbox --title "Geef een wachtwoord op voor Encryptie" --x-placement "center" --y-placement "center" 
    --icon "notice" 
    --informative-text "Geef uw wachtwoord op voor de Encryptie" 
    --button1 "Ok"`
Encpass=`echo $EncryptPassDialog | cut -d " " -f 2`
if [[ "$Encpass" == "1" ]] ; then
        BlankPasswordDialog
    else
    EncryptPassDialog2=`"$CocoaDialog" secure-inputbox --title "geef uw wachtwoord nogmaals op!" --x-placement "center" --y-placement "center" 
        --icon "notice" 
        --informative-text "Geef uw Encryptie wachtwoord nogmaals op." 
        --button1 "Ok"`
    Encpass2=`echo $EncryptPassDialog2 | cut -d " " -f 2`
    if [[ "$Encpass" != "$Encpass2" ]]; then
        TryAgain
    fi
fi
}

## 6 - BlankPassword (Encryption)

function BlankPasswordDialog {
BlankPassword=`"$CocoaDialog" msgbox --title "Wachtwoord Fout" --x-placement "center" --y-placement "center" 
        --icon "stop" 
        --button1 "Retry" 
        --text "Wachtwoord kan niet leeg zijn." 
        --informative-text "Het wachtwoord voor de Encryptie kan niet leeg zijn. Probeer het opnieuw."`
        EncryptPass
}


### Scripting

# Get UserPass for Time Machine Server connection and Set Destination
TMvolPass

# Exclude all System folders
tmutil addexclusion -p /Applications
tmutil addexclusion -p /Library
tmutil addexclusion -p /System

# Exclude any other users on the computer (Edit for your specifics)
tmutil addexclusion -p /Users/admin
tmutil addexclusion -p /Users/Shared
tmutil addexclusion -p /Users/casperadmin
tmutil addexclusion /Users/$username/Downloads

# Exclude hidden root os folders
tmutil addexclusion -p /bin
tmutil addexclusion -p /cores
tmutil addexclusion -p /etc
tmutil addexclusion -p /Network
tmutil addexclusion -p /private
tmutil addexclusion -p /sbin
tmutil addexclusion -p /tmp
tmutil addexclusion -p /usr
tmutil addexclusion -p /var
tmutil addexclusion -p /.Spotlight-V100
tmutil addexclusion -p /.Trashes

## Make Entry in the System.Keychain for disk encryption
#Get ID for destination
TMID=`tmutil destinationinfo | grep ID | awk '{print $3}'`

# Get password for encryption
EncryptPass

# Set EncryptPassword in KeyChain
security add-generic-password -a $TMID -l "Time Machine" -s "Time Machine" -w "$Encpass" -A /Library/Keychains/System.keychain

if [[ $? -gt 0 ]]; then 
    Error=`"$CocoaDialog" msgbox --title "Error" --x-placement "center" --y-placement "center" 
        --icon "stop" 
        --button1 "Retry" 
        --text "Error" 
        --informative-text "De volgende Error. Probeer het opnieuw."`
        exit 1
        fi

# Enable timemachine to start auto backing up
tmutil enable
tmutil enablelocal
tmutil startbackup --auto

rblaas
Contributor II

Ow, The above script does NOT detect a Time Machine Backup on a network location and set encryption.

It just setup Time Machine on a Network location with encryption!

Just so you know :)

PatCMP
New Contributor III

Hi @rblaas, thanks for the script! But I cant get i to work in High Sierra 10.13.6, getting syntax error: "line 28: syntax error near unexpected token `fi'".

Did you test this on HighSierra? or would it be possible for you to revise it when you have time?

Thank you very much!

rblaas
Contributor II

@PatCMP I am currently on a holiday :) When I get back I will take a look at this (please note that we currently don't actually use the script)

PatCMP
New Contributor III

Thank you!
@rblaas

rblaas
Contributor II

@PatCMP A quicklook at the script :)

in line 27/28 there is a double fi

Please remove either the fi from line 27 or 28 :)