DECISIONS-LOG records Phase 7 being opened knowingly while the uncovered set included R-ENV-01/02/03 — the very requirements prod provisioning serves. Two of those three are now covered, plus R-TLS-02. tls-lifecycle-automated R-TLS-02 L0 (in the merge gate) cert-lifecycle-automated R-TLS-02 L2 (authored, NOT executed) dev-nodes-provisioned-as-code R-ENV-01 L0 prod-baremetal-not-dev-surface R-ENV-03 L0 Coverage 42/51 (82.4%) -> 45/51 (88.2%), 6 gaps, 0 safety-critical. Remaining: R-C2-01, R-C2-02, R-NET-04, R-PLAT-01, R-SEC-03, R-STATE-04. Verified through the real runner: --layer l0 7/7 PASS, the three new ones 3/3 PASS, --strict-coverage exit 0 with 0 label/annotation mismatches. *** R-TLS-02 "REVOKED" IS NOT SATISFIED — A REAL GAP, NOT A TEST DEFECT. *** The spec requires certificates be "issued, renewed, AND REVOKED without manual steps", and docs/08 reads revocation as "removing/rotating a certificate is a declarative change". That needs enableCertificateOwnerRef: true; platform/cert-manager/helmrelease.yaml does not set it (chart default false). Verified read-only on BOTH live clusters: gateway/wildcard-home-tls has ZERO ownerReferences, so deleting the Certificate would leave the private key serving indefinitely. The L2 test asserts the requirement AS WRITTEN and will fail until this is decided. It was deliberately NOT softened — weakening a test to match the implementation is how a suite stops meaning anything. NOT FLIPPED UNATTENDED, and the reason is specific rather than caution: the wildcard Certificate lives under ./platform, which runs prune: true. With owner refs ON, any transient prune of that Certificate — exactly what happened to the CDI StorageProfiles hours ago — would delete the SERVING TLS Secret for every host, and reissuance is bounded by Let's Encrypt's duplicate-certificate limit (5/week per identical name set), which could leave a cluster with no trusted cert for a week. Security gap vs a plausible week-long TLS outage is an operator call. NON-VACUITY DEMONSTRATED, which is the part that makes these worth having: 7/7 perturbations fail for R-TLS-02, 8/8 for R-ENV-01, 13/13 for R-ENV-03, and each restored copy passes. Perturbations were applied to a throwaway copy; neither this repo nor the read-only spec repo was modified. TWO CHECKS INITIALLY PASSED WHEN PERTURBED and were rewritten — the exact degrade-to-no-op failure this repo keeps hitting. The first draft asked "is this file's DIRECTORY referenced by a kustomization", which a directory reference always satisfies, and keyed Secrets by name alone, so external-dns's copy of `cloudflare-api-token` satisfied cert-manager's reference. Now a recursive kustomize resource-graph walk from each Flux path, and Secrets keyed by (namespace, name). Both bite. SECURITY NOTE, recorded because this project has already leaked a password once: `ansible-inventory --list` PRINTS DECRYPTED VAULT VALUES — it emitted the prod become-password to stdout during exploration, since the vault passphrase file is mounted into the dev container. Both new checks use only --syntax-check and --list-hosts, and say so in a comment. Confirmed no plaintext value reached disk: vault.yml is still AES256-encrypted and the tree has no assigned value. WHAT IS NOT PROVEN, per requirement: R-TLS-02 "revoked" (above), and issuance + observed renewal await the unexecuted L2. R-ENV-01 cannot prove the running VMs came from this module (needs OpenTofu state) — it proves the four lifecycle stages exist as code and cover every dev node, and that the chain RESOLVES under real ansible-playbook. R-ENV-03 cannot prove the HARDWARE (the K8s API exposes no "am I virtual" fact; mini-PC/bare-metal is operator-attested) — it proves prod is AUTHORED as 3 directly-reached nodes with no IaC creating them, sharing dev's playbooks and roles, with no prod-only manifest, branch or role anywhere. VERIFIED BY ME: coverage delta and the 3/3 L0 pass through the real runner; the zero-ownerReferences finding on both clusters; that the new checks avoid ansible-inventory; that no credential material reached disk. Full CI gate PASS. Refs R-TLS-02, R-ENV-01, R-ENV-03, R-TEST-01..05. Co-Authored-By: Claude Opus 5 <[email protected]>
78 lines
4.7 KiB
YAML
78 lines
4.7 KiB
YAML
# =============================================================================
|
||
# L0 STATIC — prod is bare metal, and is not a development surface (R-ENV-03) [EXECUTABLE]
|
||
# Catalog: docs/14 L0 row ("Pre-apply, in CI") — runs WITHOUT a cluster.
|
||
# Spec: docs/00 R-ENV-03 ("prod is a 3× mini-PC bare-metal k3s cluster with local
|
||
# storage; it MUST NOT be an active development surface until the roadmap
|
||
# declares it ready"); docs/02 (prod = 3 physical nodes, local SSD/NVMe to
|
||
# Longhorn, "the *same* Ansible bootstrap runs; only the machine-creation step
|
||
# differs", "Never develop against prod"); roadmap/phases.md Phase 7.
|
||
#
|
||
# HOW A "MUST NOT" IS MADE TESTABLE — the same discipline as R-TEST-06's test: a
|
||
# governance rule is verified through the property that ENFORCES it, never by observing
|
||
# that nobody misbehaved today. Here that property is structural: prod receives the base
|
||
# configuration plus environment-scoped overrides and nothing else, from the same branch
|
||
# and the same Ansible code as dev, so there is no place to put prod-only work at all.
|
||
#
|
||
# ENVIRONMENT-AGNOSTIC: the prod inventory group is DERIVED (it is the group whose
|
||
# group_vars retargets the k3s roles away from the global default), the prod overlay is
|
||
# derived from CLUSTER_ENV, and the storage/replica expectations are compared against the
|
||
# node count that inventory resolves to. No hostname, address or environment name is
|
||
# written into the test.
|
||
#
|
||
# GIVEN the repository as git tracks it (plus the read-only spec repo when mounted),
|
||
# WHEN the prod inventory, overlay, IaC module and readiness record are inspected — and
|
||
# the shared provisioning chain is actually retargeted at prod with --list-hosts,
|
||
# THEN prod resolves to exactly three nodes reached directly on their own sshd; no IaC
|
||
# resource creates any of them (bare metal is imaged, not instantiated); prod's
|
||
# Longhorn path is node-local with one replica per node and the default
|
||
# StorageClass is the local driver; prod runs the same playbooks/roles as dev,
|
||
# retargeted only by variables the shared code already defines; the prod overlay
|
||
# carries only the base plus environment-scoped patches on the same Git ref dev
|
||
# tracks, with no prod-only manifest anywhere in the shared configuration; and prod
|
||
# artifacts exist only alongside the operator's recorded opening of the prod phase.
|
||
#
|
||
# STATUS: EXECUTABLE. `check.sh` is a pure offline assertion script (git, PyYAML, and —
|
||
# where present — ansible-playbook in --list-hosts mode, which opens no connection);
|
||
# conformance/run.sh --layer l0 and ci/l0.sh run it directly, and this Chainsaw Test
|
||
# wraps the SAME script.
|
||
#
|
||
# KNOWN, DELIBERATE GAPS (enumerated in check.sh's header; this test claims none of them):
|
||
# * THE HARDWARE. That the three machines are mini-PCs, are bare metal, and hold local
|
||
# SSD/NVMe is operator-attested (infra/PROD-STANDUP.md, PROD-PROVISIONING.md); the
|
||
# node-health/BIOS surveys that establish it write to gitignored artifacts, and the
|
||
# Kubernetes API exposes no "am I virtual" fact, so nothing here asserts it.
|
||
# * The LIVE prod cluster's shape (three Ready nodes, all control-plane+etcd, Longhorn
|
||
# disks at the local path) — cluster state, belonging to R-PLAT-01's still-uncovered
|
||
# HA test; asserting it here would also make this test environment-specific, since
|
||
# the same suite must pass against dev.
|
||
# * Git HISTORY: this is the working tree at HEAD.
|
||
# * Whether the roadmap SHOULD have opened prod — only that prod work is accompanied by
|
||
# the recorded declaration.
|
||
# =============================================================================
|
||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||
kind: Test
|
||
metadata:
|
||
name: prod-baremetal-not-dev-surface
|
||
labels:
|
||
conformance.home.k3s/R-ENV-03: "true"
|
||
annotations:
|
||
conformance.home.k3s/requirements: "R-ENV-03"
|
||
conformance.home.k3s/layer: "L0"
|
||
conformance.home.k3s/safety-critical: "false"
|
||
conformance.home.k3s/executable: "true"
|
||
conformance.home.k3s/intent: >-
|
||
Given the tracked tree, when prod's inventory, overlay, IaC and readiness record are
|
||
inspected, then prod is authored as three bare-metal nodes with node-local storage,
|
||
bootstrapped by dev's own code retargeted by variables, receiving only
|
||
environment-scoped overrides on the same branch — so there is nowhere to develop
|
||
against prod — and it exists only because the roadmap phase was opened.
|
||
spec:
|
||
steps:
|
||
- name: then-prod-is-bare-metal-and-carries-no-development-of-its-own
|
||
description: >-
|
||
Run the offline assertion script over the tracked tree (same script ci/l0.sh and
|
||
conformance/run.sh --layer l0 execute).
|
||
try:
|
||
- script:
|
||
content: bash ./check.sh
|