AWS EKS IRSA 解説: Pod が Service Account 経由で IAM Role を使う方法
AWS EKS で IRSA(IAM Roles for Service Accounts)を設定し、Pod が Kubernetes Service Account 経由で IAM Role を利用して、S3 や Secrets Manager に安全にアクセスする方法を解説します。
AWS の運用をしていると、データベースのバックアップを定期実行する CronJob を用意したい場面はよくあります。
たとえば AWS EKS 上で動くバックアップジョブが、Secrets Manager からデータベースの認証情報を取得し、その後バックアップファイルを S3 にアップロードするケースを考えてみます。このとき必ず出てくるのが、「Pod に AWS 権限をどう安全に持たせるか」という問題です。
まずは、よくある CronJob の定義例から見ていきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
apiVersion: v1
kind: Namespace
metadata:
name: database-backup
---
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
namespace: database-backup
name: example
spec:
provider: aws
secretObjects:
- secretName: example-db-secret
type: Opaque
data:
- key: db-user
objectName: db-user
- key: db-password
objectName: db-password
- key: db-host
objectName: db-host
- key: db-port
objectName: db-port
parameters:
objects: |
- objectName: "<database_arn>"
objectType: secretsmanager
jmesPath:
- path: username
objectAlias: db-user
- path: password
objectAlias: db-password
- path: host
objectAlias: db-host
- path: "port | to_string(@)"
objectAlias: db-port
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: database-backup-sa
namespace: database-backup
annotations:
eks.amazonaws.com/role-arn: <service_account>
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: bpm-backup
namespace: database-backup
spec:
schedule: "13 */2 * * *"
jobTemplate:
spec:
template:
spec:
serviceAccountName: database-backup-sa
containers:
- name: example-backup
image: postgres:17-alpine
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- |
export HOST=$(cat /mnt/secrets-store/db-host)
export PORT=$(cat /mnt/secrets-store/db-port)
export USERNAME=$(cat /mnt/secrets-store/db-user)
export PASSWORD=$(cat /mnt/secrets-store/db-password)
DATABASE="example-backup"
apk add --no-cache aws-cli
BACKUP_FILE="/tmp/example_backup_$(date +%Y%m%d_%H%M%S).sql"
PGPASSWORD=$PASSWORD pg_dump -h $HOST -p $PORT -U $USERNAME -d $DATABASE -f $BACKUP_FILE
aws s3 cp $BACKUP_FILE s3://<bucket_name>/example-backup/
resources:
limits:
cpu: "100m"
memory: "512Mi"
requests:
cpu: "50m"
memory: "64Mi"
volumeMounts:
- name: secrets-store
mountPath: /mnt/secrets-store
readOnly: true
volumes:
- name: secrets-store
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: example
restartPolicy: OnFailure
EKS を使っている場合、Secrets Store CSI Driver などを組み合わせて、Secrets Manager の値を Pod にマウントする構成はよくあります。
データベースの認証情報そのものはローテーションで管理できますが、S3 にアップロードするためだけに IAM User の Access Key を別途発行して使い続ける構成は、長期的には扱いづらくなりがちです。
そこで有力なのが、Kubernetes Service Account を IAM Role にひも付ける方法です。これなら Pod は実行時に一時的な AWS 権限を取得できるため、長期的な認証情報をアプリケーション内に持たせずに済みます。
準備するもの
必要になるのは、主に次の 2 つです。
- Service Account 用の IAM Role
- その Role に紐づく IAM Policy と信頼関係
IAM Policy
まず IAM Role を作成し、その ARN を控えておきます。この ARN は、上の YAML で指定している ServiceAccount の annotation に対応します。
eks.amazonaws.com/role-arn: <service_account>
次に、Secrets Manager へのアクセスと S3 へのアップロードを許可する IAM Policy をアタッチします。
Secrets Manager の値が KMS Key で暗号化されている場合は、KMS の権限も忘れずに追加してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
"Statement": [
{
"Action": [
"kms:Decrypt",
"kms:Encrypt"
],
"Effect": "Allow",
"Resource": [
"<kms_key_arn>"
]
},
{
"Action": [
"secretsmanager:GetResourcePolicy",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds"
],
"Effect": "Allow",
"Resource": [
"<secrets_manager_arn>"
]
},
{
"Action": [
"secretsmanager:ListSecrets"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::<bucket_name>/*"
}
],
"Version": "2012-10-17"
}
Trust Relationship
一番重要なのは、Trust Relationship です。
Amazon EKS クラスターを作成するときは、通常 OIDC(OpenID Connect)Identity Provider も合わせて構成します。これによって、Kubernetes Service Account と AWS IAM Role の間に信頼関係を作れるようになります。これが IRSA の仕組みです。
ここでは、その EKS クラスターに紐づく OIDC Provider を使います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<aws_account_id>:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/<identity_provider_id>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-northeast-1.amazonaws.com/id/<identity_provider_id>:sub": "system:serviceaccount:<namespace>:database-backup-sa",
"oidc.eks.ap-northeast-1.amazonaws.com/id/<identity_provider_id>:aud": "sts.amazonaws.com"
}
}
}
]
}
この設定で特に重要なのは、次の 2 点です。
subが対象の Kubernetes Service Account と正確に一致していることaudがsts.amazonaws.comになっていること
この条件が満たされていれば、Pod はその Service Account の身份を使って、OIDC 経由で AWS STS から一時的な認証情報を取得できます。
最終的にどうなるか
ここまでの設定が完了すると、CronJob は database-backup-sa という Service Account を使って EKS の OIDC Provider に対して認証を行い、対応する IAM Role の一時権限を受け取れるようになります。
つまり、この Pod は IAM User の Access Key を保持しなくても、次の処理を実行できます。
- Secrets Manager からデータベース認証情報を取得する
- データベースに接続してバックアップファイルを生成する
- 生成したバックアップファイルを S3 にアップロードする
EKS 上のワークロードから AWS リソースを使う場合、この方法はかなりおすすめです。長期的な認証情報を配る構成と比べると、IRSA のほうが安全で、運用もしやすくなります。