EKS Pod Identity Configuration
Install the EKS Pod Identity Agent, configure IAM trust for pods.eks.amazonaws.com, create Pod Identity associations, and deploy the JFrog Registry Operator with authType: podIdentity.
EKS Pod Identity is a simpler alternative to IRSA for the JFrog Registry Operator: no OIDC provider and no role-arn ServiceAccount annotation required. It uses the EKS Pod Identity Agent (a DaemonSet add-on) and an EKS API association to map a cluster, namespace, and ServiceAccount directly to an IAM role.
Set spec.authType: podIdentity (or auto) in the SecretRotator CR to use this mechanism.
Why EKS Pod Identity?
IRSA (IAM Roles for Service Accounts) ties Kubernetes identity to AWS through OpenID Connect. Each EKS cluster has a unique OIDC issuer URL. An IAM admin must register that URL as an Identity Provider in AWS, once per cluster. The IAM role trust policy must then match two exact claim values: the service account name in sub and sts.amazonaws.com in aud. One character wrong in either condition causes AssumeRoleWithWebIdentity to return AccessDenied with no indication of which condition failed. Every new cluster repeats the entire cycle: new OIDC provider, updated trust policy, annotated ServiceAccount, and coordination across two teams.
EKS Pod Identity removes OIDC entirely. The Pod Identity Agent, a DaemonSet on every worker node, serves AWS credentials through EKS Pod Identity locally or configures a custom SDK credential provider endpoint. When a pod starts, EKS injects two environment variables pointing to that endpoint. The operator fetches credentials directly: no token exchange, no OIDC validation. The IAM role trust policy uses a single fixed principal, pods.eks.amazonaws.com, that never needs updating. Adding a new cluster requires only a new association and Pod Identity.
IRSA Setup and Runtime Flow
The following sequence shows what happens when a Kubernetes admin enables IRSA on a new cluster. Setup involves two teams and per-cluster IAM changes. At runtime, every credential refresh requires an OIDC token issued by the Kubernetes control plane, validated by AWS STS against the registered OIDC provider.
sequenceDiagram
autonumber
actor KA as K8s Admin
actor IA as IAM Admin
participant AWSIAM as AWS IAM
participant EKS as EKS Cluster
participant Webhook as OIDC Webhook
participant Pod as Operator Pod
participant STS as AWS STS
participant JFrog as JFrog Platform
rect rgb(255, 235, 235)
Note over KA,IA: One-time setup <br />repeats for every new cluster
KA->>IA: Request OIDC provider<br/>for this cluster
IA->>EKS: Query OIDC issuer URL
EKS-->>IA: Cluster OIDC issuer URL
IA->>AWSIAM: Create OIDC Identity Provider
IA->>AWSIAM: Create IAM role + trust policy
Note over AWSIAM: sub = system:serviceaccount:NAMESPACE:SA<br/>aud = sts.amazonaws.com<br/>One typo → AccessDenied, no diagnostic detail
KA->>EKS: Annotate ServiceAccount<br/>eks.amazonaws.com/role-arn: ARN
end
rect rgb(235, 245, 255)
Note over Pod,JFrog: Runtime — every token refresh
Webhook->>Pod: Injects AWS_ROLE_ARN<br/>+ AWS_WEB_IDENTITY_TOKEN_FILE
Pod->>EKS: TokenRequest<br/>(aud: sts.amazonaws.com)
EKS-->>Pod: OIDC JWT
Pod->>STS: AssumeRoleWithWebIdentity
STS->>AWSIAM: Validate sub + aud conditions
AWSIAM-->>STS: OK
STS-->>Pod: Temporary credentials<br/>(AccessKey, SecretKey, SessionToken)
Pod->>STS: GetCallerIdentity (SigV4a)
Pod->>JFrog: POST /access/api/v1/aws/token
JFrog-->>Pod: Artifactory access token
end
EKS Pod Identity Setup and Runtime Flow
With EKS Pod Identity, setup is a single CLI call per cluster. No OIDC provider is created, no ServiceAccount annotation is required, and the IAM role trust policy does not change when you add more clusters. At runtime, the Pod Identity Agent serves credentials directly from a link-local endpoint. No Kubernetes JWT exchange and no STS token exchange step.
sequenceDiagram
autonumber
actor KA as K8s Admin
participant AWSIAM as AWS IAM
participant EKS as EKS API
participant Agent as Pod Identity Agent
participant Pod as Operator Pod
participant STS as AWS STS
participant JFrog as JFrog Platform
rect rgb(235, 255, 235)
Note over KA,Agent: One-time setup
KA->>EKS: Install EKS Pod Identity Agent add-on
Note over EKS,Agent: Agent runs as DaemonSet on every worker node<br/>Must be ACTIVE before operator pod starts
KA->>AWSIAM: Create IAM role + trust policy<br/>(principal: pods.eks.amazonaws.com)
Note over AWSIAM: Trust policy allows EKS to assume the role<br/>on behalf of pods using Pod Identity
KA->>EKS: create-pod-identity-association
Note over EKS: Maps cluster + namespace + ServiceAccount → role ARN<br/>No ServiceAccount annotation required
end
rect rgb(235, 245, 255)
Note over Pod,JFrog: Runtime — every token refresh
Agent->>Pod: Inject AWS_CONTAINER_CREDENTIALS_FULL_URI<br/>+ AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
Pod->>Agent: GET 169.254.170.23:80<br/>(Authorization: token-file-contents)
Agent-->>Pod: Temporary credentials<br/>(AccessKey, SecretKey, SessionToken)
Pod->>STS: GetCallerIdentity (SigV4a)
Pod->>JFrog: POST /access/api/v1/aws/token
JFrog-->>Pod: Artifactory access token
end
Scaling Across Multiple Clusters
The structural difference becomes clearest at scale. IRSA requires IAM changes for every new cluster. Pod Identity requires only a new association.
flowchart LR
subgraph IRSA ["IRSA"]
direction TB
I1["Cluster 1<br/>① Create OIDC provider<br/>② Update IAM trust policy<br/>③ Annotate ServiceAccount"]
I2["Cluster 2<br/>① Create OIDC provider<br/>② Update IAM trust policy<br/>③ Annotate ServiceAccount"]
IN["Cluster N<br/>① Create OIDC provider<br/>② Update IAM trust policy<br/>③ Annotate ServiceAccount"]
IROLE["IAM Role<br/>Trust policy **updated N times** <br/>N OIDC principals in trust conditions"]
I1 --> IROLE
I2 --> IROLE
IN --> IROLE
end
subgraph PID ["EKS Pod Identity"]
direction TB
P1["Cluster 1<br/>① create-pod-identity-association"]
P2["Cluster 2<br/>① create-pod-identity-association"]
PN["Cluster N<br/>① create-pod-identity-association"]
PROLE["IAM Role<br/>Trust policy **unchanged** <br/>Principal: pods.eks.amazonaws.com"]
P1 --> PROLE
P2 --> PROLE
PN --> PROLE
end
The table below summarizes the key differences:
| Before (IRSA) | After (EKS Pod Identity) | |
|---|---|---|
| Trust setup | OIDC Identity Provider per cluster | EKS API association: no OIDC provider needed |
| IAM trust policy | Complex JSON with sub and aud conditions | Single pods.eks.amazonaws.com principal |
| Injected env vars | AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE | AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE |
| Pod manifests | eks.amazonaws.com/role-arn annotation required | No annotation required |
| Scaling to N clusters | N IAM trust policy updates + N OIDC providers | N create-pod-identity-association calls |
| Teams required | Kubernetes admin + IAM admin | Kubernetes admin only |
| Automation | Difficult to automate cross-account/cluster | Standard AWS API: fully automatable |
How authType: auto Selects the Authentication Method
authType: auto Selects the Authentication MethodWhen spec.authType is set to auto, the operator detects which mechanism is available at runtime by inspecting injected environment variables. Pod Identity is preferred when the Agent's credential env var is present. IRSA is the fallback. The resolved method is reflected in status.authType after the first successful reconcile.
flowchart TD
subgraph Detection ["authType auto Detection Logic"]
Start[SecretRotator] --> Read[Read authType]
Read --> IsPod[podIdentity, webIdentity or auto?]
IsPod -->|podIdentity or auto| HasToken[AUTH_TOKEN_FILE set?]
IsPod -->|webIdentity| IsWeb[webIdentity]
HasToken -->|Yes| Pod[authType: podIdentity]
HasToken -->|No| IsWeb
IsWeb -->|Yes| HasAnnot[role-arn annotated?]
IsWeb -->|No| Err[Error]
HasAnnot -->|Yes| Web[authType: webIdentity]
HasAnnot -->|No| Err
end
-
Install the EKS Pod Identity Agent.
The Pod Identity Agent must run on every worker node that schedules pods using Pod Identity. AWS installs it as an EKS-managed add-on (DaemonSet). If you use only
authType: webIdentity(IRSA), skip this EKS Pod Identity Configuration.Prerequisites
The Pod Identity Agent must be ACTIVE on nodes before the operator pod starts. Workloads require the agent on the same node as the pod.
Use one of the following methods:
-
Install via the AWS Console
- Open Amazon EKS > Clusters > your cluster.
- Open the Add-ons tab and select Get More Add-ons.
- Choose Amazon EKS Pod Identity Agent and complete the wizard.
-
Install via the AWS CLI
Find a compatible add-on version for your Kubernetes version:
aws eks describe-addon-versions \ --addon-name eks-pod-identity-agent \ --kubernetes-version <YOUR_K8S_VERSION> \ --query 'addons[0].addonVersions[*].addonVersion' \ --output textInstall the add-on:
aws eks create-addon \ --cluster-name <YOUR_CLUSTER_NAME> \ --addon-name eks-pod-identity-agent \ --addon-version <VERSION_FROM_ABOVE>Wait until the add-on status is ACTIVE, then verify the agent DaemonSet is running:
kubectl get pods -n kube-system -l app.kubernetes.io/name=eks-pod-identity-agent -o wide
-
IAM Role Trust Policy
The IAM role must trust
pods.eks.amazonaws.com, so EKS can assume it for Pod Identity.Create
trust-pod-identity.json:{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowEksAuthToAssumeRoleForPodIdentity", "Effect": "Allow", "Principal": { "Service": "pods.eks.amazonaws.com" }, "Action": [ "sts:AssumeRole", "sts:TagSession" ] } ] }Create a new role using the trust policy:
aws iam create-role \ --role-name <YOUR_ROLE_NAME> \ --assume-role-policy-document file://trust-pod-identity.json \ --description "EKS Pod Identity role for JFrog Registry Operator"To update an existing role's trust policy instead:
aws iam update-assume-role-policy \ --role-name <YOUR_ROLE_NAME> \ --policy-document file://trust-pod-identity.jsonCopy the role ARN from the output (for example,
arn:aws:iam::<ACCOUNT_ID>:role/<YOUR_ROLE_NAME>). You will need the role ARN in step 3.Combining with IRSA
If you also need IRSA on the same role, add a second statement to
trust-pod-identity.jsonwith your cluster OIDC provider as the principal andsts:AssumeRoleWithWebIdentityas the action. See the AWS documentation for the OIDC statement format. -
Attach the Permissions Policy
The JFrog Registry Operator passwordless flow requires the following permissions on the role:
sts:GetCallerIdentity: used in the signed request that JFrog validates.iam:GetRole: used to readMaxSessionDurationfor token TTL alignment. Createpolicy.json(replace<ACCOUNT_ID>and<YOUR_ROLE_NAME>):
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:GetCallerIdentity", "Resource": "*" }, { "Effect": "Allow", "Action": "iam:GetRole", "Resource": "arn:aws:iam::<ACCOUNT_ID>:role/<YOUR_ROLE_NAME>" } ] }Attach it as an inline policy:
aws iam put-role-policy \ --role-name <YOUR_ROLE_NAME> \ --policy-name <YOUR_INLINE_POLICY_NAME> \ --policy-document file://policy.json -
Create the Pod Identity association.
A Pod Identity association maps
(cluster, namespace, Kubernetes ServiceAccount name)to an IAM role ARN. Create the association before the operator pod starts.ServiceAccount Must Match
The operator pod must use
serviceAccountName: ${SERVICE_ACCOUNT_NAME}in the target namespace. If the pod runs under thedefaultServiceAccount, create the association fordefaultor update the Helm chart to use the intended ServiceAccount.Export variables to match the operator pod's ServiceAccount name and namespace:
export NAMESPACE=<namespace where the operator is deployed> export SERVICE_ACCOUNT_NAME=<operator Kubernetes ServiceAccount name> export ROLE_ARN=arn:aws:iam::<ACCOUNT_ID>:role/<YOUR_ROLE_NAME> export CLUSTER_NAME=<your EKS cluster name> export CLUSTER_REGION=<your EKS region name>Create the association:
aws eks create-pod-identity-association \ --cluster-name "${CLUSTER_NAME}" \ --namespace "${NAMESPACE}" \ --service-account "${SERVICE_ACCOUNT_NAME}" \ --role-arn "${ROLE_ARN}" \ --region "${CLUSTER_REGION}"If
create-pod-identity-associationfails because the namespace and ServiceAccount combination is already mapped, useupdate-pod-identity-associationor delete the existing association and recreate it.Verify the association was created:
aws eks list-pod-identity-associations --cluster-name "${CLUSTER_NAME}"To delete an association later:
aws eks delete-pod-identity-association \ --cluster-name "${CLUSTER_NAME}" \ --association-id <ASSOCIATION_ID> -
Configure JFrog Platform.
Configure the JFrog Platform to register the IAM role for passwordless access. Follow the same steps as for IRSA:
-
Install and configure the JFrog Registry Operator.
-
Export the namespace and ServiceAccount name.
export NAMESPACE=<namespace where the operator is deployed> export SERVICE_ACCOUNT_NAME=<operator Kubernetes ServiceAccount name> -
Add the JFrog Helm Charts repository.
helm repo add jfrog https://charts.jfrog.ioIf you have already added the repository, update it:
helm repo update -
Install the Custom Resource Definition (CRD) for the operator based on the required scope.
Default Scope
The default scope is cluster-scoped.
VERSION=$(curl -s https://api.github.com/repos/jfrog/jfrog-registry-operator/releases/latest | grep '"tag_name"' | cut -d'"' -f4) kubectl apply -f https://raw.githubusercontent.com/jfrog/jfrog-registry-operator/${VERSION}/config/crd/bases/apps.jfrog.com_secretrotators_cluster_scope.yamlVERSION=$(curl -s https://api.github.com/repos/jfrog/jfrog-registry-operator/releases/latest | grep '"tag_name"' | cut -d'"' -f4) kubectl apply -f https://raw.githubusercontent.com/jfrog/jfrog-registry-operator/${VERSION}/config/crd/bases/apps.jfrog.com_secretrotators_namespaced_scope.yaml -
Install the JFrog Registry Operator via Helm.
Pod Identity vs IRSA
For Pod Identity, no
serviceAccount.annotations(role ARN annotation) is needed. The Pod Identity association handles the mapping. The operator's ServiceAccount name must match the association created in step 3.helm upgrade --install secretrotator jfrog/jfrog-registry-operator \ --set "serviceAccount.name=${SERVICE_ACCOUNT_NAME}" \ --namespace "${NAMESPACE}" \ --create-namespace -
Verify the operator pod is running.
kubectl get pods -n "${NAMESPACE}" kubectl logs <pod-name> -n "${NAMESPACE}" -
Update
secretrotator.yamlwith the JFrog Platform URL and target namespace. Setspec.authType: podIdentity, or useautoto let the operator detect Pod Identity from injected environment variables.apiVersion: apps.jfrog.com/v1alpha1 kind: SecretRotator metadata: labels: app.kubernetes.io/name: secretrotators.apps.jfrog.com app.kubernetes.io/instance: secretrotator app.kubernetes.io/created-by: artifactory-secrets-rotator name: secretrotator spec: authType: podIdentity # auto | webIdentity | podIdentity namespaceSelector: matchLabels: kubernetes.io/metadata.name: <NAMESPACE> generatedSecrets: - secretName: token-imagepull-secret secretType: docker scope: - secretName: token-generic-secret secretType: generic scope: artifactoryUrl: "<JFROG_PLATFORM_URL>" serviceAccount: name: "" namespace: "" refreshTime: 30m secretMetadata: annotations: annotationKey: annotationValue labels: labelName: labelValue # security:Checking the Active Auth Type
status.authTypeon the SecretRotator (after a successful reconcile) reflects the effective mechanism used (podIdentityorwebIdentity), including whenauthType: autois set. -
Apply the manifest and verify that secrets are created in the target namespace.
kubectl apply -f secretrotator.yaml -n "${NAMESPACE}" kubectl get secrets -n "${NAMESPACE}"
-
Pod Identity Credential Scope
Each pod has one serviceAccountName, and one Pod Identity association maps that ServiceAccount to one IAM role. A single pod therefore receives one set of Pod Identity credentials. You cannot inject two separate Pod Identity identities into the same pod.
If your use case requires two IAM roles to be active simultaneously, use one of the following approaches:
- Two pods: Assign each pod a different ServiceAccount with its own Pod Identity association.
- One broader role: Consolidate the required permissions into a single IAM role.
sts:AssumeRolechaining: Grant the Pod Identity role permission to assume a second role when the IAM trust allows it.
The same constraint applies to IRSA: one pod uses one annotated ServiceAccount and assumes one role through the standard injected credential flow.
Resolution Checklist
Use this checklist to verify your configuration before raising a support ticket.
Pod Identity path (authType: podIdentity or auto with Pod Identity env vars)
- Pod Identity Agent add-on is ACTIVE on the nodes that run the operator pod.
- IAM role trust policy includes
pods.eks.amazonaws.comas the principal. - IAM permissions policy includes
sts:GetCallerIdentityandiam:GetRoleon the role ARN. - Pod Identity association matches the operator pod's namespace and ServiceAccount name exactly.
- Egress to
169.254.170.23:80is allowed. Proxy environment variables do not intercept that traffic. - IAM role is registered in JFrog Platform per Configure JFrog Platform for Passwordless Access to EKS.
- SecretRotator CR has
authType: podIdentityorauthType: auto.
IRSA path (authType: webIdentity or auto without Pod Identity env vars)
- IAM OIDC provider exists for the cluster issuer.
- IAM role trust policy includes
sts:AssumeRoleWithWebIdentitywith the correctsubandaudconditions. - IAM permissions policy includes
sts:GetCallerIdentityandiam:GetRoleon the role ARN. - Kubernetes ServiceAccount has the annotation
eks.amazonaws.com/role-arn: <IAM role ARN>. - IAM role is registered in JFrog Platform per Configure JFrog Platform for Passwordless Access to EKS.
- SecretRotator CR has
authType: webIdentityorauthType: auto.
Troubleshoot
Verify the Agent Is Running on the Correct Node
Confirm the Pod Identity Agent pod runs on the same node as your operator pod:
kubectl get pods -n kube-system -l app.kubernetes.io/name=eks-pod-identity-agent -o wide
kubectl get pod <operator-pod-name> -n "${NAMESPACE}" -o wideBoth pods must share the same node name in the NODE column.
Network Policy Blocks Credential Requests
If you use NetworkPolicy, the Pod Identity Agent serves credentials at the link-local address 169.254.170.23. Allow egress TCP port 80 to that address for any namespace running operator pods.
IAM AccessDenied on iam:GetRole
iam:GetRoleIf operator logs show a 403 error on iam:GetRole, add the iam:GetRole permission to the role's inline policy as described in step 2.
IRSA (webIdentity) Failures
webIdentity) Failures- Missing or wrong
role-arnannotation on the ServiceAccount: The EKS webhook will not injectAWS_ROLE_ARNor the token path into the pod.webIdentitywill fail silently. Verify the annotationeks.amazonaws.com/role-arnis present and matches the IAM role ARN. - Trust policy
submismatch: If the IAM trust condition contains the wrong namespace or ServiceAccount name, STS returnsAccessDeniedonAssumeRoleWithWebIdentity. Compare thesubcondition in the trust policy against the namespace and ServiceAccount name the pod uses. - OIDC provider missing: If no IAM OIDC identity provider is configured for the cluster issuer, IRSA cannot function. Create the OIDC provider in IAM before using
webIdentity.
Frequently Asked Questions
This section provides answers to frequently asked questions.
FAQs
Q: What is the key advantage of EKS Pod Identity over IRSA for the JFrog Registry Operator?
A: EKS Pod Identity does not require an OIDC provider or a role-arn annotation on the ServiceAccount. It uses the EKS Pod Identity Agent DaemonSet and an EKS API association to map the cluster, namespace, and ServiceAccount directly to an IAM role.
Q: What permissions must the IAM role include for the JFrog Registry Operator passwordless flow?
A: The IAM role must include sts:GetCallerIdentity, used in the signed request that JFrog validates, and iam:GetRole, used to read MaxSessionDuration for token TTL alignment. Both must be scoped to the role ARN.
Q: What must be true about the Pod Identity Agent before the operator pod starts?
A: The Pod Identity Agent must be in an ACTIVE state on the nodes before the operator pod starts. The agent must run on the same node as the pod that uses Pod Identity. It is installed as an EKS-managed add-on DaemonSet.
Q: What happens if a Pod Identity association fails because the namespace and ServiceAccount are already mapped?
A: If create-pod-identity-association fails because the namespace and ServiceAccount combination is already mapped, use update-pod-identity-association or delete the existing association with delete-pod-identity-association and recreate it.
Q: Can a single pod use two separate Pod Identity IAM roles simultaneously?
A: No. Each pod has one serviceAccountName, and one Pod Identity association maps that ServiceAccount to one IAM role, so a pod receives only one set of Pod Identity credentials. To use two IAM roles, either use two separate pods with different ServiceAccounts, consolidate permissions into one role, or use sts:AssumeRole chaining.
