Our applications need access to secrets – passwords, tokens, special URLs. Platforms like Cloud Foundry and Heroku have made environment variables easy to use, and so we use them. Albeit they are typically not as secretive as we might like.
Here’s a one-liner to look up every secret that you have access to across all applications on a single Cloud Foundry platform:
cf curl /v2/apps\?results-per-page=100 | jq -r ".resources[].entity.environment_json"
Sure, you have authority to look at all those secrets. But should it really be that easy? It’s like being able to open every safe in a vault simultaneously. Do you ever have a real reason to do it? Perhaps there are really only downsides from it being abused?
The next version of Ruby on Rails 5.1 will make it easier to keep secrets more secret: developers can encrypt secrets inside the source code so that they are only available inside the running application process.
To see this in action as of 5.1.0.beta1:
gem install rails -v 5.1.0.beta1
rails new testsekrets
cd testsekrets
The generated scaffolding includes a config/secrets.yml
for storing unencrypted secrets. That is, from now on you would not use this file for production secrets.
Secretive secrets are not enabled by default. Turn them on with:
bin/rails secrets:setup
As per the instructions, store the one-time generated encryption key in 1password, or vault, or wherever your team shares secrets:
As per the instructions, don’t lose this key.
Developers of the application can either:
- Place this key in
config/secrets.yml.key
; or - Set env var
RAILS_MASTER_KEY
to this value
It is the latter that we will also use when deploying to production with Cloud Foundry below.
The file config/secrets.yml.enc
is like a little encrypted database of secrets that you can safely commit with the rest of your source code.
To edit it:
EDITOR=vim bin/rails secrets:edit
The root key of the secret secrets.yml
file is either the applicable RAILS_ENV
environment (such as production
) or shared
for secrets that are available to all runtime environments.
shared:
sharedkey: "I am shared"
production:
mykey: "I am in production"
To access our new production secret, use Testsekrets::Application.secrets
. Try the Rails console:
$ RAILS_ENV=production rails c
> Testsekrets::Application.secrets
=> {:api_key=>123, :secret_key_base=>nil,
:sharedkey=>"I am shared",
:mykey=>"I am in production",
:secret_token=>nil}
There are our additional secrets: :sharedkey
and :mykey
.
The additional keys api_key
and secret_key_base
were merged in from the unencrypted/human readable config/secrets.yml
that was created by rails new
scaffolding.
In development, you will get different secrets:
$ rails c
> Testsekrets::Application.secrets
=> {:api_key=>123,
:secret_key_base=>"f5ed76f34bfa542...",
:secret_token=>nil}
You don’t see :sharedkey
above because the config/environments/development.rb
does not have the following line, but config/environments/production.rb
does:
config.read_encrypted_secrets = true
Deploying to Cloud Foundry
Now, instead of storing secrets in environment variables, we have two changes to encrypt them:
- Use
bin/rails secrets:edit
to store the secrets; and access them within your application viaTestsekrets::Application.secrets[key]
- Pass the secret-keeping encryption key from above via
RAILS_MASTER_KEY
:
cf set-env testsekrets RAILS_MASTER_KEY $(cat config/secrets.yml.key)
cf set-env testsekrets SECRET_KEY_BASE $(rake secret)
NOTE: at the time of writing I wasn’t sure what $SECRET_KEY_BASE
used for; Rafael helped me out on Twitter. I’ve updated the command above.
@drnic secret_key_base is to encrypt session and is there since rails 4, so it is not related to the new secrets configuration.
— Rafael França (@rafaelfranca) February 24, 2017
@drnic rake secret (I think it is that) generates a valid string for that config.
— Rafael França (@rafaelfranca) February 24, 2017
Bonus
If you have Vault in production, then you can also share secrets with Cloud Foundry applications using a Vault service broker, such as https://www.hashicorp.com/blog/cloud-foundry-vault-service-broker/