Google Cloud 인증 방식

Google Cloud 인증 방식

gcp 인증 방식은 Google Cloud Platform 엔티티가 Vault에 인증할 수 있게 해요. Vault는 Google Cloud를 신뢰할 수 있는 제3자로 취급하고 Google Cloud API에 대해 인증 엔티티를 검증해요. 이 백엔드는 다음의 인증을 허용해요:

  • Google Cloud IAM 서비스 계정
  • Google Compute Engine (GCE) 인스턴스

이 백엔드는 Google Cloud에 특정한 아이덴티티에 집중하며 임의의 Google 또는 Google Workspace 사용자나 Google에 대한 일반 OAuth를 인증하지는 않아요.

이 플러그인은 hashicorp/vault-plugin-auth-gcp의 별도 GitHub 저장소에서 개발되지만 Vault 릴리스에 자동으로 번들됩니다. GCP 플러그인에 특정한 모든 기능 요청, 버그, 풀 리퀘스트는 해당 저장소에 제출하세요.

출처: 문서

본문

인증 (Authentication)

CLI 헬퍼를 통해 (Via the CLI helper)

Vault에는 서명된 JWT를 로컬에서 얻고 Vault에 요청을 보내는 CLI 헬퍼가 포함되어 있어요.

# Google Cloud 외부에서 vault로의 인증
$ vault login -method=gcp \
    role="my-role" \
    service_account="[email protected]" \
    jwt_exp="15m" \
    credentials=@path/to/signer/credentials.json
# Google Cloud 내부에서 vault로의 인증
$ vault login -method=gcp role="my-role"

더 많은 사용 정보는 vault auth help gcp를 실행하세요.

참고: project 파라미터는 Vault 1.5.9+, 1.6.5+, 1.7.2+에서 제거되었어요. 구성에 더 이상 필요하지 않으며 제공되면 무시돼요.

CLI를 통해

$ vault write -field=token auth/gcp/login \
    role="my-role" \
    jwt="eyJhbG...VCJ9..."

JWT 토큰을 얻는 방법은 JWT 생성하기를 참고하세요.

API를 통해

$ curl \
    --request POST \
    --data '{"role":"my-role", "jwt":"eyJhbG...VCJ9..."}' \
    http://127.0.0.1:8200/v1/auth/gcp/login

예상 응답은 API 문서를 참고하세요.

구성 (Configuration)

인증 방식은 사용자나 머신이 인증하기 전에 미리 구성되어야 해요. 이 단계들은 보통 운영자나 설정 관리 도구가 수행해요.

1. Google Cloud 인증 방식을 활성화합니다.

$ vault auth enable gcp

2. Vault가 Google Cloud에서 실행 중이 아니라면 인증 방식 크레덴셜을 구성합니다.

$ vault write auth/gcp/config \
    credentials=@/path/to/credentials.json

인스턴스 크레덴셜을 사용하거나 환경 변수로 크레덴셜을 지정하려면 이 단계를 건너뛸 수 있어요. 자세한 내용은 아래 Google Cloud 크레덴셜 섹션을 참고하세요.

참고: Private Google Access 환경을 사용한다면 custom_endpoint 구성 파라미터를 통해 환경의 사용자 지정 엔드포인트를 추가로 구성해야 해요.

어떤 경우에는 Vault 구성에 민감한 IAM 보안 크레덴셜을 설정할 수 없어요. 예를 들어 조직이 모든 보안 크레덴셜이 수명이 짧거나 명시적으로 머신 아이덴티티에 연결되도록 요구할 수 있어요.

Vault에 IAM 보안 크레덴셜을 제공하려면 아래와 같이 Vault 플러그인 workload identity federation(WIF)을 사용하는 것을 권장해요.

3. 또는 플러그인 workload identity federation을 위해 오디언스 클레임 값과 가정할 서비스 계정 이메일을 구성합니다.

$ vault write auth/gcp/config \
    identity_token_audience="<TOKEN AUDIENCE>" \
    service_account_email="<SERVICE ACCOUNT EMAIL>"

