Encrypted Rails Secrets on Rails 5.1

  

rails secrets encryptionRails already provides a way to handle secrets. This has gotten better in 5.1 with the introduction of encrypted secrets. Find out how to use this feature and what you need to change on your current Rails application.

This post comprises of the following sections:
Secrets Without Encryption
Encrypted Secrets
Reading the Secrets
Advantages of Encrypted Secrets
Managing the Key
A Note on Secret Key Base

Rails Secrets Without Encryption

All Rails applications need to handle secrets. At the very least, you need a secret key base. Some apps need tokens to use third-party APIs. Best practice for handling secrets in Rails is never to commit these secrets in your repository. You don't want anyone who has access or might get access to your codebase to have your credentials as well.

There are 2 ways to handle this.

  1. Read the secrets from environment variables. Commit secrets.yml to your repository.

The default secrets.yml generated by Rails read the secret_key_base from the environment variable SECRET_KEY_BASE. You can follow this pattern when adding your other secrets.

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  my_secret_token: <%= ENV["MY_SECRET_TOKEN"] %>

Using environment variables is convenient but it's not the safest way to handle secrets. A process running on your server can access the environment variables. A logging library used by the Rails app might transmit your secrets to external services.

2. Put your secrets on secrets.yml. Do not commit the file to your repository.

You don't want anyone who has access to your codebase to see your secrets. If you put your secrets directly on secrets.yml instead of using environment variables, you need to upload the file to your servers through other means.

This option is safe as long as you can securely put secrets.yml on your servers. This can be done through scp or sftp. If you're an advanced user, you can also use something like Chef Data Bags or Vault.

Encrypted Secrets

Starting with Rails 5.1, you can encrypt your secrets. Without the encryption key, you won't be able to read them. All you'll see is a bunch of characters. It's safe then to commit the encrypted file to your repository. Never commit the encryption key.

Encrypted secrets provides a few advantages over the two options above. Before we list them, let's take a look at how to use it.

This feature is off by default. If you're using Rails 5.1 and didn't set up encrypted secrets then you're not using it. To start using encrypted secrets, you need to generate a key. On your Rails root directory, run

bin/rails secrets:setup

This command creates 2 files. config/secrets.yml.key contains the key that will encrypt and decrypt your secrets. This file should not be committed to your repository. config/secrets.yml.enc contains the encrypted secrets. You can commit this file to your repository. If you get a copy of config/secrets.yml.enc but you don't have the key you won't be able to decrypt it.

After creating the key, even though config/secrets.yml.enc is not empty, it doesn't contain any secrets. To add your secrets, run

bin/rails secrets:edit

Your text editor will open an unencrypted version of your secrets (which in the beginning is just comments), similar to what you see on your secrets.yml. If you don't have EDITOR set, you can run EDITOR=vi bin/rails secrets:edit or use your favorite text editor.

You can move all of your keys from secrets.yml and delete secrets.yml. You can also move the production secrets only and keep using secrets.yml for development and test environments. If you have production secrets on both secrets.yml and secrets.yml.enc they will be merged.

After saving the file, the encrypted version will be saved to config/secrets.yml.enc.

Reading the Secrets

The Rails app will not read the secrets automatically even if you have the key and encrypted secrets in place. You need to add

config.read_encrypted_secrets = true

The setup command added this for you on config/environments/production.rb. If you want to test encrypted secrets in development, add the code above to config/environments/development.rb.

The secrets are available on Rails.application.secrets as always.

If you want to decrypt the secrets and show them on the command line, you can run

bin/rails runner "puts Rails::Secrets.read"

Advantages of Encrypted Secrets
  1. First the obvious: your secrets are encrypted. No one will be able to decrypt your secrets without the key.
  2. The encrypted secrets are saved in your repository. You'll have a history of the changes and who made them.
  3. You deploy a new secret together with your code. If you add code to access an API for example, you can deploy that code together with the token on secrets.yml.enc. Previously, you'll have to make sure you add the environment variable before deploying the new code.
  4. All secrets are in one location. Instead of managing multiple environment variables, everything is in one file.


Managing the Key

While using encrypted secrets provides a number of advantages over unencrypted secrets, we still have to find a way to put the key in place. Our options are similar to handling secrets pre-Rails 5.1. You cannot save the key to your repository because someone who can access your code can then decrypt the secrets.

1. Upload secrets.yml.key securely.

You can scp or sftp the file. In Engine Yard, you only have to do this once. When you scale your environment, the new servers will get a copy of secrets.yml.key.

Upload the key on a shared directory. Shared here means shared between releases, not a shared filesystem. On each deploy, you symlink config/secrets.yml.key to /path/to/shared/config/secrets.yml.key. You can do this on a deploy hook on Engine Yard or in Capistrano.

If you need to give a developer a copy of the key, never send it via email (unless you're using encrypted emails which most of us don't!) You can use a password manager because they use encryption.

2. Put the key on the RAILS_MASTER_KEY environment variable.

In some cases where you can't upload a file, this is the only option. Even though this is convenient, take note of the security risks mentioned above. The risks can be mitigated, but if you can upload secrets.yml.key then use that option.


A Note on Secret Key Base

The key used to encrypt secrets is different from the secret key base. The key on secrets.yml.key is used to encrypt and decrypt all secrets. It does not replace the secret key base.

The secret key base is only one of the secrets your app can use. It is however required by Rails. If you want to generate a new secret key base run,

bin/rails secret

This command is different from the encrypted secrets commands which are bin/rails secrets:setup and bin/rails secrets:edit. Note that the encrypted secrets feature uses the secrets (plural) command not secret.

Conclusion

Encrypted secrets offers a few advantages over plaintext credentials or environment variables on secrets.yml. Rails like many times in the past has provided us a simple workflow. Try the encrypted secrets feature and let us know in the comments what you think. Thank you to all Rails contributors who made this feature possible.

Free Ebook:
Should I Hire DevOps or Outsource to a Provider?

You have to invest in your infrastructure: Do you hire DevOps for this critical function, assign it to your already overworked engineers, or outsource to a provider that offers full-stack capabilities?

Should I Hire DevOps?

Christopher Rigor

 
DevOps Support Manager, Asia-Pacific at Engine Yard. Organizer of @RubyConfPH. Speaker. Interested in automation, Kubernetes, Docker, Deis, ops, Ruby.
Find me on:

Comments

Subscribe Here!