MacOS – Unmount Volume on User Logout

apfscatalinamacosmount

I have several encrypted APFS volumes, some of which have their passphrases stored only in specific users' keychains so they can unlock and mount automatically for those users.

However, when the user logs out, the volume remains mounted. While this isn't strictly a problem (as I set mount points inside the user's home folder, and have volume ownership enabled), I would prefer for the volume to unmount and lock again automatically.

This behaviour differs compared to mounting an encrypted disk image, which is mounted as a specific user (when viewing the output of mount) and which then (normally*) unmounts when the user logs out.

Is it possible to replicate the same behaviour with APFS volumes, i.e- mount automatically for user with the passphrase in their keychain when they login, and then unmount (and lock) automatically when they logout?

*I say normally, as macOS Catalina appears to have bugs related to cleaning up user processes when a user logs out, resulting in many processes still running for logged out users. This currently includes disk-image-helper, so it no longer unmounts automatically as it did under Mojave. I'm happy to accept any answer that replicates the disk image mounting/unmounting behaviour with the caveat that this currently doesn't always work for Catalina, on the basis that it should work if Apple ever fixes these bugs.

Update: I've tried doing the following in a script triggered by launchd, but launchd's kill signal doesn't appear to reach it:

#!/bin/bash
VOLUME=12345678-9012-3456-7890-123456789012
MOUNT_POINT=/Users/haravikk/Desktop/Foo

[ ! -e "${MOUNT_POINT}" ] && { mkdir "${MOUNT_POINT}" || exit 1; }

if echo -e "$(security find-generic-password -wa "${VOLUME}" | sed 's/../\\x&/g')" | diskutil apfs unlockVolume "${VOLUME}" -stdinpassphrase; then
    cleanup() {
        echo 'Unmounting'
            
        attempts=5
        while [[ ${attempts} -gt 0 ]]; do
            diskutil apfs lockVolume "${VOLUME}" && break
            [[ -n "${MOUNT_POINT}" ]] && umount "${MOUNT_POINT}" && break
            attempts=$((${attempts} - 1))
            sleep 5
        done
        if [[ ${attempts} = 0 ]]; then
            if ! diskutil unmount force "${VOLUME}"; then
                if [[ -z "${MOUNT_POINT}" ]] || ! umount -f "${MOUNT_POINT}"; then
                    echo 'All attempts to unmount failed' >&2
                fi
            fi
        fi
    }
    trap 'cleanup' SIGINT SIGHUP SIGTERM EXIT
    while true; do
        sleep 86400 &
        wait $!
    done
fi

The idea was that when the user logs out, launchd should send their processes a kill signal (SIGINT) which will trigger the script's trap and allow it to unmount the volume. But this never seems to happen; the trap isn't triggered at all.

If anyone is interested in using the basics of this script, note that you will need to have an entry in your keychain for the volume (you can do this by mounting with Disk Utility and choosing to save when prompted for the password), and must make sure that security has permission to access it.

Best Answer

You might try using a logout hook. Login and logout hooks are deprecated, but I believe they still function.

Mounting the volume should not be a problem; a user LaunchAgent would handle that nicely. The problem lies in trying to unmount the volume at logout. Have you considered writing a system LaunchDaemon that periodically polls the open APFS volumes and unmounts those that don't have an associated user? The volumes should be automatically locked by system security when they are unmounted, so I don't think you need to make a special effort for that, and it sounds like you're thinking more about cleanup than anything else. If you put the daemon on (say) a 30 second timer, it shouldn't consume too much in the way of resources, and volumes will only persist for an average of 15 second after logout.