Vault의 아이덴티티 토큰 프로바이더는 플러그인 아이덴티티 토큰 JWT를 내부적으로 서명해요. Vault와 GCP 사이에 WIF를 통한 신뢰 관계가 존재하면 인증 방식은 Vault 아이덴티티 토큰을 연합 접근 토큰으로 교환할 수 있어요.

Vault와 GCP 사이 신뢰 관계를 구성하려면:

Vault와 GCP 사이 신뢰 관계를 구축하면 GCP가 JWKS 공용 키를 가져오고 플러그인 아이덴티티 토큰 서명을 검증할 수 있게 돼요.

4. 이름 있는 역할을 만듭니다.

iam 유형 역할:

$ vault write auth/gcp/role/my-iam-role \
    type="iam" \
    policies="dev,prod" \
    bound_service_accounts="[email protected]"

gce 유형 역할:

$ vault write auth/gcp/role/my-gce-role \
    type="gce" \
    policies="dev,prod" \
    bound_projects="my-project1,my-project2" \
    bound_zones="us-east1-b" \
    bound_labels="foo:bar,zip:zap" \
    bound_service_accounts="[email protected]"

bound_service_accounts는 iam 유형 역할에만 필요하다는 점을 주의하세요.

각 유형의 전체 구성 옵션 목록은 API 문서를 참고하세요.

GCP 크레덴셜 (GCP credentials)

Google Cloud Vault 인증 방식은 공식 Google Cloud Golang SDK를 사용해요. 즉 Google Cloud에 크레덴셜을 제공하는 일반적인 방법을 지원해요.

  1. GOOGLE_APPLICATION_CREDENTIALS 환경 변수. 이것은 일반적으로 서비스 계정에 대한 Google Cloud 크레덴셜 파일의 경로로 지정돼요. 이 환경 변수가 있으면 결과 크레덴셜이 사용돼요. 크레덴셜이 유효하지 않으면 오류가 반환돼요.
  2. 기본 인스턴스 크레덴셜. 환경 변수가 없으면 기본 서비스 계정 크레덴셜이 사용돼요.

서비스 계정에 대한 자세한 내용은 Google Cloud 서비스 계정 문서를 참고하세요.

이 인증 방식을 사용하려면 서비스 계정에 다음 최소 범위가 있어야 해요:

https://www.googleapis.com/auth/cloud-platform

필요한 GCP 권한 (Required GCP permissions)

활성화된 GCP API (Enabled GCP APIs)

GCP 프로젝트에 다음 API가 활성화되어 있어야 해요:

Vault 서버 권한 (Vault server permissions)

iam 유형 Vault 역할의 경우, Vault에 주어진 서비스 계정 credentials는 다음 역할을 가질 수 있어요:

roles/iam.serviceAccountKeyAdmin

gce 유형 Vault 역할의 경우, Vault에 주어진 서비스 계정 credentials는 다음 역할을 가질 수 있어요:

roles/compute.viewer

대신 정확히 필요한 GCP 권한만 있는 사용자 지정 역할을 만들려면 다음 권한 목록을 사용하세요:

iam.serviceAccounts.get
iam.serviceAccountKeys.get
compute.instances.get
compute.instanceGroups.list

이것들은 Vault가 다음을 할 수 있게 해요:

  • (직접 인증하거나 인증하는 GCE 인스턴스와 연결된) 서비스 계정이 존재하는지 검증
  • 서비스 계정 개인 키로 서명된 JWT를 검증하기 위한 해당 공개 키를 얻기
  • 인증하는 GCE 인스턴스가 존재하는지 검증
  • GCE 역할의 바운드 필드(영역/지역, 라벨, 또는 주어진 인스턴스 그룹의 멤버십) 비교

아래 설명된 대로 그룹 별칭을 사용한다면 resourcemanager.projects.get 권한도 추가해야 해요.

Vault에 대한 인증 권한 (Permissions for authenticating against Vault)

