静的認証情報の管理をテーマにした以前の記事では、デジタルサービスへの接続に使うパスワード、トークン、API キーといったシークレットの必要性と、インフラストラクチャとデータを侵害や不正使用から守るためにシークレットを守ることの重要性について説明しました。

ソフトウェアを広範囲に提供している場合、複数のチームやプロジェクトにわたる認証情報の管理は、すぐに面倒で間違いの起こりやすい作業になり、ボトルネックや不必要なリスクが発生することがあります。長期間有効な静的認証情報は特に脆弱です。こうした認証情報はたいてい、幅広いリソースやサービスへのアクセスに利用されるので、最小権限の原則を適用するのが難しいうえ、セキュリティインシデントの発生時には影響範囲を抑えにくくなってしまう可能性があります。

このチュートリアルでは、プラットフォームエンジニア向けに、パイプラインのセキュリティの強化対策として OpenID Connect (OIDC) トークンを実装して一元的なシークレットストアで認証し、カスタム OIDC クレームで詳細なアクセス制御をする方法について説明します。その過程で、OIDC を使って CircleCI パイプラインを HashiCorp Vault に接続する方法と、Vault ポリシーおよびカスタム OIDC クレームを使ってロールベースのアクセス制御 (RBAC) を適用する方法も説明します。

OIDC とカスタムクレームを使ってロールベースの認証情報管理を実装するメリット

プラットフォームエンジニアに課せられる大きな責任の 1 つが、効率的かつ安全なセルフサービスワークフローを提供して開発チームをサポートすることです。その取り組みの基礎となるのが、効率的な CI/CD パイプラインの構築です。ソフトウェアデリバリープロセスを自動化するために、CI/CD パイプラインでは、保護されているさまざまな環境やサービスにアクセスできる必要があります。機密性の高い認証情報への広範なアクセス許可をパイプラインに付与することにはリスクが伴います。このリスクは、OIDC を使って短期間だけ有効な認証情報を実装すると回避できます。 さらに詳しく: プラットフォームエンジニアリングへの道 (英語)

OIDC は、OAuth 2.0 認証プロトコルを基に構築された ID レイヤーです。OIDC を使うと、サービスで一時的な (通常は 1 回のリクエスト期間だけ有効な) アクセストークンを使用して、リソースに対するアクセスを認証および認可できます。

プラットフォームエンジニアにとって、OIDC による認証は、長期間有効な認証情報を漏洩させてしまうことなく、必要なリソースへのアクセス許可を開発者に付与する手段となります。さらに、カスタム OIDC クレームを活用すれば、認証情報にアクセス可能なチーム、リポジトリ、プロジェクトを制限したり、セキュリティインシデントの際にはすばやくキーを無効化して入れ替えたりできます。

最小権限の原則とは、リソースへの包括的なアクセス許可を付与するのではなく、所定のタスクを行うのに必要なリソースへのアクセス許可のみをユーザーやサービスに付与する手法です。RBAC を使って CI パイプラインでのシークレットへのアクセスをプロジェクトごと、ユーザーごと、ブランチごと、またはタグごとに制限すると、この原則を守ることができます。不審な行動を検出した場合は、セキュアなシークレット管理サービスへのアクセス許可を取り消し、影響を受けるシークレットすべてをすばやく特定して即座に入れ替えられます。これにより、過度な権限が設定された共有認証情報を攻撃者に悪用され、ネットワークの広範囲にわたって侵害される事態を防ぐことができます。

HashiCorp Vault では、静的認証情報を安全に保管できます。また、プラットフォームエンジニアは、OIDC クレームを使用した RBAC 機能により、どのユーザーまたは CI/CD ワークフローがどの環境やサービスに認証できるのかを詳細に制御できます。

以降のセクションでは、HashiCorp Vault、OIDC トークン、およびカスタムクレームを使って認証情報の管理を一元化し、最小権限の原則をパイプラインに実装する方法について説明します。

OIDC で CircleCI を HashiCorp Vault に接続するための前提条件

CircleCI を HashiCorp Vault に接続するには、以下の準備が必要です。

OIDC とそのベースである OAuth 2.0 プロトコルに詳しくない場合は、ぜひこちらの記事で学んでください。OIDC と OAuth は認証と認可の業界標準となっており、AWSGoogle CloudAzure などのサービスで接続先リソースへのアクセスを保護する手段としてサポートされています。

CircleCI と Vault を使用してロールベースの OIDC 認証をセットアップする

CircleCI の設定には、.circleci/config.yml ファイルを使います。このファイルの編集は、CircleCI の Web インターフェースまたは Git リポジトリで行えます。Vault のインストールと設定は Vault のコマンドラインインターフェースで行います。設定完了後は、用意されているグラフィカル Web インターフェースで簡単にシークレットを管理できます。

