Zero-downtime with Rails credentials part II

This post is part of the Zero-downtime with Rails credentials series.

In the first post we talked about the reasons that moved us towards making a codewide change and adopt Rails credentials rather than using environment variables to manage our secrets.

In this article we are going to look at the consequences, and the impact that these have on you as a developer, and your codebase.

What impact does this change have on you?

Credential files for each environment

It is important to conceptually consider all the environments we run the application in. In our case, these are:

  • development
  • test
  • staging
  • production

Not all of these environments may deploy to a server, indeed both development and test only exist on your local machine. development when you run the development server and test when you run your test suite.

Currently, when the application runs in any of these environments, Rails looks for the .env file in order to pick up the environment variables.

This is why we currently have a .env file for each of these environments.

With this change we will be moving away from a .env file to a credential file for each environment. These will be located at config/credentials/<environment_name>.yml.enc.

The credential files we expect to have corresponding to each environment:

  • config/credentials/devlopment.yml.enc
  • config/credentials/test.yml.enc
  • config/credentials/staging.yml.enc
  • config/credentials/production.yml.enc

These files are where you will now go to add, edit or remove credentials, instead of doing so in the .env file.

New Rails Environments

Another change that you will start to see is the creation of new Rails Environments within the Rails app.

Have a look in config/environments. You will see that as things currently stand the Rails application is only aware of 3 different Rails Environments:

  • development
  • test
  • production

When you run the server locally, Rails runs in the development Rails Environment. When you run the test suite, Rails runs in the test Rails Environment. You can check this with the command Rails.env.

However, for all of our other environments, namely staging and production, Rails treats them all as production Rails Environments. The Rails.env command returns “production” in all of these environments.

This has been set up this way intentionally, using the RAILS_ENV environment variable in each .env file to set the Rails Environment to production, like so: RAILS_ENV=production.

Up until now this didn’t really matter. We are happy for Rails to treat all of the deployable environments as “production”.

But with Rails Credentials it does matter… Rails uses the Rails Environment to determine which Rails Credentials file to read from.

For example when Rails is running in the test Rails Environment it will know to use the credentials from config/credentials/test.yml.enc.

But imagine when we have credential files for each of our environments:

  • staging –> config/credentials/staging.yml.enc
  • production –> config/credentials/production.yml.enc

The way Rails works is that it will first check what its Rails Environment (RAILS_ENV) is set to and use that to infer which credential file to read from.

As it stands, on all of these environments, the Rails Environemnt is set to “production”, in other words RAILS_ENV=production, and therefore within each environment, Rails will always look for the config/credentials/production.yml.enc file.

This is a problem as we want to have different credentials for each environment, just like we have a different .env files for each environment.

In order to do this we have to create a new Rails Environment for each environment, by creating all the environments in config/environments.

So we expect to see the following files:

  • config/environments/development.rb
  • config/environments/test.rb
  • config/environments/staging.rb
  • config/environments/production.rb

We will also set RAILS_ENV on each environment to the actual name of the environment and not just production.

  • staging –> RAILS_ENV=staging
  • production –> RAILS_ENV=production

This will allow Rails to check the RAILS_ENV on the environment and use that to read from the correct credentials file.

So, to put it all together:

  1. The Rails app loads on the given environment
  2. Rails checks the value of RAILS_ENV so it knows the Rails Environment it is operating in
  3. Rails uses the correct config/environment file for that environment
  4. Rails uses the correct credentials file for that environment

Use a Rails Master Key

As mentioned above, all the Rails Credential files will be encrypted. This is why we can store them safely in version control.

However, when Rails attempts to read from these credential files, it will need a key that it can use to decrypt the files and retrieve the key value pairs in a decrypted state.

You will need to add the key to your .env file in order to be able to work with the credential files and decrypt them.

This key is highly sensitive and should not be stored in version control.

Rails uses a different master key for all the different environments.

So, wait, why do we still need a .env file?

Well, yes. We will tell you more in our next article, promise.