Reconcile image pinning to a tiered tag@digest rule
Resolve the conflict between ADR-011 (tags-not-digests) and the security work (digest pinning) with one coherent rule that respects ADR-011's stateless/stateful split: - Stateful → pin `tag@digest` (readable tag + integrity digest): legible diffs AND tamper-evidence. Snapshots cover broken updates; the digest covers swapped images. - Stateless → rolling tags (latest/stable); digest-pinning would defeat the rolling design. Integrity rests on official/verified images + disposability. Aligned across ADR-011 (decision 2), ADR-004 (image management), ADR-002 (supply-chain row), accepted-risk R1, the service checklist, and TODO 15.6. TODO 16.7 marked decided. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0e4050fa59
commit
2f4218814a
6 changed files with 26 additions and 19 deletions
14
docs/TODO.md
14
docs/TODO.md
|
|
@ -113,9 +113,9 @@
|
|||
4. Network IDS: enable Suricata on OPNsense (IDS first; IPS later?).
|
||||
5. Active security alerting: wire AIDE, `auditd`, `fail2ban`, and Suricata into
|
||||
the Loki/Grafana alerting stack (ties to 3.6).
|
||||
6. Supply-chain hygiene: enforce image digest pinning + official/verified images
|
||||
via the service checklist; revisit active scanning (Trivy/Grype) once a
|
||||
triage stack exists (accepted-risk R1).
|
||||
6. Supply-chain hygiene: enforce tiered image pinning (stateful `tag@digest`;
|
||||
stateless rolling tags — ADR-011) + official/verified images via the service
|
||||
checklist; revisit active scanning (Trivy/Grype) once a triage stack exists (R1).
|
||||
|
||||
16. **ADR-011 (update management) — resolve open questions + accept.** Committed as
|
||||
**Proposed**; resolve before marking Accepted:
|
||||
|
|
@ -129,7 +129,7 @@
|
|||
Friday timing enough at this scale?
|
||||
6. Notification/control channel — boma's own ntfy topics (ADR-013) + a "skip this
|
||||
week" / "pause" switch (ties to TODO 9).
|
||||
7. **Reconcile pinning conflict:** ADR-011 decision 2 chose *tags, not digests*
|
||||
(readability + snapshot/backup immutability), but the security work says
|
||||
*digest pinning* (accepted-risk R1, service checklist, 15.6 above). Decide one
|
||||
coherent rule (e.g. readable tag + recorded digest?) and align all of them.
|
||||
7. ~~Reconcile pinning conflict (tags vs digests).~~ DECIDED: tiered rule —
|
||||
**stateful `tag@digest`** (readable tag + integrity digest), **stateless
|
||||
rolling tags**. Aligned across ADR-011 (dec. 2), ADR-004, ADR-002 supply-chain
|
||||
row + accepted-risk R1, the service checklist, and 15.6.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ What we deliberately design against — and, just as importantly, what we do not
|
|||
| **Opportunistic external** — bots scanning, credential stuffing, mass-exploiting known CVEs in exposed services | Yes — primary | SSH key-only + fail2ban, deny-by-default firewall, security auto-patching, minimal attack surface, services behind a reverse proxy with auth |
|
||||
| **Lateral movement / blast radius** — assume one service *is* compromised; limit how far it spreads | Yes | VLAN segmentation (ADR-007), least-privilege containers, no host network mode, per-service isolation, no shared credentials |
|
||||
| **Operator / agent error** — accidental secret leak, misconfiguration, or an AI agent making an unsafe change | Yes | Vault + gitleaks, declarative firewall (no ad-hoc ports), review gates, agent guardrails (below), pre-commit hooks |
|
||||
| **Supply chain** — compromised images, base images, dependencies, collections | Acknowledged, lower priority | Baseline hygiene required: image digest pinning + prefer official/verified images (ADR-011, service checklist), gitleaks. Active vuln scanning deferred — accepted risk |
|
||||
| **Supply chain** — compromised images, base images, dependencies, collections | Acknowledged, lower priority | Baseline hygiene required: tiered image pinning (stateful `tag@digest`, stateless rolling — ADR-011) + prefer official/verified images, gitleaks. Active vuln scanning deferred — accepted risk |
|
||||
| **Targeted / physical** — a determined adversary specifically after this homelab, or physical device access | Out of scope | Not designed against at this scale; revisit if the threat model changes |
|
||||
|
||||
Supply chain is consciously deprioritized, not forgotten — see
|
||||
|
|
|
|||
|
|
@ -86,9 +86,12 @@ Managed by the `docker_host` role. Key settings:
|
|||
|
||||
## Image management
|
||||
|
||||
- Images are always pinned to a specific digest or tag in templates
|
||||
- `latest` is never used in production Compose files
|
||||
- Image updates are a deliberate operation: update the tag variable, run deploy
|
||||
- Image pinning follows the tiered model in ADR-011: **stateful** services pin
|
||||
`tag@digest` (readable tag + integrity digest); **stateless** services use rolling
|
||||
tags (`latest`/`stable`), refreshed deliberately and watched by DIUN
|
||||
- Bare `latest` is therefore acceptable only on the stateless tier; the stateful tier
|
||||
is always pinned
|
||||
- Image updates are a deliberate operation: update the tag/digest variable, run deploy
|
||||
|
||||
## Persistent data
|
||||
|
||||
|
|
|
|||
|
|
@ -27,13 +27,17 @@ Each container role declares its class, e.g. `<role>__stateful: true|false` (def
|
|||
### 2. Image pinning follows the split
|
||||
|
||||
- **Stateless → rolling tags** (`latest`/`stable`), refreshed by the weekly run and
|
||||
watched by DIUN. Always-current, cheap to roll back.
|
||||
- **Stateful → pinned** to a readable tag, **minor** where the image offers it
|
||||
(e.g. `mariadb:11.4`, not bare `:11` and not a digest). Reproducible; upgrades are
|
||||
deliberate, never incidental.
|
||||
watched by DIUN. Always-current, cheap to roll back. No digest pin — it would
|
||||
defeat the rolling design.
|
||||
- **Stateful → pinned `tag@digest`** — a readable **minor** tag where the image
|
||||
offers it (e.g. `mariadb:11.4`, not bare `:11`) **plus its digest**
|
||||
(`mariadb:11.4@sha256:…`). Reproducible and tamper-evident; upgrades are deliberate
|
||||
(bump tag and digest together), never incidental.
|
||||
|
||||
Tags, not digests — readable in diffs; immutability is bought instead via
|
||||
snapshot-before and backups.
|
||||
Readable tag **and** digest, not one or the other: the tag keeps diffs legible, the
|
||||
digest pins the exact bytes for supply-chain integrity (ADR-002, accepted-risk R1).
|
||||
Snapshot-before + backups remain the rollback mechanism for a *broken* update; the
|
||||
digest is what guards against a *swapped* image, which snapshots cannot.
|
||||
|
||||
### 3. Weekly OS + stateless run — Friday night, fail-stop, staggered
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ revisit (trigger).
|
|||
|
||||
| # | Accepted risk | Rationale | Revisit trigger |
|
||||
|---|---|---|---|
|
||||
| R1 | **Active supply-chain scanning deferred** — baseline hygiene *is* required (image digest pinning + prefer official/verified images, ADR-011 / service checklist; gitleaks), but images and dependencies are not actively vulnerability-scanned (Trivy/Grype) or signature-verified | Scanning only pays off with the capacity to triage its output; the realistic threat is opportunistic, not a targeted supply-chain attack | A monitoring/triage stack is live; hosting high-value data/finances for others; a relevant upstream compromise |
|
||||
| R1 | **Active supply-chain scanning deferred** — baseline hygiene *is* required (tiered image pinning per ADR-011 — stateful `tag@digest`, stateless rolling — prefer official/verified images; gitleaks), but images and dependencies are not actively vulnerability-scanned (Trivy/Grype) or signature-verified | Scanning only pays off with the capacity to triage its output; the realistic threat is opportunistic, not a targeted supply-chain attack | A monitoring/triage stack is live; hosting high-value data/finances for others; a relevant upstream compromise |
|
||||
| R2 | **SELinux not used** — no SELinux mandatory access control | AppArmor — Debian-native and enforced via the CIS baseline — already provides MAC; adding SELinux means two MAC systems, non-native to Debian, for no real gain | A service that ships and requires its own SELinux policy; threat model shifts toward targeted attackers |
|
||||
|
||||
_Last reviewed: 2026-06-04. The prior gaps (full CIS hardening, SELinux/AppArmor,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ This checklist is the generic **bar**. Each service answers it in its own
|
|||
|
||||
## Updates & provenance
|
||||
|
||||
- [ ] Image/source version is pinned (tag or digest), not floating `latest` (ADR-011)
|
||||
- [ ] Image pinned per ADR-011's tiered rule — stateful: `tag@digest`; stateless: rolling tag (`latest`/`stable`) acceptable
|
||||
- [ ] The update path is known — how this service gets patched
|
||||
|
||||
## Operability (security-adjacent)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue