Skip to content

Instantly share code, notes, and snippets.

@chetstone
Last active November 28, 2023 20:35
Show Gist options
  • Save chetstone/8a1294dc3aab9d494e5344dba94e09f0 to your computer and use it in GitHub Desktop.
Save chetstone/8a1294dc3aab9d494e5344dba94e09f0 to your computer and use it in GitHub Desktop.
Encrypted APFS Home Folder on Mac OSX

Encrypted APFS Home Folder on Mac OSX

Note: This is an update of Will Haley's excellent post to use APFS instead of CoreStorage.

UPDATE: Doesn't work on Catalina

The script can't read the file containing the password on the USB thumb drive. When formatted as FAT32 as described below, the user/group of the file is unknown/unknown. But I also tried formatting the USB thumb drive in HFS+, unchecking "Ignore permissions on this volume" and changing the file owner to root:wheel. The file is still not readable to the boot process. Probably something to do with new security restrictions in Catalina. A script running as root reading data from a thumb drive? Makes sense.

Back to previous program

I'll add to Will's warning below that this is a complex process on MacOS because the OS has a lot of assumptions on where things are. I initially wanted to have the installation as transparent as possible by mounting my Home on /Users/chet, but the OS wouldn't allow it.. I then tried mounting it on /home, but had trouble with backup software, notably Backblaze, which will not backup custom mount points, and Time Machine, which would do the backup but would not show it in the restore GUI. So I just ended up mounting it on the standard mount point: /Volumes.

So you want to encrypt your $HOME on your Mac but nothing else?

Let’s stop right here.

The built in option for FileVault full disk encryption is the standard on a Mac, and I recommend going that route for encryption. It is a fairly big pain to do what you’re asking.

Though, it is possible, and that is what this guide will help you to accomplish. Although it is complicated, it is possible to encrypt a user’s home folder independently of the rest of the system. We can use a keyfile to encrypt our home, and automatically mount it at boot time.

A couple caveats.

  1. Again, I have to stress, FileVault is so much simpler. Or VeraCrypt.
  2. You must be comfortable using a USB key to decrypt your drive.
  3. My process works 99% of the time, but about once every few months my $HOME fails to mount properly and I must reboot to try again.

Full disk encryption is better than partial disk encryption, and my process certainly has some holes. It is up to you to be as secure (paranoid) as you want.

Administrator account

Create an Administrator user on your Mac (with Administrator access of course) and log out of your account and log in as that user.

While it may be possible, I do not think it’s a good idea to mess with the home folder of a user while that user is logged in. That is why I ask you to create the Administrator user account.

Not only should you use that secondary account to follow these instructions, but you should keep that account around and never encrypt its home.

Why? Well, if your encrypted home folder fails to mount at boot time for some reason, you may be locked out of your Mac. That’s no fun. You can probably fix things via the recovery tools if that happens, but having a secondary admin account will greatly ease your burden if you need to troubleshoot. It’s your choice, but be warned, if something goes wrong and you get locked out of your machine, it will probably be very frustrating and annoying.

Create a keyfile

I believe that a keyfile is the only way to accomplish the goal of this article.

I cannot find any way to accomplish our goal using a password prompt. I don’t think you can inject/prompt for a password in the standard boot process without using a file that can be read via script.

I am naming my keyfile encryptedhome.key. The file should contain nothing but the password.

thePassw0rd

That is a trivial example. Will Haley uses a 1023 character random passphrase but I will use a shorter one storable in 1Password so the disk can be mounted by hand if the USB drive is lost.


cat > encryptedhome.key
 *******************************

Find a USB drive. Bonus points if it has a read only switch on it. I use an SD card which can plug into the SD slot on my iMac leaving a USB jack free.

Format the USB drive as MS-DOS (FAT), with a Master Boot Record, using Disk Utility. Name the drive KEYDRIVE01 or something that’s reasonably unique.

Copy your keyfile to the root of KEYDRIVE01.

You should backup this keyfile somewhere secure. If you lose it, you will forever lose access to your encrypted disk.

Set up our encrypted disk

I highly recommend using a dedicated disk to house this encrypted home of yours. What I mean is, a separate, distinct disk from your OS disk.

In my case, I’m using a brand new ~500GB SSD drive to act as my encrypted home.

For clarity, I’m going to refer to this disk as the “encrypted home”, even though it’s not encrypted just yet.

With your machine powered off, plug in your new encrypted home disk to your Mac. Boot up and open up your terminal.

Find the id of the disk that will server as your encrypted home.

diskutil list

My encrypted home disk is brand new and so has no partitions.

/dev/disk0 (internal, physical):
/dev/disk0 (external):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                         512.1 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS SSD T3                  511.8 GB   disk0s2

Be extremely certain that you’re looking at the correct disk before proceeding. If you choose the wrong disk, you may lose data.

Create an APFS Container on your encrypted home disk. Warning: this will erase the disk. Note that I am running this command against /dev/disk0s2 based on the output of the command above. The createContainer command runs on a partition, not the whole disk.

