Skip to main content

Configure IAM Permissions for ACK Controllers

ACK controllers need AWS IAM permissions to manage resources. This guide shows you how to configure IAM permissions using either EKS Pod Identity (recommended for EKS 1.24+) or IRSA (traditional method).

Pod Identity Authentication Flow

Click to zoom • Pod Identity authentication flow

Choose Your Authentication Method

What is Pod Identity?

EKS Pod Identity is a feature that allows pods running in your EKS cluster to assume AWS IAM roles without requiring OIDC configuration. It simplifies the authentication flow by using the EKS Pod Identity Agent, which runs as a DaemonSet in your cluster and handles credential requests automatically.

How It Works

  1. You create a Pod Identity Association that links:

    • An IAM role (with AWS service permissions)
    • A Kubernetes service account (used by the ACK controller)
    • A namespace (where the controller runs)
  2. When the ACK controller pod starts with this service account, the Pod Identity Agent automatically provides temporary AWS credentials

  3. The controller uses these credentials to manage AWS resources

Pod Identity Association

A Pod Identity Association is an EKS resource that connects your controller's service account to an IAM role. Here's what it looks like:

apiVersion: eks.services.k8s.aws/v1alpha1
kind: PodIdentityAssociation
metadata:
name: ack-s3-controller-pod-identity
spec:
clusterName: my-cluster
namespace: ack-system
serviceAccount: ack-s3-controller
roleARN: arn:aws:iam::123456789012:role/ack-s3-controller

Key components:

  • clusterName: Your EKS cluster name
  • namespace: Where your ACK controller runs (typically ack-system)
  • serviceAccount: The service account used by the controller (auto-created during installation)
  • roleARN: The IAM role with permissions for the AWS service

You can create this using either:

  • AWS CLI: aws eks create-pod-identity-association
  • ACK EKS Controller: Create a PodIdentityAssociation CRD (shown above)

Example Configuration

Here's a complete example for configuring the S3 controller with Pod Identity:

# Variables
export SERVICE=s3
export CLUSTER_NAME=my-cluster
export AWS_REGION=us-west-2
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)

# Create Pod Identity Association
aws eks create-pod-identity-association \
--cluster-name $CLUSTER_NAME \
--namespace $ACK_SYSTEM_NAMESPACE \
--service-account ack-${SERVICE}-controller \
--role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/ack-${SERVICE}-controller \
--region $AWS_REGION

# Restart controller to pick up the association
kubectl rollout restart deployment -n $ACK_SYSTEM_NAMESPACE \
ack-${SERVICE}-controller

Or using the ACK EKS controller:

apiVersion: eks.services.k8s.aws/v1alpha1
kind: PodIdentityAssociation
metadata:
name: ack-s3-controller-pod-identity
spec:
clusterName: my-cluster
namespace: ack-system
serviceAccount: ack-s3-controller
roleARN: arn:aws:iam::123456789012:role/ack-s3-controller
Creating the IAM Role (expand for details)

Before creating the Pod Identity Association, you need an IAM role with the appropriate permissions.

Step 1: Create IAM Role with Pod Identity Trust Policy

export SERVICE=s3
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)

# Create IAM role with Pod Identity trust policy
aws iam create-role \
--role-name ack-${SERVICE}-controller \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "pods.eks.amazonaws.com"},
"Action": ["sts:AssumeRole", "sts:TagSession"]
}]
}'

Step 2: Attach AWS Service Permissions

# Get recommended policy for your service
export POLICY_ARN=$(curl -s https://raw.githubusercontent.com/aws-controllers-k8s/${SERVICE}-controller/main/config/iam/recommended-policy-arn)

# Attach the policy
aws iam attach-role-policy \
--role-name ack-${SERVICE}-controller \
--policy-arn $POLICY_ARN

Step 3: Attach Inline Policies (if needed)

Some controllers need additional inline policies (like iam:PassRole):

# Check if inline policy exists
curl -s https://raw.githubusercontent.com/aws-controllers-k8s/${SERVICE}-controller/main/config/iam/recommended-inline-policy > inline-policy.json

# If the file exists and has content, apply it
if [ -s inline-policy.json ]; then
aws iam put-role-policy \
--role-name ack-${SERVICE}-controller \
--policy-name ack-${SERVICE}-inline \
--policy-document file://inline-policy.json
fi

Verify

Check that the Pod Identity Association was created:

aws eks list-pod-identity-associations \
--cluster-name $CLUSTER_NAME \
--region $AWS_REGION

# Check controller logs
kubectl logs -n $ACK_SYSTEM_NAMESPACE deployment/ack-${SERVICE}-controller

Finding Quick-Setup Policies

Each ACK controller repository publishes IAM policies intended for quick setup. These are broad AWS managed policies that get a controller running fast. They are a convenient starting point, not a production-ready least-privilege configuration:

# Policy ARN (AWS managed policy)
https://raw.githubusercontent.com/aws-controllers-k8s/${SERVICE}-controller/main/config/iam/recommended-policy-arn

# Inline policy (additional permissions)
https://raw.githubusercontent.com/aws-controllers-k8s/${SERVICE}-controller/main/config/iam/recommended-inline-policy

Examples:

  • S3: arn:aws:iam::aws:policy/AmazonS3FullAccess
  • DynamoDB: arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
  • RDS: arn:aws:iam::aws:policy/AmazonRDSFullAccess
Production use

For production, create custom policies with least-privilege permissions instead of using *FullAccess policies.

Granular IAM Role Scoping

By default, an ACK controller uses a single IAM role for every resource it reconciles. If that role is broadly permissioned, any resource the controller acts on can use all of those permissions, and anyone able to create a custom resource the controller watches can drive it to act with that role's full access. This matters whenever different workloads need different permissions, for example:

  • A shared cluster where multiple teams or tenants create ACK resources and should be isolated from one another.
  • A single team running multiple applications that manage different resource types and shouldn't share one broad role.
  • Resources that need to be reconciled into different AWS accounts.

To scope permissions narrowly instead of relying on one wide role:

  • Scope the default controller role to least-privilege. This role is the fallback used when no IAMRoleSelector matches a resource, so grant it only the permissions those unmatched resources need (or nothing beyond a baseline needed to assume roles specified by IAMRoleSelectors).
  • Use the IAMRoleSelector to map distinct IAM roles to specific namespaces, resource types, and/or accounts, so each resource is reconciled with a role limited to the permissions it needs.

Because an IAMRoleSelector maps a role to whatever matches its selectors, isolation also depends on your Kubernetes RBAC. RBAC is namespace-scoped, so restrict who can create ACK resources in each namespace that a selector maps a role to. That way a workload can only reach the role intended for its namespace. If you select namespaces by label, also control who can apply those labels to namespaces. Matching the RBAC scope to the selector scope is what actually provides isolation.

See Granular IAM Roles for how to configure roles with IAMRoleSelector.

Next Steps

Additional Resources

Built with by AWS