Edited at

GKE の Pod で デフォルトの Service Account の Credential を変更する (Workload Identity)


これまで


  • GKE で Pod から GCP の API をリクエストする場合には Secrets リソースに Google Service Account の Credential を作ってマウントしていた

  • そのため Credential を発行してダウンロードするという作業が必要となる

  • Google Service Account にはそのアプリケーションで必要となる API の各種権限を割り当てているのでセンシティブな情報と言える

  • それにも関わらず一度は作業者のローカルや作業環境などに保存する必要があるため管理が甘い (使用後即座に消さないとか) と作業者本人も含めてアプリケーション以外で使用されてしまう

  • Credential なんてダウンロードしないで Pod に Service Account を指定したい...



  • Workload Identity 登場

  • fin.


使ってみた (CronJob)


前提


  • 作業者に各種権限が付与されていること


    • 以下の権限が含まれていればいけそう (要らないのも混ざってるかも)



resourcemanager.projects.get

iam.serviceAccounts.get
iam.serviceAccounts.list
iam.serviceAccounts.create
iam.serviceAccounts.actAs
iam.serviceAccounts.setIamPolicy
iam.serviceAccounts.getIamPolicy
compute.projects.get
compute.regions.get
compute.regions.list
compute.zones.get
compute.zones.list
compute.networks.get
compute.networks.list
compute.networks.create
compute.networks.updatePolicy
compute.subnetworks.get
compute.subnetworks.list
compute.subnetworks.create
container.operations.get
container.clusters.get
container.clusters.create


  • 各種リソースの名前は以下で統一

Name
Description

${my_pj}
クラスタを作成する GCP のプロジェクト

${my_gsa}
CronJob で使用したい Google Service Account 名

${k8s_ns}
Kubernetes の Namespace 名

${k8s_ksa}
Kubernetes の Namespace 内の Service Account 名

${my_clstr}
クラスタ名

${my_nw}
GCP の VPC ネットワーク名

${my_subnw}
クラスタ用のサブネットワーク名

${my_region}
サブネットワークのリージョン

${my_zone}
クラスタのゾーン (リージョンで作るとノード数多いので)


作業

$ docker run --rm -it --name gcp-cli -w /opt google/cloud-sdk:latest

% gcloud auth login
% gcloud config set project ${my_pj}

% : Step01. ${my_gsa} の作成
% gcloud beta iam service-accounts create\
${my_gsa}\
--project ${my_pj}\
--display-name "My Google Service Account"\
--description "Qiita Example"

% : Step02. ${my_gsa} への権限設定
% gcloud projects add-iam-policy-binding\
${my_pj}\
--member ${my_gsa}@${my_pj}.iam.gserviceaccount.com\
--role resourcemanager.projects.get

% : Step03. クラスタ用のネットワーク作成
% gcloud compute networks create\
${my_nw}\
--project ${my_pj}\
--bgp-routing-mode regional\
--subnet-mode custom
% gcloud compute networks subnets create\
${my_subnw}\
--project ${my_pj}\
--network ${my_nw}\
--region ${my_region}\
--range xxx.xxx.xxx.xxx/xx\
--enable-private-ip-google-access

% : Step04. クラスタ作成
% gcloud beta container clusters create\
${my_clstr}\
--project ${my_pj}\
--zone ${my_zone}\
--identity-namespace ${my_pj}.svc.id.goog\
--network ${my_nw}\
--subnetwork ${my_subnw}\
--machine-type n1-standard-1\
--preemptible\
--disk-size 100\
--disk-type pd-standard\
--image-type cos\
--enable-ip-alias\
--enable-private-nodes\
--master-ipv4-cidr yyy.yyy.yyy.yyy/yy\
--enable-master-authorized-networks\
--master-authorized-networks zzz.zzz.zzz.zzz/zz\
--num-nodes 1\
--no-enable-legacy-authorization\
--no-enable-basic-auth\
--no-issue-client-certificate\
--metadata 'disable-legacy-endpoints=true'

% : Step05. Workload Identity からの ${my_gsa} へのアクセスを許可
% gcloud iam service-accounts add-iam-policy-binding\
${my_gsa}@${my_pj}.iam.gserviceaccount.com\
--role "roles/iam.workloadIdentityUser"\
--member "serviceAccount:${my_pj}.svc.id.goog[${k8s_ns}/${k8s_ksa}]"

% : Step06. クラスタに Namespace と Service Account を作成
% cat <<EOS | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name:
${k8s_ns}
---
apiVersion: v1
kind: ServiceAccount
metadata:
namespace:
${k8s_ns}
name:
${k8s_ksa}
annotations:
iam.gke.io/gcp-service-account:
${my_gsa}@${my_pj}.iam.gserviceaccount.com
EOS

% : Step07. Pod 作ってテスト
% kubectl run\
-it\
--rm\
--generator=run-pod/v1\
--image google/cloud-sdk\
--namespace ${k8s_ns}\
--serviceaccount ${k8s_ksa}\
workload-identity-test\
-- gcloud auth list
% kubectl run\
-it\
--rm\
--generator=run-pod/v1\
--image google/cloud-sdk\
--namespace ${k8s_ns}\
--serviceaccount ${k8s_ksa}\
workload-identity-test\
-- gcloud projects describe ${my_pj}

% : Step08. CronJob を作成
% cat <<EOS | kubectl apply -f -
apiVersion: batch/v1beta1
kind: CronJob
metadata:
namespace:
${k8s_ns}
name: workload-identity-test
spec:
schedule: "*/1 * * * *"
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
serviceAccountName:
${k8s_ksa}
containers:
- name: batch
image: google/cloud-sdk
imagePullPolicy: Always
command: ["gcloud", "projects", "describe", "
${my_pj}"]
EOS
% kubectl get cronjob --namespace ${k8s_ns}
% kubectl pod cronjob --namespace ${k8s_ns}
% kubectl get pod --namespace ${k8s_ns} --field-selector status.phase==Succeeded -o name\
| kubectl logs $(cat) --namespace ${k8s_ns}


調査


Pod 内で gcloud auth list がエラー

こんなの

ERROR: gcloud crashed (MetadataServerException): HTTP Error 403: Forbidden


  • Stack Driver で以下のようなフィルターでログ調査

resource.type="container"

resource.labels.cluster_name="${my_clstr}"
resource.labels.namespace_id="kube-system"
resource.labels.project_id="${my_pj}"
resource.labels.zone:"${my_zone}"
resource.labels.container_name="gke-metadata-server"
resource.labels.pod_id:"gke-metadata-server-"


  • IAM とかの API を enable にしてなかったり権限が足りてなかったり


参考