Supply chain security — the whole chain, end to end
A capstone that places everything the tutorial built — hardened base images, SBOMs, signing, provenance, scanning, Trusted Libraries — onto the standard container supply-chain threat model, reframed for Podman, OpenShift, and RHEL. Shows which attack vectors a near-zero-CVE distroless image neutralizes by construction, and, just as importantly, which ones it does not.
Every previous chapter solved one piece of a larger problem. This one steps back and names the problem.
Supply chain security is the discipline of making sure the software
you run is the software you intended to run — built from source you
trust, by a build system you trust, and delivered to you without
tampering. The canonical treatment for containers is Liz Rice’s
Container Security (2nd edition, O’Reilly, 2025), chapter 7. This
chapter borrows her threat model and maps it onto the Hummingbird
world: Podman and Buildah instead of Docker, Containerfile
instead of Dockerfile, registry.access.redhat.com and OpenShift
instead of a generic registry and orchestrator, and a near-zero-CVE
distroless base instead of a general-purpose one.
The headline: Red Hat Hardened Images are, in large part, an answer to this threat model. Most of the attack vectors below are neutralized by construction when the base image is built the way Hummingbird builds it. A few are not, and being honest about which is the whole point of this chapter — an over-trusted hardened image is its own risk.
The supply chain, and where it gets attacked
A container image is assembled from a chain of inputs — source
code, a Containerfile, a base image, language packages, OS
packages — and then built, stored, and deployed. Each hand-off is
a place an attacker can interfere.
Reading left to right, the classic vectors are:
- Tampered source — someone modifies the application source before it is built.
- Tampered
Containerfile— a build instruction is altered to add malware, exfiltrate build secrets, or attack the build host. ARUNline is arbitrary code execution on the builder. - Vulnerable base image or dependencies — the
FROMimage, or an OS/language package pulled during the build, already contains exploitable code (or the wrong package entirely — dependency confusion). - Build tampering / compromised build host — the build environment itself is subverted, so a clean source tree still produces a poisoned image.
- Image tampered in the registry — the stored image is swapped or modified after it was built.
- Intercepted pull — the image is altered in transit between registry and host.
- Malicious deployment definition — the Kubernetes/OpenShift YAML is changed to run a different (malicious) image; a one- character change to a registry hostname is enough.
This is the same model whether you build with Docker, Podman, or anything else. What changes with Hummingbird is how many of these vectors are closed before you write a line of policy.
Where Hummingbird changes the picture
Figure 18.2 overlays the Hummingbird answer on that same chain: the attack vectors run along the top, and the mitigation at each stage runs along the bottom.
Each mitigation along the bottom row was built and demonstrated earlier in this tutorial:
| Attack vector | What closes it | Covered in |
|---|---|---|
| Vulnerable base image | Near-zero-CVE hardened base, rebuilt continuously | §2, §6 |
| Vulnerable dependencies | Distroless minimalism (fewer packages to be vulnerable) + Trusted Libraries for Python deps | §6, §13 |
| Build tampering / compromised build host | SLSA 3 hermetic builds on Konflux; signed, verifiable provenance | §2, §5 |
| Image tampered in registry | cosign signature + SBOM + SLSA provenance attestations on the signed /hi path |
§5 |
| Intercepted pull | Verify the signature and attestations on pull with podman + cosign |
§5 |
| Malicious deployment definition | OpenShift admission control / a sigstore admission policy that refuses unsigned or unverified images | this chapter |
The throughline is the same idea you met in §2 and §5: the image does not merely assert it is safe, it carries signed evidence you can check. A hardened base turns three of the worst vectors — vulnerable base, vulnerable dependencies, build tampering — from “trust us” into “verify it yourself,” and the minimalism means there is simply less inside the image to be vulnerable in the first place. The empirical version of that claim is the scan you ran in §6: the hardened image at or near zero, the general-purpose image in the hundreds.
What Hummingbird does not close. Look again at the left of the diagram. Tampered source and a tampered
Containerfileare yours to defend: branch protection and RBAC on the repo, signed commits, code review of every newRUN/COPY, and pinning your base image by digest rather than tag. Hummingbird gives you a trustworthyFROM; it cannot stop you from writing an insecureContainerfileon top of it.
The layered attack surface
The pipeline view covers build and delivery. At runtime, the relevant picture is defence in depth: a running container sits inside a stack of boundaries, and an attacker can target any layer.
The hardened image directly shrinks the cluster of vectors aimed at the image and what runs in it:
- Compromised image — defeated by signing and verification: a
swapped image fails
cosign verifyagainst Red Hat’s key. - Poorly configured image — distroless removes most of the foot-guns: no shell, no package manager, a non-root default user (UID 65532 where technically possible), so there is far less to misconfigure.
- Exposed secrets / code exploits — fewer packages means fewer exploitable code paths, and with no shell or tools baked in, an attacker who does land in the container has very little to work with. This is the practical meaning of “small attack surface.”
What the image cannot fix is everything outside it. A misconfigured RHEL host, insecure networking, and container escape are addressed by the platform, not the base image: rootless Podman, SELinux, seccomp and capability restrictions, and OpenShift SCCs and network policies. The hardened image is one strong layer; it is not the whole onion.
Verifying on Podman, enforcing on OpenShift
Two practical habits turn all of this from theory into a gate.
On pull, verify — don’t just trust the tag. This is the §5 flow, recapped: confirm the signature, then inspect the signed attestations.
SIGNED="registry.access.redhat.com/hi/nginx:1"
# 1. Signature: did Red Hat sign this exact image?
cosign verify \
--key "https://security.access.redhat.com/data/63405576.txt" \
--insecure-ignore-tlog \
"$SIGNED" >/dev/null && echo "signature OK"
# 2. What signed supply-chain metadata is attached?
cosign download attestation "$SIGNED" \
| jq -rs '.[] | (.payload | @base64d | fromjson | .predicateType)' \
| sort -u
# -> https://slsa.dev/provenance/v0.2 (how it was built)
# https://spdx.dev/Document (what's inside)
Verify against the signed registry.access.redhat.com/hi/
path. The quay.io/hummingbird mirror is unsigned by design, so
there is nothing there for cosign to check. (See
§5 for the full signing and
verification walkthrough, including the SLSA-provenance predicate
nuance — the v0.2 predicate URI is why a bare
--type slsaprovenance does not match.)
At deploy time, enforce it. Verifying by hand proves the chain
holds once; an admission controller makes it hold for every
deployment, so no one can route around the check. In OpenShift,
that is the sigstore admission policy (or a policy engine such as
Kyverno or OPA Gatekeeper) configured to reject any image that is
not signed by the expected identity, not scanned, or not from an
approved registry. The same policy.json you wrote in §5 is the
single-host version of this idea; admission control is the
cluster-wide version.
The point of an admission gate is that it closes the malicious deployment definition vector from Figure 18.1: it does not matter what an attacker writes in a YAML file if the cluster refuses to run an image that fails verification.
What this buys you, honestly
Put the two figures together and the division of labour is clear:
- Hummingbird owns the image. Vulnerable base, vulnerable dependencies, build integrity, and image authenticity are handled by construction — near-zero CVE, distroless, hermetically built to SLSA 3, signed with verifiable SBOM and provenance. This is the part that is genuinely hard to do yourself, and the part you get for free.
- You own the edges. Source and
Containerfileintegrity, the host and platform hardening, network policy, secrets management, and the admission gate that enforces verification are yours. The hardened image makes these easier — there is less to scan, less to misconfigure, less to exfiltrate — but it does not make them someone else’s job.
A near-zero-CVE distroless base is the strongest single move you can make on the supply chain, precisely because it collapses the vectors that are hardest to defend individually. It is not, and does not claim to be, the entire defence.
Verify before moving on
You should be able to:
- sketch the container supply chain and name the attack vector at each stage, in Podman/OpenShift terms,
- say which vectors a Hummingbird base image closes by construction, and which remain your responsibility,
- verify both the signature and the attached attestations of a Hummingbird image on pull,
- explain why an OpenShift admission policy is what closes the malicious-deployment-definition vector, and
- describe the layered runtime attack surface and where the hardened image does — and does not — help.
Where to go next
This is the end of the tutorial’s through-line. From here:
- Re-run the demo suite end to end — the eight command-line demos are the moving-picture version of everything in these chapters, and demo 7 in particular is this chapter made executable.
- Revisit §5 and §6 with the threat model in mind; the commands land differently once you can name the vector each one closes.
- For the platform-hardening edges this chapter deliberately left out — rootless Podman, SELinux, OpenShift SCCs, network policy — Liz Rice’s Container Security (2nd edition) is the reference to read next; this chapter only borrowed its chapter 7.
The source for this page is in
_docs/18-supply-chain-security.md.