Vault と CircleCI を設定する

CircleCI の CI/CD プラットフォームは柔軟であり、Orb (再利用可能なパイプライン設定のパッケージ) を使ってほとんどすべてのサードパーティ製サービスと連携できます。また、各種オペレーティングシステムやアーキテクチャでのコマンドライン操作に対応し、APIWebhook もサポートされています。

Vault をセットアップし、OIDC を使って Vault と接続するように CircleCI パイプラインを設定するには、HashiCorp Vault と CircleCI を連携させる方法に関するチュートリアルを参照してください。このチュートリアルでは、Vault インスタンスをセットアップし、CircleCI パイプラインから Vault CLI を呼び出して OIDC 認証でシークレットを取得する方法について説明しています。

Success after retrieving secrets from Vault

Vault でポリシーをセットアップする

OIDC で CircleCI と Vault を接続したら、ロールベースでのワークフロータスクへのアクセスの管理を実装しましょう。この手順を完了すると、シークレットにアクセス可能なプロジェクト、ユーザー、自動 CI/CD パイプラインを詳細に制御できるようになります。

前述のリンクに示した OIDC チュートリアルでは、ポリシー 1 つと、CircleCI プロジェクト ID を参照して Vault 内のシークレットへのアクセス許可を付与するロールを用意しました。Vault では、オブジェクトを階層パス構造で保存し、ポリシーにより特定のパスに対するアクセスを許可または拒否します。

次は、CircleCI での RBAC の活用の幅を広げるために、Vault 設定で追加のポリシーを作成します。各 CI/CD パイプラインに含まれるタスクごとに、必要なシークレットが保存されている特定の Vault パスへのアクセスを各ポリシーで許可します。

以下のシェルコマンドを実行すると、サンプル Vault ポリシーが作成されます。このポリシーをロールに追加すると、パス secret/data/circleci-rbac-demo に保存されているシークレットすべてへのアクセス許可がロールに付与されます。

vault policy write circleci-rbac-demo-policy - <<EOF
# Grant users with this role access to secrets under the 'secret/data/circleci-rbac-demo/*' path
path "secret/data/circleci-rbac-demo/*" { 
  capabilities = ["read", "list"] 
}
EOF

以下のコマンドを実行すると、作成済みの全ポリシーを一覧表示できます。

vault policy list

CircleCI で OIDC クレームを使い Vault でのロールベースのアクセス制御を実装する

OIDC では、認証を受けた主体がアプリケーションに対するクレームを作成します。アプリケーションでは、このクレームに含まれる属性を使ってユーザーのアクセス許可を判断できます。今回の場合、Vault では CircleCI の OIDC クレームに含まれる情報を使用してロールを割り当てます。

ポリシーは、1 つのロールに複数割り当てることができます。この仕組みを利用して、Vault に接続する CircleCI のプロジェクトやパイプラインごとにロールを作成し、必要に応じてロールにポリシーを追加または削除することで Vault シークレットへのアクセスを管理できます。こうすれば、シークレットにアクセス可能なユーザーとサービスを詳細に制御できます。

以下の例で作成する Vault ロールでは、プロジェクトの Git リポジトリのブランチに基づいてシークレットへのアクセスを制限します。CircleCI パイプラインは、コミットごとにトリガーすることも、特定のブランチへのコミット時にのみ実行するよう設定することもできます。シークレットへのアクセスを特定のブランチのみに制限すると、たとえば、本番環境のリソースへの他のパイプライン (開発環境用など) のアクセスを禁止できます。

vault write auth/jwt/role/circleci-rbac-demo -<<EOF
{
  "role_type": "jwt",
  "user_claim": "sub",
  "user_claim_json_pointer": "true",
  "bound_claims": {
    "oidc.circleci.com/vcs-ref": "refs/heads/prod"
  },
  "policies": ["default", "circleci-rbac-demo-policy"],
  "ttl": "10m"
}
EOF

このロールは circleci-rbac-demo という名前であり、OIDC 認証に CircleCI から提供される oidc.circleci.com/vcs-ref クレームを使用します。このクレームの形式 は refs/heads/branch_name です。つまり、prod ブランチで実行されるパイプラインでの操作を制限するために、Vault でこのカスタムクレームの値が refs/heads/prod に一致するかどうかが確認されます。一致する場合、default ポリシーと circleci-rbac-demo-policy ポリシーが割り当てられます。CircleCI は、リクエストの実行時には、デフォルトの OIDC クレームとともに、ロールの割り当てに使用可能な CircleCI 固有の情報を含んだ追加のクレームも提供します。

