When we integrate third-party services into a Rails application, we need to handle the secret credentials related to those services. API tokens and secret access keys are examples of secret credentials.

In this blog, we will understand the encrypted credentials approach introduced in Rails 5.2 and dive into its most recent upgrade to the multi-environment setup in Rails 6.

In addition to adding an extra layer of security for our secret tokens by encrypting them, this will also allow us to handle our secret tokens and credentials in a way that prevents them from being accessible from outside the organisation or visible in our remote repository for all the environments that our application runs on.

Encrypted Credentials in Rails 5.2

It introduced two new files:

  • config/credentials.yml.enc: contains encrypted credentials data.
  • master.key: contains the key used to encrypt and decrypt the credentials.yml.enc.

master.key is added in the .gitignore file to prevent the master key from being exposed to the remote repository.

Editing encrypted credentials.yml.enc file with master.key:

To decrypt and open the config/credentials.yml.enc file in edit mode in the editor of your choice, use the command below:

EDITOR="code --wait" rails credentials:edit

Here, we have used VS Code as an editor. You can use any other editor as well to open and edit credentials.yml.enc file.

Using single file for all environments:

As Rails 5.2 supports only one encrypted credentials file at a time, one must explicitly define the environments and make it the main key to fit tokens for all environments in that single file.

development:
  cloudflare:
    access_key_id: devxyztoken321
    secret_access_key: devxyztoken543
production:
  cloudflare:
    access_key_id: prodxyztoken321
    secret_access_key: prodxyztoken543

Accessing secret credentials from credentials.yml.enc:

# Taking the example of Cloudflare secret credentials.

Rails.application.credentials.development[:cloudflare][:access_key_id]

Advantages:

  1. All our secret tokens and keys are encrypted in a single file which can only be accessed by the master key.
  2. We will have the credentials.yml.enc in the remote repository and hence, we will have a history of the changes.
  3. Every token for all environments can be handled from a single file.

Disadvantages:

  1. Single Point of Failure: Using a single master key to decrypt the secret tokens for all environments could result in cascading production failures, as external services won't be able to access their secret tokens via the master key if the master key is lost or deleted.
  2. Access to production secret tokens: Every developer who would want to access development tokens would also get access to production tokens. Production tokens are very sensitive and should be accessible to only a few people.

Multi-environment encrypted credentials with >= Rails 6:

Rails 6 came up with the concept of multi-environment credentials which enabled storing secret tokens in environment-specific encrypted YAML files and having separate master keys for each encrypted file.

Creating environment-specific encrypted credentials file:

If we want to create an encrypted credentials file and master key for any specific environment say staging, we can run:

# command used for creating as well as editing <environment>.yml.enc

EDITOR="code --wait" rails credentials:edit --environment=staging

Same command is used to decrypt and edit the encrypted file in the specified code editor.

config
    credentials
        development.yml.enc
        development.key
        staging.yml.enc
        staging.key
        production.yml.enc
        production.key

Accessing secret credentials from credentials.yml.enc:

When accessing tokens in Rails 6, we no longer have to specify the environment as the major key, we can simply use:

# Taking the example of Cloudflare secret credentials:

Rails.application.credentials.dig(:cloudflare, :secret_access_key)
OR
Rails.application.credentials.cloudflare[:secret_access_key]

Advantages of Rails 6 multi-environment setup:

  1. Eliminates single point of failure: It saves us from having all environment tokens in a single file.
  2. Different keys for different environments: It allows us to have different keys to decrypt and edit different environments' encrypted files.
  3. Not exposing sensitive keys: Now we just need to share the development key with all the contributors without sharing the keys for other environments, hence keeping them safe and secure.
  4. Git ignores .key files: It adds "/config/credentials/\*.key" to the ".gitignore" file, to avoid tracking keys.

Handling master key for code running on remote services(Heroku/Circle CI):

  1. Set config.require_master_key = true: By setting config.require_master_key = true in the application or environment settings, we ensure that we have a valid master key(RAILS_MASTER_KEY) for the environment in which the application is being run.

  2. Set RAILS_MASTER_KEY: The development key is kept locally in the file config/credentials/development.key. Since we don't push the test or staging keys to the remote repository for Heroku and Circle CI, the keys aren't kept in the specified directory. To get around this, we need to add RAILS_MASTER_KEY to Heroku/Circle CI's ENV configuration variables with the value holding the necessary key.

References: