GKEでhostNetworkを使ったPodでもWorkload Identityを使いたい!!

AvatarPosted by

この記事は GRIPHONE Advent Calendar 2021 16日目の記事です。

SREの徳田です。(2回目)

前回TokenRequestProjectionを使ったAWSへの認証をやったわけですが、同じ機能を使ってGCPのWorkload Identity Federationをやってみよう、という試みです。

TL;DR

  • Workload Identity Pool & Providerを設定
  • Podを作成
    • TokenRequestProjectionを使ってOIDC ID Tokenを作成
    • 認証情報構成ファイルをConfigMapなどで作成してマウント
    • 環境変数で認証情報構成ファイルを指定

背景

GKEを使ってWorkload Identityやるならこっちの方法でいいじゃん!という話なのですが、hostNetworkを有効にしたPodではWorkload Identityを使うことができません。

ということでTokenRequestで発行されるOIDC ID Tokenを使ったWorkload Identity Federationで認証をしてみようとなった次第です。

Workload Identity Federationの設定

それではまずWorkload Identity Poolを作成します。

gcloud iam workload-identity-pools create wif-test --location global

次にOIDC向けのWorkload Identity Providerを作成します。

gcloud iam workload-identity-pools providers create-oidc wif-test-provider \
    --location="global" \
    --workload-identity-pool="wif-test" \
    --issuer-uri="https://container.googleapis.com/v1/projects/utility-heading-335212/locations/asia-northeast1-a/clusters/cluster-1" \
    --allowed-audiences="gcp" \
    --attribute-mapping="google.subject=assertion.sub"
  • --issuer-url :クラスタのIssuerのURL。こちらの記事も見てみてください
  • --allowed-audiences :許可するAudience。
  • --attribute-mapping :属性マップ。複数ある場合はカンマで区切る

--attribute-mapping では google.subject=assertion.sub となっていますが、google.subject はGCP上でのユーザーの一意の識別子で、 assertion.sub はID Tokenの sub クレームであり、これらをマップするように指定しています。

ついでにTokenRequestによって発行されるID Tokenの sub クレームは以下のようになっております。

system:serviceaccount:NAMESPACE:SERVICEACCOUNT

属性のマッピングについて詳しく知りたい方は以下からどうぞ

https://cloud.google.com/iam/docs/workload-identity-federation#mapping

GCPのServiceAccountの設定

Workload Identityを使って成り代わる先のServiceAccountを作成しましょう。

gcloud iam service-accounts create test-account

次にWorkload Identity Poolから先程作成したServiceAccountに成り代われるように権限を設定します。

gcloud iam service-accounts add-iam-policy-binding test-account@utility-heading-335212.iam.gserviceaccount.com \
                    --role=roles/iam.workloadIdentityUser \
                    --member="principal://iam.googleapis.com/projects/1051542072111/locations/global/workloadIdentityPools/wif-test/subject/system:serviceaccount:default:default"

--member の指定は以下のようなフォーマットになっており、今回は sub クレームが system:serviceaccount:default:default となっているユーザーを許可するようにしています。要はdefaultネームスペースのdefaultサービスアカウントに権限を渡しています。

memberの指定方法についてはこちらを参考にしていただければと思います。

https://cloud.google.com/iam/docs/workload-identity-federation#impersonation

そして最後に作成したサービスアカウントに適当に権限を付けておきましょう。今回はviewer権限を付与します。

gcloud projects add-iam-policy-binding utility-heading-335212 \
                    --member "serviceAccount:test-account@utility-heading-335212.iam.gserviceaccount.com" \
                    --role "roles/viewer"

KubernetesのManifestの設定

それではいよいよPodを作ります。

google-cloud-sdkでは認証情報を構成ファイルとして渡してあげる必要があります。その構成ファイルを作成しておきます。

gcloud iam workload-identity-pools create-cred-config \
                    projects/1051542072111/locations/global/workloadIdentityPools/wif-test/providers/wif-test-provider \
                    --service-account="test-account@utility-heading-335212.iam.gserviceaccount.com" \
                    --output-file=config.json \
                    --credential-source-file=/secret/token \
                    --credential-source-type=text
  • --service-account :成り代わるサービスアカウントを指定
  • --output-file :構成ファイルの出力先
  • --credential-source-file :ID Tokenのパス
  • --credential-source-type :ファイルのタイプ。text / json を指定。今回は直接ID Tokenがおいてあるので text を指定