OIDC を使って Vault を連携するチュートリアルでは、CircleCI から Vault に接続するためのロールを環境変数 VAULT_ROLE に保存しました。 Vault の list コマンドを使用すると、作成済みのロールすべてを一覧表示できます。

vault list auth/jwt/role

複数のシークレットへのアクセス許可を設定したロールをいくつか用意できたところで、リポジトリの各ブランチにコミットを行い、RBAC 実装をテストしてみましょう。上記で用意したサンプルポリシーとロールを使用するリポジトリでは、実装した RBAC により、prod ブランチ以外から実行するパイプラインが失敗します。

Unsuccessful pipeline execution

Vault に保存されているシークレットを入れ替える

Vault で認証情報を一元管理することでセキュリティを高められますが、認証情報は定期的に入れ替えることをお勧めします。この方法の例として、Vault コマンドラインインターフェースを利用した Bash スクリプトを以下に示します。このスクリプトは、みなさん各自の要件にあわせて調整可能です。

#!/bin/bash

# The Vault CLI is required for this script 
# https://developer.hashicorp.com/vault/downloads

# Set the token to access Vault. 
# The variable name VAULT_TOKEN is the default that
# is read by Vault if it is not already authenticated
# Tokens can be generated from the Vault CLI 
# https://developer.hashicorp.com/vault/docs/commands/token/create
VAULT_TOKEN=$(vault token create -period=30m -field=token -tls-skip-verify)

# Secrets will be read from a CSV file containing
# a comma-separated username/password
SECRETS_FILE="./secrets.csv"
VAULT_KV_PATH="secret/circleci-demo/demo-secrets"

# Get the username/password values by splitting 
# the contents of the secrets file at the comma (,)
USERNAME="$(cat $SECRETS_FILE | cut -d',' -f1)"
PASSWORD="$(cat $SECRETS_FILE | cut -d',' -f2)"

# Update the secret stored in Vault 
# https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2
vault kv put -tls-skip-verify  "$VAULT_KV_PATH" \
	username="$USERNAME" \
	password="$PASSWORD" 

このスクリプトでは、Vault インスタンスに接続して CLI の書き込みコマンドを呼び出し、Vault の KV (キー/値) エンジン に保存されているユーザー名とパスワードを、ファイル secrets.csv から取得した新しい値に入れ替えます。このスクリプトは、cron ジョブから呼び出すことも、パイプラインのスケジュール実行に組み込むことも可能です。HashiCorp Vault には、コマンドラインインターフェースを使わず、Web サービスなどの環境から呼び出すことのできる HTTP API もあります。

構築したキーのローテーションプロセスは、スケジュールに従って呼び出すほか、インシデントの進行中にはオンデマンドで呼び出します。特に注目すべきなのは、Vault で保存データのセキュリティ確保のために使用されている暗号化キーの入れ替え機能です。不審な活動が発生した際には、Vault インスタンスをロックダウンして、全クライアントに再認証を求めることができるようになっています。

認証先のサービスが対応していれば、Vault の動的シークレット機能を使用できるので、手動でキーを入れ替える必要はありません。そのため、認証情報の保存をまとめて行う必要もありません。認証情報を求められたときには、サードパーティ製サービスから一時的なシークレットを取得し、リクエスト元に渡せば対応できます。動的シークレットは使用後即座に無効になるため、機密性の高いリソースへの認証と認可を行ううえで最も安全な方法です。

まとめ

組織を攻撃する経路は増え続けており、攻撃の脅威は絶えません。マルウェア、物理的侵害、ソーシャルエンジニアリング、脅迫、ソフトウェア脆弱性、シークレットの管理不備はいずれも、デジタルインフラストラクチャを通じて悪意のある人物がビジネスに損害を与える隙につながります。

プラットフォームエンジニアの仕事は、開発チームが効果的に作業をするために必要なツールを用意することです。用意したツールによって、組織に生じるリスクが不必要に増えてはなりません。シークレット管理のベストプラクティスを遵守することが必須です。また、チームのセキュリティ体制が万全であったとしても、ソフトウェアサプライチェーンに固有のリスクが存在するために、問題にすばやく対処するためのツールやポリシーを組織で準備しておく必要があります。

短期間だけ有効な OIDC トークンを使うことで、静的シークレットが抱えるセキュリティ上の問題の多くを解決できます。セキュアなシークレット管理システムにロールベースの OIDC 認証を組み込めば、機密性の高い認証情報を詳細に管理し、長期シークレットの保存と使用で生じるリスクを軽減できます。今すぐ CircleCI の無料アカウントを作成して、組織の認証情報のセキュリティ保護、およびソフトウェアデリバリーパイプラインの管理を始めましょう。