Google Cloud에서 Vault로 인증한다면 다음 단계를 건너뛸 수 있어요. Vault가 인스턴스 또는 파드에 구성된 서비스 계정의 아이덴티티 토큰을 생성하고 제시할 것이기 때문이에요.

앞서 언급한 권한은 Vault 서버에 부여된다는 점을 주의하세요. Vault에 대해 인증하는 IAM 서비스 계정 또는 GCE 인스턴스는 다음 역할을 가져야 해요:

roles/iam.serviceAccountTokenCreator

경고: 이 역할이 서비스 계정이 자기 자신만 사칭할 수 있도록만 적용되도록 하세요. 이 역할이 GCP 프로젝트 전체에 적용되면 서비스 계정이 거주하는 GCP 프로젝트의 모든 서비스 계정을 사칭할 수 있게 돼요. 자세한 내용은 서비스 계정 사칭 관리를 참고하세요.

플러그인 Workload Identity Federation (WIF)

이 기능에는 Vault Enterprise(새 탭에서 열림)가 필요해요.

GCP 인증 방식은 플러그인 WIF 워크플로를 지원하고 플러그인 아이덴티티 토큰이라는 신원 소스를 가져요. 플러그인 아이덴티티 토큰은 Vault 플러그인 아이덴티티 토큰 발급자가 내부적으로 서명한 JWT예요.

Vault와 GCP 사이에 workload identity federation을 통한 신뢰 관계가 구성되어 있으면 인증 방식은 아이덴티티 토큰을 작업을 수행하는 데 필요한 수명이 짧은 접근 토큰으로 교환할 수 있어요.

아이덴티티 토큰을 접근 토큰으로 교환하면 GCP 인증 방식이 민감한 IAM 보안 크레덴셜에 대한 명시적 접근을 구성하지 않고 작동할 수 있어요.

인증 방식이 플러그인 WIF를 사용하도록 구성하려면:

  1. Vault openid-configuration 및 공용 JWKS API가 GCP에서 네트워크로 도달 가능한지 확인하세요. Vault API 노출을 제한해야 한다면 API 프록시나 게이트웨이를 권장해요.
  2. GCP에 workload identity pool과 provider를 만드세요.
    • 프로바이더 URL은 /.well-known/openid-configuration 접미사를 제거한 [Vault 플러그인 아이덴티티 토큰 발급자]를 가리켜야 해요. 예: https://host:port/v1/identity/oidc/plugins.
    • 플러그인 아이덴티티 토큰의 수신자를 오디언스로 고유하게 식별하세요. 아이덴티티 풀의 기본 오디언스를 사용하거나 256자 미만의 사용자 지정 값을 사용할 수 있어요.
  3. 서비스 계정 사칭을 사용해 아이덴티티 풀 접근을 전용 서비스 계정에 부여해 GCP에서 워크로드 인증을 하세요. 플러그인 아이덴티티 토큰이 발급한 고유 sub 클레임으로 요청을 필터링해 GCP Auth 방식이 서비스 계정을 사칭할 수 있게 하세요. sub 클레임은 plugin-identity:<NAMESPACE_ID>:auth:<GCP_AUTH_MOUNT_ACCESSOR> 형식이에요.
  4. OIDC 오디언스 값과 서비스 계정 이메일로 GCP 인증 방식을 구성하세요.
$ vault write auth/gcp/config \
  identity_token_audience="//iam.googleapis.com/projects/410449834127/locations/global/workloadIdentityPools/vault-gcp-auth-43777a63/providers/vault-gcp-auth-wif-provider" \
  service_account_email="vault-plugin-wif-auth@hc-b712f250b4e04cacbadd258a90b.iam.gserviceaccount.com"

이제 인증 방식이 구성 크레덴셜에 플러그인 WIF를 사용할 수 있어요. 기본적으로 WIF 크레덴셜은 1시간의 time-to-live를 가지며 만료 시 자동으로 갱신돼요.

플러그인 WIF와 연관된 필드에 대한 자세한 내용은 API 문서를 참고하세요.

루트 크레덴셜 회전 (Root credential rotation)

