CircleCI のセキュリティを強化するために GCP 認証 に OIDC を導入する

2023-03-24

はじめに

こんにちは。株式会社 Belong で SRE をしている shigwata です。

ご存知の方も多いかと思いますが、今年の年初に CircleCI のインシデントが発生しました。このインシデントにより機密情報の漏洩が疑われ、多くの企業が対策を迫られたことは記憶に新しいかと思います。

Belong では、CI/CD に CircleCI と GitHub Actions を利用しています。GitHub Actions では、OIDC を利用した GCP 認証していましたが、CircleCI は OIDC に対応していなかったため、GCP のサービスアカウントキーを利用した認証していました。

機密情報であるサービスアカウントキーの漏洩が疑われたため、独自に開発していたキーローテーションツールを利用してすべてのサービスアカウントキーを更新しました。しかし今後のことを考えた場合、セキュリティ強化は行うべきであるため今回の件をきっかけに OIDC に移行する計画が立ち上がりました。

本記事では、OIDC をなぜ導入すべきかに触れつつ、Terraform を使って CircleCI に OIDC を用いた GCP 認証を導入する手順について紹介します。

OIDC を導入するメリット

OIDC を導入する前は、サービスアカウントキーを GCP のコンソールから発行し、環境変数に登録する必要がありました。しかし、サービスアカウントキーにはデフォルトで有効期限が設定されません。そのため漏洩しないようにユーザの責任で管理し、キーローテーションなどをする必要があります。

OIDC を利用すると、サービスアカウントキーを利用しなくても有効期限の短いトークンによる認証が可能となります。これにより、より安全な認証が行えるようになります。

設定手順

CircleCI のドキュメントに従って、OIDC を使って認証できるように設定します。

  1. GCP のセットアップ
  2. CircleCI 設定ファイルへの GCP の追加

GCP のセットアップ

Belong ではインフラの管理に Terraform を利用しているため、Terraform でサンプルコードを用意しました。

使用する際には、CIRCLECI_ORGANIZATION_IDCircleCI Web アプリの [Organization Settings > Overview] の Organization ID に書き換えてください。

locals {
  # GCPプロジェクト
  project_id = "<PROJECT_ID>"

  circleci_organization_id = "<CIRCLECI_ORGANIZATION_ID>"
  circleci_project_id      = "<CIRCLECI_PROJECT_ID>"
}

# サービスアカウント作成
resource "google_service_account" "sa" {
  project    = local.project_id
  account_id = "test-storage-sa"
}

# サービスアカウントに付与したいロールを設定
resource "google_project_iam_member" "project" {
  project = local.project_id
  role    = "roles/storage.admin"
  member  = "serviceAccount:${google_service_account.sa.email}"
}

# pool作成
resource "google_iam_workload_identity_pool" "circleci" {
  provider = google-beta

  workload_identity_pool_id = "circleci"
}

# プロバイダー作成
resource "google_iam_workload_identity_pool_provider" "circleci" {
  provider = google-beta

  workload_identity_pool_id          = google_iam_workload_identity_pool.circleci.workload_identity_pool_id
  workload_identity_pool_provider_id = "circleci-prvdr"

  attribute_mapping = {
    "attribute.aud"        = "assertion.aud"
    "attribute.iss"        = "assertion.iss"
    "attribute.project_id" = "assertion['oidc.circleci.com/project-id']"
    "google.subject"       = "assertion.sub"
  }

  oidc {
    allowed_audiences = [local.circleci_organization_id]
    issuer_uri        = "https://oidc.circleci.com/org/${local.circleci_organization_id}"
  }
}

resource "google_service_account_iam_member" "wif-sa" {
  service_account_id = google_service_account.sa.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.circleci.name}/attribute.project_id/${local.circleci_project_id}"
}

CircleCI 設定ファイルへの GCP の追加

認証に必要な $CIRCLE_OIDC_TOKEN を利用するには Context を読み込む必要があります。

また下記の環境変数を Context に追加する必要もあります。 サンプルでは gcp-oidc という名前で作成します。

namevalue
GOOGLE_PROJECT_ID{GCP プロジェクト}
OIDC_SERVICE_ACCOUNT_EMAIL{サービス アカウントのメールアドレス}
OIDC_WIP_IDcircleci
OIDC_WIP_PROVIDER_IDcircleci-prvdr

config.yml のサンプルコードは次のようになります。

version: 2.1

orbs:
  gcp-cli: circleci/gcp-cli@3.0.1

jobs:
  build:
    executor: gcp-cli/default
    steps:
      - run:
          command: |
            # OIDC トークンを一時ファイルに保存する
            echo $CIRCLE_OIDC_TOKEN > "$HOME/oidc_token"
            # 生成された OIDC ID トークンの資格情報を作成する
            gcloud iam workload-identity-pools create-cred-config \
                "projects/${GOOGLE_PROJECT_ID}/locations/global/workloadIdentityPools/${OIDC_WIP_ID}/providers/${OIDC_WIP_PROVIDER_ID}"\
                --output-file="~/gcp_cred_config.json" \
                --service-account="${OIDC_SERVICE_ACCOUNT_EMAIL}" \
                --credential-source-file="$HOME/oidc_token"
      - run:
          command: |
            # 生成された認証情報を利用するように gcloud を設定する
            gcloud auth login --brief --cred-file "~/gcp_cred_config.json"
            # ADC の設定
            echo "export GOOGLE_APPLICATION_CREDENTIALS='~/gcp_cred_config.json'" | tee -a "$BASH_ENV"
      - run:
          command: |
            # GCPにアクセスする処理を書く

workflows:
  main:
    jobs:
      - build:
          name: Generate Creds File and Authenticate
          context:
            - gcp-oidc

Belong では実際には上記のコードを CircleCI プライベート Orb で管理しています。

現在は、circleci/gcp-cli Orb の setup コマンドで OIDC に対応したことで導入しやすくなっています。しかし setup 内で gcloud のインストール コマンドが呼び出されてしまうため、社内の使い方に合わず利用していません。この PR で skip_install パラメータが提案されているので期待しています。

まとめ

サービスアカウントキーから OIDC への切り替えはセキュリティ性が高く、GCP サービスを安全に利用できるようになります。 Google Cloud プロジェクト管理の観点からも、OIDC を使用することで管理がより簡単になります。 この記事が、CircleCI で GCP を利用する際のセキュリティを強化し、効率的な管理方法を提供する手助けになると幸いです。

また弊社 Belong では一緒にサービスを育てる仲間を募集しています。 もし弊社に興味を持っていただけたら <エンジニアリングチーム紹介ページ> をご覧いただけたら幸いです。