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.

  1. 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

    1. Open Amazon EKS > Clusters > your cluster.
    2. Open the Add-ons tab and select Get More Add-ons.
    3. 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 text

    Install 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
  1. 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.json

    Copy 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.json with your cluster OIDC provider as the principal and sts:AssumeRoleWithWebIdentity as the action. See the AWS documentation for the OIDC statement format.

  2. 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 read MaxSessionDuration for token TTL alignment. Create policy.json (replace <ACCOUNT_ID> and <YOUR_ROLE_NAME>):
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "sts:GetCallerIdentity",
          "Resource": "arn:aws:iam::<ACCOUNT_ID>:role/<YOUR_ROLE_NAME>"
        },
        {
          "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
  3. 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 the default ServiceAccount, create the association for default or 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-association fails because the namespace and ServiceAccount combination is already mapped, use update-pod-identity-association or 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>
  4. Configure JFrog Platform.

    Configure the JFrog Platform to register the IAM role for passwordless access. Follow the same steps as for IRSA:

    Configure JFrog Platform for Passwordless Access to EKS

  5. Install and configure the JFrog Registry Operator.

    1. Export the namespace and ServiceAccount name.

      export NAMESPACE=<namespace where the operator is deployed>
      export SERVICE_ACCOUNT_NAME=<operator Kubernetes ServiceAccount name>
    2. Add the JFrog Helm Charts repository.

      helm repo add jfrog https://charts.jfrog.io

      If you have already added the repository, update it:

      helm repo update
    3. Install the Custom Resource Definition (CRD) for the operator based on the required scope.

      📘

      Default Scope

      The default scope is cluster-scoped.

      kubectl apply -f https://raw.githubusercontent.com/jfrog/jfrog-registry-operator/refs/heads/v3.1.1/config/crd/bases/apps.jfrog.com_secretrotators_cluster_scope.yaml
      kubectl apply -f https://raw.githubusercontent.com/jfrog/jfrog-registry-operator/refs/heads/v3.1.1/config/crd/bases/apps.jfrog.com_secretrotators_namespaced_scope.yaml
    4. 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
    5. Verify the operator pod is running.

      kubectl get pods -n "${NAMESPACE}"
      kubectl logs <pod-name> -n "${NAMESPACE}"
    6. Update secretrotator.yaml with the JFrog Platform URL and target namespace. Set spec.authType: podIdentity, or use auto to 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.authType on the SecretRotator (after a successful reconcile) reflects the effective mechanism used (podIdentity or webIdentity), including when authType: auto is set.

    7. 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:AssumeRole chaining: 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)

  1. Pod Identity Agent add-on is ACTIVE on the nodes that run the operator pod.
  2. IAM role trust policy includes pods.eks.amazonaws.com as the principal.
  3. IAM permissions policy includes sts:GetCallerIdentity and iam:GetRole on the role ARN.
  4. Pod Identity association matches the operator pod's namespace and ServiceAccount name exactly.
  5. Egress to 169.254.170.23:80 is allowed. Proxy environment variables do not intercept that traffic.
  6. IAM role is registered in JFrog Platform per Configure JFrog Platform for Passwordless Access to EKS.
  7. SecretRotator CR has authType: podIdentity or authType: auto.

IRSA path (authType: webIdentity or auto without Pod Identity env vars)

  1. IAM OIDC provider exists for the cluster issuer.
  2. IAM role trust policy includes sts:AssumeRoleWithWebIdentity with the correct sub and aud conditions.
  3. IAM permissions policy includes sts:GetCallerIdentity and iam:GetRole on the role ARN.
  4. Kubernetes ServiceAccount has the annotation eks.amazonaws.com/role-arn: <IAM role ARN>.
  5. IAM role is registered in JFrog Platform per Configure JFrog Platform for Passwordless Access to EKS.
  6. SecretRotator CR has authType: webIdentity or authType: 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 wide

Both 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

If 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

  • Missing or wrong role-arn annotation on the ServiceAccount: The EKS webhook will not inject AWS_ROLE_ARN or the token path into the pod. webIdentity will fail silently. Verify the annotation eks.amazonaws.com/role-arn is present and matches the IAM role ARN.
  • Trust policy sub mismatch: If the IAM trust condition contains the wrong namespace or ServiceAccount name, STS returns AccessDenied on AssumeRoleWithWebIdentity. Compare the sub condition 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.

Related Topics

TopicDescription
How EKS Pod Identity WorksAWS documentation on how EKS Pod Identity operates at the platform level
IAM Roles for Service AccountsAWS documentation on IRSA and OIDC-based credential injection