IAM Roles for Service Accounts configuration
IAM Role for Service Account on EKS Anywhere clusters with self-hosted signing keys
IAM Roles for Service Account (IRSA) enables applications running in clusters to authenticate with AWS services using IAM roles. The current solution for leveraging this in EKS Anywhere involves creating your own OIDC provider for the cluster, and hosting your cluster’s public service account signing key. The public keys along with the OIDC discovery document should be hosted somewhere that AWS STS can discover it.
The steps below are based on the guide for configuring IRSA for DIY Kubernetes, with modifications specific to EKS Anywhere’s cluster provisioning workflow. The main modification is the process of generating the keys.json document. As per the original guide, the user has to create the service account signing keys, and then use that to create the keys.json document prior to cluster creation. This order is reversed for EKS Anywhere clusters, so you will create the cluster first, and then retrieve the service account signing key generated by the cluster, and use it to create the keys.json document. The sections below show how to do this in detail.
Create an OIDC provider and make its discovery document publicly accessible
You must use a single OIDC provider per EKS Anywhere cluster, which is the best practice to prevent a token from one cluster being used with another cluster. These steps describe the process of using a S3 bucket to host the OIDC discovery.json
and keys.json
documents.
-
Create an S3 bucket to host the public signing keys and OIDC discovery document for your cluster . Make a note of the
$HOSTNAME
and$ISSUER_HOSTPATH
. -
Create the OIDC discovery document as follows:
cat <<EOF > discovery.json { "issuer": "https://$ISSUER_HOSTPATH", "jwks_uri": "https://$ISSUER_HOSTPATH/keys.json", "authorization_endpoint": "urn:kubernetes:programmatic_authorization", "response_types_supported": [ "id_token" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "claims_supported": [ "sub", "iss" ] } EOF
-
Upload the
discovery.json
file to the S3 bucket:aws s3 cp ./discovery.json s3://$S3_BUCKET/.well-known/openid-configuration
-
Create an OIDC provider for your cluster. Set the Provider URL to
https://$ISSUER_HOSTPATH
and Audience tosts.amazonaws.com
. -
Make a note of the
Provider
field of OIDC provider after it is created. -
Assign an IAM role to the OIDC provider.
-
Navigate to the AWS IAM Console.
-
Click on the OIDC provider.
-
Click Assign role.
-
Select Create a new role.
-
Select Web identity as the trusted entity.
-
In the Web identity section:
- If your Identity provider is not auto selected, select it.
- Select
sts.amazonaws.com
as the Audience.
-
Click Next.
-
Configure your desired Permissions poilicies.
-
Below is a sample trust policy of IAM role for your pods. Replace
ACCOUNT_ID
,ISSUER_HOSTPATH
,NAMESPACE
andSERVICE_ACCOUNT
. Example: Scoped to a service account{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/ISSUER_HOSTPATH" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "ISSUER_HOSTPATH:sub": "system:serviceaccount:NAMESPACE:SERVICE_ACCOUNT" }, } } ] }
-
Create the IAM Role and make a note of the Role name.
-
After the cluster is created you can grant service accounts access to the role by modifying the trust relationship. See the How to use trust policies with IAM Roles for more information on trust policies. Refer to Configure the trust relationship for the OIDC provider’s IAM Role for a working example.
-
Create (or upgrade) the EKS Anywhere cluster
When creating (or upgrading) the EKS Anywhere cluster, you need to configure the kube-apiserver’s service-account-issuer
flag so it can issue and mount projected service account tokens in pods. For this, use the value obtained in the first section for $ISSUER_HOSTPATH
as the service-account-issuer
. Configure the kube-apiserver by setting this value through the EKS Anywhere cluster spec:
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: Cluster
metadata:
name: my-cluster-name
spec:
podIamConfig:
serviceAccountIssuer: https://$ISSUER_HOSTPATH
Set the remaining fields in cluster spec as required and create the cluster.
Generate keys.json and make it publicly accessible
-
The cluster provisioning workflow generates a pair of service account signing keys. Retrieve the public signing key from the cluster and create a
keys.json
document with the content.git clone https://github.com/aws/amazon-eks-pod-identity-webhook cd amazon-eks-pod-identity-webhook kubectl get secret ${CLUSTER_NAME}-sa -n eksa-system -o jsonpath={.data.tls\\.crt} | base64 --decode > ${CLUSTER_NAME}-sa.pub go run ./hack/self-hosted/main.go -key ${CLUSTER_NAME}-sa.pub | jq '.keys += [.keys[0]] | .keys[1].kid = ""' > keys.json
-
Upload the
keys.json
document to the S3 bucket.aws s3 cp ./keys.json s3://$S3_BUCKET/keys.json
-
Use a bucket policy to grant public read access to the
discovery.json
andkeys.json
documents:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::$S3_BUCKET/.well-known/openid-configuration",
"arn:aws:s3:::$S3_BUCKET/keys.json"
]
}
]
}
Deploy pod identity webhook
The Amazon Pod Identity Webhook
configures pods with the necessary environment variables and tokens (via file mounts) to interact with AWS services. The webhook will configure any pod associated with a service account that has an eks-amazonaws.com/role-arn
annotation.
-
Clone amazon-eks-pod-identity-webhook .
-
Set the $KUBECONFIG environment variable to the path of the EKS Anywhere cluster.
-
Apply the manifests for the
amazon-eks-pod-identity-webhook
. The image used here will be pulled from docker.io. Optionally, the image can be imported into (or proxied through) your private registry. Change theIMAGE
argument here to your private registry if needed.make cluster-up IMAGE=amazon/amazon-eks-pod-identity-webhook:latest
-
Create a service account with an
eks.amazonaws.com/role-arn
annotation set to the IAM Role created for the OIDC provider.apiVersion: v1 kind: ServiceAccount metadata: name: my-serviceaccount namespace: default annotations: # set this with value of OIDC_IAM_ROLE eks.amazonaws.com/role-arn: "arn:aws:iam::ACCOUNT_ID:role/s3-reader" # optional: Defaults to "sts.amazonaws.com" if not set eks.amazonaws.com/audience: "sts.amazonaws.com" # optional: When set to "true", adds AWS_STS_REGIONAL_ENDPOINTS env var # to containers eks.amazonaws.com/sts-regional-endpoints: "true" # optional: Defaults to 86400 for expirationSeconds if not set # Note: This value can be overwritten if specified in the pod # annotation as shown in the next step. eks.amazonaws.com/token-expiration: "86400"
-
Finally, apply the
my-service-account.yaml
file to create your service account.kubectl apply -f my-service-account.yaml
-
You can validate IRSA by following IRSA setup and test . Ensure the awscli pod is deployed in the same namespace of ServiceAccount
pod-identity-webhook
.
Configure the trust relationship for the OIDC provider’s IAM Role
In order to grant certain service accounts access to the desired AWS resources, edit the trust relationship for the OIDC provider’s IAM Role (OIDC_IAM_ROLE
) created in the first section, and add in the desired service accounts.
-
Choose the role in the console to open it for editing.
-
Choose the Trust relationships tab, and then choose Edit trust relationship.
-
Find the line that looks similar to the following:
"$ISSUER_HOSTPATH:aud": "sts.amazonaws.com"
-
Add another condition after that line which looks like the following line. Replace
KUBERNETES_SERVICE_ACCOUNT_NAMESPACE
andKUBERNETES_SERVICE_ACCOUNT_NAME
with the name of your Kubernetes service account and the Kubernetes namespace that the account exists in."$ISSUER_HOSTPATH:sub": "system:serviceaccount:KUBERNETES_SERVICE_ACCOUNT_NAMESPACE:KUBERNETES_SERVICE_ACCOUNT_NAME"
The allow list example below applies
my-serviceaccount
service account to thedefault
namespace and all service accounts to theobservability
namespace for theus-west-2
region. Remember to replaceAccount_ID
andS3_BUCKET
with the required values.{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::$Account_ID:oidc-provider/s3.us-west-2.amazonaws.com/$S3_BUCKET" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringLike": { "s3.us-west-2.amazonaws.com/$S3_BUCKET:aud": "sts.amazonaws.com", "s3.us-west-2.amazonaws.com/$S3_BUCKET:sub": [ "system:serviceaccount:default:my-serviceaccount", "system:serviceaccount:amazon-cloudwatch:*" ] } } } ] }
-
Refer this doc for different ways of configuring one or multiple service accounts through the condition operators in the trust relationship.
-
Choose Update Trust Policy to finish.