投稿

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 EKS IRSA 解説: Pod が Service Account 経由で IAM Role を使う方法

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 つです。

  1. Service Account 用の IAM Role
  2. その 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 点です。

  1. sub が対象の Kubernetes Service Account と正確に一致していること
  2. audsts.amazonaws.com になっていること

この条件が満たされていれば、Pod はその Service Account の身份を使って、OIDC 経由で AWS STS から一時的な認証情報を取得できます。

最終的にどうなるか

ここまでの設定が完了すると、CronJob は database-backup-sa という Service Account を使って EKS の OIDC Provider に対して認証を行い、対応する IAM Role の一時権限を受け取れるようになります。

つまり、この Pod は IAM User の Access Key を保持しなくても、次の処理を実行できます。

  1. Secrets Manager からデータベース認証情報を取得する
  2. データベースに接続してバックアップファイルを生成する
  3. 生成したバックアップファイルを S3 にアップロードする

EKS 上のワークロードから AWS リソースを使う場合、この方法はかなりおすすめです。長期的な認証情報を配る構成と比べると、IRSA のほうが安全で、運用もしやすくなります。

この投稿は投稿者によって CC BY 4.0 の下でライセンスされています。

トレンドのタグ