마운트가 크레덴셜로 직접 구성되면 크레덴셜의 키를 운영자가 접근할 수 없는 Vault 생성 값으로 회전할 수 있어요. 이 작업에 대한 자세한 내용은 루트 크레덴셜 회전 API 문서를 참고하세요.

일정 기반 루트 크레덴셜 회전 (Schedule-based root credential rotation)

적절한 Vault Enterprise 라이선스가 필요해요.

rotation_schedule 필드를 사용해 GCP 인증 엔진의 루트 크레덴셜에 대한 일정 기반 자동 크레덴셜 회전을 구성해요. 예를 들어 다음 명령은 매주 토요일 자정(00:00)에 회전이 발생하도록 설정해요:

$ vault write auth/gcp/config/client \
  ...
  rotation_schedule="0 * * * SAT"
  ...

예정된 루트 크레덴셜 회전은 예정된 회전이 발생하도록 허용되는 rotation_window를 설정할 수도 있어요. Vault는 창이 만료되면 크레덴셜 회전 시도를 중지해요. 예를 들어 다음 명령은 Vault에게 1시간 범위 내에서 토요일 자정에 크레덴셜을 회전하라고 지시해요. Vault가 실패 등으로 1:00까지 크레덴셜을 회전할 수 없으면 다음 예정된 회전까지 회전 시도를 중지해요.

$ vault write auth/gcp/config/client \
  ...
  rotation_window="1h" \
  rotation_schedule="0 * * * SAT"
...

disable_automated_rotation을 true로 설정해 루트 회전을 일시적으로 비활성화할 수 있어요. 이 필드를 설정하면 false로 재설정될 때까지 루트 크레덴셜의 모든 회전을 방지해요. rotation_period를 사용하면 disable_automated_rotation 설정 또한 크레덴셜 TTL을 재설정해요.

GCP 인증 엔진의 루트 크레덴셜 회전에 대한 자세한 내용은 루트 크레덴셜 회전 API 문서를 참고하세요.

그룹 별칭 (Group aliases)

Vault 1.0부터 역할은 인증 응답에 그룹 별칭을 추가하는 add_group_aliases 불리언 파라미터를 지정할 수 있어요. 이러한 별칭은 Vault의 정책 엔진에서 보간된 값으로 사용 가능하므로 재사용 가능한 정책을 구축하는 데 도움이 될 수 있어요. 활성화되면 인증 응답에 다음 별칭이 포함돼요:

[
  "project-$PROJECT_ID",
  "folder-$SUBFOLDER_ID",
  "folder-$FOLDER_ID",
  "organization-$ORG_ID"
]

Vault 서버에 사용자 지정 역할을 사용한다면 사용자 지정 역할에 resourcemanager.projects.get 권한을 추가해야 해요.

구현 세부 사항 (Implementation details)

이 섹션은 Vault가 Google Cloud와 통신해 JWT 토큰을 인증하고 인가하는 방법에 대한 구현 세부 사항을 설명해요. 이 정보는 궁금한 사람을 위해 제공되지만 이러한 세부 사항은 인증 방식을 사용하는 데 필요한 지식은 아니에요.

IAM 로그인 (IAM login)

IAM 로그인은 iam 유형의 역할에만 적용돼요. IAM 서비스 계정에 대한 Vault 인증 워크플로는 다음과 같아요:

  1. 클라이언트가 서비스 계정 크레덴셜 projects.serviceAccounts.signJwt API 메서드를 사용해 서명된 JWT를 생성해요. 이를 수행하는 방법의 예시는 JWT 생성하기 섹션을 참고하세요.
  2. 클라이언트가 이 서명된 JWT를 역할 이름과 함께 Vault로 보내요.
  3. Vault가 JWT를 생성하는 데 사용된 키 쌍의 ID를 포함하는 kid 헤더 값과 서비스 계정 키를 찾기 위한 sub ID/이메일을 추출해요. 서비스 계정이 존재하지 않거나 키가 서비스 계정에 연결되지 않으면 Vault는 인증을 거부해요.
  4. Vault가 확인된 서비스 계정을 주어진 역할에 대해 인가해요. 성공하면 적절한 정책이 있는 Vault 토큰이 반환돼요.

