Watch Kelsey explain crypt
in this quick 5 minute video:
Fess up.
You have passwords and usernames hard coded in your apps. You have IP addresses checked in to your source code repository. You have entire configuration files that were created by the developer who wrote the app and haven't been changed since she typed git init
.
crypt
crypt
is here to lead you back to the Path of Enlightened Configuration. Store encrypted configuration values in etcd or consul using a command line application.
Decrypt them before starting your application using a wrapper script and the handy CLI tool, or inside the app using the crypt/config
library.
Standards
crypt
is built on time-tested standards like OpenPGP, base64, and gzip. Your data is encrypted using public key encryption, and can only be decrypted by when the private key is available. After compression, it is encrypted, and base64 encoded so it can be stored in your key/value store of choice. We support etcd and consul out of the box, but adding other storage tools is a trivial task, thanks to Go's interfaces.
How it works
crypt
compresses, encrypts, then encodes your value for storage, then puts it at the location you specify on your key/value store. To encrypt, you'll need access to one or more public keys.
In order to decrypt a value, your application will need access to the private key. We recommend putting the private key on the server and protecting it with standard host level security. Then make sure your application runs under the context of a named user on your system, and that user is the only user with access to the private key(ring). If you're running within Docker, just mount a volume from the directory with the private key. ** Don't put your private key in your Docker container. Seriously. Just don't. **
Usage
You can use crypt as a command line tool or as a configuration library:
Generating gpg keys and keyrings
The crypt cli and config package require gpg keyrings. ** Read about them here before you blindly follow a tutorial written by someone you don't know and shouldn't trust. **
Create a key and keyring from a batch file
vim myapp.batch
%echo Generating a configuration OpenPGP key
Key-Type: default
Subkey-Type: default
Name-Real: myapp
Name-Comment: myapp configuration key
Name-Email: app@example.com
Expire-Date: 0
%pubring .pubring.gpg
%secring .secring.gpg
%commit
%echo done
Run the following command:
gpg2 --batch --armor --gen-key myapp.batch
You should now have two keyrings, .pubring.gpg
which contains the public keys, and .secring.gpg
which contains the private keys. This is a place where you can get fancy -- create different keyrings for different environments. Make one for your production environment that the developers don't have access to. Remember separation of duties to keep your data safe.
Note the private key is not protected by a passphrase.
Create a configuration key
You can encrypt a single value, or an entire configuration file. crypt
will read the contents of the file you specify and encrypt it, then store it in your data store.
** Keep your configuration values atomic *** Don't set the database username in one key and the password in another key. Most k/v stores don't support transactions, so you might get the change to one, but not the other. Instead, keep related configurations together in a single file, and write/read them atomically.
Here's how to encrypt a file called config.json
and store it at /myapp/config in etcd:
crypt set -backend etcd -endpoint http://127.0.0.1:4001 -keyring pubring.gpg /myapp/config config.json
To retrieve it on the command line:
crypt get -backend etcd -endpoint http://127.0.0.1:4001 -keyring pubring.gpg > config.json
Or inside your application:
package main
import (
"fmt"
"log"
"os"
"github.com/bketelsen/crypt/config"
)
var (
key = "/myapp/config"
secretKeyring = ".secring.gpg"
)
func main() {
kr, err := os.Open(secretKeyring)
if err != nil {
log.Fatal(err)
}
defer kr.Close()
machines := []string{"http://127.0.0.1:4001"}
cm, err := config.NewEtcdConfigManager(machines, kr)
if err != nil {
log.Fatal(err)
}
value, err := cm.Get(key)
if err != nil {
log.Fatal(err)
}
// now use the data
//fmt.Printf("%s\n", value)
// var config MyConfigStruct
// err := json.Unmarshal(value, &config)
}
Security Note
Remember your data is only as secure as your private key. Do some research and figure out a safe and secure way to keep your key away.
Contributing
We'll happily accept contributions for new storage backends (mongodb?) and link to library implementations in other languages. Fork, and send a clean PR in a new branch.
License
The MIT License (MIT)
Copyright (c) 2014 XOR Data Exchange, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.