Proxy環境で Pod Identityを利用するにあたって、Pod Identityの流れや通信を詳しく調べる必要があったので、調査結果を残しておきます。
- 参考
■ 結論
Pod Identity を利用する場合、以下にアクセスできる必要があります。
- ロールを引き受けるPod
-
169.254.170.23
(pod-identity-agentのエンドポイント)
Pod Identityで関連付けを作成したIAMロールの認証情報を取得するのに必要
-
- pod-identity-agent
-
169.254.169.254
(インスタンスメタデータサービスのエンドポイント)
ノードロールの認証情報を取得するのに必要 -
eks-auth.ap-northeast-1.api.aws
(EKS Authエンドポイント)
AssumeRoleForPodIdentity APIを呼び出すために必要
EKSサービスエンドポイント
-
■ Pod Identityの処理の流れ
1. Podに適用するIAM Roleの作成
Podに適用するIAM RoleにはEKS Pod Identityによる使用を許可する信頼ポリシーを設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEksAuthToAssumeRoleForPodIdentity",
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}
2. Pod Identityの関連付けを作成
作成したIAMロールでPod Identityの関連付けを作成します。
Pod Identityの関連付けでは、どのNamespaceのどのServiceAccountがIAM Roleを使用できるかを設定します。
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--role-arn arn:aws:iam::111122223333:role/my-role \
--namespace pod-identity-test \
--service-account my-service-account
3. Podの作成
Pod Identityの関連付けで指定した Namespace と ServiceAccount でPodを起動します。
---
apiVersion: v1
kind: Namespace
metadata:
name: pod-identity-test
---
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: pod-identity-test
name: my-service-account
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: pod-identity-test
name: awscli
spec:
selector:
matchLabels:
app: awscli
replicas: 2
template:
metadata:
labels:
app: awscli
spec:
serviceAccountName: my-service-account
containers:
- name: aws-cli
image: public.ecr.aws/aws-cli/aws-cli:2.17.37
command: ["sleep", "infinity"]
kubectl apply -f app.yaml
4~6. EKS Pod Identity Webhookでマニフェストを変更
マニフェストがapplyされるとEKS Pod Identity Webhookが処理をインターセプトします。
※ aws/amazon-eks-pod-identity-webhook | GitHub
※ EKS Pod Identity WebhookはEKSのマネージドです。
EKS Pod Identity WebhookはPod Identityの関連付けにマニフェストで指定されたNamespaceとServiceAccountのが存在するかをチェックします。
Pod Identity の関連付けが存在する場合、Podのマニフェストに以下を追加します。
env:
- name: AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
value: "/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token"
- name: AWS_CONTAINER_CREDENTIALS_FULL_URI
value: "http://169.254.170.23/v1/credentials"
volumeMounts:
- mountPath: "/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/"
name: eks-pod-identity-token
volumes:
- name: eks-pod-identity-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: pods.eks.amazonaws.com
expirationSeconds: 86400 # 24 hours
path: eks-pod-identity-token
-
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
- value:
/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
- IAM認証情報を取得するためのサービスアカウントトークンが格納されたファイルのパス
このサービスアカウントトークンは EKS Auth APIにAWS認証情報を要求するときに利用されます。 - EKS Auth のAssumeRoleForPod Identityへのリクエスト
internal/cloud/eksauth/service.go#L46 - aws/eks-pod-identity-agent | GitHub
- value:
-
AWS_CONTAINER_CREDENTIALS_FULL_URI
- value:
http://169.254.170.23/v1/credentials
- pod-identity-agent(Daemonset)がListenしているIAM認証情報取得URL
pod-identity-agentはhostNetwork: true
で起動されており、ホストと同じネットワークネームスペースを使っているため、ノード上にpod-id-link0
というインターフェースを作成し、IP169.254.170.23
を設定、ポート80
でListenします。- pod-identity-agent は
hostNetwork: true
に設定されている
charts/eks-pod-identity-agent/templates/daemonset.yaml#L34 - eks-pod-identity-agent | GitHub - ListenするIPとポートの定義
configuration/config.go#L3 - aws/eks-pod-identity-agent | GitHub - インターフェースの作成とIPの設定処理
pkg/initalizer/executor.go#L30 - aws/eks-pod-identity-agent | GitHub - HTTPサーバーの起動処理
cmd/server.go#L97 - aws/eks-pod-identity-agent | GitHub
- pod-identity-agent は
- value:
7~8. Podを起動
変換されたマニフェストでkubeletがPodを起動します。
9. Podからpod-identity-agentの認証エンドポイントにIAM認証情報を要求
Pod内でAWS SDKを実行すると、SDKは一時的なIAM認証情報を取得するため 、Authorizationヘッダにサービスアカウントトークンを含めて AWS_CONTAINER_CREDENTIALS_FULL_URI
にリクエストを送信します。
Curlで再現するとこんな感じです。
# セッショントークンを変数に格納
SERVICE_ACCOUNT_TOKEN=$(cat $AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE)
# pod-identity-agentのエンドポイントにアクセスしてAWSの認証情報を取得
curl -H "Authorization: $SERVICE_ACCOUNT_TOKEN" "$AWS_CONTAINER_CREDENTIALS_FULL_URI" | jq .
# {
# "AccessKeyId": "XXXXXXXXXXXXXXXXXXXX",
# "SecretAccessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
# "Token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
# "AccountId": "xxxxxxxxxxxxxx",
# "Expiration": "2025-03-28T13:12:52Z"
# }
10~11. pod-identity-agentがIMDSにノードロールのIAM認証情報を要求
EKS AuthのAssumeRoleForPodIdentity APIを実行してAWSの認証情報を取得するには、IAMの eks-auth:AssumeRoleForPodIdentity
権限が必要となるため、pod-identity-agentはインスタンスメタデータサービス( IMDS)( 169.254.169.25
) にアクセスしてノードロールの認証情報を取得します。
ノード上でインスタンスメタデータサービス( IMDS)( 169.254.169.25
) への通信をtcpdumpでキャプチャすると以下のリクエストを確認できます。
tcpdump -tnlA -i any "tcp and dst port 80 and dst 169.254.169.254"
-
PUT /latest/api/token HTTP/1.1
IMDSv2 のトークン取得リクエスト
IMDSv2 では最初に PUT リクエストでトークンを取得し、それ以降のメタデータ取得にこのトークンを使う仕組みです。 -
GET /latest/meta-data/iam/security-credentials/
IAM ロール名の一覧を取得しています -
GET /latest/meta-data/iam/security-credentials/KarpenterNodeRole
指定された IAM ロール (KarpenterNodeRole) のクレデンシャル情報を取得しています。
これには一時的なアクセスキー、シークレットキー、セッショントークンなどが含まれます。
IP 10.32.1.37.34244 > 169.254.169.254.http: Flags [P.], seq 0:277, ack 1, win 491, options [nop,nop,TS val 2945826404 ecr 3130553910], length 277: HTTP: PUT /latest/api/token HTTP/1.1
E..I.|@...x.
.%.......P^,.....a....`}.....
...d..v6PUT /latest/api/token HTTP/1.1
Host: 169.254.169.254
User-Agent: aws-sdk-go-v2/1.26.1 os/linux lang/go#1.22.5 md/GOOS#linux md/GOARCH#amd64 ft/ec2-imds
Content-Length: 0
Amz-Sdk-Request: attempt=1; max=3
X-Aws-Ec2-Metadata-Token-Ttl-Seconds: 300
Accept-Encoding: gzip
................
IP 10.32.1.37.oracleas-https > 169.254.169.254.http: Flags [P.], seq 0:325, ack 1, win 491, options [nop,nop,TS val 2945826405 ecr 3130553911], length 325: HTTP: GET /latest/meta-data/iam/security-credentials/ HTTP/1.1
E..y.M@...j.
.%.......P$..\........`......
...e..v7GET /latest/meta-data/iam/security-credentials/ HTTP/1.1
Host: 169.254.169.254
User-Agent: aws-sdk-go-v2/1.26.1 os/linux lang/go#1.22.5 md/GOOS#linux md/GOARCH#amd64 ft/ec2-imds
Amz-Sdk-Request: attempt=1; max=3
X-Aws-Ec2-Metadata-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Accept-Encoding: gzip
................
IP 10.32.1.37.49431 > 169.254.169.254.http: Flags [P.], seq 0:355, ack 1, win 491, options [nop,nop,TS val 2945826406 ecr 3130553912], length 355: HTTP: GET /latest/meta-data/iam/security-credentials/KarpenterNodeRole HTTP/1.1
E...X.@.....
.%.......P..:..*......`......
...f..v8GET /latest/meta-data/iam/security-credentials/KarpenterNodeRole HTTP/1.1
Host: 169.254.169.254
User-Agent: aws-sdk-go-v2/1.26.1 os/linux lang/go#1.22.5 md/GOOS#linux md/GOARCH#amd64 ft/ec2-imds
Amz-Sdk-Request: attempt=1; max=3
X-Aws-Ec2-Metadata-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Accept-Encoding: gzip
................
※ 補足: インスタンスメタデータサービス(IMDS)について
12~14. pod-identity-agentがEKS Auth APIエンドポイントにPodのIAM認証情報を要求
pod-identity-agentがノードロールの認証情報を取得できたら、EKS Auth ( eks-auth.ap-northeast-1.api.aws
) の AssumeRoleForPodIdentity API にサービスアカウントトークンとクラスタ名を指定してリクエストを送信し、Pod Identityでサービスアカウントに関連付けられたIAMロールの認証情報を取得します。
pod-identity-agentのソースコードだとこの辺りです
internal/cloud/eksauth/service.go#L46 - aws/eks-pod-identity-agent | GitHub
※ 補足1: EKS Authのエンドポイントの一覧
※ 補足2: AssumeRoleForPodIdentity API (boto3)
15. PodがPod Identityに関連付けたIAMロールの認証情報を取得
Pod Identityに関連付けられたIAMロールの認証情報をレスポンスとして受け取ります。
{
"AccessKeyId": "XXXXXXXXXXXXXXXXXXXX",
"SecretAccessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"Token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"AccountId": "xxxxxxxxxxxxxx",
"Expiration": "2025-03-28T13:12:52Z"
}
■ さらに調査
pod-identity-agentのエンドポイント( 169.254.170.23
)について
エンドポイントの存在確認
pod-identity-agentのDaemonsetは hostNetwork: true
で起動しているため、エンドポイントはkubernetes上から確認できません。
まずはこのエンドポイントがホスト上に存在していて、pod-identity-agentプロセスがそのエンドポイントをListenしているかを確認します。
ノードにログインして下記コマンドを実行します。
# ノードが169.254.170.23:80 をListenしていることを確認します。
netstat -tuln | grep 169.254.170.23:80
# tcp 0 0 169.254.170.23:80 0.0.0.0:* LISTEN
# どのプロセスがポート80を利用しているかを確かめる
lsof -i :80
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# eks-pod-i 2699 root 6u IPv6 18423 0t0 TCP [fd00:ec2::23]:http (LISTEN)
# eks-pod-i 2699 root 7u IPv4 19305 0t0 TCP ip-10-32-1-37.ap-northeast-1.compute.internal:http (LISTEN)
# プロセスツリーでpod-identity-agentがポートを利用しているプロセスであることを確認
pstree -pa
# |-containerd-shim,1568 -namespace k8s.io -id5a5f80457fd7f9323a63824372c9182f8fb6ca
# | |-go-runner,2676 /eks-pod-identity-agent server --port 80 --cluster-name baseport-prd --probe-port 2703
# | | |-eks-pod-identit,2699 server --port 80 --cluster-name baseport-prd --probe-port 2703
ipコマンドで pod-id-link0
インターフェースに 169.254.170.23
が設定されているかを確認します。
ip a | grep -B 2 -A 5 169.254.170.23
# 3: pod-id-link0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
# link/ether 4e:a0:b6:ea:bf:2e brd ff:ff:ff:ff:ff:ff
# inet 169.254.170.23/32 scope global pod-id-link0
# valid_lft forever preferred_lft forever
# inet6 fd00:ec2::23/128 scope global
# valid_lft forever preferred_lft forever
# inet6 fe80::4ca0:b6ff:feea:bf2e/64 scope link
# valid_lft forever preferred_lft forever
pod-identity-agentのエンドポイントへのリクエストをtcpdump
ノード上で169.254.170.23
へのリクエストをtcpdumpして、pod側でaws sts get-caller-identity
を実行すると、AWS_CONTAINER_CREDENTIALS_FULL_URI
へのリクエストが確認できます。
tcpdump -tnlA -i any "tcp and port 80 and dst 169.254.170.23"
IP 10.32.1.213.53620 > 169.254.170.23.http: Flags [P.], seq 0:1385, ack 1, win 491, options [nop,nop,TS val 1679748253 ecr 1911346896], length 1385: HTTP: GET /v1/credentials HTTP/1.1
E....H@.....
.......t.P'VRB..M.....e......
d...q...GET /v1/credentials HTTP/1.1
Host: 169.254.170.23
Accept-Encoding: identity
Accept: application/json
Authorization: [サービスアカウントトークン(JWT)]
User-Agent: python-urllib3/1.26.19