Guided Problem
MiniDock Build 33: Harden a Third-Party Image
- Time
- 30m
- Level
- advanced
- Artifacts
- not specified
Hardening MiniDock
Design claim: Production hardening reduces ambient authority and verifies release inputs at every boundary rather than trusting image provenance or defaults.
Starting model
- You can classify operational signals by shape and route them without polling container state.
- The useful vocabulary at this point is deliberately small: digest pinning, signature, scan, non-root.
Packaging is not a security review
A third-party image must run with the minimum filesystem, identity, kernel, network, and supply-chain authority needed for its service. Production evidence and authority cross every subsystem, so the completed design must preserve meaning at each seam rather than rely on a privileged center.
The tempting shortcut is straightforward: run the image with default privileges because packaging implies safety. The shortcut collapses distinct signals or controls into one convenient component and hides which promise failed. Images can contain vulnerable software or malicious configuration, and shared-kernel defaults cannot infer the application's intended authority. Compromise or host loss shows why independent evidence, refusal, and recovery paths must remain visible.
Remove authority at independent seams
Most compromises arrive in the image, not through the kernel. Three gates: pin digests — deploy image@sha256:…, not a mutable tag someone can quietly repoint (Lesson 17's mutable-pointer risk, weaponized); scan layers against vulnerability databases before promotion; verify signatures so only images signed by your build system are admitted. All three are pull-time policies bolted onto machinery you already designed in Parts 2–3.
The platform combines trusted content policy, non-root identity, dropped capabilities, syscall and LSM policy, read-only filesystems, scoped secrets, limits, and network segmentation. The mechanism composes narrow contracts: each subsystem owns its decision, exports evidence, and limits the authority granted across the seam.
Read the map end to end, but stop at every boundary to identify its owner, input, output, and refusal behavior.
flowchart TB
A1["attacker publishes
poisoned image tag"] -- "blocked by" --> D1["digest pinning +
signature verification"]
A2["known CVE in a layer"] -- "blocked by" --> D2["pre-deploy scanning"]
A3["app exploited at runtime"] -- "contained by" --> D3["read-only rootfs +
no-new-privileges"]
A4["escape to host"] -- "lands unprivileged via" --> D4["user-ns remap +
L6 sandwich"]
A5["lateral movement"] -- "bounded by" --> D5["network segmentation (L28)"]
A6["credential theft from disk"] -- "prevented by" --> D6["tmpfs-only secrets (L25)"]
It layers supply-chain, identity, kernel, filesystem, secret, resource, and network controls. Its central claim is that every granted capability, writable path, secret, and network edge is justified by an explicit workload requirement; the labels therefore describe authority rather than decorative grouping.
Verify before launch and constrain after it
Layer the runtime so a compromised app is a disappointing prize. User-namespace remap (Lesson 4): container-root maps to an unprivileged host UID, so an escape lands without privileges. Read-only rootfs: the overlay upper becomes immutable; the app writes only to declared volumes and tmpfs — malware can't persist itself. no-new-privileges: setuid binaries can't re-escalate. Network segmentation (Lesson 28) bounds lateral movement; tmpfs secrets (Lesson 25) keep credentials off every disk and out of every image layer.
Admission verifies the release, runtime policy removes unnecessary power, storage and network boundaries limit reach, and observability records refusals and drift. A successful path is convincing only when the same ownership map explains interruption and recovery.
The second figure tests the same model in motion: it traces admission and two post-compromise operations refused at different boundaries.
sequenceDiagram
participant A as Attacker
participant R as Registry
participant E as Hardened engine
A->>R: repoint tag web:stable at poisoned manifest
E->>R: pull web@sha256:9f3c… (pinned digest, ignores the tag)
R-->>E: the ORIGINAL manifest — tags can lie, digests can't
E->>E: signature check: signed by our CI ✓
note over E: even if forced to use tags, the unsigned
poisoned manifest fails admission
It traces admission and two post-compromise operations refused at different boundaries. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.
Compromise should meet more than one wall
The compromised application attempts to write its root filesystem and reach the scan database directly, so independent controls must refuse both paths. The boundary test should activate several independent controls without turning any one of them into universal authority.
Every granted capability, writable path, secret, and network edge is justified by an explicit workload requirement. The final rule is compositional: system correctness comes from explicit local guarantees that continue to hold together.
Harden the completed MiniDock runtime
MiniDock wraps previously built mechanisms in a least-authority production profile without inventing a new trust boundary. MiniDock is complete when the whole ParcelFlow path can be explained from those contracts.
What carries forward
- Every granted capability, writable path, secret, and network edge is justified by an explicit workload requirement.
- Place independent preventive and containment controls around untrusted production code.
- The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
See what actually stuck.
Take the practice scenarios now.