🦓

[Rails 7.0.8]Missing `secret_key_base` for ‘production’ environment...

2024/07/15に公開

はじめに

アプリのRailsバージョンを7.1から7.0.8にバージョンダウンし、ディプロイしたらタイトルのエラーが出たのでメモとして残します。

Dockerfileエラー
1.109 (See full trace by running task with --trace) ------ Dockerfile:51 -------------------- 49 | 50 | # Precompiling assets for production without requiring secret RAILS_MASTER_KEY 51 | >>> RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile 52 | 53 |-------------------- error: failed to solve: process "/bin/sh -c SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile" did not complete successfully: exit code: 1 error: exit status 1

経緯

Rails7.1でデフォルトのDockerfileを使って開発とデプロイを行ってましたが、とある機能の実装に当たって導入しようとしているGemがRails7.0.8までしか対応していないため、7.0.8にダウングレードすることになりました。
その機能の実装を終え、本番にデプロイしようとしたら以上のエラーが出ました。

調べたこと

RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile

エラー文を見ると、Dockerfileの51行目でエラーが発生したことが分かります。
該当するコマンドはRUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompileになります。

Rails 7.1以降でrails newコマンドを使用してアプリケーションを生成すると、この行を含むDockerfileが自動的に含まれます。

このコマンドは、Dockerイメージのビルド時にアセットのプリコンパイルを行うためのものです。

SECRET_KEY_BASE_DUMMY=1の役割:

  • アセットのプリコンパイル処理中にSECRET_KEY_BASEが必要となる場合があります。
  • この環境変数にダミー値を設定することで、ビルド時のエラーを回避します。
  • 1という値に特別な意味はなく、単なるプレースホルダーです。
  • このSECRET_KEY_BASE_DUMMY値はビルド時のみ使用され、実際の運用環境では使用されません。
  • 本番環境では、適切なSECRET_KEY_BASEを別途設定する必要があります。

この方法により、config/credentials.yml.encが存在する場合、実際のSECRET_KEY_BASEがなくてもアセットのプリコンパイルが可能になりました。

https://railsguides.jp/v7.1/7_1_release_notes.html

エラーの背景

Rails 7.1以前では、本番環境でアプリケーションを実行する際にSECRET_KEY_BASE環境変数が必要でした。Dockerfileでビルド時にこの変数が設定されていない場合、エラーが発生していました。→ いまここ

参考したPRとissue

Rails 7.1以前での代替案

Rails 7.1以前のバージョンでは、この特定の方法(SECRET_KEY_BASE_DUMMY=1)を使用することは推奨されません。

互換性の問題:

  • Rails 7.1より前のバージョンでは、SECRET_KEY_BASEに有効な値が必要です。
  • ダミー値を使用すると、アセットのプリコンパイルや他の重要な操作で問題が発生する可能性があります。
  • 古いバージョンでは、この方法を使うとセキュリティ上の脆弱性を引き起こす可能性があります。

解決法

  1. 環境変数を使用しSECRET_KEY_BASE=を指定
Dockerfile
ARG SECRET_KEY_BASE
ENV SECRET_KEY_BASE=${SECRET_KEY_BASE}
RUN ./bin/rails assets:precompile

ビルド時に--build-arg SECRET_KEY_BASE=$SECRET_KEY_BASEを指定します。

  1. アプリ内で設定を追加
config/application.rb
config.secret_key_base = ENV['SECRET_KEY_BASE']
  1. コンソールからsecret_key_baseを取得
Rails.application.secret_key_base
# "######"
  1. 本番環境でSECRET_KEY_BASEを設定
    本番環境では、環境変数SECRET_KEY_BASEを設定します。

これでデプロイをうまく行きました。

ベストプラクティス

  • SECRET_KEY_BASEはビルド時ではなく、実行時に設定するのが理想的です。
  • SECRET_KEY_BASEを Dockerfile 内に直接記述することは避けてください。
  • 開発環境ではconfig/master.keyRAILS_MASTER_KEYを使用します。
    docker run -p 3000:3000 -e RAILS_MASTER_KEY=$(cat config/master.key) --rm my_app
    
  • 本番環境では、適切に管理された環境変数を使用します。

SECRET_KEY_BASEの取得

development/test以外の環境におけるsecret_key_base値の取得元は、以下の上から順に試行されるようになります。
ENV["SECRET_KEY_BASE"]
credentials.secret_key_base
secrets.secret_key_base

https://techracho.bpsinc.jp/hachi8833/2023_11_10/134819
https://api.rubyonrails.org/classes/Rails/Application.html#method-i-secret_key_base

なので、環境変数以外にもクレデンシャルから追加することができます。

EDITOR="code --wait" bin/rails credentials:edit --environment production

このコマンドは本番環境用の暗号化された認証情報を編集します。

VSCodeをエディタとして使用し、編集が完了するまで待機します。
対応するconfig/credentials/production.keyも生成されます。

コンソールから取得した値を追加します。

config/credentials/production.yml.enc
production:
  secret_key_base: YOUR_SECRET_KEY_BASE

このファイルは本番環境ではRAILS_MASTER_KEY環境変数またはconfig/master.keyに格納されたキーを使って復号化されるので、RAILS_MASTER_KEYを環境変数として渡します。

終わりに

Rails 7.1以前のバージョンでDockerを使用する場合、こちらの代替方法を使用することで、アセットのプリコンパイルを行う際のエラーを解消することができました。誰かの参考になれば嬉しいです。

Discussion