View on GitHub

Crypt

Store and retrieve encrypted configuration parameters from etcd or consul

Download this project as a .zip file Download this project as a tar.gz file

Watch Kelsey explain crypt in this quick 5 minute video: Crypt Demonstration 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.