GCE 로그인 (GCE login)

GCE 로그인은 gce 유형의 역할에만 적용되며 Google Cloud에서 실행되는 인프라에서 완료되어야 해요. 이 단계들은 로컬 노트북이나 다른 클라우드 프로바이더에서는 작동하지 않아요.

  1. 클라이언트가 GCE 인스턴스에서 인스턴스 아이덴티티 메타데이터 토큰을 얻어요.
  2. 클라이언트가 이 JWT를 역할 이름과 함께 Vault로 보내요.
  3. Vault가 JWT를 생성하는 데 사용된 키 쌍의 ID를 포함하는 kid 헤더 값을 추출해 이 JWT를 검증할 OAuth2 공용 인증서를 찾아요.
  4. Vault가 확인된 인스턴스를 주어진 역할에 대해 인가하여 인스턴스가 바인딩된 영역, 지역, 인스턴스 그룹과 일치하는지 확인해요. 성공하면 적절한 정책이 있는 Vault 토큰이 반환돼요.

JWT 생성하기 (Generating JWTs)

이 섹션은 JWT 토큰을 얻는 다양한 방법과 예시를 자세히 설명해요.

서비스 계정 크레덴셜 API (Service account credentials API)

이것은 GCP 서비스 계정 크레덴셜 API 메서드를 직접 사용해 Vault가 기대하는 클레임으로 서명된 JWT를 생성하는 방법을 설명해요. CLI가 이 과정을 대신 수행하며 훨씬 쉽고, 이것을 직접 할 이유는 거의 없다는 점을 주의하세요.

curl 예시

Vault는 다음 최소 클레임 집합을 요구해요:

{
  "sub": "$SERVICE_ACCOUNT_EMAIL_OR_ID",
  "aud": "vault/$ROLE",
  "exp": "$EXPIRATION"
}

API 메서드의 경우 만료 클레임 exp를 제공하는 것이 필요해요. 생략하면 자동으로 추가되지 않고 Vault는 인증을 거부해요. 만료는 NumericDate 값(Epoch로부터의 초)으로 지정되어야 해요. 이 값은 역할에 허용된 최대 JWT 만료 이전이어야 해요. 이것은 기본적으로 15분이며 1시간을 넘을 수 없어요.

사용자가 15분 후에 만료되는 토큰을 생성하고 gcp 역할에 max_jwt_exp가 기본값으로 설정되어 있으면 Vault는 다음 오류를 반환해요: Expiration date must be set to no more that 15 mins in JWT_CLAIM, otherwise the login request returns error "role requires that service account JWTs expire within 900 seconds. 이 경우 사용자는 더 짧은 만료의 새 서명된 JWT를 만들거나 gcp 역할에서 max_jwt_exp를 더 높은 값으로 설정해야 해요.

이 모든 정보가 있으면 JWT 토큰을 curl과 oauth2l로 서명할 수 있어요:

ROLE="my-role"
SERVICE_ACCOUNT="[email protected]"
OAUTH_TOKEN="$(oauth2l header cloud-platform)"
EXPIRATION="<your_token_expiration>"
JWT_CLAIM="{\\\"aud\\\":\\\"vault/${ROLE}\\\", \\\"sub\\\": \\\"${SERVICE_ACCOUNT}\\\", \\\"exp\\\": ${EXPIRATION}}"

$ curl \
  --header "${OAUTH_TOKEN}" \
  --header "Content-Type: application/json" \
  --request POST \
  --data "{\"payload\": \"${JWT_CLAIM}\"}" \
  "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SERVICE_ACCOUNT}:signJwt"
gcloud 예시

(현재 베타인) gcloud 명령으로도 이것을 할 수 있어요. 명령의 JWT 입력의 일부로 만료 클레임 exp를 제공해야 한다는 점을 주의하세요.

$ gcloud beta iam service-accounts sign-jwt $INPUT_JWT_CLAIMS $OUTPUT_JWT_FILE \
    [email protected] \
    --project=my-project
