GitLabのCI/CDでOIDCがproduction-readyになっていたので使ってみる
DWSの道下です。
当社ではCI/CDを構築する際Circle CIやGitHub Actionsを使うことが多いのですが、最近GitLabでCI/CDを構築する機会がありました。その際、GitLab CIでのOIDC対応状況を調べたので、まとめておきます。
OIDCを使用したクラウドサービスへのアクセス
CI/CDでAWSにアクセスしたい場合、環境変数にアクセスキーをセットして使用することもできますが、長期間有効なアクセスキーは漏洩した際の被害が大きく、一時的な認証情報を使うのがベストプラクティスとされています。
実際、今年1月に発生したCircle CIのインシデントではユーザーの環境変数の漏洩が報告されており、もしアクセスキーを環境変数に入れていれば不正利用される可能性もあったと考えられます。
そのため当社では基本的にCI/CDでクラウドサービスに接続する際は、OIDCを使用して一時的な認証情報を取得しています。
GitLabの対応状況
GitHub Actionsでは2021年11月、Circle CIでは2022年3月からそれぞれOIDCをサポートしています。
GitLabはどうかと確認したところ、最近まではα版として提供されており、将来にわたって確実に使い続けることができるかどうかはわからないという位置づけだったようですが、ちょうど今年(2023年)2月にリリースされたGitLab15.9にて、production-readyにするためのセキュリティ向上対応を行ったと書いてあり、これをもってα版ではなくなったようです。
https://about.gitlab.com/releases/2023/02/22/gitlab-15-9-released/
設定方法
実際にGitLab CIでOIDCを使ってAWSにアクセスするまでの手順を記載します。
AWSにIDプロバイダを追加
AWSコンソールのIAMの画面で、IDプロバイダの追加を行います。プロバイダのタイプはOpenID Connectを選択し、プロバイダのURL/対象者ともに、https://gitlab.com
を指定します(GitLabをセルフホストしている場合は、そのURLを入れてください。)。対象者はGitLabから渡すIDトークンのaudienceクレームの値と一致する必要があります。IDトークンのaudienceクレームは後述するCIの設定ファイルで指定できます。今回は特に変える必要もないのでデフォルトのhttps://gitlab.com
でいきます。
Assume Role用のIAM Roleを作成
一時的な認証情報を取得する際にAssume Roleする対象のIAM Roleを作成します。上記で登録したIDプロバイダを信頼するようにカスタム信頼ポリシーを書きます。
Conditionにて、gitlab.com:subに条件を課している部分が重要です。ここでGitLabで発行されたIDトークンのsubjectクレームとの一致を確認しています。GitLabのドキュメントによると、IDトークンのsubjectクレームにはproject_path:{group}/{project}:ref_type:{type}:ref:{branch_name}
の形式で、トークンを生成したプロジェクトの情報が設定されます。subjectクレームの一致条件が無い場合、(RoleのARNさえ分かれば)他人の全く関係ないプロジェクトで生成されたトークンでもこのRoleを使えることになってしまいます。必ず自分のプロジェクトのパスを指定してください。
今回は任意のブランチで実行できるように、"project_path:your-group/your-project:*
"と、最後はワイルドカードにしています。
CI設定ファイル(.gitlab-ci.yml)の記述
OIDCを使って一時的な認証情報を取得できることを確認するために、GitLabのドキュメントを参考に以下のようなgitlab-ci.ymlを作成します。
stages:
- oidc-test
get-aws-credentials:
stage: oidc-test
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com
script:
- >
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"
$(aws sts assume-role-with-web-identity
--role-arn ${ROLE_ARN}
--role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}"
--web-identity-token ${GITLAB_OIDC_TOKEN}
--duration-seconds 3600
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text))
- aws sts get-caller-identity
id_tokensというキーワードを使っています。これを書くことで、IDトークンが生成され、指定した名前(今回はGITLAB_OIDC_TOKEN)で環境変数に格納されます。audに指定したhttps://gitlab.comがIDトークンのaudienceクレームに入るので、AWSにIDプロバイダを追加する際に設定した対象者と一致する必要があります。
CI_JOB_JWT_V2
という何もしなくてもIDトークンが格納されている環境変数もあるのですが、こちらはGitLab16.5で廃止となる旨がGitLabのドキュメントに書いてあります。OIDCがα版として提供されていた頃はこちらを使っていたようですが、今後も使い続けられるのはid_tokensキーワードを使って生成したトークンのみです。
ただし、id_tokensが使えるのはGitLab15.7以降なので、セルフホストしているGitLabでバージョンが古い場合などはCI_JOB_JWT_V2
を使わざるを得ないといった状況もあり得るかもしれません。
scriptのassume-role-with-web-identityの引数で指定しているROLE_ARNという環境変数には、先程作成したIAM RoleのARNをセットしておく必要があります。GitLabの画面から、Settings > CI/CD > Variablesで環境変数の設定画面を開き、環境変数の追加を行ってください。
CI_PROJECT_ID, CI_PIPELINE_IDなどの環境変数も使用していますが、これらはGitLabでCIを動かす際、最初から値が入っています。
動作確認
上記の設定を終え、.gitlab-ci.ymlをリポジトリにpushしたところ、下記のように取得した一時的な認証情報でawsコマンドを実行できました(UserIdなどはブラウザで編集して消していますが実際にはちゃんと表示されました)。
環境変数にはAssume Role先のIAM RoleのARNしか入れていないので安心ですね。
まとめ
最近production-readyになったばかりなのもあり、日本語で検索するとα版のIDトークンを使った記述しか見当たらなかったため記事にまとめてみました。少しでもお役に立てば幸いです。