This content originally appeared on DEV Community and was authored by jl03
How to Stop Shipping Garbage Before It Ships You
If your build still pulls
ubuntu:latest, you’re not “cloud-native.” You’re running a randomizer in production.
Why Pinning Matters
Every tag that isn’t a digest is a liability.
:latest is not convenience; it’s negligence.
| What you write | What happens next |
|---|---|
FROM node:latest |
You rebuild on Tuesday and ship a new kernel vulnerability. |
pip install flask |
You pull in a supply-chain bomb before lunch. |
terraform init |
You silently upgrade the AWS provider, then wonder why IAM exploded. |
If you don’t pin it, you can’t attest it.
If you can’t attest it, you can’t trust it.
The Tools of the Trade
OPA (Open Policy Agent)
A fast, embeddable policy engine.
It doesn’t care what file you feed it — only that you speak JSON.
Conftest
A CLI built on top of OPA.
It handles the boring bits:
- parses Dockerfiles, YAML, Terraform, JSON
- feeds them into OPA
- exits 1 on fail, 0 on pass That’s all you need for CI or pre-commit hooks.
Install it:
# macOS / Linux
brew install conftest@0.56.0
# or
curl -L https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_$(uname -s)_$(uname -m).tar.gz \
| tar -xz -C /usr/local/bin
Run it:
conftest test Dockerfile --parser dockerfile
It will parse your Dockerfile into JSON, run your Rego rules, and tell you exactly why your laziness failed policy.
Crane
Part of Google’s go-containerregistry toolkit.
It’s the BOFH’s Swiss army knife for container registries.
Use it to fetch digests, manifests, and metadata — fast, no daemon, no Docker socket.
Install it:
brew install go-containerregistry
# or
go install github.com/google/go-containerregistry/cmd/crane@0.20.02
Pin any image:
img=python:3.12
echo "$img@$(crane digest "$img")"
Result:
python:3.12@sha256:deadbeefbadc0ffee123456789...
Now you know exactly which bits are running in prod.
No excuses, no surprises.
🪓 Policy as Code: The OPA Way
Create policies/dockerfile/pinning.rego:
package policies.dockerfile.pinning
deny[msg] {
some s
f := s.Commands[_]
f.Name == "from"
not contains(f.Value, "@sha256:")
msg := sprintf("FROM %q missing digest", [f.Value])
}
deny[msg] {
f := input.Stages[_].Commands[_]
f.Name == "user"
trim(f.Value) == "root"
msg := "Dockerfile must set non-root USER"
}
Test it manually:
conftest test Dockerfile --parser dockerfile --output table
Expected output:
FAIL - Dockerfile - FROM "ubuntu:latest" missing digest
🧷 Make It Automatic: Pre-commit Hook
.git/hooks/pre-commit:
#!/usr/bin/env sh
set -eu
conftest pull ghcr.io/your-org/precommit:1 >/dev/null || true
files=$(git diff --cached --name-only | grep -Ei '(Dockerfile|\.ya?ml)$' || true)
[ -z "$files" ] && exit 0
conftest test --parser=dockerfile --output table $files
Try to commit a floating Dockerfile and watch it fail:
FAIL - Dockerfile - FROM "nginx:latest" missing digest
✖ OPA pre-commit checks failed.
Success: laziness stopped at the gate.
The Philosophy
-
Small tools.
cranefor digests.conftestfor checks.jqandyqfor glue. - Zero GUIs. If it doesn’t fit in a pipeline, it doesn’t belong.
-
Fast failure. The only “UX” a BOFH cares about is
exit 1. - Determinism > decoration. Pretty dashboards are for people who don’t read logs.
TL;DR
-
Pin everything —
image@sha256:digest. - Write Rego policies once; run everywhere.
- Use Conftest in pre-commit and CI.
- Use Crane to fetch digests and enforce reproducibility.
- Automate the yelling so you can focus on real problems.
This content originally appeared on DEV Community and was authored by jl03
jl03 | Sciencx (2025-11-12T22:28:25+00:00) Pin It or Bin It. Retrieved from https://www.scien.cx/2025/11/12/pin-it-or-bin-it/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.