What's the correct way of defining secret_key_base on Rails 6?

Viewed 9244

What's the correct way of defining secret_key_base on Rails 6 now that we have per-environment credentials?

My environment has the variable SECRET_KEY_BASE but Rails is not picking it up. I tried defining secret_key_base in config\credentials\production.yml.enc but it has no effect on Rails.application.credentials.secret_key_base

I know config/secrets.yml with

staging:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

works, but, is that the Rails 6 way?

3 Answers

The right way to access and check for secret_key_base in Rails 6 is no longer:~

Rails.application.credentials.secret_key_base

it now is:

Rails.application.secret_key_base

I'm not sure if this is Rails 6 or it's been like this forever. This becomes pretty clear when looking at this method, and its implementation:

https://github.com/rails/rails/blob/09a2979f75c51afb797dd60261a8930f84144af8/railties/lib/rails/application.rb#L410-L427

# The secret_key_base is used as the input secret to the application's key generator, which in turn
# is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
#
# In development and test, this is randomly generated and stored in a
# temporary file in <tt>tmp/development_secret.txt</tt>.
#
# In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
# the correct place to store it is in the encrypted credentials file.
def secret_key_base
  if Rails.env.development? || Rails.env.test?
    secrets.secret_key_base ||= generate_development_secret
  else
    validate_secret_key_base(
      ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
    )
  end
end

Both development and test mode have their own way of generating and storing the secret key base. For everything else, it pics it up from the environment, or credentials or secrets, in that order.

Docker users in development might consider this in their entrypoint.sh:

if [ "$RAILS_ENV" = "development" ]; then
   printf $SECRET_KEY_BASE > ./tmp/development_secret.txt
fi

I've tried to solve that problem few days ago.

And what I learned:

First attempt

I try to use credentials per environment with

$ EDITOR=nano rails credentials:edit --environment development

$ EDITOR=nano rails credentials:edit --environment staging

$ EDITOR=nano rails credentials:edit --environment production

My creds files and keys were placed in config/credentials.

I set necessary variables straight there. It's usable solution, but we met a problem with our deployment at Kubernetes cluster, when our devopses wants to use helm configs. So, predefined credentials is not applicable for that case.


Second attempt

After that I've tried to use ENV-variables in my credentials files. Unfortunately, it's not works too:

secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>

Final attempt

Finally, I did graceful degradation to gem config with default configuration, when you per-environment settings placed there:

config/settings.yml
config/settings/development.yml
config/settings/production.yml
config/settings/test.yml

And my settings.yml file consists only ENV-variables, like so:

secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>

db:
  host: <%= ENV['DB_HOST'] %>
  port: <%= ENV['DB_PORT'] %>
  pool: <%= ENV['DB_POOL'] %>
  user: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  database: <%= ENV['DB_DATABASE'] %>

...

It's workable solution, but seems like step-backward.

As I know now, we cant use ENV-vars in credentials any simple way.

Related