Rails 5.2: encrypted secrets

March 22, 2018


How we started to use Rails 5.2 Encrypted Secrets for securing Amazon S3 key pairs for use with Active Storage.

Here is a useful starting point: Rails Encrypted Credentials on Rails 5.2

Our development and production envionment is Linux, and we precompile our assets before deployment.

Part 1: Encrypt your secrets

With Rails 5.2 a sort of ‘password safe’ is introduced, whereby you commit your encrypted secret credentials to source control, but not your master password.

To encrypt your secret credentials, you execute the $ rails credentials:edit command in your terminal. It is useful to read the help text that goes with it:

$ rails credentials:help
Usage:
  bin/rails credentials [options]

=== Storing Encrypted Credentials in Source Control

The Rails credentials commands provide access to encrypted credentials, so you can safely store access tokens, database passwords, and the like safely inside the app without relying on a mess of ENVs.

This also allows for atomic deploys: no need to coordinate key changes to get everything working as the keys are shipped with the code.

=== Setup

Applications after Rails 5.2 automatically have a basic credentials file generated that just contains the secret_key_base used by MessageVerifiers/MessageEncryptors, like the ones signing and encrypting cookies.

For applications created prior to Rails 5.2, we’ll automatically generate a new credentials file in config/credentials.yml.enc the first time you run bin/rails credentials:edit. If you didn’t have a master key saved in config/master.key, that’ll be created too.

Don’t lose this master key! Put it in a password manager your team can access. Should you lose it no one, including you, will be able to access any encrypted credentials.

Don’t commit the key! Add config/master.key to your source control’s ignore file. If you use Git, Rails handles this for you.

Rails also looks for the master key in ENV["RAILS_MASTER_KEY"], if that’s easier to manage.

You could prepend that to your server’s start command like this:

RAILS_MASTER_KEY=“very-secret-and-secure” server.start

=== Editing Credentials

This will open a temporary file in $EDITOR with the decrypted contents to edit the encrypted credentials.

When the temporary file is next saved the contents are encrypted and written to config/credentials.yml.enc while the file itself is destroyed to prevent credentials from leaking.

On Linux, to have $ rails credentials:edit open our editor (pluma) and wait for us to edit the contents, we run this:

$ EDITOR="time pluma" rails credentials:edit

‘time’ makes it wait while we edit and save the file. You can substitute ‘pluma’ with whatever editor you use. The file opens in your editor, and we edited ours to look like this:

#credentials.yml.enc.12345
aws:
  access_key_id: QWERJKLQCMCNVTNDSIFEGJS
  secret_access_key: gtmmnYKdV4GGU9bj7QpBFIia8kpfXfuNR+MBgKeB

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: tis2CaJpYDRuw0CkpFY15d3f4u1VyGudZxhYHTyDtis2CaJpYDRuw0CkpFY15d3f4u1VyGudZxhYHTyDtis2CaJpYDRuw0CkpFY15d3f4u1VyGudZxhYHTyDgfhfghht

and save the file:

New credentials encrypted and saved.

Note that some indentation errors such as one additional space before the ‘aws’ in the above example may fail silently and your file could be incorrectly encrypted, so take care to get it right. This happened to us when we later got this error:

ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`

Part 2: Update your configuration(s)

To use the credentials in the production environment, add the following to config/environments/production.rb

#config/environments/production.rb
...
config.require_master_key = true
...

Update anywhere where the unencrypted credentials are configured, for example we changed config/storage.yml to look like this:

# config/storage.yml
amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: us-west-1
  bucket: www.mySite.com

You will need to add an environment key value pair in your production environment the value can be found in ‘config/master.key’.

RAILS_MASTER_KEY=rzW47Pnq8825x9jpc6vRwsi7jo3mLJ4q

Part 3: Precompile assets and deploy

On precompiling the assets for production, we got this problem:

$ RAILS_ENV=production rake assets:clean assets:precompile
rake aborted!
ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`

The solution was to re-edit and save the credentials to correct an indentation error, as noted above.

To conclude

It worked!

Encrypted secrets makes it easier to keep third party access tokens, database passwords, and the like safely inside the app without relying on a mess of environment variables.

It also solved our Amazon S3 access problems with [Active Storage direct upload] (/post/active-storage-on-amazon-s3).

This is a move towards atomic deployment: keys can now be shipped with the code.

© 2020 Keith P | Follow on Twitter | Git