Golang 예시

이에 대한 자세한 내용은 Google Open Source 블로그를 읽어보세요.

GCE

이 토큰은 Vault 1.8.2 이상에서 자동 생성할 수 있어요.

GCE 토큰은 GCE 인스턴스에서만 생성할 수 있어요.

  1. Vault는 GCE/GKE 인스턴스에서 아이덴티티 토큰을 자동으로 발견할 수 있어요. 이는 다음과 같이 Vault에 인증을 단순화해요:
$ vault login \
  -method=gcp \
  role="my-gce-role"
  1. JWT 토큰은 인스턴스의 메타데이터 서버의 "service-accounts/default/identity" 엔드포인트에서도 얻을 수 있어요.
Curl 예시
ROLE="my-gce-role"

$ curl \
  --header "Metadata-Flavor: Google" \
  --get \
  --data-urlencode "audience=http://vault/${ROLE}" \
  --data-urlencode "format=full" \
  "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity"

API

GCP Auth Plugin은 완전한 HTTP API를 제공해요. 자세한 내용은 API 문서를 참고하세요.

Terraform

Vault Terraform 프로바이더로 GCP 인증 리소스를 프로그래밍 방식으로 관리할 수 있어요. 자세한 내용은 Terraform Registry 문서를 참고하세요.

코드 예시 (Code example)

다음 예시는 Google Cloud 인증 방식으로 Vault에 인증하는 것을 보여줘요. (Go/C# 코드)

Go:

package main

import (
    "context"
    "fmt"
    "os"

    vault "github.com/hashicorp/vault/api"
    auth "github.com/hashicorp/vault/api/auth/gcp"
)

// Fetches a key-value secret (kv-v2) after authenticating to Vault
// via GCP IAM, one of two auth methods used to authenticate with
// GCP (the other is GCE auth).
func getSecretWithGCPAuthIAM() (string, error) {
    config := vault.DefaultConfig() // modify for more granular configuration

    client, err := vault.NewClient(config)
    if err != nil {
        return "", fmt.Errorf("unable to initialize Vault client: %w", err)
    }

    // For IAM-style auth, the environment variable GOOGLE_APPLICATION_CREDENTIALS
    // must be set with the path to a valid credentials JSON file, otherwise
    // Vault will fall back to Google's default instance credentials.
    // Learn about authenticating to GCS with service account credentials at https://cloud.google.com/docs/authentication/production
    if pathToCreds := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); pathToCreds == "" {
        fmt.Printf("WARNING: Environment variable GOOGLE_APPLICATION_CREDENTIALS was not set. IAM client for JWT signing and Vault server IAM client will both fall back to default instance credentials.\n")
    }

    svcAccountEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", os.Getenv("GCP_SERVICE_ACCOUNT_NAME"), os.Getenv("GOOGLE_CLOUD_PROJECT"))

    // We pass the auth.WithIAMAuth option to use the IAM-style authentication
    // of the GCP auth backend. Otherwise, we default to using GCE-style
    // authentication, which gets its credentials from the metadata server.
    gcpAuth, err := auth.NewGCPAuth(
        "dev-role-iam",
        auth.WithIAMAuth(svcAccountEmail),
    )
    if err != nil {
        return "", fmt.Errorf("unable to initialize GCP auth method: %w", err)
    }

    authInfo, err := client.Auth().Login(context.TODO(), gcpAuth)
    if err != nil {
        return "", fmt.Errorf("unable to login to GCP auth method: %w", err)
    }
    if authInfo == nil {
        return "", fmt.Errorf("login response did not return client token")
    }

    // get secret from the default mount path for KV v2 in dev mode, "secret"
    secret, err := client.KVv2("secret").Get(context.Background(), "creds")
    if err != nil {
        return "", fmt.Errorf("unable to read secret: %w", err)
    }

    // data map can contain more than one key-value pair,
    // in this case we're just grabbing one of them
    value, ok := secret.Data["password"].(string)
    if !ok {
        return "", fmt.Errorf("value type assertion failed: %T %#v", secret.Data["password"], secret.Data["password"])
    }

    return value, nil
}

