開発本部SREグループの金澤です。
今回は GitLab にアルファ版としてサポートされた OIDC(OpenID Connect)
を使用して、よりセキュアな環境で GitLab CI/CD を実施するようにした内容をご紹介します。
現状と課題
弊社製品の intdash のサーバサイドソフトウェアである intdash Server は、基本的にクラウドインフラを使用して構築しており、Terraform や Ansible による構成管理を行っています。コード管理には GitLab を利用しており、GitLab はセルフマネージドで運用しています。
GitLab は契約の種類(SaaS・セルフマネージド)を問わず CI/CD 機能を使用することができます。インフラのソースコードについても本機能を利用してコードの正常性を担保するようにしています。コードの内容と実装が相違ないことの確認や、必要な定期処理の実施が主な目的です。
インフラにおける CI/CD 利用時はその特性上、クラウドインフラへのリソースアクセスを行う機会が多く、これまでは各クラウドインフラのID・アクセス管理(IdAM/IAM)サービスから認証情報を払い出し使用していたのですが、払い出した認証情報のローテーション対応や万が一の漏洩に対するリスク管理が必要となり、セキュリティの管理的にはあまり望ましいオペレーションではありませんでした。
CI/CD と OpenID Connect
OIDC(OpenID Connect)は、OAuth 2.0 をベースとする認証プロトコルです。
GitHub の CI/CD 機能である GitHub Action は昨年11月末に対応アナウンスがあり話題になりましたが、GitLab も今年1月にリリースされた GitLab 14.7
にてアルファ版として機能対応が発表されました。
本機能を使用することによって、ID・アクセス管理サービスから認証情報を払い出す必要はなくなり、一時的な認証情報によってクラウドインフラのリソースを操作することが可能となりました。払い出されるのは一時的な認証情報となるので、前述したローテーション管理やリスク管理を軽減することが期待できます。GitLab ではこのような認証ワークフローで動作します。
設定
それでは実際に設定していきます。今回は「あらかじめ認証情報を登録せず GitLab CI からクラウドインフラリソース(Amazon S3)を参照できること」を確認します。
設定する内容についてはGitLab 公式ドキュメントで紹介されています。この内容を踏まえながら、Terraform コードベースで設定していきます。アプトポッドではクラウドインフラとして主に AWS を利用していますので、登録するクラウドインフラを AWS としています。
クラウドインフラでOpenIDプロバイダーを登録する
クラウドインフラに OpenID プロバイダーを登録します。aws_iam_openid_connect_provider
リソースを使用して OpenID プロバイダーを設定します。
- url: GitLab インスタンス URL
- client_id_list: GitLab インスタンス URL
- thumbprint_list: こちらを参照して算出する。
resource "aws_iam_openid_connect_provider" "oidc-sample" { url = "https://gitlab.example.net" client_id_list = [ "https://gitlab.example.net", ] thumbprint_list = [ "算出した値", ] }
手動で登録する場合 thumbprint_list
の値は AWS コンソール上に存在する「サムプリント取得」というボタンを押下することで取得することが出来ますが、コードで表現する場合は事前に取得する必要があります。事前取得ではなく自動化したい場合は、tls_certificate
というデータリソースを使用して実現することができます。(今回は実施していません。)
登録した OpenID プロバイダー用の IAM ロールを作成する
登録した OpenID プロバイダーを信頼されたエンティティに設定するかたちで、IAM ロールを作成します。
- statement.condition.variable: GitLab インスタンス URL および JWT フィールド
- ex) "gitlab.example.com:sub"
- statement.condition.values: 対象とする GitLab プロジェクトおよびブランチ
- ex) "project_path:mygroup/myproject:ref_type:branch:ref:main"
下記の例のstatement.condition.values
ではワイルドカードを指定することもできます。ワイルドカードを指定することによって1つのプロジェクトにおける複数のブランチを対象とすることができます。その場合、statement.condition.test
の値をStringLike
としてください。指定の方法は公式ドキュメントにも記載がありますのでご参照ください。
resource "aws_iam_role" "oidc-sample" { name = "role_oidc_sample" assume_role_policy = data.aws_iam_policy_document.oidc-sample-principal.json } data "aws_iam_policy_document" "oidc-sample-principal" { statement { actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = [aws_iam_openid_connect_provider.oidc-sample.arn] } effect = "Allow" condition { test = "StringEquals" variable = "gitlab.example.com:sub" values = ["project_path:mygroup/myproject:ref_type:branch:ref:main"] } } } resource "aws_iam_policy" "oidc-sample" { name = "for-oidc" path = "/" policy = data.aws_iam_policy_document.oidc-sample-policy.json } data "aws_iam_policy_document" "oidc-sample-policy" { statement { actions = [ "s3:*", ] resources = ["*"] effect = "Allow" } } resource "aws_iam_role_policy_attachment" "oidc-sample" { role = aws_iam_role.oidc-sample.name policy_arn = aws_iam_policy.oidc-sample.arn }
GitLab プロジェクトへの変数定義
作成した IAM ロールの ARN を GitLab CI の変数定義に登録します。今回登録する変数は IAM ロールの ARN のみとしています。
CI/CD を動作させて確認する
実際に CI/CD を動作させて確認してみます。.gitlab-ci.yaml
は以下のように記載しています。表記中の CI_JOB_JWT_V2
はOIDCサポートと同時に導入されたJWT(JSON Web Token)です。こちらを利用してクラウドインフラとJWTトークンベースの接続を行っています。CI スクリプトの中で echo
を行い存在の確認をしようとしています。
.gitlab-ci.yaml
image: name: amazon/aws-cli:latest entrypoint: - '/usr/bin/env' assume role: script: - echo ${CI_JOB_JWT_V2} - > STS=($(aws sts assume-role-with-web-identity --role-arn ${ROLE_ARN} --role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" --web-identity-token $CI_JOB_JWT_V2 --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text)) - export AWS_ACCESS_KEY_ID="${STS[0]}" - export AWS_SECRET_ACCESS_KEY="${STS[1]}" - export AWS_SESSION_TOKEN="${STS[2]}" - aws s3 ls
結果
GitLab CI の結果を記載します。GitLab 上にはアクセスキーなどの認証情報を登録していませんでしたが、aws s3 ls
で現在の S3 バケットの一覧情報が表示されました。CI_JOB_JWT_V2
については、[MASKED]
といった形で隠蔽されることが分かりました。このことから aws sts assume-role-with-web-identity
で一時的な認証情報が取得できていそうです。
(中略) $ echo ${CI_JOB_JWT_V2} [MASKED] $ STS=($(aws sts assume-role-with-web-identity --role-arn ${ROLE_ARN} --role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" --web-identity-token $CI_JOB_JWT_V2 --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text)) $ export AWS_ACCESS_KEY_ID="${STS[0]}" $ export AWS_SECRET_ACCESS_KEY="${STS[1]}" $ export AWS_SESSION_TOKEN="${STS[2]}" $ aws s3 ls 2020-06-23 06:08:59 analytics-blobs-dev-log-personal 2020-06-23 06:20:23 analytics-blobs-dev-personal :
注意点
冒頭でも記載しました通り、(2022/07/07 時点で) 本機能はアルファ版であり本番環境での使用はまだ適していないことにご注意ください。
まとめ
今回は、GitLab でサポートされた OIDC(OpenID Connect)
機能を利用して、よりセキュアな環境で GitLab CI/CD を実施するようにした内容をご紹介しました。
あらかじめ認証情報を払い出し GitLab シークレットに登録することなく、クラウドインフラのリソースを操作できることが確認出来ました。これにより GitLab CI/CD におけるセキュリティ面の向上はもちろん、CI/CD を利用したいが認証情報の取り扱いなどから生まれる心理的な障壁の一端を無くすことができると考えています。
本記事が少しでも参考になりましたら幸いです。