Rails 6 から入った multi environment credentials を使う

最初は社内ブログに書いてたけど、動機は「探してもあんまり日本語のよさそうな情報がなかったから」なのでここに書きます。適当な理解という感じですが、いまのところうまくいっています。

前提

❯❯❯ be rails --version
Rails 6.0.0
❯❯❯ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]

tl;dr

  • 環境ごとに credentials ファイルとその鍵を分けられるようになりました。
    • bundle exec rails credentials:edit --environment staging のような感じで編集します。
  • RAILS_MASTER_KEY に、 RAILS_ENV と一致した適切な鍵を入れれば自動で decrypt されて実行されます。
    • Rails.application.credentials.muse[:minami_kotori] たとえばこんな感じでアクセスできます。

rails/rails の実装 PR

Add support for multi environment credentials. by morgoth · Pull Request #33521 · rails/rails

やりかた

  • いままでの bundle exec rails credentials:edit に option を付け加えるだけです。実際どういう風に中身を書いてアクセスするかは後述します。
    • ex: bundle exec rails credentials:edit --environment production
    • ex: bundle exec rails credentials:edit --environment staging
    • ex: bundle exec rails credentials:edit --environment development
    • ex: bundle exec rails credentials:edit --environment test
  • decrypt するには、後述するようなディレクトリ構造で鍵を持ってきて置くか、実行する RAILS_ENV に応じた RAILS_MASTER_KEY を設定します。
  • 実行している RAILS_ENVcredential が存在するときはそこを勝手に読みにいってくれるので(実装参照)、特に追加する設定はないです。 .gitignore にも追加してくれます。
    • ただ、 secret_key_base については、 production のは最初からあった config/credentials.yml.enc から持ってくると楽です。
    • staging のものは bundle exec rake secret とかで出したものを設定するとよいでしょう。

https://github.com/rails/rails/pull/33521/files#diff-19128a84fe2ae7019ccdb86efc86f684R281-R299

def credentials_available_for_current_env?
  File.exist?("#{root}/config/credentials/#{Rails.env}.yml.enc")
end

def default_credentials_content_path
  if credentials_available_for_current_env?
    File.join(root, "config", "credentials", "#{Rails.env}.yml.enc")
  else
    File.join(root, "config", "credentials.yml.enc")
  end
end

def default_credentials_key_path
  if credentials_available_for_current_env?
    File.join(root, "config", "credentials", "#{Rails.env}.key")
  else
    File.join(root, "config", "master.key")
  end
end

ディレクトリ構造とか

こういう風になります。

❯❯❯ tree config/credentials
config/credentials
├── development.key
├── development.yml.enc
├── production.key
├── production.yml.enc
├── staging.key
├── staging.yml.enc
├── test.key
└── test.yml.enc

ファイルの中身はこんな感じ。

muse:
  minami_kotori: kawaii
  private_key: |
    -----BEGIN RSA PRIVATE KEY-----
    naisyo no kagi dayo
    -----END RSA PRIVATE KEY-----

secret_key_base: himitsu <3

コードからは、こういう感じでアクセスできます。

kotori = Rails.application.credentials.muse[:minami_kotori]
key = OpenSSL::PKey::RSA.new(Rails.application.credentials.muse[:private_key])

うれしいこと

  • 権限に応じて適切に見られる情報を管理できる
    • たとえば、 production.key は正社員とかつよい権限のひとしか知らない、とかができる
    • 業務委託でお願いするひとには test.keydevelopment.key だけわたす、とか
  • 環境によって変化する値を気にしなくてよくなる
    • unstable と production で値が異なるときとか
  • kubesec とかでやるにはむずかしい機微情報を管理できる?
    • 環境変数に入っちゃうのは良し悪しだと思うので…。

tips

  • VSCode とかで編集したいひとは EDITOR='code --wait' をつけるとよいです。
    • EDITOR='code --wait' bundle exec rails credentials:edit --environment staging みたいな。
  • 鍵がなかったり間違っていたりして decrypt に失敗したときは nil が入るので、気をつけないと肝心なところで壊れます。
    • kubesec とか CircleCI で RAILS_MASTER_KEY を入れている場合は改行が入っていないか気をつけましょう。