C#:

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Iam.v1;
using Newtonsoft.Json;
using VaultSharp;
using VaultSharp.V1.AuthMethods;
using VaultSharp.V1.AuthMethods.GoogleCloud;
using VaultSharp.V1.Commons;

using Data = Google.Apis.Iam.v1.Data;

namespace Examples
{
    public class GCPAuthExample
    {
        /// <summary>
        /// Fetches a key-value secret (kv-v2) after authenticating to Vault via GCP IAM,
        /// one of two auth methods used to authenticate with GCP (the other is GCE auth).
        /// </summary>
        public string GetSecretGcp()
        {
            var vaultAddr = Environment.GetEnvironmentVariable("VAULT_ADDR");
            if(String.IsNullOrEmpty(vaultAddr))
            {
                throw new System.ArgumentNullException("Vault Address");
            }

            var roleName = Environment.GetEnvironmentVariable("VAULT_ROLE");
            if(String.IsNullOrEmpty(roleName))
            {
                throw new System.ArgumentNullException("Vault Role Name");
            }

            // Learn about authenticating to GCS with service account credentials at https://cloud.google.com/docs/authentication/production
            if(String.IsNullOrEmpty(Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS")))
            {
                Console.WriteLine("WARNING: Environment variable GOOGLE_APPLICATION_CREDENTIALS was not set. IAM client for JWT signing will fall back to default instance credentials.");
            }

            var jwt = SignJWT();

            IAuthMethodInfo authMethod = new GoogleCloudAuthMethodInfo(roleName, jwt);
            var vaultClientSettings = new VaultClientSettings(vaultAddr, authMethod);

            IVaultClient vaultClient = new VaultClient(vaultClientSettings);

            // We can retrieve the secret after creating our VaultClient object
            Secret<SecretData> kv2Secret = null;
            kv2Secret = vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path: "/creds").Result;

            var password = kv2Secret.Data.Data["password"];

            return password.ToString();
        }

        /// <summary>
        /// Generate signed JWT from GCP IAM
        /// </summary>
        private string SignJWT()
        {
            var roleName = Environment.GetEnvironmentVariable("GCP_ROLE");
            var svcAcctName = Environment.GetEnvironmentVariable("GCP_SERVICE_ACCOUNT_NAME");
            var gcpProjName = Environment.GetEnvironmentVariable("GOOGLE_CLOUD_PROJECT");

            IamService iamService = new IamService(new BaseClientService.Initializer
            {
                HttpClientInitializer = GetCredential(),
                ApplicationName = "Google-iamSample/0.1",
            });

            string svcEmail = $"{svcAcctName}@{gcpProjName}.iam.gserviceaccount.com";
            string name = $"projects/-/serviceAccounts/{svcEmail}";

            TimeSpan currentTime = (DateTime.UtcNow - new DateTime(1970, 1, 1));
            int expiration = (int)(currentTime.TotalSeconds) + 900;

            Data.SignJwtRequest requestBody = new Data.SignJwtRequest();
            requestBody.Payload = JsonConvert.SerializeObject(new Dictionary<string, object> ()
            {
                { "aud", $"vault/{roleName}" } ,
                { "sub", svcEmail } ,
                { "exp", expiration }
            });

            ProjectsResource.ServiceAccountsResource.SignJwtRequest request = iamService.Projects.ServiceAccounts.SignJwt(requestBody, name);

            Data.SignJwtResponse response = request.Execute();

            return JsonConvert.SerializeObject(response.SignedJwt).Replace("\"", "");
        }

        public static GoogleCredential GetCredential()
        {
            GoogleCredential credential = Task.Run(() => GoogleCredential.GetApplicationDefaultAsync()).Result;
            if (credential.IsCreateScopedRequired)
            {
                credential = credential.CreateScoped("https://www.googleapis.com/auth/cloud-platform");
            }
           return credential;
        }
    }
}

더 알아보기 (Learn more)