Files
ops-warden/wiki/WorkloadSecurityPosture.md

140 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Workload Security Posture — NetKingdom standard (draft)
> **Status:** ops-warden-authored draft, WARDEN-WP-0015 T1. **Pending promotion to
> canon** along two homes (see *Canon layering*). Until landed, this file is the
> authoritative working draft; the canon copies supersede it once merged.
>
> **ops-warden's role:** *author + conformance*. ops-warden does **not** enforce this
> standard at runtime (flex-auth) and does **not** hold the secrets (OpenBao). It
> authors the ops-security slice and ships conformance checks + dev-tier doubles.
NetKingdom IT-security posture is defined along **two orthogonal axes**. A workload's
right to receive a secret depends on **both**, unified by a secret-flow lattice.
---
## Axis A — Environment posture (how the secret store is secured)
The lifecycle tier of the *secret store backing a workload*. Contracts are identical at
every tier (so automation and the `warden access` proxy run unchanged); only the
backend's security posture changes.
**R1 — Contract parity, posture divergence.** Identical interface at every tier; only
posture changes. This is why dev-tier contract doubles ("fake bao") work.
**R2 — Promote topology, regenerate material.** Secret *values* are never promoted up
the ladder; only *structure* (paths, policy shape, names). Values are generated fresh
per tier. Test conveniences (reuse, single-unseal) stay quarantined in test.
**R3 — Dev touches no real data, ever.** An insecure personal mock store in dev is
sanctioned *iff* dev uses only synthetic data. Absolute invariant.
**R4 — Phase-changes are ceremonies, not copies.** `test → prod` is a gated checklist
(regenerate secrets, switch unseal model, enable break-glass, human sign-off),
referencing the existing net-kingdom `security-bootstrap-*` and unseal-custody docs —
not duplicating them.
| | dev | test | prod |
| --- | --- | --- | --- |
| backend | mock / contract double | OpenBao `-dev` (single-unseal) | OpenBao sealed (Shamir 3-of-5) |
| real values | forbidden (synthetic) | generated, reuse allowed | generated fresh, reuse forbidden |
| unseal | n/a | single key / auto | 3-of-5 + break-glass |
| real user/business data | never | never | allowed |
| audit | optional | on | full, tamper-evident |
---
## Axis B — Workload maturity (how trusted a workload is)
**Production is a posture, not a maturity.** A workload can run in prod posture yet be
low maturity (alpha with friendly customers). Maturity gates *which secrets and data
classes* a prod workload may touch. Levels are a total order `M0 < M1 < M2 < M3`.
| Level | Phase | Max `DataClassification` it may handle | Promotion gate (into this level) |
| --- | --- | --- | --- |
| **M0** | Experimental / PoC | synthetic only | — (entry level) |
| **M1** | Alpha / early-access | low-criticality, loss-acceptable; **no** `confidential`/`restricted` | friendly-customer scope agreed, basic SLO, data-handling note |
| **M2** | Beta / GA | up to `confidential`; SLOs; audited | security review, SLO history, on-call, incident runbooks |
| **M3** | Critical / regulated | `restricted`; break-glass; compliance | pen-test, 3-of-5 custody, human-in-loop ops, compliance audit |
`DataClassification` (`confidential`, `restricted`, …) is **reused** from the
info-tech-canon Data Model — not redefined here. Promotion gates **reuse** the
info-tech-canon DevSecOps Model's quality/policy gates and `DeploymentVerification`
(SLOs / smoke / canary / operator confirmation), applied to maturity advancement.
---
## The combined rule — secret-flow lattice
A secret carries a `required_maturity` (and implicitly the `required_maturity` of its
`DataClassification`). Delivery is **no-write-down**:
```
deliver(secret → workload) is permitted only if
workload.env_posture == prod # Axis A
AND workload.maturity >= secret.required_maturity # Axis B
AND workload.maturity >= required_maturity(dataclass(secret)) # data class floor
```
**"Critical-infrastructure secrets must not be transferred to workloads below maturity
M"** is exactly the second clause. The lattice is **checkable** by ops-warden
(conformance) and **enforceable** at runtime by flex-auth. Access *semantics* (who, on
behalf of whom) remain governed by the CARING Access Governance Standard.
Worked example: an `NPM_AUTH_TOKEN` used only by a build pipeline → `required_maturity:
M1`, dataclass `internal`. A production database password for regulated user data →
`required_maturity: M3`, dataclass `restricted`; it may be delivered only to a
prod-posture, M3 workload.
---
## Using this to refine blockers
When a workstream says "blocked on security", classify it before escalating. The
classification decides whether the blocker is real, belongs to an owning subsystem, or
can be removed by a dev/test double.
| Question | Result |
| --- | --- |
| Is the work **dev** or **test** posture only? | Use synthetic contract doubles or generated test values. Do not wait on real production secrets. |
| Is the work **prod** posture with real values? | Require owner custody (usually OpenBao), flex-auth policy where applicable, and non-secret evidence only. |
| Is workload maturity below the secret's `required_maturity` or data-class floor? | This is a real IT-security blocker until the workload advances, the secret is reclassified, or the design avoids the secret. |
| Does a route exist and the lane is `exec_capable`? | `warden access --fetch/--exec` may remove operator copy/paste as a blocker by proxying the owner's tool as the caller. |
| Is unseal, break-glass, or issuer custody unresolved? | Keep it as an operator ceremony/design blocker; do not paper it over with agent-visible values. |
The evidence to record is route id, owner, env posture, workload maturity,
`required_maturity`, policy decision id, OpenBao path/version, populated-key count,
smoke id, or token accessor. Never record the secret value.
This is the practical bridge from WARDEN-WP-0014 (`warden access`) to WP-0015: access
assist can remove manual secret handling friction, while posture/maturity decides
whether the secret may flow at all.
---
## Canon layering (where each part lands)
| Part | Canonical home | ops-warden role |
| --- | --- | --- |
| Generic `WorkloadMaturityLevel` concept + the secret-flow lattice | **info-tech-canon** (DevSecOps / Landscape; reuses Data Model `DataClassification`, Security Model criticality) | Contribute; do not fork |
| NetKingdom M0M3 security **requirements** + env-posture ceremonies | **net-kingdom canon** (beside `openbao-unseal-custody-models.md`, `responsibility-map.md`) | Author the ops-security slice |
| Machine-readable descriptors + conformance checker + dev doubles | **ops-warden** (`registry/policy/`, `scripts/`) | Own (WP-0015 T2T4) |
| Runtime enforcement of the lattice | **flex-auth** | Route; do not enforce here |
---
## Boundaries preserved
- **OpenBao** holds secret values. ops-warden never custodies them.
- **flex-auth** decides allow/deny (incl. enforcing this lattice at runtime).
- **CARING / Access Control** governs access semantics and delegation.
- **key-cape** establishes identity. ops-warden authors the standard and *checks
conformance* — it does not become a broker, PDP, or IdP (responsibility-map).
---
## See also
- `wiki/OperatorAccessAssist.md` — the posture-aware `warden access` fetch surface
- `net-kingdom/docs/openbao-unseal-custody-models.md`, `responsibility-map.md`,
`platform-root-custody.md`, `security-bootstrap-*`
- info-tech-canon: Security Model, DevSecOps Model, Data Model, CARING Access Governance
- `workplans/WARDEN-WP-0015-secret-lifecycle-tiering.md`