master.key とは
Rails の master.key とは認証情報を暗号化する時に使用されるキーファイルです。rails credentials:edit
で認証情報を credentials.yml.enc に暗号化(ENCrypt) して保存する時に使用されます。認証機能に Firebase authentication を利用している場合、Firebase の API キーを格納している方が多いのではないでしょうか。
10.1 Custom Credentials
Rails stores secrets in config/credentials.yml.enc, which is encrypted and hence cannot be edited directly. Rails uses config/master.key or alternatively looks for the environment variable ENV["RAILS_MASTER_KEY"] to encrypt the credentials file. Because the credentials file is encrypted, it can be stored in version control, as long as the master key is kept safe.
master.key は .dockerignore でコンテナイメージに入れないようにしておく
私の場合 fly.io のデプロイ手順で Dockerfile を生成したので、master.key はデフォルトで除外する設定になっていました。スクラッチから作る時は Github などから 参考となる .dockerignore をいくつか見て回るのがよいかもしれません。
➜ grep master.key .dockerignore
config/master.key
master.key がない場合に Rails を失敗させる config.require_master_key 設定
config.require_master_key を true に設定することで、キーファイル master.key もしくは環境変数 RAILS_MASTER_KEY が存在せず Rails が認証情報を取得できない場合に、Rails コマンドを異常終了させることができます。
config/environments/development.rb
# Causes the app to not boot if a master key hasn't been made available through
# ENV["RAILS_MASTER_KEY"] or the config/master.key file.
config.require_master_key = true
コンテイメージビルド失敗: 抱える矛盾
この設定を有効にした場合、認証情報を取得できない場合は rails db:migrate も失敗するようになります。コンテナイメージを作成する場合に必要となる rails db:migrate が失敗するのでこのままではコンテナイメージを作成することができません。
Dockerfile
RUN bundle install
RUN bundle exec rails db:migrate
➜ docker buildx build -t camelot:latest .
[+] Building 27.8s (13/14)
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 626B 0.0s
(... snip ...)
=> [ 8/10] RUN bundle install 23.8s
=> ERROR [ 9/10] RUN bundle exec rails db:migrate 2.8s
------
> [ 9/10] RUN bundle exec rails db:migrate:
#0 2.753 Missing encryption key to decrypt file with. Ask your team for your master key and write it to /camelot/config/master.key or put it in the ENV['RAILS_MASTER_KEY'].
------
ERROR: failed to solve: executor failed running [/bin/sh -c bundle exec rails db:migrate]: exit code: 1
➜
コンテナイメージビルド成功: docker buildx build の Secret to expose the build (--secret)
を利用して矛盾を解決する
コンテナイメージに master.key は含めたくないがビルドには必要、という矛盾を解決するために docker buildx build が提供している機能が Secret to expose the build (--secret)
です。この機能を利用することで、master.key をビルド時に「一時的に」渡すことが可能になります。
Dockerfile 側で「一時的に」渡すがイメージには含めたくないファイルを RUN --mount ...
で指定し、コマンド側で docker buildx build --secret ...
で指定して受け渡しをそれぞれ設定します。
Dockerfile
RUN bundle install
RUN --mount=type=secret,id=master_key,target=config/master.key,required=true bundle exec rails db:migrate
➜ docker buildx build --secret id=master_key,src=./config/master.key -t camelot:latest .
[+] Building 29.4s (15/15) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 626B 0.0s
(... snip ...)
=> [ 8/10] RUN bundle install 23.1s
=> [ 9/10] RUN --mount=type=secret,id=master_key,target=config/master.key,required=true bundle exec rails db:migrate 3.6s
=> [10/10] COPY ./db/development.sqlite3 /camelot/db/ 0.0s
=> exporting to image 1.7s
=> => exporting layers 1.6s
=> => writing image sha256:900504dca3fe2761332f919e0526baa016c053722466844412efdacf0a371d37 0.0s
=> => naming to docker.io/library/camelot:latest 0.0s
➜
Secret to expose the build (--secret)
機能を使用することで、先ほどはコンテナイメージビルド時に失敗した rails db:migrate が「一時的に」ファイルを渡すことで無事成功するようになりました。ビルドしたイメージの中にファイルは含まれていません。
➜ docker run -it camelot:latest ls -l config/master.key
ls: cannot access 'config/master.key': No such file or directory
ランタイム時に master.key を環境変数でコンテナに渡す: ローカルホストの場合
ビルドタイム時には「一時的に」渡すことができましたが、master.key がコンテナイメージに含まれていないため、ランタイム時は環境変数を使用して渡すことが必要になります。ローカルマシンであれば -e オプションが使えます。
➜ docker run -p 3000:3000 --name camelot-on-docker -e RAILS_MASTER_KEY='__THIS_IS_SECRET__' -it camelot:latest
ランタイム時に master.key を環境変数でコンテナに渡す: AWS AppRunner の場合
AWS AppRunner の場合は AWS Management Console から App Runner > Service > [Service name] > Configuration > Configure Service > Service Settings > Environment variables から設定可能です。master.key の内容を Plain text として貼り付けてもいいですが Secrets Manager や SSM Parameter Store 経由で設定するとより安全かもしれません。
参考情報
https://guides.rubyonrails.org から特定の Rails のバージョンに絞って検索する場合、site 検索が便利です。