Kubernetes Controller to establish AWS IAM Role Trust Policy statements for IAM Roles for ServiceAccounts (IRSA).
AWS EKS supports IAM Roles for ServiceAccounts (IRSA) to enable Pods to obtain temporary AWS IAM credentials to access AWS services. This is achieved by annotating ServiceAccount objects with the corresponding AWS IAM Role's ARN:
kind: ServiceAccount
apiVersion: v1
metadata:
name: my-service-account
namespace: default
annotations:
# AWS IAM Role (ARN) associated with this ServiceAccount
eks.amazonaws.com/role-arn: "arn:aws:iam::000000000000:role/role-name"
In order for the Pod to be able assume that AWS IAM Role an EKS cluster specific Trust Policy statement must be added to the IAM Role.
{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::000000000000:oidc-provider/oidc.eks.eu-central-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.eu-central-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:sub": "system:serviceaccount:default:my-service-account"
}
}
}
]
}
Usually, this is done upfront either manually or using infrastructure management tools like
Pulumi, Terrform or CloudFormation. However, this approach fails if Kubernetes
clusters are not known upfront and can come and go dynamically. Each EKS cluster will have
a unique (unpredictable) OpenID Connect Provider (oidc-provider
) ARN, which must be added
to the IAM Role's trust policy.
This controller addresses this poblem and dynamically manages the required trust policy statements for the current Kubernetes cluster. It is implemented by two Kubernetes (sub)controllers:
- ServiceAccount Controller watches for
ServiceAccount
objects that contain aneks.amazonaws.com/role-arn
annotation, and createsTrustPolicyStatement
object for each suchServiceAccount
.ServiceAccount
objects can opt-out by setting theiam.aws.rustrial.org/trust-policy-statement
annotation (or label) to value "disable
". - TrustPolicyStatement Controller watches for
TrustPolicyStatement
objects and usesRoleUsagePolicy
objects to verify whether the namespace of theTrustPolicyStatement
is authorized to use (assume) the corresponding IAM Role or not. If authorized, it will update the AWS IAM Role's trust policy using the UpdateAssumeRolePolicy API endpoint.
Note: ServiceAccount
objects can opt-out by setting the iam.aws.rustrial.org/trust-policy-statement
annotation (or label) to disable
.
---
# Auhtorize the use of the `arn:aws:iam::000000000000:role/cluster-autoscaler-role` role in
# namespace `kube-system`.
kind: RoleUsagePolicy
metadata:
name: cluster-autoscaler
namespace: kube-system
spec:
roleArn: "arn:aws:iam::000000000000:role/cluster-autoscaler-role"
namespaces:
- kube-system
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: cluster-autoscaler
namespace: kube-system
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::000000000000:role/cluster-autoscaler-role"
For example with the above objects in place, the controller will create an TrustPolicyStament
object like this:
apiVersion: iam.aws.rustrial.org/v1alpha1
kind: TrustPolicyStatement
metadata:
finalizers:
- iam.aws.rustrial.org
name: cluster-autoscaler
namespace: kube-system
ownerReferences:
- apiVersion: v1
blockOwnerDeletion: false
controller: true
kind: ServiceAccount
name: cluster-autoscaler
uid: 109070af-b481-42f2-8dbe-c8f8d1221fea
spec:
providers:
- providerArn: >-
arn:aws:iam::000000000000:oidc-provider/oidc.eks.eu-central-1.amazonaws.com/id/F9C16C0A32FC4A6972962AA8025418C7
statementSid: EKSeb7a1df89825c0d695a3f40d5d8749fc04b0011990ea23d758a7ae7f5cee08ddX
roleArn: "arn:aws:iam::000000000000:role/cluster-autoscaler-role"
serviceAccountName: cluster-autoscaler
status:
authorizations:
- kind: RoleUsagePolicy
name: cluster-autoscaler
namespace: kube-system
conditions:
- lastTransitionTime: "2021-03-02T10:07:13Z"
message: ""
observedGeneration: 1
reason: Success
status: "True"
type: Ready
The controller can be run in cluster or namespace mode, by default it will run in cluster mode.
- In cluster mode the controller will process
ServiceAccount
andTrustPolicyStatement
objects from all namspaces. - In namespace mode it will only process
ServiceAccount
andTrustPolicyStatement
objects from the namespace specified in the environment variableWATCH_NAMESPACE
. This can be usefull to run the controller in a multi-tenant cluster without cluster-admin rights.
No matter in which mode the controller is running it will only read RoleUsagePolicy
objects
from the storage namespace, which defaults to the namespace the controller is running in.
The storage namespace can be changed by setting the STORAGE_NAMESPACE
environment variable.
Carefully choose your storage namespace and make sure only authorized entities can create or
modify RoleUsagePolicy
objects in that namespace.
Check the Helm Chart Readme for instructions on how to install this controller.
The controller uses finalizer pattern to make sure stale Trust Policy Statements are removed from AWS IAM Roles. However, there are some edge conditions (e.g. controller is no longer running or Kubernetes cluster is deleted) which can lead to stale Trust Policy Statements not being removed from AWS IAM Roles.
As a (best-effort) mitigation measurement, the controller runs a garbage collector which will scan all IAM Roles in the current AWS account and remove stale Trust Policy Statements. The garbage collector will remove Trust Policy Statements which adhere to the following rules:
- The Trust Policy Statement's
Sid
is not empty. - Single (non array) principal entry of type
Federated
, which refers to an EKS OpenID Connect Provider in the same AWS account as the Role and matches the following regular expression "^arn:aws:iam::(\d+):oidc-provider/(oidc\.eks\.[^.]+\.amazonaws\.com/id/.*)$
". - The referred OpenID Connect Provider does no longer exist.
The Trust Policy Statements gargabe collector is turned-on by default and can be turned-off
by setting the environment variable DISABLE_TRUST_POLICY_STATEMENT_GC
(any value will do).
By default, garbage collection will run once per hour, the interval can be changed by setting
the environment variable TRUST_POLICY_STATEMENT_GC_INTERVAL_SECONDS
.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
- The Unlicense (UNLICENSE or https://opensource.org/licenses/unlicense)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be triple licensed as above, without any additional terms or conditions. See the WAIVER and CONTRIBUTING.md files for more information.