There is a great article on the AWS Blog: How to use AWS Secrets Manager and ABAC for enhanced secrets management in Amazon EKS . The only downside of that approach is that it uses OpenID Connect (OIDC) and IRSA for authentication. But there is now a better and simpler option: Amazon EKS Pod Identity .
Using EKS Pod Identity, you can connect External Secrets Operator (ESO) with AWS Secrets Manager and still benefit from Attribute-Based Access Control (ABAC) for fine-grained permissions.
Prerequisites
Before you start, make sure you have:
- An active AWS account
- Permission to create IAM resources
- Permission to manage KMS keys for Secrets Manager secrets
- An existing EKS cluster
- A user with permission to modify Kubernetes resources
- AWS CLI,
kubectl,helm, andeksctlinstalled - External Secrets Operator (ESO) installed
The setup requires two IAM roles:
- A role with access to Secrets Manager and KMS keys
- An EKS Pod Identity role for ESO
Follow the original AWS article until the section “Create an IAM role to access Secrets Manager secrets”, then continue here.
Create an IAM Role for the ESO Service Account
Let’s start by creating a role for the ESO service account.
Create a file named eso-policy.json:
{
"Statement": [
{
"Action": ["sts:AssumeRole", "sts:TagSession"],
"Effect": "Allow",
"Resource": "arn:aws:iam::123456789012:role/external-secrets-abac",
"Sid": "AssumeRole"
}
],
"Version": "2012-10-17"
}
This policy allows the ESO role to assume the external-secrets-abac role, which we will create later.
Next, create a trust policy for EKS Pod Identity. Save it as eso-trust-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": ["sts:TagSession", "sts:AssumeRole"]
}
]
}
Now create the ESO role and attach the policy:
aws iam create-policy --policy-name eso-policy --policy-document file://eso-policy.json
aws iam create-role --role-name external-secrets --assume-role-policy-document file://eso-trust-policy.json
aws iam attach-role-policy --role-name external-secrets --policy-arn arn:aws:iam::123456789012:policy/eso-policy
Create an IAM Role to Access Secrets Manager
Next, we will create a policy that grants ESO access to AWS Secrets Manager and KMS keys.
Create eso-abac-policy.json:
{
"Statement": [
{
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds"
],
"Condition": {
"StringEquals": {
"secretsmanager:ResourceTag/eks-secret-app": "${aws:PrincipalTag/eks-secret-app}"
}
},
"Effect": "Allow",
"Resource": "*",
"Sid": "ExternalSecretsAccess"
},
{
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds"
],
"Condition": {
"StringEquals": {
"secretsmanager:ResourceTag/eks-secret-shared": "${aws:PrincipalTag/eks-secret-shared}"
}
},
"Effect": "Allow",
"Resource": "*",
"Sid": "ExternalSecretsAccessShared"
},
{
"Action": ["kms:Decrypt"],
"Effect": "Allow",
"Resource": ["<KMS Key ARN>"],
"Sid": "ExternalSecretsKMSAccess"
}
],
"Version": "2012-10-17"
}
This policy conditionally allows access to secrets when aws:PrincipalTag values match the resource tags in Secrets Manager.
Replace <KMS Key ARN> with your actual KMS key ARN. By default AWS Secrets Manager uses AWS managed aws/secretsmanager key.
Next, create a trust policy for the ABAC role: eso-abac-trust-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/external-secrets"
},
"Action": ["sts:AssumeRole", "sts:TagSession"],
"Condition": {
"ForAnyValue:StringEquals": {
"aws:TagKeys": ["eks-secret-app", "eks-secret-shared"]
}
}
}
]
}
This trust policy allows assuming the role only by arn:aws:iam::123456789012:role/external-secrets role and only if one of the following session tags are present.
Now create and attach the policy to the role:
aws iam create-policy --policy-name eso-abac-policy --policy-document file://eso-abac-policy.json
aws iam create-role --role-name external-secrets-abac --assume-role-policy-document file://eso-abac-trust-policy.json
aws iam attach-role-policy --role-name external-secrets-abac --policy-arn arn:aws:iam::123456789012:policy/eso-abac-policy
Create an EKS Pod Identity Association
This step links the ESO Kubernetes service account with the IAM role created above:
aws eks create-pod-identity-association \
--cluster-name my-eks \
--namespace external-secrets \
--service-account external-secrets \
--role-arn arn:aws:iam::123456789012:role/external-secrets
Create Secrets in AWS Secrets Manager
Now create two secrets in AWS Secrets Manager and tag them appropriately:
aws secretsmanager create-secret \
--name MySecretOne \
--secret-string '{"username":"user1","password":"pass1"}' \
--tags Key=eks-secret-app,Value=my-secret-app
aws secretsmanager create-secret \
--name MySecretTwo \
--secret-string '{"apikey":"12345"}' \
--tags Key=eks-secret-shared,Value=shared-secret-id
Verify that both secrets are created and tagged correctly:
aws secretsmanager list-secrets --query 'SecretList[*].{Name:Name, Tags:Tags}'
Test the Setup with External Secrets Operator
Create a namespace and deploy the following External Secrets Operator objects:
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
role: arn:aws:iam::123456789012:role/external-secrets-abac
sessionTags:
- key: eks-secret-app
value: my-secret-app
- key: eks-secret-shared
value: shared-secret-id
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-secret-one
spec:
refreshInterval: 15s
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: app-secret-one
data:
- secretKey: MySecretOne
remoteRef:
key: MySecretOne
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-secret-two
spec:
refreshInterval: 15s
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: app-secret-two
data:
- secretKey: MySecretTwo
remoteRef:
key: MySecretTwo
If everything is configured correctly, ESO will use the EKS Pod Identity to assume the role external-secrets, which then assumes the ABAC role external-secrets-abac with session tags eks-secret-app and eks-secret-shared.
With these permissions, ESO will be able to synchronize the secrets directly into your Kubernetes cluster.
You can verify the secrets were created:
kubectl -n namespace get secrets
NAME TYPE DATA AGE
app-secret-one Opaque 1 1m
app-secret-two Opaque 1 1m
Why Two Roles?
Two roles are required because we need to pass session tags from one role to another.
While EKS Pod Identity automatically provides default tags, they do not include custom tags such as eks-secret-app or eks-secret-shared.
Currently, Pod Identity agent verifies the identity of the caller (ESO) and hence it can not impersonate a different service account.
More details can be found in the ESO GitHub issue .
Default tags added by EKS Pod Identity:
"eks-cluster-name": "my-cluster",
"kubernetes-pod-uid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"eks-cluster-arn": "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster",
"kubernetes-pod-name": "external-secrets-xxxxxxxxxx-xxxxx",
"kubernetes-namespace": "external-secrets",
"kubernetes-service-account": "external-secrets"
Why Two Tags?
AWS Secrets Manager does not allow to have the same tag with different values for the same secret. But in some cases multiple applications need to access the same secret. For example, a shared API key for multiple applications.
To solve this problem we can use eks-secret-shared tag. The value will be the same for all applications that need to access the same secret. In this case the secret will be accessible to all applications that have eks-secret-shared tag with the same value.
While eks-secret-app tag will be used to access secrets specific to the application. The value will be different for each application. In this case the secret will be accessible to the application that has eks-secret-app tag with the same value.
Conclusion
By combining EKS Pod Identity, AWS Secrets Manager ABAC, and External Secrets Operator, you get a modern, secure, and scalable approach to managing secrets in Kubernetes.
Key benefits of this approach:
- Simplified authentication: EKS Pod Identity eliminates the need for OIDC providers and IRSA configuration
- Fine-grained access control: ABAC uses tags to control which secrets each application can access
- Reduced operational overhead: A single IAM role handles all secret access; adding new applications only requires updating tags
- Lower risk of misconfiguration: No need to modify existing policies when onboarding new workloads
- Flexible sharing: The dual-tag approach (
eks-secret-appandeks-secret-shared) supports both app-specific and shared secrets
This setup is ideal for teams looking to automate secret synchronization between AWS and Kubernetes while maintaining strong security boundaries, all without the complexity of legacy IRSA-based solutions.
