RAILS_MASTER_KEY and per-environment init

Should RAILS_MASTER_KEY be set to the contents of <env>.key in any given <env> of development, staging, production? The ActiveRecord::Encryption guide doesn’t give any environment-specific information, and invoking the init command it instructs you to creates a global-environment master key just called master.key.

On the other hand, Add support for multi environment credentials. by morgoth · Pull Request #33521 · rails/rails · GitHub allows you to create per-environment credentials via rails credentials:edit --environment <env>. This is great, but there’s no documentation about whether or not a Rails server running an environment such as staging for example will still look in ENV["RAILS_MASTER_KEY"] for its master key, and also, if it will use the key supplied in that env var to decrypt staging.yml.enc on boot.

I’d love it if the docs were clearer on this topic, because I don’t know the answer.

This is how I use Rails credentials for a few projects:

  • bin/rails credentials:edit - This contains only the keys for credentials, it’s sort of the template/guide for the rest of the environments. config/master.key and config/credentials.yml.enc are both committed to git as they don’t contain actual secrets. Whenever a new configuration setting is needed it’s always added here first and then once staging and production credentials are ready it’s added to the other credential files.
  • bin/rails credentials:edit -e development - These are credentials specific to the developer’s machine. The reason I started using the default one and development is in the event that developer A has production access to different APIs than developer B. It also just helps with general development and being able to tweak your credentials without having to deal with diffs, diffing credentials is still pretty rough.
  • bin/rails credentials:edit -e staging - Staging only credentials, only config/credentials/staging.yml.enc is committed to git.
  • bin/rails credentials:edit -e production - Production only credentials, only config/credentials/production.yml.enc is committed to git.

.gitignore ends up looking like this:

/config/credentials/development.yml.enc
/config/credentials/*.key

When deploying rails looks for the most specific set of credential files first and then to the most general. In order to not share the your keys across all of your environments you just need to ensure that either ENV['RAILS_MASTER_KEY'] is set in your runtime or config/master.key(or the more specific config/credentials/#{Rails.env}.key) is available and set to that current environment’s key.

You can get an idea of the load order in the PR on this line.

2 Likes

This is exactly what I needed, thanks! Happy to submit a PR to the Rails guides if that’s how we can get these details into them.

I’d also be curious to hear how others are handling multi-environment setup, that’s just what’s been working for us.

My biggest pain point with credentials right now is when you have credential changes on two different branches that you need to resolve. You have to have two copies of the repo and then switch to one of those branches in each and then visually spot check the credentials. Other than that I really love using Rails credentials as it simplifies a lot of the setup pretty significantly.

1 Like

I suppose there’s a number of different use cases that could be more or less painful. I’ve only been using it with the ActiveRecord::Encryption suite (i.e., storing the result of rails db:encryption:init --<env> in .yml.enc files), but I can see how if you have a number of different variables you’re storing in there, the ciphertext starts to stymie a DVCS workflow pretty quickly.

In our case we’re sticking to the development credentials being the same for all devs with the key stored securely elsewhere and provisioned using a different mechanism.

I think that if the requirements became complex enough (multiple dev permission trees, multiple environments, etc.), you’d end up having to move the encrypted YAML and keys out to a secure storage + provisioner, which would remove diffing and conflicts from the equation entirely. I expect that to happen in the projects I’m working on sooner than later.

1 Like

In addition to what everyone else said I’d also recommend not creating a separate environment for staging.

Making staging closer to production by running it in production mode, except with a different set of credentials has lots of benefits, the main one being that the code that runs in staging runs in the same environment it’s going to run in production.

2 Likes

Now that 7.1 uses the development credentials by default rather than the “global” ones, have you changed your pattern here for your template credentials? Since it becomes impossible to edit the “global” file once you create a development.key/development.yml.enc.

1 Like

@jturmel Surprisingly you can actually still edit the default credentials, it’s not very obvious with the upgrade though but also not my favorite.

Editing the “global” credentials:

RAILS_ENV=nil bin/rails credentials:edit
1 Like