A container registry is the point every image passes through on its way to production, so whoever can push to it, or overwrite a tag in it, can decide what code your clusters run. Registries are often configured once and then forgotten: shared admin accounts, pull secrets that never expire, mutable tags and stale images accumulate while the registry is treated as plain storage. This guide covers registry access control for people and machines, tag and digest discipline, signing and verification with Sigstore and the OCI referrers API, scanning and pruning, public mirrors and pull-through caches, monitoring and a phased rollout. It is for platform engineers, cloud security teams and anyone who runs Docker Hub organisations, cloud registries (Amazon ECR, Azure Container Registry, Google Artifact Registry, GitHub Container Registry) or self-hosted Harbor. Building secure images and hardening the cluster are covered in separate guides linked below.
Key takeaways
- Write access is production access. NIST SP 800-190 says all write access to a registry should be authenticated and push rights scoped to the repositories each team owns; anything that can push can change what runs.
- Tags move, digests do not. Turn on tag immutability, deploy by digest and remember SP 800-190's warning that "latest" is only a label, not a guarantee of freshness.
- Signatures only help if they are verified at admission. Sign by digest in CI, store signatures beside the image through the OCI referrers API, and reject unsigned images in the cluster.
- Machine credentials dominate registry access. Replace shared admin accounts and static pull secrets with OIDC federation, managed identities, scoped robot accounts and kubelet credential providers.
- A registry is also a leak channel. Studies of Docker Hub found secrets in thousands of public images; scan images for secrets before push and revoke, not just delete, anything found.
How a registry works and where the risk sits
The OCI Distribution Specification defines the HTTP API every compliant registry implements. Knowing the endpoints makes access policies concrete:
| Operation | OCI endpoint | Security relevance |
|---|---|---|
| Pull manifest or blob | GET /v2/<name>/manifests/<tag-or-digest>, GET /v2/<name>/blobs/<digest> | Read access; log reads of sensitive repositories |
| Push blob and manifest | POST /v2/<name>/blobs/uploads/, PUT /v2/<name>/manifests/<tag-or-digest> | Write access; a manifest PUT to an existing tag overwrites it unless tags are immutable |
| Cross-repository mount | POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<other_name> | Requires read on the source; scope policies accordingly |
| List tags | GET /v2/<name>/tags/list | Discovery; anonymous listing reveals product and version names |
| Delete | DELETE /v2/<name>/manifests/<digest>, DELETE /v2/<name>/blobs/<digest> | Optional in the spec; restrict to lifecycle automation |
| Referrers | GET /v2/<name>/referrers/<digest> | Where signatures, SBOMs and attestations attached to an image are discovered (added in distribution-spec 1.1) |
The specification deliberately does not define authentication; each registry layers its own. It does say clients must not forward Authorization headers across hosts unless configured to, and that upstream credentials should not be sent to a proxy registry unless the credential owner configured it. NIST SP 800-190 section 3.2 groups registry risks into three: insecure connections (credentials intercepted, images substituted), stale images (old vulnerable versions deployed by mistake) and insufficient authentication and authorisation (a compromised registry spreads compromise to every downstream container and host).
In ATT&CK terms, T1525 Implant Internal Image describes an adversary planting a backdoored image in an organisation's own registry for persistence, and T1195.002 Compromise Software Supply Chain covers poisoning images that others pull. T1552.001 Credentials In Files applies to secrets baked into image layers.
Registry access control for people and machines
Most registry traffic is machine to machine: CI pushes, clusters pull, scanners read. The access model should make that explicit.
- Authenticate everything sensitive: SP 800-190 says access to registries holding proprietary or sensitive images should require authentication, all write access must be authenticated, push rights scoped per team, and the registry federated with existing directory accounts where possible.
- Separate push from pull: CI jobs that build get push to their own repositories; runtime clusters get pull only; nobody gets delete except lifecycle automation. Harbor, for example, will not grant Push Repository without Pull Repository, but pull-only accounts are possible and should be the default for clusters.
- Disable shared admin accounts: the Azure Container Registry admin account is disabled by default, bypasses Azure RBAC and has full push and pull rights; Microsoft advises against sharing it. Keep it off unless a specific service requires it.
- Use namespaces for ownership: one repository namespace per team or product, with permissions on the namespace, so ownership and blast radius follow the same boundary.
- Control public visibility: GitHub Container Registry packages are private by default and can inherit repository permissions; Docker Hub repositories can be public. Require that work images are published only from corporate namespaces, never from personal accounts.
Where identity fits: robot accounts, OIDC federation and pull secrets
Registry credentials are non-human identities and should be governed like any other. The table compares the main options, from most to least preferred.
| Credential type | Examples | Lifetime | Use for | Main risk |
|---|---|---|---|---|
| Federated workload identity (OIDC) | CI OIDC token exchanged for a cloud role that can push to ECR, ACR or Artifact Registry; GITHUB_TOKEN for ghcr.io | Minutes to hours | CI push | Over-broad trust policy (any branch or any repository can assume the role) |
| Managed identity or node role | Azure managed identity, cloud node roles, kubelet credential provider plugins | Short-lived, issued on demand | Cluster and service pulls | Every pod on the node shares the node's pull rights |
| Scoped robot or service account | Harbor project robot accounts, ACR repository-scoped tokens, Docker organisation access tokens | Configurable expiry | Non-cloud CI, external consumers | Set to "never expire" and forgotten |
| Personal access token | Docker Hub PAT, GitHub PAT (classic) with write:packages | Often no expiry | Interactive use only | Tied to a person; leaves with them or leaks from their laptop |
| Shared admin or password | ACR admin account, registry root user | Until rotated | Avoid | No attribution, no least privilege |
- OIDC federation for CI: GitHub recommends switching from PATs to
GITHUB_TOKENfor ghcr.io; for cloud registries, exchange the CI OIDC token for a role whose trust policy pins the repository and branch or environment. See the Workload Identity Federation Guide. - Know the token defaults: Docker Hub PATs do not expire unless you choose 30 days, 90 days or a custom date up to one year, and expiry cannot be changed after creation; Docker recommends organisation access tokens for organisation-wide automation. Azure service principal passwords default to one year;
az acr logintokens last three hours. GitHub warns that selectingwrite:packagesin the UI also selectsrepoby default, which grants far more than registry access. - Robot accounts: Harbor shows a robot secret once and does not store it; lost secrets can only be refreshed. Set an expiry rather than "Never Expired", name accounts after the consuming system, and replace legacy
robot$accounts created before Harbor v2.2. - Kubernetes pull secrets: an
imagePullSecretsentry references a Secret of typekubernetes.io/dockerconfigjson; its contents are base64 encoded, not encrypted, and anyone who can read the Secret, or create pods in the namespace, can use the registry credential. Attaching it to a ServiceAccount spreads it to every pod using that account. Prefer the kubelet image credential provider (stable since Kubernetes v1.26), which fetches short-lived registry credentials on demand instead of storing them in the cluster. See the Kubernetes NHI Security Guide. - Inventory and ownership: list every robot account, token and pull secret with owner, scope, last use and expiry. See the CI/CD Pipeline Identity Security Guide and the Secrets Management Guide.
Tags, digests and immutability
An OCI tag is a mutable pointer of up to 128 characters; a digest is the content hash of the manifest. A deployment that references a tag runs whatever the tag points to at pull time.
- Enable tag immutability: in Amazon ECR, set
--image-tag-mutability IMMUTABLE; a push to an existing tag then fails withImageTagAlreadyExistsException.IMMUTABLE_WITH_EXCLUSIONallows named exceptions such as alatestconvenience tag. Other registries offer equivalent tag retention or immutability rules. - Deploy by digest: write
image@sha256:...into manifests produced by CI, or let an admission controller resolve tags to digests. SP 800-190 recommends immutable, versioned names so known-good images are deployed. - Use semantic release tags: one tag per build, never reused; keep floating tags only for developer convenience and never reference them from production.
- Watch the trade-offs: AWS notes that tag immutability prevents ECR from refreshing pull-through cache images that reuse a tag, so treat cache repositories separately.
The Checkmarx KICS supply chain attack shows the cost of mutable tags: on 22 April 2026 attackers overwrote the latest, alpine, debian and v2.1.20 and v2.1.21 tags on the official Docker Hub repository, and pipelines that pulled latest ran a scanner that exfiltrated reports full of credentials.
Signing, attestations and verification
- Sign in CI, by digest: Sigstore cosign supports keyless signing, where an OIDC identity (such as the CI workload) obtains a short-lived certificate, and key-based signing with keys held in a KMS or vault referenced by URI. Sign the digest the build produced, not a tag.
- Store signatures beside the image: cosign stores signatures in the registry using the OCI 1.1 referrers mechanism, so a registry that supports the referrers API exposes signatures, SBOMs and attestations for any digest. Check your registry's support before relying on it; the specification defines a tag-based fallback for registries without the API.
- Attach evidence:
cosign attestattaches a signed predicate such as an SBOM, vulnerability scan or SLSA provenance to the image. See the SBOM Guide. - Verify identity, not just a signature: keyless verification requires both
--certificate-identityand--certificate-oidc-issuer, so you check which workflow or account signed, not merely that someone did. - Enforce at admission: the Sigstore policy-controller validates images in namespaces labelled
policy.sigstore.dev/include=trueagainstClusterImagePolicyresources, resolves tags to digests, supportsspec.mode: warnfor testing, and can evaluate attestations with CUE or Rego. Other admission controllers offer similar checks. See the Kubernetes Admission Control Guide.
Choosing a signing model
| Factor | Keyless (OIDC and transparency log) | Key-based (KMS or HSM) |
|---|---|---|
| Key management | No long-lived key to protect or rotate | Key must be protected, rotated and access-controlled |
| Verification policy | Pins the signer identity and issuer | Pins a public key |
| Offline or air-gapped clusters | Harder; needs bundles and trust roots available locally | Straightforward |
| Privacy | Signer identity is recorded in a public log when the public instance is used | No public record |
| Best fit | Cloud-hosted CI with OIDC, open source distribution | Regulated or disconnected environments, private distribution |
Scanning, pruning and stale images
- Scan on push and continuously: Amazon ECR basic scanning covers OS packages on push or manually; enhanced scanning through Amazon Inspector adds language packages and continuous re-scanning, with EventBridge events when new findings appear. A one-time scan at push ages quickly; continuous scanning matters for images that run for months.
- Scan for secrets, not only CVEs: the Docker Hub secrets leak study by Flare found exposed keys in 10,456 public images uploaded in November 2025, many via
.envfiles, hard-coded tokens and whole project directories copied into builds. The earlier RWTH Aachen study found secrets in 8.5% of 337,171 images, including 52,107 valid private keys. - Prune stale images: SP 800-190 recommends pruning unsafe or vulnerable images, automated with time triggers and labels. Use lifecycle or retention policies that keep a defined number of releases per repository and delete untagged manifests, while retaining anything currently deployed.
- Quarantine, then delete: move images with critical findings to a blocked state or remove pull permission first, so a rollback does not depend on a deleted image.
Image build hygiene (base images, multi-stage builds, non-root users) is covered in the Container Image Security Guide.
Public registries, mirrors and pull-through caches
- Pull public images through your own registry: Amazon ECR pull-through cache rules sync upstreams such as ECR Public, the Kubernetes registry, Quay, Docker Hub, GitHub Container Registry and others into your private registry, where your IAM policies, scanning and replication apply. ECR checks the upstream at most once every 24 hours for a cached tag. Upstream credentials live in Secrets Manager under the
ecr-pullthroughcache/prefix. - Block direct internet pulls from clusters: egress rules and admission policy should only allow your registry hostnames, so a typo or a malicious manifest cannot pull from an arbitrary registry.
- Promote, do not mirror blindly: treat the cache as a quarantine; promote approved, scanned and digest-pinned base images to a curated repository that production uses.
- Delay automated adoption: a short cooldown before dependency bots adopt new upstream tags gives time for a poisoned release to be detected.
Implementing registry security: a phased rollout
- Inventory: list every registry, including personal and team Docker Hub accounts, repositories, visibility, and every credential with push or pull rights and its owner.
- Close the worst gaps first: disable shared admin accounts, make work repositories private or move them to corporate namespaces, revoke unused tokens, set expiries on robot accounts and enforce TLS for every client.
- Move CI to federation: replace stored registry passwords in CI with OIDC or
GITHUB_TOKEN, one pilot pipeline first, then by template. - Turn on immutability and scanning: enable tag immutability on new repositories, then existing ones after checking which pipelines reuse tags; enable scan on push and continuous scanning.
- Sign and verify in warn mode: add signing to CI, deploy the admission policy in warn mode, and review warnings for two to four weeks to find unsigned images and third-party images that need an exception or a mirrored, re-signed copy.
- Enforce per environment: switch to enforce for production namespaces first, then staging. Keep a documented break-glass procedure that relabels a namespace to warn, with approval and logging.
- Rollback: because policies and repository settings are configuration, keep them in version control and roll back by reverting the change; never by granting broad push rights or disabling admission cluster-wide.
Detection and measurement
Send registry audit logs (for cloud registries, the provider's control plane and data plane logs; for Harbor, its audit log) to your SIEM. Alert on:
- Pushes or tag changes outside CI identities, and any overwrite of a release tag.
- Pulls of sensitive repositories from unexpected networks or identities, which SP 800-190 suggests logging.
- New tokens, robot accounts or permission changes; enabling of an admin account; repositories changed to public.
- Admission denials for signature failures, which may indicate tampering or a broken pipeline.
| Metric | Target |
|---|---|
| Share of production workloads running signed, digest-pinned images verified at admission | 100% for production |
| Registry credentials with no expiry or no named owner | Zero |
| Share of repositories with tag immutability enabled | All release repositories |
| Running images with critical vulnerabilities older than SLA | Falling, within SLA |
| Secrets found in images and time to revocation | Revocation within hours |
| Pushes by non-CI identities per month | Near zero, each explained |
Framework mapping
| Registry control | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | ATT&CK technique addressed |
|---|---|---|---|
| Scoped push and pull, no shared accounts | AC-3, AC-6, IA-5 | PR.AA-01, PR.AA-05 | T1078.004 Cloud Accounts |
| Encrypted connections to registries | SC-8 | PR.DS-02 | T1195.002 Compromise Software Supply Chain |
| Signing and admission verification | CM-14, SI-7, SI-7(15), SR-11 | PR.PS-06 | T1525 Implant Internal Image |
| Provenance and SBOM attestations | SR-4 | GV.SC-05 | T1195.002 Compromise Software Supply Chain |
| Scanning and pruning | RA-5, SI-2 | ID.RA-01, PR.PS-02 | T1610 Deploy Container |
| Secret scanning of images | IA-5 | PR.AA-01 | T1552.001 Credentials In Files |
| Registry audit logging | AU-2, AU-12 | DE.CM-09 | T1525 Implant Internal Image |
NIST SP 800-190 sections 3.2 and 4.2 are the most specific government guidance on registries; see the NIST SP 800-190 Container Security Guide.
Common failure patterns
- Deleting instead of revoking: Flare reported that around a quarter of developers removed exposed secrets from Docker Hub images within a day or two, but in most cases did not revoke the credential, which stayed usable. Pulled copies persist; revoke at the provider.
- Mutable release tags: production references
latestor a reused version tag, so an overwrite, as in the KICS case, deploys without any change on your side. - Incomplete rotation after a compromise: Checkmarx reports that the April 2026 wave used cached credentials left over from a March intrusion that the clean-up had missed. After any registry credential leak, enumerate and test every token, robot account and cached credential, not just the one you know about.
- Pull secrets copied everywhere: one long-lived pull credential with read on every repository is added to every namespace and the default ServiceAccount. A single namespace compromise exposes all images.
- Signing without enforcement: CI signs images but no admission policy checks them, or the policy is left in warn mode indefinitely.
- Work images on personal accounts: contractors and developers publish to personal Docker Hub namespaces outside corporate monitoring, a pattern the Flare study highlighted.
Practitioner checklist
- Inventory every registry, repository and registry credential, with owner, scope, last use and expiry.
- Disable shared admin accounts and make push rights per team namespace, pull-only for clusters.
- Replace stored registry passwords in CI with OIDC federation or platform-issued workflow tokens.
- Replace static
imagePullSecretswith managed identities or kubelet credential providers where the platform supports them. - Enable tag immutability on release repositories and deploy by digest.
- Sign every image by digest in CI and enforce signer identity and issuer at admission for production.
- Enable scan on push and continuous scanning, including secret scanning, and revoke any credential found.
- Route public images through a pull-through cache and promote approved images to a curated repository.
- Apply lifecycle policies to prune stale and untagged images while keeping deployed versions.
- Send registry audit logs to the SIEM and alert on non-CI pushes, tag overwrites and visibility changes.
Standards and references
- Open Container Initiative: Distribution Specification
- NIST: SP 800-190 Application Container Security Guide
- Sigstore: Signing Containers with Cosign
- Sigstore: Policy Controller Overview
- AWS: Amazon ECR Image Tag Mutability
- Microsoft Learn: Authenticate with Azure Container Registry
- Harbor: Create Project Robot Accounts
- MITRE ATT&CK: Implant Internal Image
Related NHI Mgmt Group resources: Container Security Guide · Software Supply Chain Security Guide · CI/CD Security Guide · DevSecOps Guide · Kubernetes Security Guide