The path to design
Base images (public) → curated base images → app build (CI) → scan → sign → registry
→ deploy (AKS / Container Apps / App Service) → admission checks → runtime scanning
Every arrow is a place where an untrusted or vulnerable image can slip in.
1. Registry layout and SKU
| Decision | Typical choice |
|---|---|
| Number of registries | One per environment tier (non-prod, prod) or one shared with repository-scoped permissions; prod promotion by import |
| SKU | Premium for private endpoints, geo-replication, higher throughput, retention policies, connected registries |
| Network | Private endpoint, public access disabled (CI agents inside the network) |
| Auth | Entra ID; AKS kubelet identity with AcrPull; no admin user |
| Geo-replication | Replica in each region you run workloads (and the DR region) |
2. Curate base images
Don't let builds pull FROM node:latest from Docker Hub directly:
- Import approved base images into a
base/namespace on a schedule:
az acr import --name acrplatform --source mcr.microsoft.com/dotnet/aspnet:8.0 --image base/dotnet-aspnet:8.0
- Or use artifact cache rules to proxy upstream registries through ACR with your auth and logging.
- Pin versions (and digests for critical apps); rebuild apps when base images update.
- Prefer minimal images (distroless, chiseled, Alpine where compatible) to shrink the vulnerability surface.
3. Build and scan in CI
- Build with BuildKit /
az acr build; generate an SBOM (syft,docker sbom, or build tooling). - Scan the image in the pipeline (Trivy, Defender CLI or similar); fail the build above an agreed severity with a fix available.
- Tag with immutable tags (
1.4.2, git SHA). Avoid deployinglatest.
4. Scan in the registry and at runtime
Defender for Containers scans images in ACR and running images in AKS, reporting vulnerabilities by image and by workload. New CVEs appear after build; registry and runtime scanning catch them.
Define a process: who triages findings, timelines by severity, how a rebuild is triggered.
5. Sign and verify
- Sign images in CI with Notation and a certificate in Key Vault.
- Verify at admission in AKS (Ratify + Azure Policy / Gatekeeper, or built-in image integrity features where available).
- Combine with an allowed registries policy so only your ACR is usable:
| Policy | Effect |
|---|---|
| Kubernetes cluster containers should only use allowed images | Deny images not from acrprod.azurecr.io/ |
| Container registries should not allow unrestricted network access | Audit/Deny |
| Container registries should have local admin account disabled | Deny |
6. Promotion between environments
Build once; promote the same digest:
az acr import --name acrprod --source acrnonprod.azurecr.io/orders-api:1.4.2 --image orders-api:1.4.2
Prod deploys only from the prod registry, which only CI's promotion identity can write to.
7. Hygiene
- Retention policy for untagged manifests (Premium) and scheduled purge of old tags with
acr purgevia ACR Tasks. - Lock production tags (
--write-enabled false) to prevent overwrites. - Monitor storage size, pull throttling and auth failures.
Warning
Mutable tags in production (re-pushing
1.4.2with different content) defeat scanning, signing and rollback. Lock tags or deploy by digest.
Design review questions
- Where can an image come from, and what stops an unknown registry being used?
- How long from a critical base-image CVE to all affected apps rebuilt and deployed?
- Who can write to the prod registry?
Checklist
- Registry layout, Premium features, private access, no admin user
- Curated base images (import or artifact cache), pinned
- CI scan + SBOM; immutable tags
- Registry/runtime scanning with triage process
- Signing and admission verification; allowed registries policy
- Promote by digest; retention and tag locks