When setting up systems, configuring applications and making changes I prefer to script things rather than follow instructions. Main drawback of scripting is that it takes more time but has (in my view) a number of advantages
- Scripting makes things reproducible
- Allows for checking and validation
- (when needed) can produce change logs
- It can be reused when the same change has
However, frequently these scripts contain sensitive information (e.g. passwords) and must be transferred to someone (or another system) so they should not be stored in clear text. This post explains how to encrypt shell scripts using openssl
for cross-platform use.
The actual use case for this is a future post where I describe the script I am working on at this moment. This script generates another script to configure a MacOS machine to authenticate against FreeIPA. As the generated script contains the (generated) password to connect to the LDAP server as well as the Kerberos Keytab for the MacOS machine, I needed these to be encrypted.
A side problem I encountered when working on a solution (which wasn’t that complex in the end) was that the two platforms used different implementation of openssl. Linux uses OpenSSL (v1.1.0i-fips at this moment) while MacOS uses LibreSSL (v2.6.5 at this moment). Due to some difference in the implementation encrypted data from one platform could not be decrypted on the other when using the defaults for aes-256 encryption. It turned out that the two libraries / implementations used different default hash algorithms to create the encryption key from the entered password. By explicitly defining it to use the sha256 algorithm this was resolved.
The basic steps to password-protect a shell script are:
- write / generate the script to be encrypted
- run the script through openssl to encrypt it and base64 encode the result, e.g. with the command:
openssl aes-256-cbc -md sha256 -a -e < in.sh > out.sh
OpenSSL will ask for the password and encrypt the contents ofin.sh
and write it toout.sh
- wrap the output of this step in a shell-script using a so-called here-document, which uses openssl to decrypt the contents and pipes that to a shell, i.e.
#!/bin/sh
openssl aes-256-cbc -md sha256 -a -d << EOS | sh
<base-64 encoded encrypted data from out.sh>
EOS - now by running the generated script the user will be prompted for the password and if the password is correct run the script normally.
The bash
script below is the initial (stand-alone) version I created that worked fine. As already mentioned I will be integrating this into another script but felt it was useful to provide it here and describe this simple approach.
#!/bin/bash -e
die() { echo $*; exit 1; }
[ $# == 0 ] && die "usage: $0 <infile> [outfile]"
[ ! -f "$1" ] && die "Error: $1 does not exist"
FILE=${2:-$1.enc}
[ -f "$FILE" ] && die "Error: $FILE already exists"
CIPHER='aes-256-cbc -md sha256'
cat > $FILE <<EOS
#!/bin/sh
openssl $CIPHER -a -d << EOF | sh
`openssl $CIPHER -a -e < $1`
EOF
EOS
This script expects the name of the file to encrypt (and optionally the output file name) as parameters and does the job. It will refuse to overwrite the output file to minimize the chance of errors. To revert the operation and extract the encrypted file to stdout
run (on Linux):
tail -n +3 out.sh | head -n -1 | openssl aes-256-cbc -md sha256 -a -d
Obviously this is not fail-safe, AES-256-CBC can be cracked though it is still better than sending / using cleartext shell scripts with passwords. If you see any flaws or better ways for this, please let me know through the comments. Thanks!