This post could have been titled, "How We Use OpenPGP to Protect Our Secrets." All the use cases illustrated below use a tool called GnuPG to take care of the crypto details for us. GnuPG is a complete and free implementation of the OpenPGP standard as defined by RFC4880 (also known as PGP). GnuPG features a versatile key management system that allows you to encrypt and sign your data and communication and access modules for all kinds of public key directories. GnuPG, also known as GPG, is a command line tool with features for easy integration with other applications. It supports all the platforms we are interested in: Linux, macOS, Android, Windows, etc.
There are other OpenPGP implementations or libraries that could be used to achieve similar results. The practices described in this article have become standard at Lincoln Loop over the course of several years. During the convergence, we have evaluated the alternatives with the following principles in mind:
- No vendor lock-in
- Industry standard with several competitive implementations
- Cross platform (installed by default on Ubuntu)
- Easy to add or remove participants in the secrets
What is Encryption and Why it is Important?
Encryption is the process of changing information in such a way as to make it unreadable by anyone except those possessing special knowledge (usually referred to as a "key") that allows them to change the information back to its original, readable form.
The importance of encryption lies in the fact that it lets you securely protect data that you don't want just anyone to access. It protects our data when it's sitting on our computers, in data centers, and when it's transmitted across the Internet. This article makes an excellent case for why we should use it.
All the use cases covered below can be reduced to either an
We provide a clear text
secretFile.txt and a list of one or multiple targeted recipients, and we get back an encrypted file
gpg2 --encrypt --recipient 6A8D785C --sign --armor secretFile.txt
The command above is simple, but I want to stress the use of three parameters.
--sign: Sign the encrypted output (so decrypters can verify who the encrypter was). The key to be used for signing is chosen by default or can be set with the --local-user and --default-key options.
--armor: Create ASCII armored output. The default is to create the binary OpenPGP format.
--recipient USER-ID: Encrypt the message for USER-ID. This option can be repeated if you want to encrypt the message for several persons.
We provide an encrypted file
secretFile.txt.asc and we get back a clear text stream on stdout.
gpg2 --decrypt secretFile.txt.asc
Notes 1: GPG also provides an option to hide the recipient
--hidden-recipient. If you decide to use it, you will need to specify the private key when you want to decrypt the file
As of June, on my laptop I have Ubuntu 16.04 installed; I have 2 version of GnuPG installed
gpg2. When I can choose I use
gpg2 so-called modern version instead of
gpg classic version.
yml@carbon$ gpg --version gpg (GnuPG) 1.4.20 Copyright (C) 2015 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Home: ~/.gnupg Supported algorithms: Pubkey: RSA, RSA-E, RSA-S, ELG-E, DSA Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256 Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 Compression: Uncompressed, ZIP, ZLIB, BZIP2 ~ yml@carbon$ gpg2 --version gpg (GnuPG) 2.1.11 libgcrypt 1.6.5 Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Home: ~/.gnupg Supported algorithms: Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256 Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 Compression: Uncompressed, ZIP, ZLIB, BZIP2
GPG Applied to the Enterprise
Now that we have a good understanding of the mechanics, let's see how we can apply it to cover the following higher level use cases needed at a company:
- encrypt / decrypt file
- encrypt / decrypt Server configuration
- password managements
This is a direct application of the procedure above where you need to decide the list of
--recipient you want to encrypt the message to.
You encrypt the message in a way that could be decrypted by each coworker. First, you must import/verify all their public keys.
gpg2 --recv-keys --keyserver=keyserver.ubuntu.com <KEYID_COWORKER1>
gpg2 --encrypt --recipient <KEYID_COWORKER1> --recipient <KEYID_COWORKER2> --sign --armor secretFile.txt
You encrypt the message in a way that it could be decrypted by a private key that is used by one of your app in your infrastructure (Salt, Jenkins, etc.).
gpg2 --encrypt --recipient <KEYID_APP1> --sign --armor secretFile.txt
The private key used by your apps (Salt, Jenkins, etc.) can be encrypted and backed up in a shared folder using the first example. If this key has a password, you should also back it up and share it with the people that might need to use it. Remember that a password protected private key without a password is useless.
Encrypt Server Configuration
At Lincoln Loop, we use Salt to configure our servers. The Salt tree contains sensitive information that may or may not be stored on external services (github.com or bitbucket.org) or transmitted over unsecured networks. To mitigate this risk, salt provides a GPG renderer
This renderer will decrypt GPG ciphers store inside your Salt state tree.
Any key in the SLS file can be a GPG cipher and this renderer will decrypt before passing it off to Salt. This allows you to safely store secrets in source control in such a way that only your Salt master can decrypt them and distribute them only to the minions that need them.
The typical use-case would be to use ciphers in your pillar data and keep a secret key on your master. You can put the public key in source control so that developers can add new secrets quickly and easily.
This renderer requires the GPG binary. No Python libraries are required as of the 2015.8.0 release.
#!yaml|gpg a-secret: | -----BEGIN PGP MESSAGE----- Version: GnuPG v1 hQEMAweRHKaPCfNeAQf9GLTN16hCfXAbPwU6BbBK0unOc7i9/etGuVc5CyU9Q6um [ Truncated for brieviety ] skqmFTbOiA===Eqsm -----END PGP MESSAGE-----
Sharing a password is not a recommended practice in general, but sometimes there is no alternative. Passwords are encrypted in a shared folder. The filename is the account name, and the file content is a text file that contains three elements separated by a
The example above does not keep the filename secret. You are leaking this piece of information to the outside world. This is fine for our use, but you need to evaluate this assumption in the context of the threat model you are working within.
encrypt_cmd : gpg2 --quiet --encrypt --sign --recipient <KEYID_COWORKER1> --recipient <KEYID_COWORKER2> --armor decrypt_cmd : gpg2 --quiet --decrypt
To keep things simple, for some years now we have been using a Python wrapper called kip that is built around this concept.
This barely scratches the surface of what you can do with GPG. Some people are even using it to log in to websites. GPG is a powerful tool that will help you to protect sensitive information. However, you need to be careful with your encryption/decryption practice inside your company and make sure that everyone in your company has a good understanding of GPG and the recommended way to share secrets.
You should decrypt your shared secrets and restore the encrypted private keys on a regular basis to make sure that they aren't corrupted and that you have all the required information to use them.