Safeguard
Container Security

Securing Container Registries: From Push to Pull

Your registry is the single point every image passes through — and a favorite target for attackers who want to poison many deployments at once. Here is how to lock it down end to end.

Marcus Chen
Cloud Security Engineer
5 min read

The container registry is the chokepoint of your entire delivery pipeline. Every image that runs in production passes through it, which makes it both the best place to enforce security policy and the highest-value target for an attacker. Poison one widely pulled base image in a registry and you have compromised every downstream deployment that trusts it — no per-target exploitation required. The 2019 Docker Hub incident, which exposed data for roughly 190,000 accounts and prompted a mass rotation of tokens and keys, was an early reminder that the registry itself is part of your attack surface, not neutral infrastructure. Securing it means controlling who can push, guaranteeing that what was pushed is what gets pulled, and continuously checking that the images sitting inside it have not aged into liabilities.

Control who can push, tightly

Push access is the crown jewel. Anyone who can push to a namespace your clusters trust can inject arbitrary code into production. Registries — whether Harbor, Amazon ECR, Google Artifact Registry, Azure Container Registry, GHCR, or Quay — all support fine-grained access control, and the rule is the same everywhere: humans should rarely, if ever, have direct push rights to production repositories. Push should come from CI, authenticated with short-lived, workload-scoped credentials rather than long-lived static tokens.

  • Grant CI a push role scoped to specific repositories, not registry-wide write.
  • Use OIDC federation (for example, GitHub Actions to ECR) so no long-lived registry password lives in a CI secret at all.
  • Give developers pull-only access by default; production push happens through the pipeline.
  • Separate registries or projects by trust tier — internal builds, third-party mirrors, and production-approved images should not share one flat namespace.

Make tags immutable and sign everything

A mutable tag is a lie waiting to happen: myapp:v1.4 can point at different bytes next week than it does today, whether through an honest re-push or a malicious one. Enable tag immutability so a published tag can never be overwritten, and pull by digest in your deployments for a cryptographic guarantee of exactly what runs. Then sign images so provenance is verifiable. Sigstore's cosign reached general availability in 2021 and is now the de facto standard for keyless signing using short-lived OIDC-backed certificates:

# Sign in CI using keyless (OIDC) signing
cosign sign --yes \
  registry.example.com/payments/api@sha256:2b3c4d5e...

# Verify before deploy
cosign verify \
  --certificate-identity-regexp '^https://github.com/acme/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  registry.example.com/payments/api@sha256:2b3c4d5e...

Signing proves an image came from your pipeline and was not tampered with in the registry. It does not prove the image is free of vulnerabilities — that is a separate check — which is why the two belong together at admission.

Verify at admission, not on the honor system

A signed, immutable image in the registry is worthless if the cluster will happily run an unsigned one pushed by hand. Enforce verification at the moment of scheduling with an admission controller. Kyverno's verifyImages rule or the Sigstore policy-controller reject anything that fails the check:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences:
            - "registry.example.com/payments/*"
          attestors:
            - entries:
                - keyless:
                    issuer: "https://token.actions.githubusercontent.com"
                    subject: "https://github.com/acme/*"

Do not let approved images rot

An image that passed its scan at push time accumulates newly disclosed CVEs while it sits in the registry, untouched. Registry-side scanning that re-evaluates stored images against updated vulnerability data is what catches the "clean six months ago" problem. Combine it with a retention and quarantine policy: automatically flag or block images that exceed your vulnerability threshold or pass an age limit without a rebuild.

Retention hygiene is a security control in its own right, not just a cost measure. Registries accumulate thousands of dangling and untagged manifests — the debris of every CI run — and any one of them can still be pulled by digest if its reference leaks into a manifest or a cached deployment. Set lifecycle rules that purge untagged images after a short window, cap the number of tags retained per repository, and keep a small, explicitly promoted set of production-approved images separate from the churn of development builds. The smaller and better-labeled your registry, the less there is for an attacker to rediscover and the faster your incident responders can answer "is this image still reachable?"

Registry hardening checklist

ControlWhy it matters
Least-privilege push (CI only, scoped)Prevents arbitrary code injection into prod
OIDC federation over static tokensRemoves long-lived leakable credentials
Tag immutabilityGuarantees pulled bytes match published bytes
Deploy by digestCryptographic pin to an exact image
Image signing (cosign)Verifiable provenance and tamper evidence
Admission verificationRejects unsigned or non-compliant images
Continuous registry scanningCatches CVEs disclosed after push
Retention / quarantine policyStops stale, vulnerable images from lingering

How Safeguard helps

Safeguard treats the registry as a first-class control point. Container security scanning runs continuously against the images stored in your registry, not just at build, so an image that ages into a critical CVE is flagged the day the advisory lands rather than the day it is exploited. Griffin AI filters those findings by reachability so the alerts your team acts on are the exploitable ones, and the Safeguard CLI plugs signing and verification into the same pipeline that pushes your images, so provenance and vulnerability policy are enforced together. Weighing registry-aware platforms against posture tools? Our Safeguard vs Aqua and Safeguard vs Prisma comparisons lay out the differences.

Make your registry a gate, not a blind spot. Create a free Safeguard account or read the documentation to connect your registry.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.