Previously, I demonstrated how to set up an encrypted store in Mac OS, but didn’t describe why you might want to do such a thing.

Encrypt

Using an encrypted store like this can help keep your company’s sensitive passwords safe should your computer be compromised.
We use Capistrano to deploy to production servers, and in our deploy.rb cap variables are read from environment variables, and then forwarded on to the production machines.

set :live_production_mysql_pass, ENV['PROD_MYSQL_PASS']

Now obviously, we don’t want these sensitive variables set all the time, only when it’s time to deploy. To achieve this, we simply execute a shell script, stored on the secure volume, which sets the appropriate variables.

Store Your Secrets

When the secure volume is mounted, a custom .bash_profile is loaded which bootstraps your original ~/.bash_profile and adds your secrets to the environment:

source ~/.bash_profile
export PROD_MYSQL_PASS='top-secret'
 
echo "Exported secure environment, please close this terminal when you're through"
export PS1="SECURE $PS1"

When this script is executed, the environment variables within it are set, and a warning to close the terminal is emitted.
In order to automate the steps of mounting the secure volume, launching the interactive shell, and unmounting the volume, we have to use a couple of different tools.

Mount, Execute, Eject

The first is hdid which mounts a volume by filename. The output of this command provides both the device file, like /dev/disk1 and the mount point, like /Volumes/Vault.

The mount point is used to construct the path to the custom bash profile which initializes sensitive environment variables in a new interactive shell, launched with a pristine environment. This results in an isolated terminal with mingled secure and user-specific environment variables and settings. Additionally, the secure volume remains mounted for the lifespan of this secure session, allowing your deploy to read files like production ssh keys, or any other restricted resource, from this secure location.

Finally, when the interactive terminal exits, hdiutil ejects the disk device for the mounted volume.

Set It Up

The code to do all of this is stored in a bash function. Add the following lines to your ~/.bash_profile or equivalent and set the VAULT_DMG variable to the name of your encrypted disk image.

# Read Secure Volume credentials for deploy
VAULT_DMG=~/Vault.dmg
function sv () {
    SECURE_MOUNT_INFO=`hdid $VAULT_DMG`;
    SECURE_MOUNT_DEVICE=`echo -e $SECURE_MOUNT_INFO | cut -d ' ' -f1`;
    SECURE_MOUNT_PATH=`echo -e $SECURE_MOUNT_INFO | cut -d ' ' -f2`;
    bash --init-file $SECURE_MOUNT_PATH/.bash_profile;
    hdiutil eject $SECURE_MOUNT_DEVICE &> /dev/null;
    unset SECURE_MOUNT_INFO SECURE_MOUNT_PATH SECURE_MOUNT_DEVICE
}

2 Comments

  1. bryanl

    Seems awfully complicated. How about implementing two way encryption, so you can just encrypt the entire database.yml on the filesystem.

  2. duncanbeevers

    One advantage to using an encrypted disk image it that it can be easily shared between developers without requiring those developers to share the private key necessary to decrypt the disk image. Additionally, it provides natural, automatic encapsulation of the period during which sensitive information is available.

    It’s also a very general solution since the secure volume can be used to store arbitrary secure files. This can be a bit of hassle when using per-file two-way encryption.
    This solution also integrates seamlessly with the Mac keychain which makes for a nice workflow.

    Let me know how two-way encryption works out for you, or if you’ve already been using it, please share some details of how you use it in practice.

Leave a Comment

Enclose code in <code lang="ruby"></code> if you care.
Preview your comment using the button below.