Jun 18, 2025
Jun 19, 2025
N/A Views
MD

IAM Roles for Service Accounts (IRSA) は、KubernetesのServiceAccountに対してAWSのIAMロールを関連付ける機能です。これにより、PodからAWSサービスに対する認証をより安全に行うことができます。

EKSでは標準でIRSAが利用できますが、他のKubernetes環境でAPI Serverに追加の設定をすれば利用可能です。この記事では、MicroK8sでIRSAを有効にする手順を説明します。

目次

OIDC Issuerの準備

IRSAを利用するためには、まずOIDC Issuerを準備する必要があります。
必要なパブリックエンドポイントは

  • OpenID Connect Discovery (.well-known/openid-configuration)
  • JSON Web Key Set (上記のJSONのうちjwks_uriで指定されるもの)

だけなので、JSONファイルを静的に配置するだけでもIRSAで使用するためのOIDC Issuerとして機能します。

今回はGitHubレポジトリにこの2ファイルを置き、OIDC Issuerとして使用します。

まず、作業ディレクトリを作成します:

mkdir oidc-issuer
cd oidc-issuer
echo private_key.pem > .gitignore

次に、OIDC Issuerで使用する公開鍵・秘密鍵のペアを生成します:

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
openssl pkcs8 -topk8 -inform PEM -in private.pem -out private_key.pem -nocrypt
rm -f private.pem

生成した公開鍵をJWKS形式に変換します。このために、専用のツールを使用します (要: Java 17+):

git clone https://github.com/making/pem-to-jwks.git
cd pem-to-jwks && ./mvnw clean package -DskipTests && cd -
java -jar pem-to-jwks/target/pem-to-jwks-0.0.1-SNAPSHOT.jar
echo pem-to-jwks >> .gitignore

生成されたjwks.jsonをGitHubリポジトリに公開します。:

GITHIB_REPO=making/oidc-issuer

git init
git add -A
git commit -m "Initial commit"
git branch -M main
git remote add origin git@github.com:${GITHIB_REPO}.git
git push -u origin main

JWKSファイルがGitHub上で正しく公開されているか確認します:

curl -s https://raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main/jwks.json

次にOpenID Connect Discoveryの設定ファイルを作成します:

mkdir -p .well-known
cat << EOF > .well-known/openid-configuration
{
  "response_types_supported": [
    "id_token"
  ],
  "issuer": "https://raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main",
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "subject_types_supported": [
    "public"
  ],
  "jwks_uri": "https://raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main/jwks.json"
}
EOF

.well-known/openid-configurationをリポジトリに追加してプッシュします:

git add -A
git commit -m "Add OpenID Connect configuration"
git push origin main

OpenID Connect Discoveryの設定が正しく公開されているか確認します:

curl -s https://raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main/.well-known/openid-configuration

MicroK8sの設定

MicroK8sでIRSAを使用するために、APIサーバーの設定を変更します。まず、先ほど生成した鍵ファイルをMicroK8sのホスト上に場所にコピーします。ここでは以下のパスに配置したことにします。:

  • /opt/k8s/public.pem
  • /opt/k8s/private_key.pem

次に、MicroK8sのAPIサーバー設定を変更してこの鍵とIssuerを使ってService Account Tokenが生成されるようにします:

GITHIB_REPO=making/oidc-issuer

cat <<EOF | sudo tee -a /var/snap/microk8s/current/args/kube-apiserver
--api-audiences=https://kubernetes.default.svc
--service-account-issuer=https://raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main
--service-account-key-file=/opt/k8s/public.pem
--service-account-signing-key-file=/opt/k8s/private_key.pem
EOF

--service-account-****オプションはデフォルトで設定されており、重複して設定すると先勝ちになるので、デフォルトの設定行をコメントアウトします:

#--service-account-key-file=${SNAP_DATA}/certs/serviceaccount.key
...
#--service-account-issuer='https://kubernetes.default.svc'
#--service-account-signing-key-file=${SNAP_DATA}/certs/serviceaccount.key

設定を反映させるためにMicroK8sを再起動します:

sudo snap restart microk8s

