fivenines
39/40

Guided Problem

MiniDock Build 33: Harden a Third-Party Image

Time
30m
Level
advanced
Artifacts
not specified
Progress0%
Lesson 39 · Production

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.

Architecture — mitigations pinned to the attack chain
  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.

Sequence — a poisoned tag dies at admission
  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.
Next step

See what actually stuck.

Take the practice scenarios now.