Railsアプリを本番デプロイしたらsecret_key_baseが未定義っておこられたので、どういう扱いになってるのか確認してみた。
確認したRailsのバージョンは6.0.0.rc1。

はじめにまとめ


secret_key_baseの設定場所は4箇所ある:
  1. 環境変数: ENV["SECRET_KEY_BASE"]
  2. credencials: config/credentials.yml.enc
  3. secrets: config/secrets.yml (Encrypted secretsが有効ならconfig/secrets.yml.enc
  4. tmp/development_secret.txt
環境ごとの評価順:
  1. developmentかtestなら、secrets、tmp/development_secret.txtの順
  2. それ以外の環境なら、環境変数、credencials、secretsの順

secret_key_baseの取得

 Rails.applicatin.secret_key_base
#=> "bb7a320962dda4262e21262f80229946f5a3b3c05cc8968d71d1ae9e40c64b5d7afb5340c196134089590b69d8a51a05ee801217b90eb859236bf4b14f8d5a76"

secret_key_baseの定義場所

Rails::Applicationにメソッドとして定義されている。

railties/lib/rails/application.rb
 module Rails
   # snip
 
   class Application < Engine
     # snip
 
     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
   end
 end
  • 環境がdevelopmentかtestの場合
    • secrets.secret_key_baseが未設定なら、generate_development_secretを実行し結果をsecrets.secret_key_baseに保存する
    • secrets.secret_key_baseを返す
  • それ以外の場合
    • ENV["SECRET_KEY_BASE"]credentials.secret_key_basesecrets.secret_key_baseの順番で評価してバリデーションした上で結果を返す

secrets.secret_key_baseとは

Rails::Application#secretsが定義されてる。

railties/lib/rails/application.rb
     def secrets
       @secrets ||= begin
         secrets = ActiveSupport::OrderedOptions.new
         files = config.paths["config/secrets"].existent
         files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets
         secrets.merge! Rails::Secrets.parse(files, env: Rails.env)
 
         # Fallback to config.secret_key_base if secrets.secret_key_base isn't set
         secrets.secret_key_base ||= config.secret_key_base
 
         secrets
       end
     end
  • config/secrets.ymlを読み込む
  • Rails 5.1で導入されたEncrypted secretsが有効ならconfig/secrets.yml.encを読み込む
  • config/secrets.ymlにsecret_key_baseが未定義ならconfigから読み込む
config/secrets.ymlはRails 4.1で導入されたようなので、それ以前はconfig/application.rbとかに書いてたのかな?
https://guides.rubyonrails.org/4_1_release_notes.html#config-secrets-yml

generate_development_secretとは

railties/lib/rails/application.rb
       def generate_development_secret
         if secrets.secret_key_base.nil?
           key_file = Rails.root.join("tmp/development_secret.txt")
 
           if !File.exist?(key_file)
             random_key = SecureRandom.hex(64)
             FileUtils.mkdir_p(key_file.dirname)
             File.binwrite(key_file, random_key)
           end
 
           secrets.secret_key_base = File.binread(key_file)
         end
 
         secrets.secret_key_base
       end
 secret_key_baseが未設定ならtmp/development_secret.txtを自動生成してその内容を返すらしい。へー。

まとめ

  •  開発中はsecret_key_baseが自動生成されるので未設定で良さそう
  •  本番環境は環境変数、credencials、secretsなど適切な方法で設定しておく