既存のPodのService Account Tokenを再生成するために、乱暴ですがすべてのPodを再作成します:

kubectl delete pod -A --all --force

全てのPodがReadyになるまで待ちます。

Service Account Tokenの確認

設定が正しく適用されているかを確認するため、テスト用のPodを起動してService Account Tokenを確認します:

kubectl run -it netshoot --image=nicolaka/netshoot --rm --restart=Never --command=true -- bash

Pod内で、Service Account Tokenの内容をデコードして確認します:

$ cat /run/secrets/kubernetes.io/serviceaccount/token | cut -d'.' -f2 | sed 's/$/===/' | base64 -d | jq .
{
  "aud": [
    "https://kubernetes.default.svc"
  ],
  "exp": 1781800378,
  "iat": 1750264378,
  "iss": "https://raw.githubusercontent.com/making/oidc-issuer/refs/heads/main",
  "jti": "dc1712b8-db40-4b1a-89cd-8b69e26ad4ca",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "apricot",
      "uid": "e6edbd10-b5c9-461f-83f5-eaf92559ef07"
    },
    "pod": {
      "name": "netshoot",
      "uid": "0d1dadab-331a-442d-9359-d4e79ab24c7f"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "aa0d94f9-f02c-4560-bcdf-50f10f5a95f0"
    },
    "warnafter": 1750267985
  },
  "nbf": 1750264378,
  "sub": "system:serviceaccount:default:default"
}

トークンのissフィールドが設定したOIDC Issuer URLになっていることを確認します。これで、IRSAに必要なトークンの設定が正しく行われています。

確認が完了したら、Podを終了します:

exit

EKSの場合は、/var/run/secrets/eks.amazonaws.com/serviceaccount/tokenに別途Web Identity Tokenが格納されますが、本稿の手法では
/run/secrets/kubernetes.io/serviceaccount/tokenのService Account TokenをIAMのWeb Identity Tokenとして使用します。

AWS OIDC Identity Providerの作成

次に、AWSでOIDC Identity Providerを作成します。まず、GitHub(raw.githubusercontent.com)のSSL証明書のフィンガープリントを取得します:

FINGERPRINT=$(openssl s_client -servername raw.githubusercontent.com -showcerts -connect raw.githubusercontent.com:443 </dev/null 2>/dev/null | openssl x509 -fingerprint -sha1 -noout | sed 's/sha1 Fingerprint=//' | sed 's/://g')

取得したフィンガープリントを使用してOIDC Identity Providerの設定ファイルを作成し、AWSに登録します:

cat <<EOF > oidc-provider.json
{
    "Url": "https://raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main",
    "ClientIDList": [
        "https://kubernetes.default.svc"
    ],
    "ThumbprintList": [
        "${FINGERPRINT}"
    ]
}
EOF

aws iam create-open-id-connect-provider --cli-input-json file://oidc-provider.json

IAMロールの作成

作成したOIDC Identity ProviderのARNを取得します:

OIDC_PROVIDER_ARN=$(aws iam list-open-id-connect-providers --query "OpenIDConnectProviderList[?ends_with(Arn, 'raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main')].Arn" --output text)

特定のServiceAccountに対してIAMロールを作成します。ここでは、defaultネームスペースのaws-cli ServiceAccountを使用します:

NAMESPACE=default
SERVICE_ACCOUNT_NAME=aws-cli
cat << EOF > k8s_${NAMESPACE}_${SERVICE_ACCOUNT_NAME}-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "${OIDC_PROVIDER_ARN}"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main:sub": "system:serviceaccount:${NAMESPACE}:${SERVICE_ACCOUNT_NAME}",
                    "raw.githubusercontent.com/${GITHIB_REPO}/refs/heads/main:aud": "https://kubernetes.default.svc"
                }
            }
        }
    ]
}
EOF

aws iam create-role --role-name k8s_${NAMESPACE}_${SERVICE_ACCOUNT_NAME} --assume-role-policy-document file://k8s_${NAMESPACE}_${SERVICE_ACCOUNT_NAME}-trust-policy.json

IAMポリシーの設定

作成したIAMロールに対して、S3へのアクセス権限を付与します。この例では、特定のプレフィックスを持つS3バケットへのフルアクセス権限を設定します:

