Recently I set up Bitnami Cloud Tools for AWS to facilitate AWS configuration and use from the command line. After creating an administrative IAM (as not to use the main AWS login), and created and uploaded/associated the necessary X.509 credentials for that IAM login, I realized that anyone who would gain access to the local dev server would also gain full access to several AWS Virtual Private Cloud configurations. Not a terribly likely occurrence, but would I like to risk it? Say, when I have the cloud tools configured on Ubuntu on my laptop, someone could conceivably steal the laptop, and with a little technical expertise, gain access to the Ubuntu instance (running in a VM), and hence to the AWS VPCs.
At least in this case having the IAM credentials and the X.509 keys on a USB drive would be impractical (and would probably increase the likelihood that the keys would get misplaced and end up in the wrong hands). On Windows it’s a simple task to set up an encrypted vault using one of many available utilities to achieve such. But how to do that on Linux? After some digging I came across a Wiki entry Ubuntu: Make a secure vault. It worked fine, but via cut-and-paste that appeared rather cumbersome for daily operations. So I set out to write couple of scripts to make things easier.
First, you need to have cryptsetup package installed. Then you can make use of the setup-crypt script below. These scripts are quick utility scripts that don’t have a separate configuration file; you may want to edit some of the variables on top of the script, namely “CRYPT_HOME” (depending on where you want to place your encrypted vault file), “CRYPT_MOUNTPOINT” (depending on where you want to mount it), and “CRYPT_DISK_SIZE” (the capacity of the encrypted vault in megabytes).
#!/bin/bash CRYPT_HOME=/root/crypto CRYPT_DISK=cryptdisk CRYPT_DISK_FQFN=${CRYPT_HOME}/${CRYPT_DISK} CRYPT_DISK_SIZE=64 # size in megabytes CRYPT_LABEL=crypt-disk CRYPT_MOUNTPOINT=/mnt/crypto LOOPBACK_DEVICE=`losetup -f` CRYPTSETUP=`which cryptsetup` if [ $? -ne 0 ] ; then echo "ERROR - cryptsetup not found! Install it first with 'apt-get install cryptsetup'." exit 1 fi IAM=`whoami` if [ ! "${IAM}" = "root" ]; then echo "ERROR - Must be root to continue." exit 1 fi SETUP_INCOMPLETE=true function cleanup { if [ ! "$1" = "called" ] && [ ! "$1" = "nodelete" ]; then echo echo echo "Crypto-disk setup interrupted. Cleaning up." fi if [ -b /dev/mapper/${CRYPT_LABEL} ]; then cryptsetup luksClose /dev/mapper/${CRYPT_LABEL} fi losetup -d ${LOOPBACK_DEVICE} > /dev/null 2>&1 if [ "$1" = "nodelete" ]; then exit 0 else rm -rf ${CRYPT_HOME} exit 1 fi } mkdir ${CRYPT_HOME} > /dev/null 2>&1 # Capture errors if [ $? -ne 0 ]; then if [ -d ${CRYPT_HOME} ]; then REASON="Directory already exists." else REASON="" fi echo "ERROR - Could not create directory '${CRYPT_HOME}'. ${REASON}" echo "Continuing..." else echo echo "OK - '${CRYPT_HOME}' directory created." fi cd /root/crypto if [ -f $CRYPT_DISK_FQFN ]; then echo "ERROR - Crypt disk already exists. Cannot continue." exit 1 fi trap cleanup INT dd if=/dev/zero of=cryptdisk bs=1M count=${CRYPT_DISK_SIZE} # Capture errors if [ $? -ne 0 ]; then echo "ERROR - Could not create raw container. Cannot continue." cleanup called exit 1 else echo echo "OK - ${CRYPT_DISK_SIZE}MB raw device created." fi losetup ${LOOPBACK_DEVICE} ${CRYPT_DISK_FQFN} # Capture errors if [ $? -ne 0 ] then echo "ERROR - Loopback device in use. Cannot continue." cleanup called exit 1 fi cryptsetup luksFormat ${LOOPBACK_DEVICE} # Capture errors if [ $? -ne 0 ] then echo "ERROR - Could not format the raw container. Cannot continue." cleanup called exit 1 fi echo echo "NOTE: Use the same password you set above!" cryptsetup luksOpen ${LOOPBACK_DEVICE} ${CRYPT_LABEL} # Capture errors if [ $? -ne 0 ]; then echo "ERROR - Could not open LUKS CryptoFS. Cannot continue." cleanup called exit 1 else echo "OK - LUKS CryptoFS Opened." fi mkfs.ext4 /dev/mapper/${CRYPT_LABEL} # Capture errors if [ $? -ne 0 ] then echo "ERROR - File system creation failed. Cannot continue." cleanup called else echo "OK - Encrypted file system created." echo "Closing handles." cleanup nodelete exit 0 fi
After you save the above script to a file, and make the file executable (chmod 500 filename), you’re good to go. If you don’t want the encrypted vault file located at /root/crypto/, or want a vault of a different size than the rather small default of 64MB (I’m just saving a handful of AWS keys, so I didn’t need a larger vault file), edit the variables on top of the script before running it. Once started, follow the prompts and the encrypted vault file is created for you. If an error occurs during the vault creation process, if the vault file already exists, or if you cancel the script, any changes made up to that point are rolled back.
To mount and access the vault, save the following two scripts for mounting and unmounting the vault respectively:
#!/bin/bash CRYPT_MOUNTPOINT=/mnt/crypto CRYPT_DISK_FQFN=/root/crypto/cryptdisk CRYPT_LABEL=crypt-disk LOOPBACK_DEVICE=`losetup -f` if [ ! -f ${CRYPT_DISK_FQFN} ]; then echo "Crypt disk '${CRYPT_DISK_FQFN}' missing. Cannot continue." exit 1 fi if [ ! -d ${CRYPT_MOUNTPOINT} ]; then echo "Mountpoint '${CRYPT_MOUNTPOINT}' missing. Cannot continue." exit 1 fi function check_mounted { if grep -qsE "^[^ ]+ $1" /proc/mounts; then _RET=true else _RET=false fi } check_mounted $CRYPT_MOUNTPOINT if ${_RET} ; then echo "Mountpoint '${CRYPT_MOUNTPOINT}' already mounted. Cannot continue." exit 1 fi losetup ${LOOPBACK_DEVICE} ${CRYPT_DISK_FQFN} > /dev/null 2>&1 # Capture errors if [ $? -ne 0 ]; then echo "ERROR - Loopback device in use." exit 1 else echo "OK - Loopback device mapped." fi cryptsetup luksOpen ${LOOPBACK_DEVICE} ${CRYPT_LABEL} > /dev/null 2>&1 # Capture errors if [ $? -ne 0 ]; then echo "ERROR Opening LUKS CryptoFS. Removing the loopback device." losetup -d ${LOOPBACK_DEVICE} exit 1 else echo "OK - LUKS CryptoFS Opened." fi mount /dev/mapper/${CRYPT_LABEL} ${CRYPT_MOUNTPOINT} > /dev/null 2>&1 # Capture errors if [ $? -ne 0 ]; then echo "ERROR mounting CryptoFS." cryptsetup luksClose /dev/mapper/${CRYPT_LABEL} losetup -d ${LOOPBACK_DEVICE} exit 1 else echo "OK - Mounted CryptoFS." exit 0 fi
#!/bin/bash CRYPT_MOUNTPOINT=/mnt/crypto CRYPT_DISK=/root/crypto/cryptdisk CRYPT_LABEL=crypt-disk LOOPBACK_DEVICE=`losetup -j ${CRYPT_DISK} | awk '{print $1}' | sed '$s/.$//'` CAN_RELEASE=true if grep -qsE "^[^ ]+ ${CRYPT_MOUNTPOINT}" /proc/mounts; then umount ${CRYPT_MOUNTPOINT} > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "WARNING - Could not unmount ${CRYPT_MOUNTPOINT}! Device busy." CAN_RELEASE=false else echo "Crypto-disk was unmounted." fi else echo "Crypto-disk was not mounted." fi if $CAN_RELEASE; then if [ -b /dev/mapper/${CRYPT_LABEL} ]; then cryptsetup luksClose /dev/mapper/${CRYPT_LABEL} > /dev/null 2>&1 fi losetup -d ${LOOPBACK_DEVICE} > /dev/null 2>&1 fi
Similarly make these scripts executable before running them. If you modified the encrypted vault location/name, or the mount point location during the creation process, you’ll want to make corresponding changes the the variable atop these scripts.
You can place these utility scripts in /usr/local/bin or other location on your path (or symlink from a location on your path) to avoid having to type the full path every time.
With the encrypted vault created using setup-crypt, you can then mount the vault using mount-crypt and access the contents of the vault at /mnt/crypto, and finally unmount the vault with umount-crypt. Since the vault is protected by a single passoword, be sure to set an appropriately safe password to match the required security level.
To further improve the security, you probably want to unmount the vault whenever you’re not logged in. Most likely contents of a vault such as this are intended for interactive use. You can always unmount and hence “lock” the vault with umount-crypt command, but it is a good idea to run umount-crypt automatically at logout. Depending on your shell you can crete/edit .zslogout (zsh), .bash_logout (bash), or .logout (tcsh/csh) at the user home directory (likely in “/root” since opening/closing loopback handles can only be done by the root), and place the following code in it:
#!/bin/zsh
# NOTE: You need to adjust the path to the login shell above
/opt/crypto/umount-crypt
I also close the vault at system shutdown/reboot, by symlinking the following from /etc/rc6.d/S40umount-crypto:
#!/bin/bash # # umount-crypto - Unmounts a crypto-drive if mounted # -> convenience script to be called in the shutdown/reboot sequence of Ubuntu # from /etc/rc6.d, e.g. as "/etc/rc6.d/S40umount-crypto" start() { echo "umount-crypto: nothing to do!" } stop() { echo "Unmounting LUKS CryptoFS filesystem..." umount /mnt/crypto> /dev/null 2>&1 cryptsetup luksClose /dev/mapper/crypt-disk > /dev/null 2>&1 losetup -d /dev/loop0 > /dev/null 2>&1 } status() { echo "No status available." } restart() { echo "restart ..." start } reload() { echo "start ..." start } force_reload() { echo "force-reload ..." start } case $1 in start) start ;; stop) stop ;; status) status ;; restart) restart ;; reload) reload ;; force-reload) force_reload ;; *) echo "This is a non-interactive crypto-disk unmount script." ;; esac exit 0
And that’s all there is to it! With your files safely inside a locked, encrypted vault, only you and the NSA have access to them! 😉
P.S.
To utilize the vault with Bitnami Cloud Tools, I have created folders for each AWS account I want to access under /mnt/crypto/, e.g. /mnt/crypto/aws_account_a, /mnt/crypto/aws_account_b, etc. Each folder contains similarly named files (as found in bitnami-awstools-x.x-x/config folder), like so:
aws-config.txt
aws-credentials.txt
ec2.crt
ec2.key
To switch from account to another I (re-)symlink the contents of the desired account from bitnami-awstools-x.x-x/config/, for example:
ln -sf /mnt/crypto/aws_account_b/* /opt/bitnami-awstools-x.x-x/config/
This way, once the vault is locked, the access to any and all of the AWS accounts via cloud tools goes away. Switching between the accounts could, of course, be scripted easily as well.