Workload Identityを用いてGKEからAWSのリソースにアクセスする
この記事で使用する手順は主に dotintl/gtoken に沿っています
Workload Identity とは
Workload Identity
GKEアプリケーションが Google API 提供のサービスを使用するための推奨された方法です
GoogleサービスアカウントのメールアドレスがAnnotateされたKubernetesサービスアカウントを用いることで、サービスアカウントを使用してクラウドサービスへの認証を行うことができます
AWSにも似たような機能があります
今回は、Workload Identityを用いてAWSのサービスに対し認証情報を持たずにセキュアにアクセスを行う方法を紹介します。
アプローチ
AWS IAM Role を GKEのPodに割り当てる
- AWS
- OIDC IDプロバイダーのIAMロールを作成する
- Googleのサービスアカウントで
AssumeRole
できるようにする
- Google
- サービスアカウントを作成する
- Workload Identityを通じてPodに有効なOIDCトークンを付与する
- OIDCトークンを用いてAWSに作成したIAMロールに
AssumeRole
する
AWS SDKは、次の環境変数が設定されている場合、STSサービスから一時的なAWS認証情報をリクエストします
AWS_WEB_IDENTITY_TOKEN_FILE
AWS_ROLE_ARN
AWS_ROLE_SESSION_NAME
OSSであるdointl/gtoken を用いることで簡単にセットアップができます
gtoken
gtokenとは、OIDC ID tokenの期限が切れる前に更新する役割をもつコンテナ
gtoken-webhook
のデプロイ
gtoken-webhook とは
特定のAnnotationがついたサービスアカウントで実行されるPodに対して、gtoken
をinjectするサービスです
デプロイには、Webhookサービスとデプロイメントを作成する必要があります。
また、gtokenのデプロイには通常のサービスと違い、証明書を作成する必要があります。
git clone https://github.com/doitintl/gtoken
./deployment/webhook-create-signed-cert.sh
証明書の作成が完了したら、デプロイメントとサービスを作成します。
kubectl apply -f deployment/deployment.yaml
kubectl apply -f deployment/service.yaml
次にMutatingWebhookConfiguration
をApplyします。
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-gtoken-webhook-cfg
labels:
app: gtoken-webhook
webhooks:
- name: gtoken.doit-intl.com
clientConfig:
service:
name: gtoken-webhook-svc
namespace: default
path: "/pods"
caBundle: ${CA_BUNDLE}
rules:
- operations: [ "CREATE" ]
apiGroups: ["*"]
apiVersions: ["*"]
resources: ["pods"]
ここで、${CA_BUNDLE}
を適切に設定する必要があります。
CAを置き換えるスクリプトが同梱されているので、それを使います。
cat ./deployment/mutatingwebhook.yaml | ./deployment/webhook-patch-ca-bundle.sh > ./deployment/mutatingwebhook-bundle.yaml
kubectl apply -f deployment/mutatingwebhook-bundle.yaml
gtoken-webhook の RBACを構成する
gtoken-webhook
が使用するサービスアカウントと、その権限を作成します。
# create a service account
kubectl create -f deployment/service-account.yaml
# create a cluster role
kubectl create -f deployment/clusterrole.yaml
# create a cluster role binding
kubectl create -f deployment/clusterrolebinding.yaml
GKE Workload Identity の設定
以下の設定で、いくつかの環境変数を使います。
.envrc
などで定義しておくと便利です。
PROJECT_ID
CLUSTER_NAME
GSA_NAME
KSA_NAME
KSA_NAMESPACE
AWS_ROLE_NAME
AWS_POLICY_NAME
- $AWS_ROLE_NAME に紐付けたいポリシーの名前
- 生成されるもの
GSA_ID
gcloud iam service-accounts describe --format json $GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com | jq -r '.uniqueId'
で取得可能
AWS_ROLE_ARN
$AWS_ROLE_NAME
を 生成したときに割り当てられるARN
aws iam get-role --role-name $ROLE_NAME --query Role.Arn --output text
で取得可能
Gcloud Workload Identity を 有効にする
Workload Identity
ノードプールに--workload-metadata-from-node=GKE_METADATA_SERVER
を付加すると、そのノードでWorkload Identity
を使うことができます
gcloud beta container clusters create ${CLUSTER_NAME} --identity-namespace=${PROJECT_ID}.svc.id.goog
gcloud beta container clusters update ${CLUSTER_NAME} --identity-namespace=${PROJECT_ID}.svc.id.goog
この場合、既存のノードプールへの影響はなく、新しいノードプールでは--workload-metadata-from-node=GKE_METADATA_SERVER
が有効になります。
よって、Workload Identityを使用するにはノードプールを更新するか、新しく作成する必要があります。
# 既存のノードプールを更新する場合
gcloud beta container node-pools update ${NODEPOOL_NAME} \
--cluster=${CLUSTER_NAME} \
--workload-metadata-from-node=GKE_METADATA_SERVER
# 新しくノードプールを作成する場合
gcloud beta container node-pools create ${NODEPOOL_NAME} \
--cluster=${CLUSTER_NAME} \
--workload-metadata-from-node=GKE_METADATA_SERVER
Google Cloud Service Account を作成する
gcloud iam service-accounts create ${GSA_NAME}
GSA_ID=$(gcloud iam service-accounts describe --format json ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com | jq -r '.uniqueId')
GSA_NAME
に以下の役割を与えます。
- roles/iam.workloadIdentityUser
- GKE ワークロードのサービスアカウントを使用するため
-
roles/iam.serviceAccountTokenCreator`
- サービス アカウント権限を使用するため
gcloud iam service-accounts add-iam-policy-binding \
--role roles/iam.serviceAccountTokenCreator \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[${K8S_NAMESPACE}/${KSA_NAME}]" \
${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
gcloud iam service-accounts add-iam-policy-binding \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[${K8S_NAMESPACE}/${KSA_NAME}]" \
${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
参考にしたブログ では一行になってましたが、やったらできなかったので2つに分けてます
Google OIDC フェデレーション用のAWS IAM Roleを作成する
Google OIDC フェデレーション用のAWS IAM Roleを作成するためのトラストポリシーを作成しておきます。
cat > gcp-trust-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "accounts.google.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"accounts.google.com:sub": "${GSA_ID}"
}
}
}
]
}
EOF
AWS IAM Roleを作成します
# assume-role-policy-documentでさっきのファイルを指定する
aws iam create-role --role-name ${AWS_ROLE_NAME} --assume-role-policy-document file://gcp-trust-policy.json
# policy を割り当てる
aws iam attach-role-policy --role-name ${AWS_ROLE_NAME} --policy-arn arn:aws:iam::aws:policy/${AWS_POLICY_NAME}
Kubernetes サービスアカウントのアノテーションに使用するため、ARNを取得しておきます
AWS_ROLE_ARN=$(aws iam get-role --role-name ${ROLE_NAME} --query Role.Arn --output text)
Kubernetes namespace と サービスアカウントを作成します
# Kubernetes namespace の作成
kubectl create namespace ${K8S_NAMESPACE}
# Kubernetes service account の作成
kubectl create serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME}
# service account に GKE Workload Identity(GCP ServiceAccountのメールアドレス) を annotate
kubectl annotate serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME} \
iam.gke.io/gcp-service-account=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
# service account に AWS Role ARN を annotate
kubectl annotate serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME} \
amazonaws.com/role-arn=${AWS_ROLE_ARN}
試す
GKE Workload Identity
kubectl run --rm -it \
--generator=run-pod/v1 \
--image google/cloud-sdk:slim \
--serviceaccount $KSA_NAME \
--namespace $K8S_NAMESPACE \
workload-identity-test
root@workload-identity-test:/ gcloud auth list
Credentialed Accounts
ACTIVE ACCOUNT
* test-service-account@$PROJECT_ID.iam.gserviceaccount.com
To set the active account, run:
$ gcloud config set account `ACCOUNT`
正しく設定できていた場合、Googleサービスアカウントのメールアドレスのみが表示されるはずです。
kubectl run -it --rm --generator=run-pod/v1 --image mikesir87/aws-cli --serviceaccount ${KSA_NAME} test-pod -n {$K8S_NAMESPACE}
If you don't see a command prompt, try pressing enter.
root@test-pod:/aws#
root@test-pod:/aws# aws sts get-caller-identity
{
"UserId": "XXXXX:gtoken-webhook-vqbhasptyassltln",
"Account": "XXXXXXXX",
"Arn": "arn:aws:sts::XXXXXXX:assumed-role/test-gcp-service-account/gtoken-webhook-vqbhasptyassltln"
}
root@test-pod:/aws# exit
指定したARNが表示されていたら成功です。
クリーンアップ
# remove aws iam role
aws iam detach-role-policy --role-name $AWS_ROLE_NAME --policy-arn arn:aws:iam::aws:policy/$AWS_POLICY_NAME
aws iam delete-role --role-name $AWS_ROLE_NAME
# remove k8s sa, namespace
kubectl delete sa -n $K8S_NAMESPACE $KSA_NAME
kubectl delete namespace $K8S_NAMESPACE
#remove Google Cloud IAM
gcloud iam service-accounts remove-iam-policy-binding \
--role roles/iam.serviceAccountTokenCreator \
--member "serviceAccount:$PROJECT_ID.svc.id.goog[$K8S_NAMESPACE/$KSA_NAME]" \
${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
gcloud iam service-accounts remove-iam-policy-binding \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:$PROJECT_ID.svc.id.goog[$K8S_NAMESPACE/$KSA_NAME]" \
${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
gcloud iam service-accounts delete $GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com
# disable workload identity
# update node
gcloud beta container node-pools update $K8S_NODEPOOL --cluster=$K8S_CLUSTER --workload-metadata-from-node=EXPOSED
# disable workload identity
gcloud beta container clusters update $CLUSTER_NAME --disable-workload-identity
# delete resources
kubectl delete -f deployment/
おわりに
この記事は3月に株式会社オプティマインドで検証した記録です。