Context

Rails 5.2 has been released not long ago with dozens of new features introduced, among the highlighted ones are ActiveStorage and new encrypted credentials. ActiveStorage might not been used for every developers, but encrypted credentials has changed how secret keys work in Rails applications and it'll affect the deployment process.

Let's see what's the key difference of the credentials management between Rails 5.2 and previous versions:

Before

  • Use of secret_key_base in config/secrets.yml
  • Every credentials have to be configured in environment variables to keep the credentials secure.
  • The environment variables are all being called in config/secrets.yml

After

  • The credentials are encrypted in local with the use of config/master.key. config/master.key is not commited to repo and it's ignored in git by default.
  • The encrypted credentials commited to repo is config/credentials.yml.enc
  • To configure the credentials, use $ rails credentials:edit or $ EDITOR=vim rails credentials:edit if EDITOR is not configured.

Problem

Problem comes in when config/master.key is not commited in the repo. When config/master.key is not found in the production server, the Rails app in the server couldn't decrypt the credentials thus the credentials couldn't be used.

We failed to deploy to production due to this error:

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

Solution

There are two ways to put the master key to production server:

  1. Use of environment keys (ENV[RAILS_MASTER_KEY])
  2. Copy config/master.key to your server manually without commiting to git

Solution #1 didn't work out well for capistrano in our case, thus we have used solution #2 for this app. Copying the file to production server that's being managed by capistrano means I have to configure something so that I don't copy the same key to server everytime we deploy the application. These are the steps to deploy the application successfully for the first time:

  1. Copy config/master.key in your local to the production server under <project_root>/shared/config/master.key. If you don't have the key, please get it from your colleagues or whoever that initialized the Rails app.
  2. Configure your capistrano's config/deploy.rb to include this line:
      set :linked_files, %w{config/master.key}
    
    By doing this you're telling Capistrano to symlink config/master.key to <project_root>/shared/config/master.key which contains the master key you've just copied to the server.
  3. Deploy your app again and verify that deployment is successful.
  4. Commit this changes to your repo. Don't check-in your config/master.key!

Side note: In context of using capistrano for deployment, #3 and #4 is interchangable, whichever more comfortable to you. Typically #4 followed by #3 is more comfortable and safer but running #3 first means you'll have more room for try-and-error instead of doing multiple commits due to the script being used for deployment is the local script.

Conclusion

This might seem complicated at first glance, but thinking of how the credentials are being managed locally without exposing it in your git repo, the way of copying master key to production and sharing master key to your colleagues seems to be easier compared to setting up environment variables remotely and informing the tech team when there's any change on the credentials. It helps to secure the credentials by not exposing the credentials explicitly to each member of your team too.

  1. https://keithpblog.org/post/encrypted-secrets/
  2. https://www.engineyard.com/blog/rails-encrypted-credentials-on-rails-5.2