$ diskutil ap createContainer /dev/disk0s2
Creating container with disk0s2
Started APFS operation on disk0s2 SSD T3
Creating a new empty APFS Container
Unmounting Volumes
Switching disk0s2 to APFS
Creating APFS Container
Created new APFS Container disk5
Disk from APFS operation: disk5
Finished APFS operation on disk0s2 SSD T3

Your encrypted home disk layout should look something like this if you do diskutil list disk5 , where disk5 is the output of the previous command.

/dev/disk5 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +511.9 GB   disk5
                                 Physical Store disk0s2

Create an encrypted volume inside the APFS Container that we just created a moment ago. I am calling my volume ChesterHome. You can name it however you like, but then make sure you use your updated name(s) in the rest of my steps.

cat /Volumes/KEYDRIVE01/encryptedhome.key|diskutil ap addVolume disk5 APFS ChesterHome -stdinpassphrase -passphraseHint "EncryptedHome in 1PW"
Exporting new encrypted APFS Volume "ChesterHome" from APFS Container Reference disk5
Started APFS operation on disk5
Preparing to add APFS Volume to APFS Container disk5
Creating APFS Volume
Created new APFS Volume disk5s1
Mounting APFS Volume
Setting volume permissions
Disk from APFS operation: disk5s1
Finished APFS operation on disk5

Unlock our encrypted disk at boot

Great. Now we have an encrypted drive and a USB key to unlock it, but how do we unlock it at boot when we need to login? OS X has no built in mechanism for this, so we’ll have to script it ourselves.

Technically, we can't unlock the drive as part of the login process. At least, not to my knowledge. So, we must unlock the disk at boot time. We'll unlock it then using our USB key.

Get the UUID of your USB key.

$ diskutil  list |grep KEYDRIVE01
   1:                 DOS_FAT_32 KEYDRIVE01              7.7 GB     disk4s1

$ diskutil info disk4s1 |grep "Volume UUID"
   Volume UUID:               68D1757B-0DFE-3CD9-97B1-4D3B2C75ECF9

Get the UUID of your Encrypted Volume using the APFS volume disk from the output of the addVolume command above.

$ diskutil ap list |grep disk5s1
    +-> Volume disk5s1 E9E409ED-F847-4973-BD77-757A452965C1
        APFS Volume Disk (Role):   disk5s1 (No specific role)

Mount your encrypted disk as your new $HOME

By default, your volume would be mounted under /Volumes. If you're OK with that (and you should be), skip to Create a Script.

Will Haley was able to mount it at /Users/will (the default $HOME ) but I was not able to get the system to mount a volume over an active home directory, possibly due to new security restrictions in Mojave.

To mount the encrypted home disk at /home and our users subdirectory /home/chet, a couple of steps are needed. First, we need to take /home out of the automounter (which is rarely used on MacOS).

sudo -e /etc/auto_master
... comment out the line beginning with /home

Make an entry in fstab

I found this did not seem to do anything, but if you want, execute sudo vifs and add the following line to fstab:

UUID=YOUR_ENCRYPTED_VOLUME_UUID none apfs rw,noauto,nobrowse 0 2

Again, substitute your own UUID. This line tells the system not to automount the volume at boot, and not to show the mounted drive on the desktop and the finder side bar.

Create a script to unlock and mount the disk at startup

Now create a file with the following contents at /opt/mount.sh.

Replace YOUR_USB_KEY_UUID and YOUR_ENCRYPTED_VOLUME_UUID in the script below with the correct UUIDs for your USB key and encrypted volume found when you ran the commands above!

Modify the script as needed for your system.

#!/bin/bash
set -e
set -u

USB_KEY_UUID=68D1757B-0DFE-3CD9-97B1-4D3B2C75ECF9
ENCRYPTED_VOLUME_UUID=E9E409ED-F847-4973-BD77-757A452965C1

#LOG=/dev/stdout
LOG=/tmp/mount.log

echo "mount attempted at $(date)" > $LOG 2>&1

# Must ensure the USB key is mounted so we can access the keyfile.
diskutil mount $USB_KEY_UUID >> $LOG 2>&1

diskutil list >> $LOG 2>&1

KEY_NAME=$(diskutil info $USB_KEY_UUID | grep "Volume Name" | awk '{print $3}')

echo $KEY_NAME >> $LOG 2>&1

KEYFILE="/Volumes/$KEY_NAME/encryptedhome.key"

i="0"

while [ ! -f $KEYFILE ]
do
    sleep 1
    i=$[$i+1]
    if [ $i -ge 30 ]
    then
        echo "Could not find $KEYFILE" >> $LOG 2>&1
        exit 1
    fi
done

password=$(cat $KEYFILE)

echo $password |wc >> $LOG 2>&1

# Unlock the encrypted volume using the key. This will also mount it on /Volumes
printf $password|diskutil ap unlockVolume $ENCRYPTED_VOLUME_UUID -stdinpassphrase -nomount  >> $LOG 2>&1

