Hardening practices
What each "hardened" property actually means + how to verify it.
PodArmor's TrustHeadline chip row and HardeningBadges on every image card aren't decorative — each badge reflects a property we apply at build time and you can verify externally.
Non-root
Every image runs as a non-root UID by default. For our deploy images (distroless), that's UID 65532 (nonroot in Google's distroless convention). For our build images, it's a deliberately-created podarmor user with UID 65532.
Verify:
docker run --rm <image> id
# uid=65532(nonroot) gid=65532(nonroot)The Dockerfile's last USER directive is USER 65532:65532, and there's no USER root anywhere later. Customers can override at runtime if they need to (docker run --user 0:0), but the image's default never runs as root.
No SUID / SGID
Build-time step strips every SUID / SGID bit on every file in the image:
RUN find / -xdev \( -perm -4000 -o -perm -2000 \) -type f \
-exec chmod -s {} + 2>/dev/null || trueThis blocks the most common Linux privilege-escalation path inside a container even if the runtime accidentally allows it.
Verify:
docker run --rm <image> find / -xdev \( -perm -4000 -o -perm -2000 \) -type f
# (no output)No shell / no package manager (deploy images only)
Distroless images by definition contain no /bin/sh, no apt, no dpkg, no curl. There's no interactive shell to drop into, no way for a runtime exploit to spawn apt-get install.
Verify:
# This should error.
docker run --rm --entrypoint /bin/sh <image> -c "echo hi"
# OCI runtime exec failed: exec failed: unable to start container process: ...Build images intentionally keep /bin/sh because Maven's mvn wrapper script needs it. But they still don't have apt, dpkg, or any package manager — we strip those after the user is created.
CIS Section 4 aligned
The image-level checks from the CIS Docker Benchmark Section 4:
| Check | Description | How we satisfy it |
|---|---|---|
| 4.1 | Create a user for the container | Non-root by default |
| 4.3 | Do not install unnecessary packages | Aggressive dpkg --purge --force-all of unused packages |
| 4.4 | Scan and rebuild for security patches | Continuous re-scan; auto-rebuild on upstream patches |
| 4.6 | Add HEALTHCHECK | Every image's Dockerfile has a HEALTHCHECK directive |
| 4.7 | Do not use update instructions alone | Versions pinned everywhere |
| 4.8 | Remove setuid/setgid permissions | SUID/SGID stripped at build time |
| 4.9 | Use COPY instead of ADD | Confirmed |
| 4.10 | Do not store secrets in Dockerfiles | Confirmed |
| 4.11 | Install verified packages only | Adoptium tarballs sha256-verified; apt packages from official Debian repos |
CIS Section 5 (runtime checks like --cap-drop, --read-only, seccomp profile) is the responsibility of your orchestrator (Kubernetes, ECS, etc.) — not something an image can enforce by itself.
SBOM included
Every image-version ships with syft-generated SBOMs in both SPDX 2.3 and CycloneDX 1.5 formats. The portal exposes download endpoints; the SBOMs are also published as OCI referrers on the image manifest, discoverable with cosign tree or oras discover.
See Download an SBOM for details.
Signed with Cosign
Every image is signed using Cosign keyless mode (OIDC against GitHub Actions). The transparency-log entry is publicly verifiable on Rekor.
See Verify a Cosign signature for the verification command.
What's not in every image (yet)
- Multi-arch. Some images publish both
linux/amd64andlinux/arm64; some publish amd64 only. The TrustHeadline chip surfaces this honestly per-image. - FIPS. No FIPS variants today. When we ship them, they'll carry a real FIPS chip; until then the chip is absent.
- STIG. Same — no claim until we've actually run the validation.