#!/usr/bin/env bash
# tests/octavia-pki/run-tests.sh -- harness for scripts/octavia-pki.sh (verify + reissue)
#
# WHAT THIS HARNESS IS FOR. The script asserts things the runbook only hoped for, so the
# assertions themselves must be proven capable of FAILING -- "a checker that cannot fail is
# not a gate". Every case below constructs a specific defect and requires the script to
# catch it. T1 is the non-zero floor: a CORRECT fixture must PASS, because a script that
# fails on everything is as useless as one that passes on everything.
#
# THE CASE THAT MATTERS MOST IS T2. F8 records that the runbook's heredoc paste hazard can
# yield a controller certificate with NO SANs while every command still prints OK, and that
# nothing in this repo asserted the SAN set. T2 is that assertion.
#
# T12 guards the opposite error: openssl prints IPv6 SANs EXPANDED AND UPPERCASE
# (`2602:F3E2:F02:11:0:0:0:57`) while the overlay carries the compressed form. A naive
# string compare would FAIL a CORRECT certificate. T12 proves we do not.
#
# Fixtures are thrown away. Nothing here touches the real ~/octavia-pki or the real repo:
# the script's OCTAVIA_PKI_HOME / OCTAVIA_PKI_REPO / OCTAVIA_PKI_THIS_HOST overrides exist
# for exactly this reason.
#
# T22..T36 cover `reissue`, and they are the ONLY sanctioned way to exercise a real mint:
# the fixtures are throwaway CAs under $TMPROOT, so a mint here signs nothing that matters.
# THE RULE THOSE CASES FOLLOW: assert the resulting ARTIFACT, never `verify`'s verdict. In
# the live (unarmed) posture A12/A13 RECORD rather than fail, so a `verify` PASS is
# perfectly compatible with the wrong-zone certificate reissue exists to replace -- taking
# PASS as proof would make this harness a checker that cannot fail.
set -uo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd -- "$SCRIPT_DIR/../.." && pwd)"
CHECK="$REPO/scripts/octavia-pki.sh"
P=0; F=0
ok() { P=$((P+1)); echo " [ok] $1"; }
no() { F=$((F+1)); echo "FAIL $1"; }
TMPROOT="$(mktemp -d)"
trap 'rm -rf "$TMPROOT"' EXIT
V4="10.12.4.57"; V6="2602:f3e2:f02:11::57"
KV4="10.12.4.50"
# Build a throwaway fixture: a fake repo (git-initialised so check-ignore works) plus a
# per-DC PKI workspace. $1=env name $2=site $3=CA label $4=SAN mode $5=dns base
# $6=caenc: encrypt the CONTROLLER CA key for real (default `no`)
# SAN mode: full | none | v4only | wrongv4
#
# caenc=yes exists for the reissue cases. The default fixture CA key is `-nodes`
# (unencrypted), which cannot distinguish a minter that handles the passphrase correctly
# from one that ignores it entirely -- openssl accepts `-passin` against an unencrypted key
# and simply never consults it. With caenc=yes the passphrase becomes load-bearing, so
# T31 can prove the WRONG one fails. Default `no` keeps every pre-existing case
# behaviourally identical.
mkfix() {
local name="$1" site="$2" label="$3" sanmode="$4" dnsbase="${5:-example}" caenc="${6:-no}"
local E="$TMPROOT/$name"; local R="$E/repo"; local W="$E/home/octavia-pki/$site"
mkdir -p "$R/overlays" "$R/creds-manifests" "$W/issuing-ca" "$W/controller-ca" "$W/controller"
printf 'headend fixturehost\n' > "$R/creds-manifests/host-identity"
# BOTH rules, mirroring the real repo's .gitignore: the overlay glob (F4) AND `*.tmp`,
# because `reissue` writes `<overlay>.tmp` before os.replace() and gates on that path
# being ignored too. The real repo covers it with a root `*.tmp` rule; a fixture carrying
# only the overlay glob would make the minter refuse for a reason the live tree does not
# have -- and T7/T30 still empty this file wholesale, so the F4 case keeps its teeth.
printf 'overlays/*octavia-pki.yaml\n*.tmp\n' > "$R/.gitignore"
git -C "$R" init -q 2>/dev/null; git -C "$R" add -A >/dev/null 2>&1 || true
cat > "$R/overlays/${site}-vips.yaml" <<YML
applications:
keystone:
options:
vip: "$KV4 10.12.8.50 10.12.12.50"
octavia:
options:
vip: "$V4 10.12.8.57 10.12.12.57 $V6 fd50:840e:74e2:220::57 fd50:840e:74e2:221::57"
YML
# two self-signed CAs, distinct trust domains
( cd "$W/issuing-ca"
openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-384 -sha384 -nodes \
-days 3650 -subj "/CN=${label} Omega Cloud Octavia Issuing CA/O=Neumatrix" \
-keyout issuing-ca.key.enc -out issuing-ca.cert.pem >/dev/null 2>&1
printf '0123456789012345678901234567890123456789012\n' > passphrase.txt
chmod 600 passphrase.txt issuing-ca.key.enc; chmod 644 issuing-ca.cert.pem )
( cd "$W/controller-ca"
# The passphrase is written FIRST because caenc=yes consumes it while making the key.
# 43 chars + LF = 44 bytes, matching what `openssl rand -base64 32 | tr -d '\n'` yields
# and what 1.0-GEN.b (and the reissue precondition) assert by length.
printf '0123456789012345678901234567890123456789012\n' > passphrase.txt
if [ "$caenc" = "yes" ]; then
# genpkey-then-req, which is the RUNBOOK's own shape (1.0-GEN.b). `openssl req` on
# OpenSSL 3.0.13 rejects a cipher option outright ("Unknown option or message digest:
# aes-256-cbc"), so the encryption has to happen at key-generation time.
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -aes-256-cbc \
-pass file:passphrase.txt -out controller-ca.key.enc >/dev/null 2>&1
openssl req -new -x509 -sha384 -key controller-ca.key.enc -passin file:passphrase.txt \
-days 3650 -subj "/CN=${label} Omega Cloud Octavia Controller CA/O=Neumatrix" \
-out controller-ca.cert.pem >/dev/null 2>&1
else
openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-384 -sha384 -nodes \
-days 3650 -subj "/CN=${label} Omega Cloud Octavia Controller CA/O=Neumatrix" \
-keyout controller-ca.key.enc -out controller-ca.cert.pem >/dev/null 2>&1
fi
chmod 600 passphrase.txt controller-ca.key.enc; chmod 644 controller-ca.cert.pem )
( cd "$W/controller"
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out controller.key >/dev/null 2>&1
chmod 600 controller.key
# CN TRACKS $dnsbase (2026-07-30). It used to be the literal `octavia-controller.example`
# regardless of the SAN zone, which made every fixture's CN wrong-by-construction and left
# the new A13 subject-CN assertion untestable in its PASSING direction. The live generator
# builds the CN and the two DNS SANs from the SAME paste (1.0-GEN.c), so a fixture whose
# CN and SANs disagree does not model any certificate this repo can actually produce.
{ printf '[req]\ndistinguished_name=dn\nreq_extensions=v3_req\nprompt=no\n\n[dn]\nCN=octavia-controller.%s\nO=Neumatrix\n\n[v3_req]\nkeyUsage=critical,digitalSignature,keyEncipherment\nextendedKeyUsage=clientAuth,serverAuth\n' "$dnsbase"
case "$sanmode" in
none) : ;; # F8's failure mode: NO subjectAltName at all
*) printf 'subjectAltName=@alt_names\n\n[alt_names]\nDNS.1=octavia-controller.%s\nDNS.2=octavia.%s\n' "$dnsbase" "$dnsbase" ;;
esac
case "$sanmode" in
full) printf 'IP.1 = %s\nIP.2 = %s\n' "$V4" "$V6" ;;
v4only) printf 'IP.1 = %s\n' "$V4" ;;
wrongv4) printf 'IP.1 = %s\nIP.2 = %s\n' "10.99.99.99" "$V6" ;;
esac
} > controller.cnf
openssl req -new -sha256 -key controller.key -config controller.cnf -out controller.csr >/dev/null 2>&1
# -passin is passed unconditionally: openssl ignores it on an unencrypted key, so one
# code path serves both caenc modes.
openssl x509 -req -sha256 -in controller.csr -CA ../controller-ca/controller-ca.cert.pem \
-CAkey ../controller-ca/controller-ca.key.enc -passin file:../controller-ca/passphrase.txt \
-CAcreateserial -days 730 \
-extfile controller.cnf -extensions v3_req -out controller.cert.pem >/dev/null 2>&1
# 600 on the certs AND the serial, matching the generator's E2 fix at source. The serial is
# created by -CAcreateserial at the inherited umask, so it must be tightened explicitly --
# exactly the omission that let the live material sit 664 with `verify` still reading 26/0.
chmod 600 controller.cert.pem ../controller-ca/controller-ca.cert.srl
cat controller.cert.pem controller.key > controller.bundle.pem; chmod 600 controller.bundle.pem )
# The deploy overlay: 5 keys, ASCII, 0600, gitignored -- modelled on 1.0-GEN.d, which
# writes four base64(PEM) values plus the issuing-CA passphrase as a PLAIN string.
# REAL VALUES (2026-07-30), not the former "QUJD" placeholders: reissue's surgery proof
# asserts that lb-mgmt-controller-cacert STILL decodes to the controller CA certificate
# after the rewrite, and that the new lb-mgmt-controller-cert decodes to the promoted
# bundle. A placeholder can satisfy neither, and a fixture that cannot model the real
# file cannot prove a gate against it.
{ echo "applications:"; echo " octavia:"; echo " options:"
echo " lb-mgmt-issuing-cacert: \"$(base64 -w0 < "$W/issuing-ca/issuing-ca.cert.pem")\""
echo " lb-mgmt-issuing-ca-private-key: \"$(base64 -w0 < "$W/issuing-ca/issuing-ca.key.enc")\""
echo " lb-mgmt-issuing-ca-key-passphrase: \"$(head -1 "$W/issuing-ca/passphrase.txt")\""
echo " lb-mgmt-controller-cacert: \"$(base64 -w0 < "$W/controller-ca/controller-ca.cert.pem")\""
echo " lb-mgmt-controller-cert: \"$(base64 -w0 < "$W/controller/controller.bundle.pem")\""
} > "$R/overlays/${site}-octavia-pki.yaml"
chmod 600 "$R/overlays/${site}-octavia-pki.yaml"
echo "$E"
}
run() { # run <env-dir> <site> [host] -> sets RC, OUT
local E="$1" site="$2" host="${3:-fixturehost}"
OUT="$(OCTAVIA_PKI_REPO="$E/repo" OCTAVIA_PKI_HOME="$E/home" OCTAVIA_PKI_THIS_HOST="$host" \
bash "$CHECK" verify "$site" 2>&1)"; RC=$?
}
# run_reissue <env-dir> <site> <host> [flags...] -> sets RC, OUT
# Mirrors run(), but the host is POSITIONAL AND REQUIRED rather than defaulted: reissue
# takes flags after the site, so a trailing optional host would be ambiguous with them, and
# T26's whole point is passing a wrong host deliberately.
run_reissue() {
local E="$1" site="$2" host="$3"; shift 3
OUT="$(OCTAVIA_PKI_REPO="$E/repo" OCTAVIA_PKI_HOME="$E/home" OCTAVIA_PKI_THIS_HOST="$host" \
bash "$CHECK" reissue "$site" "$@" 2>&1)"; RC=$?
}
# Digest the four things a reissue may change, so a case can assert that NOTHING moved.
# Files that do not exist hash as the literal "-", which is still a stable comparand.
sha_state() { # sha_state <env-dir> <site>
local E="$1" site="$2" f
for f in "$E/home/octavia-pki/$site/controller/controller.key" \
"$E/home/octavia-pki/$site/controller/controller.cert.pem" \
"$E/home/octavia-pki/$site/controller/controller.bundle.pem" \
"$E/repo/overlays/${site}-octavia-pki.yaml"; do
if [ -f "$f" ]; then sha256sum < "$f" | cut -d' ' -f1; else echo "-"; fi
done
}
# The CN and the DNS SAN list of a certificate, read DIRECTLY. The reissue cases assert on
# these rather than on `verify` PASS, because in the live (unarmed) posture A12/A13 record
# rather than fail -- so a PASS from verify is NOT evidence the names are right.
cert_cn() { openssl x509 -in "$1" -noout -subject -nameopt multiline 2>/dev/null | sed -n 's/^ *commonName *= *//p' | head -1; }
cert_dns() { openssl x509 -in "$1" -noout -ext subjectAltName 2>/dev/null | grep -oE 'DNS:[^,[:space:]]+' | sed 's/^DNS://' | sort | tr '\n' ' '; }
cert_ser() { openssl x509 -in "$1" -noout -serial 2>/dev/null | sed 's/^serial=//'; }
cert_pub() { openssl x509 -in "$1" -noout -pubkey 2>/dev/null | openssl pkey -pubin -pubout -outform DER 2>/dev/null | sha256sum | cut -d' ' -f1; }
echo "=== octavia-pki harness ==="
# T1 -- NON-ZERO FLOOR. A correct fixture must PASS, or nothing below proves anything.
E=$(mkfix good vr1-dc0 "VR1 DC0" full); run "$E" vr1-dc0
[ "$RC" = 0 ] && ok "T1 a correct PKI PASSES (non-zero floor)" \
|| { no "T1 baseline must pass (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T2 -- THE F8 CASE. No subjectAltName at all: the runbook's paste hazard output.
E=$(mkfix nosan vr1-dc0 "VR1 DC0" none); run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "NO subjectAltName" \
&& ok "T2 a cert with NO SANs FAILS -- the defect nothing previously asserted (F8)" \
|| { no "T2 must catch a SAN-less certificate (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T3 -- v6 SAN missing while the overlay declares a v6 provider leg (D-109 note 2026-07-29).
E=$(mkfix v4only vr1-dc0 "VR1 DC0" v4only); run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "MISSING this DC's provider v6" \
&& ok "T3 a v4-only SAN set FAILS when the overlay declares a v6 leg" \
|| { no "T3 must catch a missing v6 SAN (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T4 -- the SAN carries an address that is not this DC's VIP.
E=$(mkfix wrongip vr1-dc0 "VR1 DC0" wrongv4); run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "MISSING this DC's provider v4" \
&& ok "T4 a SAN naming the wrong v4 address FAILS" \
|| { no "T4 must catch a wrong v4 SAN (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T5 -- WRONG DC in the CA subject. This is the sibling of F8: the runbook guards ${DC:?}
# but NOT ${DC_LABEL}, so an unset label yields a DC-less CA with no error anywhere.
E=$(mkfix wronglabel vr1-dc0 "VR1 DC1" full); run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "A4 issuing CA subject does NOT contain" \
&& ok "T5 a CA subject naming the wrong DC FAILS" \
|| { no "T5 must catch a wrong-DC CA subject (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T6 -- E2: a group-writable CA certificate is a SUBSTITUTABLE TRUST ANCHOR. This is the
# finding the generating session dismissed as harmless, so it is pinned here.
E=$(mkfix gwcert vr1-dc0 "VR1 DC0" full)
chmod 664 "$E/home/octavia-pki/vr1-dc0/issuing-ca/issuing-ca.cert.pem"; run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "WRITABLE by group/other" \
&& ok "T6 a group-writable CA cert FAILS (E2)" \
|| { no "T6 must catch a group-writable CA cert (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T7 -- F4: the overlay carries CA key blobs plus a plaintext passphrase. If it is not
# gitignored, a CA private key is committable in a repo SEC-004 records as PUBLIC.
E=$(mkfix notignored vr1-dc0 "VR1 DC0" full)
: > "$E/repo/.gitignore"; run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "NOT gitignored" \
&& ok "T7 an un-gitignored overlay FAILS (F4)" \
|| { no "T7 must catch an un-gitignored overlay (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T8 -- a truncated overlay that still parses. 5 keys is the contract.
E=$(mkfix shortovl vr1-dc0 "VR1 DC0" full)
printf 'applications:\n octavia:\n options:\n lb-mgmt-issuing-cacert: "QUJD"\n' \
> "$E/repo/overlays/vr1-dc0-octavia-pki.yaml"
chmod 600 "$E/repo/overlays/vr1-dc0-octavia-pki.yaml"; run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "lb-mgmt-\* key(s), expected 5" \
&& ok "T8 an overlay with the wrong key count FAILS" \
|| { no "T8 must catch a short overlay (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T9 -- WRONG HOST must REFUSE, never report absence. The F6 lesson: measuring the wrong
# machine's filesystem and calling the result a verdict is worse than declining to look.
E=$(mkfix wronghost vr1-dc0 "VR1 DC0" full); run "$E" vr1-dc0 someotherhost
[ "$RC" = 3 ] && printf '%s' "$OUT" | grep -q "PKI lives on" \
&& ok "T9 a run on the wrong host REFUSES (exit 3), not FAIL and not PASS" \
|| { no "T9 wrong host must refuse (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T10 -- an ABSENT host binding must REFUSE rather than assume this host is the right one.
E=$(mkfix nobinding vr1-dc0 "VR1 DC0" full)
: > "$E/repo/creds-manifests/host-identity"; run "$E" vr1-dc0
[ "$RC" = 3 ] && printf '%s' "$OUT" | grep -q "no 'headend' binding" \
&& ok "T10 a missing headend binding REFUSES rather than guessing" \
|| { no "T10 missing binding must refuse (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T11 -- an absent workspace is a CONCLUSIVE observation, so FAIL (not REFUSE): we looked.
E=$(mkfix nowork vr1-dc0 "VR1 DC0" full)
rm -rf "$E/home/octavia-pki/vr1-dc0"; run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "has not been generated" \
&& ok "T11 an un-generated DC FAILS (looked, nothing there) rather than refusing" \
|| { no "T11 absent workspace must fail (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T12 -- R7's whole point: no cross-DC amphora root-of-trust. Before F1 the generator's
# paths were shared, so generating dc1 OVERWROTE dc0 and these were byte-identical.
E=$(mkfix indep vr1-dc0 "VR1 DC0" full)
mkdir -p "$E/home/octavia-pki/vr1-dc1"
cp -r "$E/home/octavia-pki/vr1-dc0/." "$E/home/octavia-pki/vr1-dc1/"
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "IDENTICAL to vr1-dc1" \
&& ok "T12 shared material between DCs FAILS -- the state F1 existed to prevent" \
|| { no "T12 must catch cross-DC shared material (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T13 -- THE FALSE-FAIL GUARD. openssl prints v6 SANs expanded+uppercase while the overlay
# carries the compressed form. T1 already covers a correct cert, so this asserts the
# normalisation explicitly: the v6 assertion must be REACHED and PASS, not skipped.
E=$(mkfix v6norm vr1-dc0 "VR1 DC0" full); run "$E" vr1-dc0
printf '%s' "$OUT" | grep -q "SAN carries this DC's provider v6 VIP" \
&& ok "T13 an expanded/uppercase v6 SAN is normalised and matches (no false FAIL)" \
|| { no "T13 v6 normalisation must match a correct cert"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T16 -- the CA SERIAL file is issuance STATE, not residue. If it is missing, the next
# `-CAcreateserial` starts a fresh sequence and the same CA can issue a DUPLICATE serial.
# It is declared in creds-matrix.tsv, so the gate must assert it too -- the register and the
# gate have to agree on what should exist, or one of them is lying.
E=$(mkfix noserial vr1-dc0 "VR1 DC0" full)
rm -f "$E/home/octavia-pki/vr1-dc0/controller-ca/controller-ca.cert.srl"; run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "controller-ca.cert.srl" \
&& ok "T16 a missing CA serial file FAILS -- duplicate-serial risk on the next reissue" \
|| { no "T16 must catch a missing CA serial (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T17 -- the build INTERMEDIATES must NOT be required. The generator now deletes them
# (E3 fix at source), so a workspace without them is CORRECT and must still PASS. Without
# this case, adding them to A2 later would silently make every clean workspace fail.
E=$(mkfix nointermediates vr1-dc0 "VR1 DC0" full)
rm -f "$E/home/octavia-pki/vr1-dc0/controller/controller.csr" \
"$E/home/octavia-pki/vr1-dc0/controller/controller.cnf"; run "$E" vr1-dc0
[ "$RC" = 0 ] && ok "T17 a workspace with the intermediates DELETED still PASSES (E3 fix at source)" \
|| { no "T17 deleted intermediates must not fail the gate (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T18 -- the CA SERIAL must be mode-checked too. This case exists because the first version of
# the script MISSED it: `verify` read 26/0 on live material while `creds-matrix.py` E2 reported
# the serial 664 on both DCs. Two gates that can both see a file must not disagree about it.
# The serial holds no secret, so the exposure is INTEGRITY -- rewriting it forces the next
# issuance to reuse a serial.
E=$(mkfix gwserial vr1-dc0 "VR1 DC0" full)
chmod 664 "$E/home/octavia-pki/vr1-dc0/controller-ca/controller-ca.cert.srl"; run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "controller-ca.cert.srl is 664" \
&& ok "T18 a group-writable CA serial FAILS -- duplicate-serial forcing (found by P5, not by this gate)" \
|| { no "T18 must catch a group-writable serial (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# --- A12: the DNS SANs, and the arming that makes F9 unmissable -------------------------
# F9's whole risk is that a future session forgets. So A12 arms itself from the change that
# makes the SANs matter (os-public-hostname appearing), rather than from a note.
# T19 -- UNARMED is the live posture: os-public-hostname set nowhere, so the DNS names resolve
# to nothing and must NOT fail the run. Without this case, arming A12 would have broken every
# current verify -- the same trap T17 guards for the deleted intermediates.
E=$(mkfix dnsinert vr1-dc0 "VR1 DC0" full); run "$E" vr1-dc0
[ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q "A12 DNS SANs are INERT" \
&& ok "T19 with os-public-hostname unset, DNS SANs are INERT and the run still PASSES" \
|| { no "T19 unarmed A12 must not fail the run (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T20 -- ARMED with a wrong-region name is F9 exactly: the live certs say `dc0.vr0` on BOTH
# VR1 DCs, so once hostnames are live the name belongs to another deployment.
E=$(mkfix dnsarmed vr1-dc0 "VR1 DC0" full "omega.dc0.vr0.cloud.neumatrix.local")
printf ' os-public-hostname: keystone.omega.dc0.vr0.cloud.neumatrix.local\n' \
>> "$E/repo/overlays/vr1-dc0-vips.yaml"
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "is not in this DC's expected zone 'omega.dc0.vr1.cloud.neumatrix.local'" \
&& ok "T20 ARMED + a wrong-region DNS SAN FAILS (F9, self-arming)" \
|| { no "T20 armed A12 must fail a wrong-region SAN (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T21 -- ARMED with the CORRECT zone now PASSES. ASSERTION REPLACED, NOT DELETED (DOCFIX-205):
# this case previously asserted a REFUSAL (rc=3, "DC-LABEL cannot be validated") on the grounds
# that D-008 + D-106's `dc1.vr1`/`dc2.vr1` left the per-DC label unruled. That premise was
# already dead -- D-117 (2026-07-13) retired `dc1`/`dc2` in favour of `dc0`/`dc1` and says so in
# its own Status line. The label is derivable, so the check asserts it instead of refusing.
E=$(mkfix dnsregionok vr1-dc0 "VR1 DC0" full "omega.dc0.vr1.cloud.neumatrix.local")
printf ' os-public-hostname: keystone.omega.dc0.vr1.cloud.neumatrix.local\n' \
>> "$E/repo/overlays/vr1-dc0-vips.yaml"
run "$E" vr1-dc0
[ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q "A12 DNS SANs are all in this DC's expected zone" \
&& ok "T21 ARMED + the D-117-correct zone PASSES (assertion replaces the former refusal)" \
|| { no "T21 armed A12 must pass the correct zone (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T21b -- the capability the refusal never had: a WRONG DC LABEL in the RIGHT region. This is
# the exact cross-DC mix-up F1 proved possible on the PKI paths (dc1's material under dc0's
# name). Region-only checking passed it; the derived-zone assertion catches it.
E=$(mkfix dnswrongdc vr1-dc0 "VR1 DC0" full "omega.dc1.vr1.cloud.neumatrix.local")
printf ' os-public-hostname: keystone.omega.dc1.vr1.cloud.neumatrix.local\n' \
>> "$E/repo/overlays/vr1-dc0-vips.yaml"
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q "is not in this DC's expected zone 'omega.dc0.vr1.cloud.neumatrix.local'" \
&& ok "T21b ARMED + the OTHER DC's label in the right region FAILS (cross-DC mix-up)" \
|| { no "T21b armed A12 must fail a wrong DC label (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T21c -- vr1-dc1 derives its OWN zone, so the check is not a dc0 constant wearing a variable.
E=$(mkfix dnsdc1ok vr1-dc1 "VR1 DC1" full "omega.dc1.vr1.cloud.neumatrix.local")
printf ' os-public-hostname: keystone.omega.dc1.vr1.cloud.neumatrix.local\n' \
>> "$E/repo/overlays/vr1-dc1-vips.yaml"
run "$E" vr1-dc1
[ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q "A12 DNS SANs are all in this DC's expected zone 'omega.dc1.vr1.cloud.neumatrix.local'" \
&& ok "T21c vr1-dc1 derives dc1.vr1, proving the zone is per-DC and not hardcoded" \
|| { no "T21c vr1-dc1 must derive its own zone (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T14 -- args. A bare `dcN` has been a live defect class; it must be REJECTED, not guessed.
RC=0; bash "$CHECK" verify dc0 >/dev/null 2>&1 || RC=$?
[ "$RC" = 2 ] && ok "T14 a bare 'dc0' is REJECTED (exit 2), never coerced" || no "T14 bare site must exit 2 (rc=$RC)"
RC=0; bash "$CHECK" generate vr1-dc0 >/dev/null 2>&1 || RC=$?
[ "$RC" = 2 ] && ok "T15 'generate' exits 2 -- unimplemented pending the D-137 fork-1 ruling" || no "T15 generate must exit 2 (rc=$RC)"
# =========================================================== reissue: the minting half ===
# EVERY CASE BELOW ASSERTS THE RESULTING ARTIFACT, NOT `verify`'s VERDICT. In the live
# (unarmed) posture A12 and A13 RECORD rather than fail, so a `verify` PASS is compatible
# with a certificate whose names are completely wrong -- which is precisely the certificate
# reissue exists to replace. A harness that accepted PASS as proof would be a checker that
# cannot fail, and this repo has measured that failure mode twice already.
DC0_ZONE="omega.dc0.vr1.cloud.neumatrix.local"
DC1_ZONE="omega.dc1.vr1.cloud.neumatrix.local"
FIXPASS="0123456789012345678901234567890123456789012"
# T22 -- POSITIVE FLOOR for the minter. The default fixture's names are `*.example`, so a
# reissue must land the DERIVED zone. Asserted by reading the certificate directly.
E=$(mkfix reissue_ok vr1-dc0 "VR1 DC0" full); CERT="$E/home/octavia-pki/vr1-dc0/controller/controller.cert.pem"
run_reissue "$E" vr1-dc0 fixturehost
GOTDNS="$(cert_dns "$CERT")"; GOTCN="$(cert_cn "$CERT")"
if [ "$RC" = 0 ] && [ "$GOTCN" = "octavia-controller.$DC0_ZONE" ] \
&& [ "$GOTDNS" = "octavia-controller.$DC0_ZONE octavia.$DC0_ZONE " ]; then
ok "T22 reissue MINTS the derived zone: CN + both DNS SANs read back correct from the cert itself"
else
no "T22 reissue must mint the derived zone (rc=$RC cn='$GOTCN' dns='$GOTDNS')"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T23 -- THE RE-RUN GUARD. A second reissue must REFUSE 4 and change NOTHING. The sha
# comparison is the real assertion: an implementation that rotated the key BEFORE evaluating
# the guard would still exit 4, and only the digests would show it.
BEFORE="$(sha_state "$E" vr1-dc0)"
run_reissue "$E" vr1-dc0 fixturehost
AFTER="$(sha_state "$E" vr1-dc0)"
if [ "$RC" = 4 ] && [ "$BEFORE" = "$AFTER" ]; then
ok "T23 a second reissue REFUSES 4 (already correct) and key/cert/bundle/overlay are ALL byte-unchanged"
else
no "T23 re-run guard must refuse 4 and change nothing (rc=$RC, state changed: $([ "$BEFORE" = "$AFTER" ] && echo no || echo YES))"
printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T24 -- --force overrides the guard and really re-mints. PUBKEY change is the load-bearing
# half: a new serial alone would be satisfied by re-certifying the SAME key.
OLDSER="$(cert_ser "$CERT")"; OLDPUB="$(cert_pub "$CERT")"
run_reissue "$E" vr1-dc0 fixturehost --force
NEWSER="$(cert_ser "$CERT")"; NEWPUB="$(cert_pub "$CERT")"
if [ "$RC" = 0 ] && [ "$OLDSER" != "$NEWSER" ] && [ "$OLDPUB" != "$NEWPUB" ]; then
ok "T24 --force reissues an already-correct cert: serial CHANGED and the public key CHANGED (a genuinely fresh key)"
else
no "T24 --force must re-mint with a fresh key (rc=$RC ser $OLDSER->$NEWSER pubchanged=$([ "$OLDPUB" != "$NEWPUB" ] && echo yes || echo NO))"
printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T25 -- VACUOUS TRUTH. A cert with ZERO DNS SANs is the WORST case in the estate (F8's
# output). A naive guard walks the SAN list, finds nothing wrong because there is nothing
# there, and REFUSES to fix it. This case pins that reissue proceeds.
E=$(mkfix reissue_nosan vr1-dc0 "VR1 DC0" none); CERT="$E/home/octavia-pki/vr1-dc0/controller/controller.cert.pem"
run_reissue "$E" vr1-dc0 fixturehost
if [ "$RC" = 0 ] && [ "$(cert_dns "$CERT")" = "octavia-controller.$DC0_ZONE octavia.$DC0_ZONE " ]; then
ok "T25 a SAN-LESS cert is REISSUED (rc 0), not refused as 'already correct' -- the vacuous-truth guard holds"
else
no "T25 a SAN-less cert must be reissued, not refused (rc=$RC dns='$(cert_dns "$CERT")')"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T26 -- WRONG HOST. Host authority runs FIRST, before any file or directory is created, so
# the assertion is not merely rc 3 but that the filesystem is untouched -- no archive, no
# staging dir, nothing newer anywhere. Repeated with --force, which must NOT bypass it.
E=$(mkfix reissue_wronghost vr1-dc0 "VR1 DC0" full)
MARK="$TMPROOT/reissue_wronghost.mark"; : > "$MARK"; sleep 1
run_reissue "$E" vr1-dc0 someotherhost
NEWER1="$(find "$E" -newer "$MARK" 2>/dev/null | wc -l | tr -d ' ')"; RC1=$RC
run_reissue "$E" vr1-dc0 someotherhost --force
NEWER2="$(find "$E" -newer "$MARK" 2>/dev/null | wc -l | tr -d ' ')"; RC2=$RC
if [ "$RC1" = 3 ] && [ "$RC2" = 3 ] && [ "$NEWER1" = 0 ] && [ "$NEWER2" = 0 ]; then
ok "T26 a run on the wrong host REFUSES 3 and creates NOTHING (no archive, no staging) -- and --force does not bypass it"
else
no "T26 wrong host must refuse 3 with nothing created (rc=$RC1/$RC2 newer=$NEWER1/$NEWER2)"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T27 -- the CA serial file is issuance STATE. Without it this minter cannot continue the
# CA's sequence, and -CAcreateserial would silently start a new random one (duplicate-serial
# risk). It must stop BEFORE minting, with the workspace untouched.
E=$(mkfix reissue_noserial vr1-dc0 "VR1 DC0" full)
rm -f "$E/home/octavia-pki/vr1-dc0/controller-ca/controller-ca.cert.srl"
BEFORE="$(sha_state "$E" vr1-dc0)"; run_reissue "$E" vr1-dc0 fixturehost; AFTER="$(sha_state "$E" vr1-dc0)"
# The message asserted here comes from the ARTIFACT-SET check (P2), not the dedicated serial
# gate (P5): under the ruled precondition ordering P2 runs first and catches the same
# absence. P5 is kept anyway so a future reader finds the -CAserial reasoning stated where
# they will look for it. The assertion is on the file being NAMED, which holds either way.
if [ "$RC" != 0 ] && [ "$BEFORE" = "$AFTER" ] && printf '%s' "$OUT" | grep -q "controller-ca.cert.srl"; then
ok "T27 a missing CA serial STOPS the reissue (rc=$RC) with nothing minted or changed"
else
no "T27 missing serial must stop the mint (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T28 -- the serial is genuinely CONSUMED from the CA's own sequence.
# MEASURED (OpenSSL 3.0.13): -CAserial issues with the INCREMENTED value and writes that
# same value back, so the new certificate's serial equals the POST-mint file content, NOT
# the pre-mint content. The brief's suggested "new == pre-mint .srl" equality is therefore
# FALSE on this build and is deliberately NOT shipped; the two inequality gates below are
# what actually holds -- and they are what matters, since the defect being guarded is a
# reused or unrecorded serial.
E=$(mkfix reissue_serial vr1-dc0 "VR1 DC0" full)
CERT="$E/home/octavia-pki/vr1-dc0/controller/controller.cert.pem"
SRLF="$E/home/octavia-pki/vr1-dc0/controller-ca/controller-ca.cert.srl"
OLDSER="$(cert_ser "$CERT")"; SRL_PRE="$(tr -d ' \t\r\n' < "$SRLF")"
run_reissue "$E" vr1-dc0 fixturehost
NEWSER="$(cert_ser "$CERT")"; SRL_POST="$(tr -d ' \t\r\n' < "$SRLF")"
ADV="$(python3 -c 'import sys;print("YES" if int(sys.argv[2],16)>int(sys.argv[1],16) else "NO")' "$SRL_PRE" "$SRL_POST" 2>/dev/null || echo NO)"
if [ "$RC" = 0 ] && [ "$OLDSER" != "$NEWSER" ] && [ "$ADV" = "YES" ]; then
ok "T28 the new serial DIFFERS from the old and the CA serial file ADVANCED ($SRL_PRE -> $SRL_POST)"
else
no "T28 serial must be consumed from the CA sequence (rc=$RC old=$OLDSER new=$NEWSER srl $SRL_PRE->$SRL_POST adv=$ADV)"
printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T29 -- OVERLAY SURGERY. Exactly one value may move. The other four carry CA private-key
# blobs and the issuing-CA passphrase; a rewrite that disturbed them would be a silent
# credential corruption that no other gate reads.
E=$(mkfix reissue_surgery vr1-dc0 "VR1 DC0" full)
OVL="$E/repo/overlays/vr1-dc0-octavia-pki.yaml"; cp "$OVL" "$TMPROOT/ovl.pre"
run_reissue "$E" vr1-dc0 fixturehost
SURG="$(python3 - "$TMPROOT/ovl.pre" "$OVL" "$E/home/octavia-pki/vr1-dc0/controller/controller.bundle.pem" \
"$E/home/octavia-pki/vr1-dc0/controller-ca/controller-ca.cert.pem" <<'PY' 2>&1
import base64, hashlib, sys
pre = open(sys.argv[1]).read().splitlines(True)
post = open(sys.argv[2]).read().splitlines(True)
if len(pre) != len(post): print("FAIL linecount"); sys.exit()
ch = [i for i in range(len(pre)) if pre[i] != post[i]]
if len(ch) != 1: print("FAIL changed=%d" % len(ch)); sys.exit()
def kv(ls):
d = {}
for l in ls:
s = l.strip()
if s.startswith("lb-mgmt-") and ":" in s:
k, v = s.split(":", 1); d[k.strip()] = v.strip().strip('"')
return d
a, b = kv(pre), kv(post)
if set(a) != set(b): print("FAIL keyset"); sys.exit()
for k in a:
if k != "lb-mgmt-controller-cert" and a[k] != b[k]: print("FAIL other " + k); sys.exit()
if a["lb-mgmt-controller-cert"] == b["lb-mgmt-controller-cert"]: print("FAIL value unchanged"); sys.exit()
sha = lambda x: hashlib.sha256(x).hexdigest()
if sha(base64.b64decode(b["lb-mgmt-controller-cert"])) != sha(open(sys.argv[3], "rb").read()):
print("FAIL decode!=bundle"); sys.exit()
if sha(base64.b64decode(b["lb-mgmt-controller-cacert"])) != sha(open(sys.argv[4], "rb").read()):
print("FAIL cacert"); sys.exit()
print("OK")
PY
)"
if [ "$RC" = 0 ] && [ "$SURG" = "OK" ]; then
ok "T29 surgery changes EXACTLY ONE line: other four values byte-identical, line count preserved, new value decodes to the bundle, cacert still decodes to the controller CA"
else
no "T29 overlay surgery must change exactly one value (rc=$RC surg=$SURG)"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T30 -- F4 TIMING, the case that decides whether the F4 gate is worth anything. An
# un-gitignored overlay must stop the run BEFORE the mint, not after: learning it afterwards
# means a CA private key is committable AND the workspace has already moved.
E=$(mkfix reissue_f4 vr1-dc0 "VR1 DC0" full)
: > "$E/repo/.gitignore"
BEFORE="$(sha_state "$E" vr1-dc0)"; run_reissue "$E" vr1-dc0 fixturehost; AFTER="$(sha_state "$E" vr1-dc0)"
NBK="$(find "$E/home/octavia-pki/backups" -type f 2>/dev/null | wc -l | tr -d ' ')"
if [ "$RC" = 1 ] && [ "$BEFORE" = "$AFTER" ] && [ "$NBK" = 0 ] && printf '%s' "$OUT" | grep -q "NOT gitignored"; then
ok "T30 an un-gitignored overlay FAILS 1 BEFORE the mint -- nothing minted, nothing changed, not even a backup taken (F4 timing)"
else
no "T30 F4 must stop the run before minting (rc=$RC changed=$([ "$BEFORE" = "$AFTER" ] && echo no || echo YES) backups=$NBK)"
printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T31 -- WRONG CA PASSPHRASE, against a REAL encrypted CA key (caenc=yes). Without a real
# encryption this case cannot exist: openssl accepts -passin on an unencrypted key and never
# consults it, so a minter that mishandled the passphrase would pass silently.
E=$(mkfix reissue_badpass vr1-dc0 "VR1 DC0" full example yes)
printf 'wrongwrongwrongwrongwrongwrongwrongwrongwro\n' > "$E/home/octavia-pki/vr1-dc0/controller-ca/passphrase.txt"
chmod 600 "$E/home/octavia-pki/vr1-dc0/controller-ca/passphrase.txt"
BEFORE="$(sha_state "$E" vr1-dc0)"; run_reissue "$E" vr1-dc0 fixturehost; AFTER="$(sha_state "$E" vr1-dc0)"
NSTG="$(find "$E/home/octavia-pki" -maxdepth 1 -name '.reissue-*' 2>/dev/null | wc -l | tr -d ' ')"
if [ "$RC" = 1 ] && [ "$BEFORE" = "$AFTER" ] && [ "$NSTG" = 0 ] && printf '%s' "$OUT" | grep -q "REFUSED to sign"; then
ok "T31 a WRONG CA passphrase FAILS 1 against a really-encrypted CA key: nothing promoted, no staging left behind"
else
no "T31 wrong passphrase must fail with nothing promoted (rc=$RC changed=$([ "$BEFORE" = "$AFTER" ] && echo no || echo YES) staging=$NSTG)"
printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T32 -- VIP plausibility, both verdicts. A VIP that PARSES but sits outside this DC's
# provider prefix is a measured defect (FAIL 1); a VIP that cannot be read at all is a
# refusal to look (REFUSE 3). Conflating the two is what "could not look is never nothing
# there" exists to prevent.
E=$(mkfix reissue_vipbad vr1-dc0 "VR1 DC0" full)
sed -i 's/vip: "10.12.4.50 /vip: "10.99.0.50 /' "$E/repo/overlays/vr1-dc0-vips.yaml"
BEFORE="$(sha_state "$E" vr1-dc0)"; run_reissue "$E" vr1-dc0 fixturehost; AFTER="$(sha_state "$E" vr1-dc0)"
RC_OUT=$RC; OK_OUT=0
[ "$RC_OUT" = 1 ] && [ "$BEFORE" = "$AFTER" ] && printf '%s' "$OUT" | grep -q "OUTSIDE keystone's provider prefix" && OK_OUT=1
E=$(mkfix reissue_vipgone vr1-dc0 "VR1 DC0" full)
printf 'applications:\n keystone:\n options:\n vip: "10.12.4.50"\n' > "$E/repo/overlays/vr1-dc0-vips.yaml"
run_reissue "$E" vr1-dc0 fixturehost
if [ "$OK_OUT" = 1 ] && [ "$RC" = 3 ]; then
ok "T32 a VIP outside this DC's prefix FAILS 1 (measured defect); an unreadable octavia VIP block REFUSES 3 (could not look)"
else
no "T32 VIP gates must split FAIL vs REFUSE (outside rc=$RC_OUT ok=$OK_OUT, missing rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T33 -- vr1-dc1 derives its OWN zone through the MINTER, not just the checker. Proves the
# names are computed per-DC rather than being a dc0 constant wearing a variable.
E=$(mkfix reissue_dc1 vr1-dc1 "VR1 DC1" full); CERT="$E/home/octavia-pki/vr1-dc1/controller/controller.cert.pem"
run_reissue "$E" vr1-dc1 fixturehost
if [ "$RC" = 0 ] && [ "$(cert_cn "$CERT")" = "octavia-controller.$DC1_ZONE" ] \
&& [ "$(cert_dns "$CERT")" = "octavia-controller.$DC1_ZONE octavia.$DC1_ZONE " ]; then
ok "T33 reissuing vr1-dc1 mints dc1.vr1 names -- the minter derives per-DC, it does not carry a dc0 constant"
else
no "T33 vr1-dc1 must mint its own zone (rc=$RC cn='$(cert_cn "$CERT")')"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T34 -- --dry-run must build and ASSERT the config, print it, and mint nothing at all.
E=$(mkfix reissue_dry vr1-dc0 "VR1 DC0" full)
BEFORE="$(sha_state "$E" vr1-dc0)"; run_reissue "$E" vr1-dc0 fixturehost --dry-run; AFTER="$(sha_state "$E" vr1-dc0)"
NBK="$(find "$E/home/octavia-pki/backups" -type f 2>/dev/null | wc -l | tr -d ' ')"
NSTG="$(find "$E/home/octavia-pki" -maxdepth 1 -name '.reissue-*' 2>/dev/null | wc -l | tr -d ' ')"
if [ "$RC" = 0 ] && [ "$BEFORE" = "$AFTER" ] && [ "$NBK" = 0 ] && [ "$NSTG" = 0 ] \
&& printf '%s' "$OUT" | grep -q "subjectAltName = @alt_names" \
&& printf '%s' "$OUT" | grep -q "DNS.1 = octavia-controller.$DC0_ZONE"; then
ok "T34 --dry-run prints the asserted config and mints NOTHING (no key, no cert, no backup, no staging)"
else
no "T34 --dry-run must mint nothing (rc=$RC changed=$([ "$BEFORE" = "$AFTER" ] && echo no || echo YES) backups=$NBK staging=$NSTG)"
printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T35 -- NO LEAK. The script's own header claims it never prints key material; this is that
# claim in executable form, over the FULL stdout+stderr of a SUCCESSFUL run (the run that
# handles the most secret material). Three probes: PEM headers, the fixture's CA
# passphrase, and any long run of base64-alphabet characters.
# The 64-hex exclusion is deliberate and narrow: a sha256 digest is exactly 64 characters
# drawn from the base64 alphabet, and the archive's digest is printed ON PURPOSE (it is how
# an operator identifies the backup). Excluding ONLY /^[0-9a-f]{64}$/ keeps the probe's
# teeth -- a base64 PEM body line is 64 chars but is not pure lowercase hex, and the overlay
# value is thousands of characters long.
E=$(mkfix reissue_leak vr1-dc0 "VR1 DC0" full example yes)
run_reissue "$E" vr1-dc0 fixturehost
LEAK=""
printf '%s' "$OUT" | grep -q -- "-----BEGIN" && LEAK="$LEAK PEM-header"
printf '%s' "$OUT" | grep -qF "$FIXPASS" && LEAK="$LEAK passphrase"
B64="$(printf '%s' "$OUT" | grep -Eo '[A-Za-z0-9+/=]{64,}' | grep -vE '^[0-9a-f]{64}$' | head -1 || true)"
[ -n "$B64" ] && LEAK="$LEAK base64-run"
if [ "$RC" = 0 ] && [ -z "$LEAK" ]; then
ok "T35 a SUCCESSFUL reissue leaks nothing: no PEM header, no passphrase, no long base64 run in stdout+stderr"
else
no "T35 reissue output must carry no key material (rc=$RC leaks:$LEAK)"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
# T36 -- EXIT 5, the PARTIAL code. Every other exit path is reachable by a bad input; this
# one only happens when promotion itself fails part way, and an untested rollback path is a
# rollback path nobody has ever seen work. Forced by making the controller directory
# unwritable AFTER every precondition has passed, so the run gets all the way to `mv`.
# The assertion that matters is that the operator is handed a RESTORE COMMAND, not just a
# number: exit 5 means "something moved and you must put it back".
if [ "$(id -u)" = 0 ]; then
ok "T36 SKIPPED (running as root: directory permissions cannot force a promotion failure)"
else
E=$(mkfix reissue_partial vr1-dc0 "VR1 DC0" full)
chmod 555 "$E/home/octavia-pki/vr1-dc0/controller"
run_reissue "$E" vr1-dc0 fixturehost
chmod 755 "$E/home/octavia-pki/vr1-dc0/controller"
if [ "$RC" = 5 ] && printf '%s' "$OUT" | grep -q "PARTIAL" && printf '%s' "$OUT" | grep -q "restore: tar xzf"; then
ok "T36 a promotion that fails part way exits 5 (PARTIAL) and prints the exact restore command"
else
no "T36 a failed promotion must exit 5 with a restore command (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'
fi
fi
# =====================================================================================
# T37..T43 -- THE ASSERTIONS MUTATION TESTING PROVED COULD NOT FAIL (added 2026-07-30)
#
# WHY THESE EXIST, stated plainly because it is the lesson: on 2026-07-30 a mutation pass
# broke or DELETED A13, A14 and A12's inert positive one at a time and the harness stayed
# at 38/38 PASS every time. Assertions with no failing-direction fixture are decoration --
# "a checker that cannot fail is not a gate" applies to the harness itself, not just to the
# script. Each case below breaks exactly one property and requires the named assertion to
# catch it. Two of them (T37, T38) pin properties that had NO assertion at all until today,
# and both were reachable by dropping a single token from the runbook's hand-pasted mint.
#
# resign(): re-issue an existing fixture's controller cert with a custom [v3_req] body and
# lifetime, reusing that fixture's CA. This is how a defect is INTRODUCED into an otherwise
# healthy workspace, so each case isolates one property.
resign() { # resign <env> <site> <v3_req-body> <days> [keyfile]
local E="$1" site="$2" body="$3" days="$4" keyf="${5:-controller.key}"
local W="$E/home/octavia-pki/$site"
( cd "$W/controller" || exit 1
{ printf '[req]\ndistinguished_name=dn\nreq_extensions=v3_req\nprompt=no\n\n[dn]\nCN=octavia-controller.%s\nO=Neumatrix\n\n[v3_req]\n' "example"
printf '%s\n' "$body"
printf '\n[alt_names]\nDNS.1=octavia-controller.example\nDNS.2=octavia.example\nIP.1 = %s\nIP.2 = %s\n' "$V4" "$V6"
} > re.cnf
openssl req -new -sha256 -key "$keyf" -config re.cnf -out re.csr >/dev/null 2>&1
openssl x509 -req -sha256 -in re.csr -CA ../controller-ca/controller-ca.cert.pem \
-CAkey ../controller-ca/controller-ca.key.enc -passin file:../controller-ca/passphrase.txt \
-CAcreateserial -days "$days" -extfile re.cnf -extensions v3_req \
-out controller.cert.pem >/dev/null 2>&1
chmod 600 controller.cert.pem
cat controller.cert.pem "$keyf" > controller.bundle.pem; chmod 600 controller.bundle.pem
rm -f re.cnf re.csr )
}
# T37 -- A15. A certificate with NO keyUsage and NO extendedKeyUsage. MEASURED 2026-07-30:
# before A15 this minted, promoted and left the harness at 38/38. Losing clientAuth breaks
# amphora->controller mTLS ONLY, so it fails at LB-create time with every gate reading PASS.
E=$(mkfix noeku vr1-dc0 "VR1 DC0" full)
resign "$E" vr1-dc0 'subjectAltName=@alt_names' 730
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'A15 the controller cert carries NEITHER' \
&& ok "T37 a cert with NO keyUsage and NO EKU FAILS (A15) -- the mTLS break that read PASS until today" \
|| { no "T37 A15 must fail a cert with no usage extensions (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T37b -- A15, the PARTIAL loss, which is the nastier one: serverAuth present, clientAuth
# gone. TLS works in one direction, so a smoke test that only talks TO the controller passes.
E=$(mkfix noclientauth vr1-dc0 "VR1 DC0" full)
resign "$E" vr1-dc0 'keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=@alt_names' 730
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'clientAuth' \
&& ok "T37b a cert missing ONLY clientAuth FAILS (A15) -- one-directional mTLS, the hardest shape to debug" \
|| { no "T37b A15 must fail a partial EKU set (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T38 -- A16. THE HEADLINE HOLE: `openssl x509 -req` defaults to -days 30. Dropping that one
# token from the runbook's hand-pasted block yields a 30-day controller certificate that
# passed every gate in this repo FOREVER -- `checkend`/`notAfter`/`enddate` appeared zero
# times in the verify half until A16.
E=$(mkfix shortdated vr1-dc0 "VR1 DC0" full)
resign "$E" vr1-dc0 'keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=clientAuth,serverAuth
subjectAltName=@alt_names' 20
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'A16 the controller cert expires within 30 days' \
&& ok "T38 a short-dated (20-day) cert FAILS (A16) -- what a missing '-days 730' actually produces" \
|| { no "T38 A16 must fail a short-dated cert (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T39 -- A17. The overlay is what reaches the charm; the workspace is only where it was made.
# MEASURED: an overlay whose embedded cert decoded to a DIFFERENT bundle reported PASS 34/0.
# This is reachable by a SIGKILL between promotion and surgery, by a half-completed restore,
# and by 1.0-GEN.d's cwd-relative reads (F12).
E=$(mkfix ovldesync vr1-dc0 "VR1 DC0" full)
OTHERB="$(mkfix ovldesync2 vr1-dc1 "VR1 DC1" full)"
python3 - "$E/repo/overlays/vr1-dc0-octavia-pki.yaml" \
"$OTHERB/home/octavia-pki/vr1-dc1/controller/controller.bundle.pem" <<'PY'
import base64, sys
p, other = sys.argv[1], sys.argv[2]
v = base64.b64encode(open(other, "rb").read()).decode()
out = []
for l in open(p):
if l.lstrip().startswith("lb-mgmt-controller-cert:"):
out.append(' lb-mgmt-controller-cert: "%s"\n' % v)
else:
out.append(l)
open(p, "w").writelines(out)
PY
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'A17 the overlay.*does NOT decode to controller.bundle.pem' \
&& ok "T39 an overlay carrying a DIFFERENT bundle FAILS (A17) -- workspace and deploy input can no longer silently disagree" \
|| { no "T39 A17 must fail an overlay/workspace desync (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T39b -- A17, the EMPTY value. A truncated write leaves the KEY present, so A10's count still
# reads 5 and every shape check passes. GEN.d has no non-empty guard on its five captures.
E=$(mkfix ovlempty vr1-dc0 "VR1 DC0" full)
sed -i 's|^ lb-mgmt-controller-cert: .*| lb-mgmt-controller-cert: ""|' \
"$E/repo/overlays/vr1-dc0-octavia-pki.yaml"
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'A17 an overlay value is EMPTY' \
&& ok "T39b an EMPTY overlay value FAILS (A17) -- A10's 5-key count cannot see it" \
|| { no "T39b A17 must fail an empty overlay value (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T40 -- A14's FAILING direction. Mutation testing deleted A14 wholesale and the harness
# stayed green: it had no negative fixture at all. A cert issued for a DIFFERENT key is the
# state a partial promotion leaves (2 of 3 files moved).
E=$(mkfix keymismatch vr1-dc0 "VR1 DC0" full)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \
-out "$E/home/octavia-pki/vr1-dc0/controller/controller.key" >/dev/null 2>&1
chmod 600 "$E/home/octavia-pki/vr1-dc0/controller/controller.key"
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'A14 controller.key does NOT match' \
&& ok "T40 a key that does not match the cert FAILS (A14) -- the partial-promotion state" \
|| { no "T40 A14 must fail a cert/key mismatch (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T41 -- A13's FAILING direction, armed. Mutation testing showed A13 was pinned only in its
# PASSING direction (as collateral of T21c). Correct SANs, wrong CN: the exact half-fix an
# implementer produces by editing [alt_names] and forgetting [req_distinguished_name], which
# are different sections of the same config.
E=$(mkfix cnwrong vr1-dc0 "VR1 DC0" full "omega.dc0.vr1.cloud.neumatrix.local")
( cd "$E/home/octavia-pki/vr1-dc0/controller" || exit 1
{ printf '[req]\ndistinguished_name=dn\nreq_extensions=v3_req\nprompt=no\n\n[dn]\nCN=octavia-controller.omega.dc0.vr0.cloud.neumatrix.local\nO=Neumatrix\n\n[v3_req]\nkeyUsage=critical,digitalSignature,keyEncipherment\nextendedKeyUsage=clientAuth,serverAuth\nsubjectAltName=@alt_names\n\n[alt_names]\nDNS.1=octavia-controller.omega.dc0.vr1.cloud.neumatrix.local\nDNS.2=octavia.omega.dc0.vr1.cloud.neumatrix.local\nIP.1 = %s\nIP.2 = %s\n' "$V4" "$V6"
} > cn.cnf
openssl req -new -sha256 -key controller.key -config cn.cnf -out cn.csr >/dev/null 2>&1
openssl x509 -req -sha256 -in cn.csr -CA ../controller-ca/controller-ca.cert.pem \
-CAkey ../controller-ca/controller-ca.key.enc -passin file:../controller-ca/passphrase.txt \
-CAcreateserial -days 730 -extfile cn.cnf -extensions v3_req -out controller.cert.pem >/dev/null 2>&1
chmod 600 controller.cert.pem
cat controller.cert.pem controller.key > controller.bundle.pem; chmod 600 controller.bundle.pem
rm -f cn.cnf cn.csr )
printf ' os-public-hostname: keystone.omega.dc0.vr1.cloud.neumatrix.local\n' \
>> "$E/repo/overlays/vr1-dc0-vips.yaml"
run "$E" vr1-dc0
[ "$RC" = 1 ] && printf '%s' "$OUT" | grep -q 'A13 os-public-hostname IS SET' \
&& ok "T41 ARMED + correct SANs but WRONG CN FAILS (A13) -- the half-fix, now pinned in its failing direction" \
|| { no "T41 A13 must fail a wrong CN when armed (rc=$RC)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
# T42 -- A12/A13's INERT POSITIVE must not be VACUOUS. Mutation testing made the positive
# unconditional and nothing failed: a SAN-LESS certificate -- F8's exact output -- collected
# "DNS SANs are all in this DC's expected zone". A positive that a zero-name cert can earn is
# worse than no positive at all, because it reads as confirmation.
E=$(mkfix inertvacuous vr1-dc0 "VR1 DC0" none)
run "$E" vr1-dc0
printf '%s' "$OUT" | grep -q 'DNS SANs are all in this DC.s expected zone' \
&& { no "T42 a SAN-less cert must NOT earn A12's positive zone line"; printf '%s\n' "$OUT" | sed 's/^/ /'; } \
|| ok "T42 a SAN-less cert earns NO positive zone line -- the inert positive is not vacuous"
# T43 -- the re-run guard's COUNT half. Mutation testing refuted the assumption that T25
# covered this: on a zero-SAN cert the loop simply never sets a hit, so the count guard is
# not what saves it. The count's real teeth are the THREE-SAN case -- two correct names plus
# a stale one. Without the count check, reissue REFUSES 4 and the stale third name survives.
E=$(mkfix threesan vr1-dc0 "VR1 DC0" full "omega.dc0.vr1.cloud.neumatrix.local")
( cd "$E/home/octavia-pki/vr1-dc0/controller" || exit 1
{ printf '[req]\ndistinguished_name=dn\nreq_extensions=v3_req\nprompt=no\n\n[dn]\nCN=octavia-controller.omega.dc0.vr1.cloud.neumatrix.local\nO=Neumatrix\n\n[v3_req]\nkeyUsage=critical,digitalSignature,keyEncipherment\nextendedKeyUsage=clientAuth,serverAuth\nsubjectAltName=@alt_names\n\n[alt_names]\nDNS.1=octavia-controller.omega.dc0.vr1.cloud.neumatrix.local\nDNS.2=octavia.omega.dc0.vr1.cloud.neumatrix.local\nDNS.3=octavia.omega.dc0.vr0.cloud.neumatrix.local\nIP.1 = %s\nIP.2 = %s\n' "$V4" "$V6"
} > t3.cnf
openssl req -new -sha256 -key controller.key -config t3.cnf -out t3.csr >/dev/null 2>&1
openssl x509 -req -sha256 -in t3.csr -CA ../controller-ca/controller-ca.cert.pem \
-CAkey ../controller-ca/controller-ca.key.enc -passin file:../controller-ca/passphrase.txt \
-CAcreateserial -days 730 -extfile t3.cnf -extensions v3_req -out controller.cert.pem >/dev/null 2>&1
chmod 600 controller.cert.pem
cat controller.cert.pem controller.key > controller.bundle.pem; chmod 600 controller.bundle.pem
rm -f t3.cnf t3.csr )
run_reissue "$E" vr1-dc0 fixturehost
if [ "$RC" = 4 ]; then
no "T43 a THREE-SAN cert must NOT be treated as already-correct (rc=4 leaves the stale third name)"
else
D3="$(OCTAVIA_PKI_REPO="$E/repo" OCTAVIA_PKI_HOME="$E/home" OCTAVIA_PKI_THIS_HOST=fixturehost \
bash "$CHECK" verify vr1-dc0 2>&1 | grep -c 'A9 SAN carries 2 DNS names' || true)"
[ "$RC" = 0 ] && [ "$D3" = "1" ] \
&& ok "T43 a cert with a stale THIRD DNS SAN is REISSUED down to exactly 2 -- the count guard has teeth" \
|| { no "T43 three-SAN cert must reissue to exactly 2 names (rc=$RC, twoname=$D3)"; }
fi
# T44 -- THE RE-RUN GUARD MUST NOT REFUSE A CERTIFICATE `verify` REJECTS.
# FOUND BY ADVERSARIAL REVIEW, and it is the defect that would have bitten at the 2-year
# renewal rather than today. Names correct, IP SAN stale (the exact DOCFIX-067 scenario the
# runbook already recorded once, when R14 moved the VIPs off 10.12.4.233): the FIRST version
# of the guard compared names only, so `verify` FAILED while `reissue` exited 4 saying
# "nothing to fix". The remedy refused the defect the checker had just reported.
# The guard now requires verify to return clean as well, so the two can never disagree.
E=$(mkfix guardstale vr1-dc0 "VR1 DC0" wrongv4 "omega.dc0.vr1.cloud.neumatrix.local")
run "$E" vr1-dc0
if [ "$RC" != 1 ]; then
no "T44 setup: the stale-IP fixture should make verify FAIL (rc=$RC)"
else
run_reissue "$E" vr1-dc0 fixturehost
if [ "$RC" = 4 ]; then
no "T44 the guard REFUSED a cert that verify FAILS -- the remedy is refusing the reported defect"
else
OCTAVIA_PKI_REPO="$E/repo" OCTAVIA_PKI_HOME="$E/home" OCTAVIA_PKI_THIS_HOST=fixturehost \
bash "$CHECK" verify vr1-dc0 >/dev/null 2>&1 \
&& ok "T44 names-correct-but-verify-FAILS is reissued, not refused -- and verify is clean afterwards" \
|| { no "T44 reissue ran (rc=$RC) but verify is still not clean afterwards"; }
fi
fi
# ===================================================================================
# T45..T47 -- lib-identity.sh: the cloud name and DNS base live in ONE place (2026-07-30)
#
# Operator direction: this estate is torn down and rebuilt repeatedly and the names change
# every time, so identity must not be typed into several files. Region and DC are already
# derived from the site token; these cases pin the remaining two constants.
# T45 -- THE CENTRALISATION MUST BE REAL, NOT DECORATIVE. A constant that nothing can vary is
# indistinguishable from a literal, and this repo has shipped exactly that before. Override
# both values and require the derived zone to follow BOTH of them.
E=$(mkfix identityvar vr1-dc0 "VR1 DC0" full "omega.dc0.vr1.cloud.neumatrix.local")
OUT="$(OCTAVIA_PKI_REPO="$E/repo" OCTAVIA_PKI_HOME="$E/home" OCTAVIA_PKI_THIS_HOST=fixturehost \
CLOUD_NAME=acme CLOUD_DOMAIN=example.test bash "$CHECK" verify vr1-dc0 2>&1)"
if printf '%s' "$OUT" | grep -q "acme.dc0.vr1.example.test"; then
ok "T45 CLOUD_NAME/CLOUD_DOMAIN really drive the derived zone (acme.dc0.vr1.example.test)"
else
no "T45 the derived zone must follow lib-identity.sh, not a literal"
printf '%s\n' "$OUT" | grep -i 'zone' | head -3 | sed 's/^/ /'
fi
# T46 -- FAIL CLOSED on a missing identity file. Without it every derived zone would be
# silently wrong, and a certificate gate that invents the estate's identity is worse than
# none. REFUSE (3), not FAIL: we could not look up what the names are.
CP="$TMPROOT/nolib"; mkdir -p "$CP"
cp "$CHECK" "$CP/octavia-pki.sh" # copied WITHOUT its sibling lib-identity.sh
RC=0; OUT="$(bash "$CP/octavia-pki.sh" verify vr1-dc0 2>&1)" || RC=$?
[ "$RC" = 3 ] && printf '%s' "$OUT" | grep -q 'lib-identity.sh not readable' \
&& ok "T46 a missing lib-identity.sh REFUSES (exit 3) rather than guessing the estate's identity" \
|| { no "T46 missing identity file must refuse (rc=$RC)"; printf '%s\n' "$OUT" | head -3 | sed 's/^/ /'; }
# T47 -- SHELL AND OPENTOFU MUST AGREE. HCL cannot source a shell file, so opentofu carries
# its own `domain_suffix` default. Two copies of one fact is a drift generator: if they part,
# the libvirt plane domains and the certificate zones describe different estates and NOTHING
# else would notice. This is the same standing shape as D-137 ruling 2's render-drift check.
SH_DOMAIN="$(CLOUD_DOMAIN= bash -c '. '"$REPO"'/scripts/lib-identity.sh; echo "$CLOUD_DOMAIN"')"
TF_DOMAIN="$(awk '/variable "domain_suffix"/,/^}/' "$REPO/opentofu/variables.tf" \
| sed -n 's/.*default *= *"\([^"]*\)".*/\1/p' | head -1)"
if [ -n "$SH_DOMAIN" ] && [ "$SH_DOMAIN" = "$TF_DOMAIN" ]; then
ok "T47 lib-identity.sh and opentofu/variables.tf agree on the base domain ('$SH_DOMAIN')"
else
no "T47 base-domain DRIFT: lib-identity.sh='$SH_DOMAIN' opentofu/variables.tf='$TF_DOMAIN' -- the certificate zones and the libvirt plane domains would describe different estates"
fi
echo
if [ "$F" = 0 ]; then echo "octavia-pki: $P/$P PASS"; exit 0; fi
echo "octavia-pki: FAILURES: $F (passed $P)"; exit 1