Automating ECR Pull Secrets on ROSA Using the External Secrets Operator and STS
This content is authored by Red Hat experts, but has not yet been tested on every supported configuration.
Amazon Elastic Container Registry (ECR) issues short-lived authorization tokens that expire after 12 hours. On Red Hat OpenShift Service on AWS (ROSA), workloads that pull images from private ECR repositories need those tokens refreshed before they expire — otherwise pods fail to start with ImagePullBackOff errors.
The
External Secrets Operator
(ESO) solves this by generating and automatically refreshing ECR tokens as Kubernetes dockerconfigjson pull secrets. Combined with AWS STS and IAM Roles for Service Accounts (IRSA), this removes every long-lived credential from the picture.
The External Secrets Operator for Red Hat OpenShift is available in OperatorHub on ROSA and OpenShift. It is the recommended, fully-supported distribution and is the version used throughout this guide. See the Red Hat documentation for details.
This guide covers two approaches — pick the one that fits your operating model.
Approach A — Namespace-scoped pull secret
Each namespace owns its own IAM role, service account, and ESO resources.
- IAM role: one role per namespace/service account
- ESO resources:
ECRAuthorizationToken+ExternalSecretper namespace - Namespace onboarding: team creates resources in their namespace
- Isolation: strong — each namespace has its own IRSA binding
- Pros: least-privilege per team; compromise of one role does not affect others
- Cons: more IAM roles to manage; each team must create ESO resources
- Best for: multi-tenant clusters, strict isolation
Approach B — Centrally managed with label-based namespace injection
A platform team manages ESO resources once at the cluster level. Namespaces opt in via a label.
- IAM role: one shared role on the ESO controller
- ESO resources:
ClusterGenerator+ClusterExternalSecret(cluster-scoped) - Namespace onboarding: platform admin adds a label to the namespace
- Isolation: shared — single role covers all labeled namespaces
- Pros: single setup for the entire cluster; easy onboarding via
oc label - Cons: broader blast radius — a misconfigured shared role affects all labeled namespaces
- Best for: platform-managed clusters, central governance
Prerequisites
- A ROSA cluster deployed with STS
ocCLIawsCLIjq
Validate STS
Confirm your cluster is configured with STS before proceeding:
You should see an HTTPS URL such as https://oidc.op1.openshiftapps.com/<unique-id>. If the output is empty, see the
Red Hat documentation on creating an STS cluster
.
Prepare environment variables
AWS_REGION and ECR_REPOSITORY to match your environment.Install the External Secrets Operator for Red Hat OpenShift
Option 1: Via the OpenShift Web Console
Navigate to Ecosystem → Software Catalog and search for External Secrets Operator.
Select External Secrets Operator for Red Hat OpenShift and install it with the default settings (all namespaces, automatic approval).
Option 2: Via the CLI
Create the ExternalSecretsConfig operand
The operator manager pod installs, but the ESO controllers will not start until you create an ExternalSecretsConfig CR named cluster. First, wait for the operator CSV to finish installing:
Then create the operand:
Verify the installation
Wait for the operator manager pod to become ready:
After the ExternalSecretsConfig operand is created, the operator deploys the ESO controller pods into the external-secrets namespace. Verify they are running:
You should see three pods in Running state.
Create an ECR repository (optional)
ECR_REPOSITORY variable set in the previous step matches the name of your existing repository.To push a test image into the new repository so you can validate the pull secret later:
Approach A — Namespace-scoped pull secret
Complete all Prerequisites , Install the External Secrets Operator for Red Hat OpenShift , and Create an ECR repository steps before continuing.
In this model each namespace owns its own IAM role, service account, and ESO resources. This provides the strongest isolation and is the recommended starting point.
A1) Create an IAM policy for ECR token retrieval
ecr:GetAuthorizationToken requires Resource: "*". All other actions are scoped to the specific repository.A2) Create the application namespace and a dedicated service account
The External Secrets Operator needs a service account annotated with an IAM role to authenticate against AWS via IRSA. A dedicated service account (rather than default) is used so the IAM trust policy grants ECR access only to the ESO generator — not to every pod in the namespace.
A3) Create an IAM role with an IRSA trust policy
The trust policy restricts role assumption to the specific service account and namespace created above.
A4) Attach the policy and annotate the service account
A5) Create an ECR token generator
The ECRAuthorizationToken custom resource tells the External Secrets Operator how to request a token. The serviceAccountRef binds the generator to the IRSA-annotated service account.
A6) Create an ExternalSecret to render the pull secret
The ExternalSecret uses the generator from the previous step and produces a standard kubernetes.io/dockerconfigjson secret that OpenShift can use as an image pull secret. The refreshInterval is set to 11 hours, ensuring the token is always refreshed before the 12-hour expiry window.
A7) Verify the generated secret
The STATUS column should show SecretSynced. Then confirm the Kubernetes secret exists:
Expected output:
A8) Link the pull secret to service accounts
By default, OpenShift pods use the default service account. That service account does not automatically know about the newly created pull secret. The oc secrets link --for=pull command adds the secret to the service account’s imagePullSecrets list so the kubelet presents the credentials when pulling images.
A9) Validate with a test pod
Watch the pod until it reaches Running:
If the pod starts successfully the entire pipeline — IRSA, token generator, and pull secret — is working correctly.
A10) Cleanup
Approach B — Centrally managed with label-based namespace injection
Complete all Prerequisites , Install the External Secrets Operator for Red Hat OpenShift , and Create an ECR repository steps before continuing.
In this model, a platform team manages ESO resources once at the cluster level. Any namespace that needs ECR pull secrets simply receives a label.
How it works
- The ESO controller service account is annotated with a single shared IRSA role.
- A
ClusterGeneratordefines how to obtain ECR tokens cluster-wide. - A
ClusterExternalSecretwatches for namespaces with a specific label and creates anExternalSecret(and therefore a pull secret) in each matching namespace automatically.
B1) Create the shared IAM role for the ESO controller
OIDC_ENDPOINT and AWS_ACCOUNT_ID variables from the prerequisites section.B2) Annotate the ESO controller service account
Restart the operator deployment so it picks up the annotation:
B3) Create a cluster-scoped ECR token generator
The ClusterGenerator wraps the ECRAuthorizationToken spec so it is available across all namespaces without redefining it per team.
B4) Create a ClusterExternalSecret with a namespace label selector
The ClusterExternalSecret acts as a template: for every namespace carrying the label ecr-pull-secret: "true", the operator creates a namespaced ExternalSecret which in turn produces the pull secret.
B5) Onboard namespaces by adding a label
Within a minute the operator will create an ExternalSecret and the resulting ecr-docker-credentials secret in each labeled namespace:
B6) Link the pull secret in each namespace
As explained in
Approach A, step A8
, linking is required so that pods running under the default service account can use the generated pull secret when pulling images from ECR.
B7) Validate with a test pod
B8) Remove a namespace from ECR pull secret distribution
To stop injecting the secret into a namespace, remove the label:
- in the label key tells oc label to remove the label.The operator will delete the ExternalSecret and the generated secret from that namespace.