15

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?

Marta Silva
  • 733
  • 1
  • 6
  • 13
  • 1
    Using an ENV var defeats all the safety benefits of using encrypted secrets in the first place. The encrypted secrets are not run through ERB like secrets.yml by design. "The Rails 6 way" is to not use an ENV var in the first place as we know that they are easily compromised. – max Mar 16 '20 at 15:09

3 Answers3

30

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.

Marta Silva
  • 733
  • 1
  • 6
  • 13
  • this documentation is confusing, because it says it does work in credentials. How is that not Rails.application.credentials.secret_key_base? – toobulkeh Jul 01 '21 at 02:00
  • I got confused `Rails.application.credentials.secret_key_base` and `Rails.application.secret_key_base` all works for me but returning different value – buncis Jan 05 '23 at 15:44
0

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.

cnnr
  • 1,267
  • 4
  • 18
  • 23
  • Using ENV vars is a step backwards from using encrypted secrets. Just like putting credentials in plaintext in secrets.yml is a step back from ENV vars. Any gem on the system that leaks ENV will compromise them. – max Mar 16 '20 at 15:14
  • 1
    @max I almost agree with you, but I don't know yet, how to solve task when you need to change (or set) something in encrypted credentials at server-side, how to separate infrastructure from code. – cnnr Mar 17 '20 at 08:53
  • @cnnr I think you could completely ignore config/credentials and deploy them with an external tool. The encryption should be easy to apply. – sekrett Aug 27 '20 at 08:17
-1

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
multipolygon
  • 2,194
  • 2
  • 19
  • 23