Experimenting with vault

One problem I’ve been strugglinging with over the past few months is how to configure a secure way to set and get variables for my various environments. I’ve considered building a tool for this (one that you’ll probably hear about soon) but while deciding what the MVP should be I realized that I might be able to strap a few different tools together to get the result I’m looking for.

Vault is a…. security management framework built by hashicorp, and Consul is a distributed database / service discovery service.

From a high level, I need the ability to run deploy $environment and have the environment deployed. At the moment I have [a make script based system]() that can do this, but it’s limited to getting it’s inputs from environment variables.

Without changing the deploy script, how can I pipe in new variables? (hopefully) consul + vault + envconsul to the rescue!

OK, let’s get consul & vault setup and talking to each other then let’s get some variables set.

So first I’ll go ahead and create a folder with a docker-compose.yml config (great for prototyping networked services) which looks like this

Then I export the variables for vault and check it’s status

# using 8250 because in dev mode it auto creates a listener on 8250 apparently!
$ export VAULT_ADDR=

$ vault status                                                                                        
Sealed: false
Key Shares: 1
Key Threshold: 1
Unseal Progress: 0
Version: Vault v0.6.1
Cluster Name: vault-cluster-5a34b707
Cluster ID: bbe6cfc4-42b1-69c1-88a0-ce2914ce4d0b

High-Availability Enabled: true
	Mode: active

Great, looks like it’s working.

OK, now let’s get envconsul working

For testing, I’ll add a busybox service to my docker-compose file, and mount the envconsul.hcl file I’ll need to create inside of it. Since the normal busybox image doesn’t have ca-certs or openssl and I need to download envconsul, I’ll use a different image like so

# docker-compose.yml
  image: yikaus/alpine-bash
  command: /bin/bash
    - consul
    - vault
$ docker-compose run busybox
bash-4.3# apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/community/x86_64/APKINDEX.tar.gz
v3.4.6-17-gbd473fa [http://dl-cdn.alpinelinux.org/alpine/v3.4/main]
v3.4.6-18-g07184c8 [http://dl-cdn.alpinelinux.org/alpine/v3.4/community]
OK: 5978 distinct packages available

bash-4.3# apk add wget openssl ca-certificates
(1/3) Installing ca-certificates (20160104-r4)
(2/3) Installing openssl (1.0.2j-r0)
(3/3) Installing wget (1.18-r0)
Executing busybox-1.24.2-r9.trigger
Executing ca-certificates-20160104-r4.trigger
OK: 15 MiB in 19 packages

bash-4.3# wget https://releases.hashicorp.com/envconsul/0.6.1/envconsul_0.6.1_linux_amd64.zip
--2016-12-08 02:48:57--  https://releases.hashicorp.com/envconsul/0.6.1/envconsul_0.6.1_linux_amd64.zip
Resolving releases.hashicorp.com...
Connecting to releases.hashicorp.com||:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3341174 (3.2M) [application/zip]
Saving to: 'envconsul_0.6.1_linux_amd64.zip'

envconsul_0.6.1_linux_amd64.zip        100%[===========================================================================>]   3.19M  13.6MB/s    in 0.2s

2016-12-08 02:48:57 (13.6 MB/s) - 'envconsul_0.6.1_linux_amd64.zip' saved [3341174/3341174]

bash-4.3# unzip envconsul_0.6.1_linux_amd64.zip
Archive:  envconsul_0.6.1_linux_amd64.zip
  inflating: envconsul

With that all setup I can now do this

bash-4.3# ./envconsul -consul consul:8500 -prefix cluster/production -once -sanitize -upcase env
no_proxy=*.local, 169.254/16

Now of course, those redis keys at the bottom came from me adding them (I used the ui at consul:8500/ui) But you can see how this could be useful with my makefile if I did envconsul make $environment deploy



Now that I’ve shown you a toy example, I’m sure you’re curious as to how we could use this in a real production environment.

Well for my particular use case, my company has our infrastructure setup something like this

    mesos_master_ip: some-value
      - logging (cluster wide)
      - redis (cluster wide)
      - postgres (cluster wide)

          - RAILS_ENV: staging
          - DEFAULT_HOST_NAME: partner-staging.mycomapny.com
          - DATABASE_URL: postgres://$(database)/some-databasename
          - JWT_SECRET: 1234abc # no really secret in staging but anyway

          - RAILS_ENV: production
          - DEFAULT_HOST_NAME: partner.mycomapny.com
          - JWT_SECRET: secret
          - DATABASE_USERNAME: secret
          - DATABASE_PASSWORD: secret
          PRIVATE_DATABASE: postgres:9.4