この記事はBeeX Advent Calendar 2020の12/3の記事です。
Service Accountの仕組みを理解するために、Step By Stepでスクリプト化してみました。
スクリプト
#!/bin/bash set -eu CLUSTER_NAME=$1 SA_NAMESPACE=$2 SA_NAME=$3 ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) AWS_REGION=ap-northeast-1 ROLE_NAME=eks-role-${CLUSTER_NAME}-${SA_NAMESPACE}-${SA_NAME} TEMP_FILE=./tmp_${ROLE_NAME}_doc.json ####### # 1.OIDC Providerの証明書のThumbprint取得 # ブラウザから取得します。(後述) OIDC_THUMBPRINT=9e99a48a9960b14926bb7f3b02e22da2b0ab7280 ####### # 2.OIDC ProviderをIAMに登録 # OIDC ProviderがIdPとしてIAMに登録されていることを確認。登録されていない場合は新規登録 OIDC_PROVIDER_URL=$(aws eks describe-cluster --region ${AWS_REGION} --name ${CLUSTER_NAME} --query 'cluster.identity.oidc.issuer' --output text) CLUSTER_ID=$(echo ${OIDC_PROVIDER_URL} | awk -F"/" '{print $5}') IS_OIDC_PROVIDER_EXIST=$(aws iam list-open-id-connect-providers --query 'OpenIDConnectProviderList[].Arn' --output text | grep ${CLUSTER_ID} | wc -l) if [ "${IS_OIDC_PROVIDER_EXIST}" == "0" ];then OIDC_PROVIDER_ARN=$(aws iam create-open-id-connect-provider --url ${OIDC_PROVIDER_URL} --thumbprint-list ${OIDC_THUMBPRINT} --query 'OpenIDConnectProviderArn' --output text) # AudienceとしてSTSを追加 aws iam add-client-id-to-open-id-connect-provider --open-id-connect-provider-arn ${OIDC_PROVIDER_ARN} --client-id sts.amazonaws.com fi ####### # 3 EKSで認証されたSAがロールをAssumeできるように設定 cat <<EOF > ${TEMP_FILE} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${CLUSTER_ID}" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.${AWS_REGION}.amazonaws.com/id/${CLUSTER_ID}:sub": "system:serviceaccount:${SA_NAMESPACE}:${SA_NAME}" } } } ] } EOF aws iam create-role --role-name ${ROLE_NAME} --assume-role-policy-document file://${TEMP_FILE} ####### # 4. ロールを紐付けたServiceAccountの作成 cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: ${SA_NAME} namespace: ${SA_NAMESPACE} annotations: eks.amazonaws.com/role-arn: arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME} EOF
使ってみる
名前空間の作成
Namespaceを作成して、そこにService Accountを作成してみます。
# 先に名前空間作成しておきます
$ kubectl create ns my-namespace
スクリプトの実行
スクリプトを実行します。IAMの設定、IAM Roleの作成、Service Accountの作成と紐付けまで行ってくれます。
$ /bin/bash ./create_service_account.sh my-cluster my-namespace my-account
# -- 省略 --
serviceaccount/my-account created
ServiceAccountの確認
以下のイメージをPodでデプロイして、Service Accountが紐付けられたロールをAssumeしていることを確認します。
cat <<EOF > Dockerfile FROM amazon/aws-cli CMD ["sts", "get-caller-identity"] EOF
上記のDockerfileのイメージをECRにPushしておき、以下のようにJobから呼び出してみます。
- serviceAccountName
に先程作成したService Accountが指定されてます
- image
はECRのイメージ名に変更します。
cat <<EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: get-caller-identity namespace: my-namespace spec: completions: 1 parallelism: 1 template: spec: serviceAccountName: my-account containers: - name: get-caller-identity image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-image:latest restartPolicy: Never EOF
kubectl apply
してみるとジョブが一つ走ります。その際に aws sts get-caller-identity
が実行されAssumeしているロールが確認できますが、先程作成したロールにService Accountが紐付いていることがわかります。
$ kubectl get po -n my-namespace NAME READY STATUS RESTARTS AGE get-caller-identity-dfvx8 0/1 Completed 0 15s $ kubectl logs -n my-namespace get-caller-identity-dfvx8 { "UserId": "xxxxxxxxx:botocore-session-xxxxxxxx", "Account": "123456789012", "Arn": "arn:aws:sts::123456789012:assumed-role/eks-role-my-cluster-my-namespace-my-account/botocore-session-xxxxxxxx" }
やっていることを少し解説
1.OIDC Providerの証明書のThumbprint取得
IAMの画面から取得できます。
または、ブラウザから以下のURL叩いて取得してください。
https://oidc.eks.ap-northeast-1.amazonaws.com
2.OIDC ProviderをIAMに登録
登録されているのは以下の画面の部分です。IDの発行元としてAWSのIAMに登録します。
3 EKSで認証されたSAがロールをAssumeできるように設定
OIDC Providerにて発行されたIDを信頼します。
今回は system:serviceaccount:${SA_NAMESPACE}:${SA_NAME}
と言う風にService Account名まで固定してIAMロールを紐付けてますが、
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${CLUSTER_ID}" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.${AWS_REGION}.amazonaws.com/id/${CLUSTER_ID}:sub": "system:serviceaccount:${SA_NAMESPACE}:${SA_NAME}" } } } ] }
以下のように名前空間にIAMロールを紐付けることも可能です。
"Condition": { "StringLike": { "oidc.eks.${AWS_REGION}.amazonaws.com/id/${CLUSTER_ID}:sub": "system:serviceaccount:${SA_NAMESPACE}:*" }
4. ロールを紐付けたServiceAccountの作成
以下のService Accoutが作成されます。
$ kubectl get serviceaccounts -n my-namespace my-account NAME SECRETS AGE my-account 1 40m
このServiceAccountを紐付けると内部的に様々な設定がされます。先程のシンプルなPodの定義を出力してみると面白いです。
$ kubectl get pod -n my-namespace get-caller-identity-dfvx8 -oyaml