すると作業フォルダに config.json というファイルで以下のような内容のものができます。

{
  "type": "external_account",
  "audience": "//iam.googleapis.com/projects/1051542072111/locations/global/workloadIdentityPools/wif-test/providers/wif-test-provider",
  "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
  "token_url": "https://sts.googleapis.com/v1/token",
  "credential_source": {
    "file": "/secret/token",
    "format": {
      "type": "text"
    }
  },
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-account@utility-heading-335212.iam.gserviceaccount.com:generateAccessToken"
}

この作成ファイルを使ってConfigMapとPodを作成します。

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gcloud-config
data:
  config.json: |-
    {
      "type": "external_account",
      "audience": "//iam.googleapis.com/projects/1051542072111/locations/global/workloadIdentityPools/wif-test/providers/wif-test-provider",
      "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
      "token_url": "https://sts.googleapis.com/v1/token",
      "credential_source": {
        "file": "/secret/token",
        "format": {
          "type": "text"
        }
      },
      "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-account@utility-heading-335212.iam.gserviceaccount.com:generateAccessToken"
    }
---
apiVersion: v1
kind: Pod
metadata:
  name: wif-test
spec:
  containers:
  - name: main
    image: google/cloud-sdk:slim
    command:
    - bash
    - -c
    - |
      gcloud auth login --cred-file $GOOGLE_APPLICATION_CREDENTIALS
      gcloud container clusters list
    env:
    - name: GOOGLE_APPLICATION_CREDENTIALS
      value: /config/config.json
    volumeMounts:
    - name: gcloud-config
      mountPath: /config
      readOnly: true
    - name: token
      mountPath: /secret
      readOnly: true
  volumes:
  - name: gcloud-config
    configMap:
      name: gcloud-config
  - name: token
    projected:
      sources:
      - serviceAccountToken:
          path: token
          audience: gcp
  restartPolicy: Never
  terminationGracePeriodSeconds: 0

要点としては

  • 構成ファイルを入れたConfigMapのマウント先と GOOGLE_APPLICATION_CREDENTIALS のパスを合わせる
  • TokenRequestで作成したTokenを含むProjected Volumeのマウント先と構成ファイルで指定したID Tokenのパスを合わせる
  • audienceにWorkload Identity Providerを作成した際に --allowed-audience で指定した値と同じものを指定

また、今回はgcloudコマンドを使うため gcloud auth login コマンドを実行していますが、クライアントライブラリを活用したツールなどでは環境変数からクレデンシャルを読み込んでくれます。

それでは上記のManifestを適用してログを確認してみます。

$ kubectl apply -f a.yaml
configmap/gcloud-config created
pod/wif-test created
$ kubectl logs -f wif-test

Authenticated with external account credentials for: [test-account@utility-heading-335212.iam.gserviceaccount.com].
Your current project is [utility-heading-335212].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID
NAME       LOCATION           MASTER_VERSION   MASTER_IP      MACHINE_TYPE  NODE_VERSION     NUM_NODES  STATUS
cluster-1  asia-northeast1-a  1.21.5-gke.1302  35.187.223.85  e2-medium     1.21.5-gke.1302  3          RUNNING

ログの通り、サービスアカウントに成り代わってクラスタの情報を取得することができました!!🎉

さいごに

TokenRequestProjectionとWorkload Identity Federationを使ってGCPのサービスアカウントに成り代わってGCPを操作することができました。

hostNetwork を有効にしているとノードプールのインスタンスに権限を渡すか、サービスアカントを作成して権限を付与し、JSONのSecretを渡して上げる必要がありましたが、TokenRequestProjectionとWorkload Identity Federationを組み合わせることで、外からクレデンシャルを渡すことなく認証処理を動かすことができました。

このケースに当たることはなかなか無いとは思いますが、もしこの手段が活用できるケースが有った場合は参考にしていただければ幸いです。

それでは!