## mount_apfs requires a device, not a UUID
ENCRYPTED_VOLUME_ID=$(diskutil info $ENCRYPTED_VOLUME_UUID | grep 'Device Identifier' | awk '{print $3}')
#
echo "Disk name $ENCRYPTED_VOLUME_ID" >> $LOG 2>&1
#
# sleep 15
# let diskarbitrationd handle mounting later???
# mount -v -t apfs "/dev/$ENCRYPTED_VOLUME_ID" /Volumes >> $LOG 2>&1

sleep 20 # try to get of message that it's already unmounted

diskutil info $ENCRYPTED_VOLUME_UUID >> $LOG 2>&1
diskutil unmount $USB_KEY_UUID >> $LOG 2>&1

echo "mount finished at $(date)" >> $LOG 2>&1

Lock that script down a bit and make it executable.

sudo chown root:wheel /opt/mount.sh
sudo chmod 500 /opt/mount.sh
sudo chmod +x /opt/mount.sh

OK, so how do we get that to run at boot?

Create a file at /Library/LaunchDaemons/org.dewachen.startup.mounthome.plist using these contents.

Again, you can rename that file if you want, but make sure you update the name appropriately elsewhere!

<?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>RunAtLoad</key>
    <true />
    <key>Label</key>
    <string>org.dewachen.startup.mounthome</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/mount.sh</string>
    </array>
</dict>
</plist>

Set our mount script to run at boot.

launchctl load -w /Library/LaunchDaemons/org.dewachen.startup.mounthome.plist

Now comes the ultimate test. Keep your USB key plugged in, and reboot. Log in as any user and verify that the volume is mounted on /Volumes/ChesterHome/. You should see it in the Finder.

Create a new folder in /Volumes/ChesterHome. Reboot.

Do you see your folder in /Volumes/ChesterHome? Great. Data is persisting as we’d hope.

Try unplugging your USB key and rebooting. You should see that the encrypted home disk does not mount now. Re-connect your USB key and reboot again.

Hopefully the encrypted home disk is mounting properly each time you reboot as long as the USB key is plugged in.

If your encrypted home is failing to mount automatically, take a step back and try to see where you went wrong.

Can you run the mount.sh script manually with sudo? Do you see any errors? Do you see a file created at /tmp/mount.log indicating that a mount was even attempted? Keep at it. I’m sure you’ll figure it out, and my apologies if I left out a step.

Tighten permissions on the mounted disk

By default, external drives are mounted with "permissive" permissions so any user with physical possesion of the drive can see the files on it. But this is your home directory— we don't want that. Find your volume with diskutil list:

/dev/disk4 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +511.9 GB   disk4
                                 Physical Store disk0s2
   1:                APFS Volume ChesterHome             383.2 GB   disk4s1

Then use enable ownership on that volume:

sudo diskutil enableOwnership disk4s1

This setting is stored in a persistent database on your boot disk.

Move your $HOME data to the encrypted disk

You may not have paid attention at the beginning, but to reiterate, I highly recommend that you log out of your account and log in as another administrator for these next steps. In fact, before you move your own home directory, try it with a test user. Open the Users & Groups preference pane and create a new Standard user with the short user name of newuser. Log in as that user and make sure the new account works, then log out and execute the following command.

sudo rsync -a /Users/newuser /Volumes/ChesterHome

Now go back to the Users & Groups preference pane and right-click on your New User and select Advanced Options. Change the Home directory to /Volumes/ChesterHome/newuser. Now reboot and when the system comes back up, you should be able to log in as newuser. Look around and make sure everything is OK. Create a new file on the Desktop then open Terminal, where you should be able to see it in /Volumes/ChesterHome/newuser/Desktop.

Now you are ready to move your own home directory. Log out of your main account and log in as Administrator. In order to avoid any issues with permissions, I decided to use Carbon Copy Cloner to do the copying rather than rsync, which might not be able to transfer all the Apple-specific ACL's, extended attributes and so forth.

Clean up

Once you’re satisfied and your data is backed up securely and your USB key is backed up securely, then you should delete your old home directory at /Users/chet (or wherever) from the unencrypted disk. Also delete newuser and its home directory and copy in /Volumes/ChesterHome/newuser and /Users/newuser

After all, what is the point of encrypting our $HOME if the old unencrypted data is still there in the open?

Be extremely careful that you are deleting the data from the unencrypted disk, and not from your encrypted disk.

Finally, and this is important, with KEYDRIVE01 mounted, open your Spotlight Preference Pane and add the drive to the Privacy pane. This keeps Spotlight from trying to index the USB disk at startup which can interfere with it being automatically unmounted. Besides, you don't want your key appearing in Spotlight!

These steps took a lot of trial and error. FileVault is a much safer and easier way to get encryption, and although I have not used VeraCrypt, it is probably much simpler too.

For any of this to be worthwhile, please don’t leave your USB key plugged in to your Mac when you leave the house ;)

Citations

This site was a huge help in understanding Core Storage.

This site was a huge help in understanding Launchd and boot scripts in OS X.

@spassigl
Copy link

Seems this is not working for MacOS Monterey (12.0.1) either.
I loved the idea of having a USB thumb drive to control the access to the external drive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment