GitLab CI/CDでHashiCorp Vaultシークレットを使用する
- プラン: Premium、Ultimate
- 提供形態: GitLab.com、GitLab Self-Managed、GitLab Dedicated
GitLab CI/CDでHashiCorp Vaultのシークレットを使用できます。IDトークンを使用して、HashiCorp Vaultで認証する。
CIジョブでVaultのシークレットを使用する前に、Vaultサーバーを設定する必要があります。HashiCorp Vaultを使用した認証とシークレットの読み取りのチュートリアルには、IDトークンを使用した認証に関する詳細が記載されています。
以下の例では、vault.example.comのURLをVaultサーバーのURLに、gitlab.example.comをGitLabインスタンスのURLに置き換えてください。
Vaultサーバーを設定する
Vaultサーバーを設定するには:
次のコマンドを実行して認証方法を有効にします。これらは、GitLabインスタンスのOIDC Discovery URLをVaultサーバーに提供するため、Vaultは公開署名キーをフェッチし、認証時にJSON Webトークン(JWT)を検証できます:
$ vault auth enable jwt $ vault write auth/jwt/config \ oidc_discovery_url="https://gitlab.example.com" \ bound_issuer="gitlab.example.com"特定のパスおよびオペレーションへのアクセスを許可または禁止するようにVaultサーバーでポリシーを設定します。この例では、本番環境に必要なシークレットのセットへの読み取りアクセスを許可します:
vault policy write myproject-production - <<EOF # Read-only permission on 'ops/data/production/*' path path "ops/data/production/*" { capabilities = [ "read" ] } EOFプロジェクトまたはネームスペースに制限して、Vaultサーバーのロールを設定します。
Vaultサーバーの詳細を提供するには、次のCI/CD変数を作成します:
VAULT_SERVER_URL: :VaultサーバーのURL。例:https://vault.example.com:8200。VAULT_AUTH_ROLE: オプション。認証を試行するときに使用するロール。ロールが指定されていない場合、Vaultは、認証方法の設定時に指定されたデフォルトロールを使用します。VAULT_AUTH_PATH: オプション。認証方法がマウントされているパス。デフォルトはjwtです。VAULT_NAMESPACE: オプション。シークレットの読み取りと認証に使用するVault Enterpriseネームスペース。以下のとおりになります:- Vaultでは、ネームスペースが指定されていない場合は
root(「/」)ネームスペースが使用されます。 - Vaultオープンソースでは、この設定は無視されます。
- HashiCorp Cloud Platform(HCP)Vaultでは、ネームスペースが必要です。HCP Vaultは、デフォルトで
adminネームスペースをルートネームスペースとして使用します。たとえばVAULT_NAMESPACE=adminなどです。
- Vaultでは、ネームスペースが指定されていない場合は
サーバーロールを構成する
CIジョブは、認証を試行するときにロールを指定します。ロールを使用して、さまざまなポリシーをグループ化できます。認証が成功すると、これらのポリシーが結果のVaultトークンにアタッチされます。
バインドされたクレームは、JWTクレームに一致する定義済みの値です。バインドされたクレームを使用すると、特定のGitLabユーザー、特定のプロジェクト、または特定のGit参照に対して実行されているジョブへのアクセスを制限できます。バインドされたクレームは、必要に応じていくつでも持つことができますが、認証を成功させるには、すべてが一致する必要があります。
バインドされたクレームをユーザーロールや保護ブランチなどのGitLab機能と組み合わせることで、これらのルールを調整して、特定のユースケースに適合させることができます。この例では、認証が許可されるのは、本番環境リリースに使用されるパターンに一致する名前の保護されたタグに対して実行されているジョブのみです:
$ vault write auth/jwt/role/myproject-production - <<EOF
{
"role_type": "jwt",
"policies": ["myproject-production"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_audiences": "https://vault.example.com",
"bound_claims_type": "glob",
"bound_claims": {
"project_id": "42",
"ref_protected": "true",
"ref_type": "tag",
"ref": "auto-deploy-*"
}
}
EOF提供されたクレーム(project_idやnamespace_idなど)のいずれかを使用して、常にロールをプロジェクトまたはネームスペースに制限してください。このように制限しないと、このGitLabインスタンスによって生成されたJWTは、このロールを使用して認証できる可能性があります。
IDトークンJWTクレームの完全なリストについては、GitLab CI/CDでHashiCorp Vaultのシークレットを使用するチュートリアルをレビューしてください。
有効期限、IPアドレス範囲、使用回数など、結果のVaultトークンにいくつかの属性を指定することもできます。オプションの完全なリストは、JSON Webトークンメソッドのロールの作成に関するVaultのドキュメントに記載されています。
CIジョブでVaultのシークレットを使用する
ジョブに少なくとも1つのIDトークンが定義されている場合、secretsキーワードは、そのトークンを自動的に使用してVaultで認証します。
Vaultサーバーの構成後、Vaultに保存されているシークレットを使用するには、secrets:vaultキーワードを使用します:
job_using_vault:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
DATABASE_PASSWORD:
vault: production/db/password@ops
token: $VAULT_ID_TOKENこの例では:
production/dbは、シークレットのパスです。passwordは、フィールドです。opsは、シークレットエンジンがマウントされているパスです。production/db/password@opsは、ops/data/production/dbのパスに変換されます。- 認証は
$VAULT_ID_TOKENを使用します。
GitLabがVaultからシークレットをフェッチした後、値は一時ファイルに保存されます。このファイルのパスは、fileタイプの変数と同様に、DATABASE_PASSWORDという名前のCI/CD変数に保存されます。
デフォルトの動作を上書きするには、fileオプションを明示的に設定します:
secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
DATABASE_PASSWORD:
vault: production/db/password@ops
file: false
token: $VAULT_ID_TOKENこの例では、シークレット値は、それを保持するファイルを指すのではなく、DATABASE_PASSWORD変数に直接配置されます。
シークレットエンジン
GitLab Runnerは、secrets:engine:nameキーワードで異なるシークレットエンジンをサポートしています:
| シークレットエンジン | secrets:engine:nameの値 | Runnerバージョン | 詳細 |
|---|---|---|---|
| KVシークレットエンジン - バージョン2 | kv-v2 | 13.4 | kv-v2は、エンジンタイプが明示的に指定されていない場合にGitLab Runnerが使用するデフォルトのエンジンです。 |
| KVシークレットエンジン - バージョン1 | kv-v1またはgeneric | 13.4 | genericキーワードのサポートは、GitLab 15.11で導入されました。 |
| <AWSシークレットエンジン | generic | 16.11 | |
| HashiCorp Vault Artifactory Secrets Plugin | generic | 16.11 | このシークレットバックエンドは、JFrog Artifactoryサーバー(5.0.0以降)と通信し、指定されたスコープでアクセストークンを動的にプロビジョニングします。 |
別のシークレットエンジンを使用する
デフォルトでは、kv-v2シークレットエンジンが使用されます。別のエンジンを使用するには、設定のvaultの下にengineセクションを追加します。
たとえば、Artifactoryのシークレットエンジンとパスを設定するには、次のようにします:
job_using_vault:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
JFROG_TOKEN:
vault:
engine:
name: generic
path: artifactory
path: production/jfrog
field: access_token
file: falseこの例では、シークレット値はartifactory/production/jfrogからフィールドaccess_tokenで取得されます。
トラブルシューティング
自己署名証明書エラー: certificate signed by unknown authority
Vaultサーバーが自己署名証明書を使用している場合、ジョブログに次のエラーが出力されます:
ERROR: Job failed (system failure): resolving secrets: initializing Vault service: preparing authenticated client: checking Vault server health: Get https://vault.example.com:8000/v1/sys/health?drsecondarycode=299&performancestandbycode=299&sealedcode=299&standbycode=299&uninitcode=299: x509: certificate signed by unknown authorityこのエラーを解決するには、2つのオプションがあります:
- 自己署名証明書をGitLab RunnerサーバーのCAストアに追加します。Helmチャートを使用してGitLab Runnerをデプロイした場合は、独自のGitLab Runnerイメージを作成する必要があります。
VAULT_CACERT環境変数を使用して、証明書を信頼するようにGitLab Runnerを設定します:- systemdを使用してGitLab Runnerを管理している場合は、GitLab Runnerの環境変数を追加する方法を参照してください。
- Helmチャートを使用してGitLab Runnerをデプロイした場合は、次のようにします:
GitLabにアクセスするためのカスタム証明書を提供し、GitLabの証明書の代わりに、Vaultサーバーの証明書を必ず追加してください。GitLabインスタンスも自己署名証明書を使用している場合は、同じ
Secretに両方を追加できるはずです。values.yamlファイルに次の行を追加します:## Replace both the <SECRET_NAME> and the <VAULT_CERTIFICATE> ## with the actual values you used to create the secret certsSecretName: <SECRET_NAME> envVars: - name: VAULT_CACERT value: "/home/gitlab-runner/.gitlab-runner/certs/<VAULT_CERTIFICATE>"
GitLab Development Kit(GDK)を使用して、開発モードでVaultサーバーをローカルで実行している場合も、このエラーが発生する可能性があります。Vaultサーバーの自己署名証明書を信頼するように手動でシステムに指示できます。このサンプルチュートリアルでは、macOSで同じことを行う方法について説明しています。
resolving secrets: secret not found: MY_SECRETエラー
GitLabがVaultでシークレットを見つけられない場合、次のエラーが表示されることがあります:
ERROR: Job failed (system failure): resolving secrets: secret not found: MY_SECRETvault値がCI/CDジョブで正しく設定されていることを確認します。
Vault CLIでkvコマンドを使用して、シークレットを取得できるかどうかを確認し、CI/CD設定のvault値の構文決定に役立てることができます。たとえば、シークレットを取得するには、次のようにします:
$ vault kv get -field=password -namespace=admin -mount=ops "production/db"
this-is-a-password