はじめに
Rails初学者です。GCのStorageとAPIを使用したアプリケーションをHerokuにデプロイする際、認証設定に苦労したのでメモを書き留めておきます。
【使用環境】
・MacOS BigSur
・Ruby 2.6.5, Ruby on Rails 6
GCPのサービスアカウントについて
サービスアカウントを作成するとjson形式の鍵ファイルをダウンロードでき、そのファイルパスを環境変数GOOGLE_APPLICATION_CREDENTIALS
に設定することで認証ができます。
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/my-key.json"
しかし、この鍵ファイルをGitやHerokuに渡すわけには行きません。
また、公式ドキュメントにありますが、開発環境と本番環境ではサービスアカウントを使い分ける必要があるようです。そのため、Rails 6からサポートされたMulti environment credentialsでそれぞれの秘密鍵情報を記載しました。
Multi environment credentialsの設定
credentialsファイルから読み込む設定
google:
service: GCS
credentials:
type: "service_account"
project_id: <%= Rails.application.credentials.dig(:gcs, :project_id) %>
private_key_id: <%= Rails.application.credentials.dig(:gcs, :private_key_id) %>
private_key: <%= Rails.application.credentials.dig(:gcs, :private_key).dump %>
client_email: <%= Rails.application.credentials.dig(:gcs, :client_email) %>
client_id: <%= Rails.application.credentials.dig(:gcs, :client_id) %>
auth_uri: "https://accounts.google.com/o/oauth2/auth"
token_uri: "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url: <%= Rails.application.credentials.dig(:gcs, :client_x509_cert_url) %>
project: <%= Rails.application.credentials.dig(:gcs, :project) %>
bucket: <%= Rails.application.credentials.dig(:gcs, :bucket) %>
次のコマンドで本番環境用のcredentialsファイルを作成
% bundle exec rails credentials:edit --environment production
configディレクトリ内にcredentialsというディレクトリが作成され、設定ファイルとキーファイルが対で生成されます。
config
∟credentials
∟ production.key
∟ production.yml.enc
∟ master.key
∟ credentials.yml.enc
設定ファイルは暗号化されているため、次のコマンドでviを使用して編集します。
% EDITOR="vi" bin/rails credentials:edit --environment production
secret_key_base: ******
gcs:
project_id: ******
private_key_id: ******
private_key_id: ******
private_key: "-----BEGIN PRIVATE KEY-----\n******************
***************************************************************
*********************************\n-----END PRIVATE KEY-----\n"
client_email: ******@******.iam.gserviceaccount.com
client_id: ******
client_x509_cert_url: https://www.googleapis.com/robot/v1/metadata/x509/******.iam.gserviceaccount.com
project: ******
bucket: ******
secret_key_baseは元々のcredentials.yml.encと同じ。
サービスアカウントを作成して、ダウンロードしたjson形式の鍵ファイルの中に記載されている各項目を書いていきます。private_keyはとても長く改行文字を含んでいるためダブルクォーテーション(”)で囲みます。
そして、production.keyの中身をHerokuに環境変数 RAILS_MASTER_KEYとして保存します。
% heroku config:set RAILS_MASTER_KEY=`cat config/credentials/production.key`
これでいけるかと思いきや
GCSへのデータアップロードは出来るのですが、APIにリクエスト送信すると「認証できないよ」というエラーが。結局、環境変数GOOGLE_APPLICATION_CREDENTIALS
を読み込めないと駄目なのか。
いろいろ調べた中で、次の方法を試しました。
まず、Herokuに任意の名前で環境変数を作成、鍵ファイルの中身を保存する。
% heroku config:set GOOGLE_CREDENTIALS="$(< /PATH/TO/my-key.json)"
次に、appディレクトリ内に空のjsonファイルを作成し、そのjsonファイル内に先程の環境変数を出力するという記述を.profileファイル内に書き込む。
aplicationディレクトリ
∟app
∟gcp_key.json
∟**.profile**
echo ${GOOGLE_CREDENTIALS} > /app/gcp_key.json
最後に、Herokuに環境変数GOOGLE_APPLICATION_CREDENTIALS
にjsonファイルパスを設定。
% heroku config:set GOOGLE_APPLICATION_CREDENTIALS=/app/gcp_key.json
.profileに記述したコマンドは、Herokuのアプリケーションを格納するコンテナ(Dyno)が起動すると同時に実行されるとのこと。
ここまでの設定で漸く本番環境でも期待の動作をしてくれました。
補足 サービスアカウントのロール(権限)について
オーナー、編集者、閲覧者といった基本ロールの他に、膨大な数のロールが用意されています。基本ロールは設定権限が多いため、本番環境には使用するなとドキュメントにはあったものの、どれを選んで良いものか。
今回、色々と試行した中では「サービスアカウントユーザー」(オペレーションをサービスアカウントとして実行)と「ストレージ管理者」(GCSリソースの全てを管理)の2つを選んで上手く行きましたが、今後不具合などあれば加筆します。