What does setting the environment variable `DOCKER_CONTENT_TRUST=1` actually change about `docker push` and `docker pull`, how does the key hierarchy behind it work, and why have most teams moved to something else?
answer
- DOCKER_CONTENT_TRUST=1 → sign on push, refuse unsigned on pull
- TUF roles: root (offline) / targets / snapshot / timestamp
- timestamp key defeats freeze + rollback
- docker trust inspect --pretty, targets/releases delegation
- legacy: tag-scoped, Docker-CLI-only, no transparency log
basics
~20 sIt turns on Docker Content Trust: pushes get signed via a Notary server and pulls or FROM references refuse unsigned or unverified tags. It uses TUF roles — an offline root key, a per-repository targets key, plus server-held snapshot and timestamp keys. Teams moved on because it is tag-scoped, Docker-client-only and painful to operate.
solid answer
~50 s`DOCKER_CONTENT_TRUST=1` enables **Docker Content Trust (DCT)**. With it set, `docker push` also signs the tag through a **Notary v1** server paired with the registry, and `docker pull` / `docker build`'s `FROM` will only accept tags with a valid signature — an unsigned tag fails rather than silently proceeding. Under the hood it is **TUF (The Update Framework)**: an **offline root key**, a per-repository **targets (repository) key** used for day-to-day signing, and server-managed **snapshot** and **timestamp** keys that provide freshness and prevent rollback/freeze attacks. Delegations (`targets/releases`) let multiple signers publish. Why it faded: signatures are attached to **tags**, not arbitrary artifacts; only the Docker CLI honours it — containerd, Kubernetes and CI tools ignore the variable entirely; key loss is unrecoverable and rotation is awkward; there is no transparency log; and it requires a Notary deployment. Docker deprecated it, and the ecosystem moved to **cosign/Sigstore** and the Notary Project's `notation`, which store detached signatures as OCI artifacts.
code
bash · 5 linesexport DOCKER_CONTENT_TRUST=1
docker push registry.example.com/acme/api:1.4 # signs the tag via Notary
docker trust inspect --pretty registry.example.com/acme/api:1.4
docker trust signer add --key alice.pub alice registry.example.com/acme/apigo deeper
Know the variable exists, that it makes pushes signed and unsigned pulls fail, and that modern projects use cosign instead.
Explain the TUF roles and what each protects against, and name the concrete limitations that made DCT legacy.
Discuss operational reality: key custody and rotation, delegations for multiple signers, and why enforcement must live at admission rather than in a client environment variable.
Position it in a migration story — what a move from DCT to cosign/notation costs, how trust roots and identities are re-established, and what enforcement guarantees you actually gain.
## What the switch does Docker Content Trust is opt-in per client. Export `DOCKER_CONTENT_TRUST=1` (or pass `--disable-content-trust=false`) and two behaviours change: - **`docker push`** signs the pushed tag: the digest is recorded and signed into TUF metadata held by a **Notary** server that sits alongside the registry, and the client prompts for/creates keys on first use. - **`docker pull`, `docker run`, and `FROM` in a build** refuse to use a tag that has no valid trust data. Instead of trusting the registry's tag→digest mapping, the client asks Notary for the signed mapping and pulls **that** digest. That last point is the real value: DCT converts a mutable tag lookup into a signed, freshness-checked assertion of which digest the tag means. ## The TUF key hierarchy TUF exists to survive both key compromise and stale-metadata attacks. DCT uses four roles: - **Root key** — the anchor of trust for a repository. Generated on first signed push, meant to be kept **offline** (backed up outside the machine). Losing it means the repository's trust data cannot be recovered. - **Targets (repository) key** — signs the actual tag→digest entries. Used routinely, held by whoever publishes. - **Snapshot key** — signs a consistent view of the metadata set; can be delegated to the Notary server. - **Timestamp key** — held by the server, re-signed frequently and short-lived, so a client can detect **freeze attacks** (an attacker replaying old metadata to hide an update) and **rollback** to a previously good-but-now-vulnerable version. **Delegations** — typically `targets/releases` — let a team add multiple signers without sharing the repository key, and `docker trust signer add` manages them. `docker trust inspect --pretty <image>` shows signers and signed tags. ## Why the ecosystem moved on 1. **Tag-scoped, Docker-only.** Trust data covers tags in the Docker client's world view. Containerd, CRI-O, Kubernetes, buildkit-driven CI and every non-Docker tool ignore `DOCKER_CONTENT_TRUST` completely — so 'we enabled DCT' rarely meant production verified anything. 2. **Operational fragility.** Key generation on first push surprises people; passphrases end up in CI secrets; root key loss is terminal; there is no organisation-wide identity model, just per-repo keys. 3. **No transparency.** Nothing records signing events publicly or internally, so you cannot detect a signature made with a stolen key after the fact. 4. **Infrastructure requirement.** You need a Notary server bound to the registry; hosted support dwindled, and Docker announced deprecation of Content Trust/Notary v1 on Docker Hub. 5. **Only one kind of statement.** Modern supply chains want SBOMs, build provenance and scan attestations attached to an artifact, not just 'this tag is signed'. ## What replaced it Two successors, both storing signatures as **OCI artifacts in the registry itself** (no separate Notary service): - **cosign / Sigstore** — dominant in practice; supports key pairs, KMS keys and 'keyless' short-lived certificates tied to an OIDC workflow identity, with a public transparency log. - **Notary Project `notation`** — the successor line to Notary v1, X.509/PKI-oriented, appealing to organisations with existing certificate authorities. Both sign **digests**, not tags, so signatures survive retagging and mirroring, and both are verified by admission controllers that Kubernetes actually runs. ## What to say in an interview Know DCT exists, know it is TUF-based with root/targets/snapshot/timestamp roles, know the variable and `docker trust inspect`, and be able to explain crisply why it is legacy: tag-scoped, client-scoped, key-fragile, no transparency, superseded by cosign and notation. Recognising legacy tooling *and* the reason it lost is the whole point of this question.
- What attack does the short-lived timestamp key defend against?Freeze and rollback attacks. Without a freshness signal, an attacker who controls the distribution path can keep serving old, validly signed metadata so clients never see a security update, or serve a previously signed vulnerable version. The timestamp role is re-signed frequently and expires quickly, so clients detect stale metadata and refuse it.
- A team sets `DOCKER_CONTENT_TRUST=1` in CI and says production images are now verified. What is wrong with that claim?The variable only affects the Docker CLI on that machine. Kubernetes nodes pull through containerd or CRI-O, which have no knowledge of Content Trust, so nothing is verified at deploy time. Real enforcement needs verification where workloads are admitted — an admission controller checking cosign or notation signatures — not an environment variable on a build agent.
saying these in an interview costs you the question
- Believing `DOCKER_CONTENT_TRUST=1` makes a Kubernetes cluster verify images.
- Thinking DCT signs image digests as arbitrary artifacts rather than tags in Notary metadata.
- Not knowing the root key is meant to be offline, or that losing it is unrecoverable.
- Presenting DCT as current best practice instead of deprecated legacy.
- Confusing Notary v1 (DCT) with the Notary Project's `notation`, which stores OCI signatures.