oknm.jp

secret_key_baseがどういうふうに扱われているか


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

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 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を自動生成してその内容を返すらしい。へー。

まとめ