Search code examples
bashpermissionscontainersalpine-linuxdocker-container

How to work with private keys securely in bash?


How to securely work with private keys in a bash script? My first attempt involves storing the encrypted key pair in a field in the database(mongo). However, I've since realised that I will have to use these keys in bash scripts, after being decrypted, in order to scp or ssh(key has to be a file so this will also increase IO).

This occurs inside an alpine linux container. Should I be concerned about using echo on a string containing a key?

I've also considered storing the keys in a container volume as files (id_rsa, id_rsa.pub) and referencing those paths in the database(or hardcoding a path). I am working with very many key pairs which go from DB to UI to API(3 separate containers) to BASH(in the API container)to create a connection and perform functions on each of these hosts. Which helps with the IO problem.

Is there a better way to do this? Should I be using some sort of keystore that I can store references for inside my DB?

In order of importance, Secure - Low IO - High Efficiency.


Solution

  • Thanks @Philippe for suggesting the use of PGP! My solution is:

    Create a master key using this script:

    #!/bin/bash
    cd ~/
    mv .gnupg .gnupg.bak
    echo "Existing keys were moved to '.gnupg.bak'."
    # rm -rf .gnupg
    mkdir -m 0700 .gnupg
    touch .gnupg/gpg.conf
    chmod 600 .gnupg/gpg.conf
    echo << EOF >  .gnupg/gpg.conf
    ################################################################################
    # GnuPG Options
    
    # (OpenPGP-Configuration-Options)
    # Assume that command line arguments are given as UTF8 strings.
    utf8-strings
    
    # (OpenPGP-Protocol-Options)
    # Set the list of personal digest/cipher/compression preferences. This allows 
    # the user to safely override the algorithm chosen by the recipient key 
    # preferences, as GPG will only select an algorithm that is usable by all 
    # recipients.
    personal-digest-preferences SHA512 SHA384 SHA256 SHA224
    personal-cipher-preferences AES256 AES192 AES CAST5 CAMELLIA192 BLOWFISH TWOFISH CAMELLIA128 3DES
    personal-compress-preferences ZLIB BZIP2 ZIP
    
    # Set the list of default preferences to string. This preference list is used 
    # for new keys and becomes the default for "setpref" in the edit menu. 
    default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
    
    # (OpenPGP-Esoteric-Options)
    # Use name as the message digest algorithm used when signing a key. Running the 
    # program with the command --version yields a list of supported algorithms. Be 
    # aware that if you choose an algorithm that GnuPG supports but other OpenPGP 
    # implementations do not, then some users will not be able to use the key 
    # signatures you make, or quite possibly your entire key.
    # 
    # SHA-1 is the only algorithm specified for OpenPGP V4. By changing the 
    # cert-digest-algo, the OpenPGP V4 specification is not met but with even 
    # GnuPG 1.4.10 (release 2009) supporting SHA-2 algorithm, this should be safe.
    # Source: https://tools.ietf.org/html/rfc4880#section-12.2
    cert-digest-algo SHA512
    digest-algo SHA256
    
    # Selects how passphrases for symmetric encryption are mangled. 3 (the default) 
    # iterates the whole process a number of times (see --s2k-count).
    s2k-mode 3
    
    # (OpenPGP-Protocol-Options)
    # Use name as the cipher algorithm for symmetric encryption with a passphrase 
    # if --personal-cipher-preferences and --cipher-algo are not given. The 
    # default is AES-128. 
    s2k-cipher-algo AES256
    
    # (OpenPGP-Protocol-Options)
    # Use name as the digest algorithm used to mangle the passphrases for symmetric 
    # encryption. The default is SHA-1. 
    s2k-digest-algo SHA512
    
    # (OpenPGP-Protocol-Options)
    # Specify how many times the passphrases mangling for symmetric encryption is 
    # repeated. This value may range between 1024 and 65011712 inclusive. The 
    # default is inquired from gpg-agent. Note that not all values in the 
    # 1024-65011712 range are legal and if an illegal value is selected, GnuPG will 
    # round up to the nearest legal value. This option is only meaningful if 
    # --s2k-mode is set to the default of 3. 
    s2k-count 1015808
    
    ################################################################################
    # GnuPG View Options
    
    # Select how to display key IDs. "long" is the more accurate (but less 
    # convenient) 16-character key ID. Add an "0x" to include an "0x" at the 
    # beginning of the key ID.
    keyid-format 0xlong
    
    # List all keys with their fingerprints. This is the same output as --list-keys 
    # but with the additional output of a line with the fingerprint. If this 
    # command is given twice, the fingerprints of all secondary keys are listed too.
    with-fingerprint
    with-fingerprint
    
    # Use gpg-agent for SSH instead of ssh-agent
    use-agent
    EOF
    
    cd .gnupg
    
    cat >master <<EOF
        %echo Generating Master PGP key
        Key-Type: RSA
        Key-Length: 4096
        Subkey-Type: RSA
        Subkey-Length: 4096
        Name-Real: Master
        Name-Comment: Master
        Name-Email: [email protected]
        Expire-Date: 0
        %no-ask-passphrase
        %no-protection
        %pubring pubring.kbx
        %secring trustdb.gpg
        %commit
    EOF
    
    gpg2 --verbose --no-greeting --batch --gen-key master
    echo -e "5\ny\n" |  gpg2 --command-fd 0 --expert --no-greeting --batch --edit-key [email protected] trust;
    echo "Master key has been created and verified. :)" > TESTFILE
    gpg2 -e -a -r [email protected] TESTFILE
    rm TESTFILE
    rm master
    gpg2 -d TESTFILE.asc
    rm TESTFILE.asc
    

    Create sub keys using this script:

    #!/bin/bash
    echo -e "addkey\n8\nS\nA\nQ\n4096\n0\nsave" |  gpg2 \
        --command-fd 0 --verbose --no-greeting --expert --edit-key --batch --passphrase "" [email protected];
    fingerprints=$(gpg2 --list-keys --with-subkey-fingerprint | grep "      " | sed 's/      //g')
    fingerprint=$(echo $fingerprints | awk '{print $NF}')
    publicId=$(echo ${fingerprint: -8})
    fingerprint=$(echo $fingerprints | awk '{print $1}')
    privateId=$(echo ${fingerprint: -8})
    echo "Using master key with ID:"
    echo $privateId
    echo "Sub key added with ID:"
    echo $publicId
    

    and storing the IDs printed from that last script in mongo, master as 'private' and sub as 'public'.

    Sending public auth keys to remote hosts with gpg2 --export-secret-key $ID | ssh $REMOTE_MACHINE gpg2 --import