export BUCKET_PREFIX=k8s-${NAMESPACE}-${SERVICE_ACCOUNT_NAME}

cat <<EOF > s3-prefix-full-access-${BUCKET_PREFIX}-policy.json
{
  "Version": "2012-10-17",
  "Statement": [

    {
      "Sid": "FullAccessTo$(echo ${BUCKET_PREFIX} | sed 's/\-//g')Buckets",
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "arn:aws:s3:::${BUCKET_PREFIX}-*",
        "arn:aws:s3:::${BUCKET_PREFIX}-*/*"
      ]
    },
    {
      "Sid": "ListBucketsForConsoleAccess",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "*"
    }
  ]
}
EOF

aws iam put-role-policy --role-name k8s_${NAMESPACE}_${SERVICE_ACCOUNT_NAME} --policy-name s3-prefix-full-access-${BUCKET_PREFIX} --policy-document file://s3-prefix-full-access-${BUCKET_PREFIX}-policy.json

テスト用Podの作成

IRSAの動作を確認するため、AWS CLIが実行可能なテスト用のPodを作成します:

export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export AWS_REGION=ap-northeast-1
cat <<EOF > aws-cli.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: aws-cli
  namespace: default
---
apiVersion: v1
kind: Pod
metadata:
  name: aws-cli
  namespace: default
spec:
  serviceAccountName: aws-cli
  containers:
  - name: aws-cli
    image: public.ecr.aws/aws-cli/aws-cli
    command: [ "sleep", "infinity" ]
    env:
    - name: AWS_REGION
      value: ${AWS_REGION}
    - name: AWS_ROLE_ARN
      value: arn:aws:iam::${AWS_ACCOUNT_ID}:role/k8s_default_aws-cli
    - name: AWS_WEB_IDENTITY_TOKEN_FILE
      value: /run/secrets/kubernetes.io/serviceaccount/token
    - name: AWS_ROLE_SESSION_NAME
      value: k8s-demo
  restartPolicy: Never
---
EOF

kubectl apply -f aws-cli.yaml

AWSサービスアクセスの動作確認

作成したPodに接続してIAMロールでAWSサービスに正しくアクセスできることを確認します:

kubectl exec -ti aws-cli -c aws-cli -- bash

Pod内でAWSの認証情報を確認します。IRSAが正しく設定されていれば、IAMロールを使用した認証が行われます:

aws sts get-caller-identity

次のような出力が得られれば成功です:

{
    "UserId": "*********************:k8s-demo",
    "Account": "*****",
    "Arn": "arn:aws:sts::*****:assumed-role/k8s_default_aws-cli/k8s-demo"
}

設定したIAMポリシーが正しく動作することを確認します。まず、許可されたプレフィックスでS3バケットを作成してみます:

aws s3 mb s3://k8s-default-aws-cli-demo
make_bucket: k8s-default-aws-cli-demo

次に、許可されていないプレフィックスでS3バケットを作成してみます:

aws s3 mb s3://k9s-default-aws-cli-demo
make_bucket failed: s3://k9s-default-aws-cli-demo An error occurred (AccessDenied) when calling the CreateBucket operation: User: arn:aws:sts::*****:assumed-role/k8s_default_aws-cli/k8s-demo is not authorized to perform: s3:CreateBucket on resource: "arn:aws:s3:::k9s-default-aws-cli-demo" because no identity-based policy allows the s3:CreateBucket action

期待通りアクセスが拒否されました。これで、IAMポリシーによる権限制御が正しく機能していることが確認できました。

exit

本稿では、MicroK8sでIRSAを有効にする手順を説明しました。

試してはいませんが、Kindやその他、API Serverの設定が可能なKubernetes環境でも同様の手順でIRSAを有効にできるはずです。

API Serverの設定が許可されてないK8sを使用している場合は、本稿の手法は使えません。代わりにService Account Tokenを外向けに再発行する裏技を使うことで無理やりIRSAを実現することもできました。
興味のある方は https://github.com/categolj/jwt-reissuer のREADMEを参照してください。

Found a mistake? Update the entry.
Share this article: