Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
3.9 KiB
Phase 5 — Verification Hard Gate (ssh-check + firewall-diff)
Goal: fail-closed verification. Phase 6 refuses to run unless BOTH
ssh-checkANDfirewall-diffexit 0. Verify criterion:SSH_CHECK_OK = trueANDFW_DIFF_OK = true.
5.a — Pull config artefacts from the VM
scp "${ADMIN_USER}@${VM_IP}:/etc/ssh/sshd_config" <run-dir>/sshd_config
ssh "${ADMIN_USER}@${VM_IP}" "sudo tar -C /etc/ssh -cf - sshd_config.d" \
| tar -C <run-dir>/ -xf -
ssh "${ADMIN_USER}@${VM_IP}" "sudo ufw status numbered" > <run-dir>/ufw-status.txt
The ufw status requires sudo on most distros — the admin user has it
via NOPASSWD:ALL from harden-base.sh. If sudo requires TTY, prefix
sudo -n and surface the failure.
All captured files are READ ONLY, for ssh-check / firewall-diff to
parse. We NEVER push config back from the workstation.
5.b — Run ssh-check
_primitives/_rust/ssh-check/target/release/ssh-check \
--config <run-dir>/sshd_config \
--drop-in <run-dir>/sshd_config.d \
--allow-user "${ADMIN_USER}" \
--json > <run-dir>/ssh-check.json
SSH_EXIT=$?
Exit 0 → SSH_CHECK_OK=true. Exit 2 → SSH_CHECK_OK=false and
<run-dir>/ssh-check.json lists the violating directives with
file:line precision. Exit 1 → usage/parse error; surface the stderr and
loop back to Phase 4.
5.c — Run firewall-diff
_primitives/_rust/firewall-diff/target/release/firewall-diff \
--intent <run-dir>/firewall-intent.yaml \
--status-file <run-dir>/ufw-status.txt \
--json > <run-dir>/firewall-diff.json
FW_EXIT=$?
Exit 0 → FW_DIFF_OK=true. Exit 2 → the JSON lists missing (in intent,
not live) and extra (in live, not intent) rules; default_mismatches
flags a non-deny inbound policy.
5.d — Decision tree
ssh-check |
firewall-diff |
Action |
|---|---|---|
| 0 | 0 | Proceed to Phase 6. |
| 2 | 0 | Loop to 4.a with the sshd_config.d fix + re-ship harden-base.sh. |
| 0 | 2 | Ask user: apply the missing/extra deltas via ufw commands, or update firewall-intent.yaml (the intent was wrong). ONE AskUserQuestion. |
| 2 | 2 | Both failed — show both JSON reports; recommend a single fresh harden-base.sh re-run first (common-mode fix), then re-verify. |
| 1 | 1 | Workstation issue (missing binary, bad path) — NOT a VM problem. Rebuild the Rust primitives (cargo build --release in _primitives/_rust/). |
5.e — The AskUserQuestion
Exactly ONE AskUserQuestion, gated on the decision tree above:
Verification results: ssh-check=<PASS|FAIL>,
firewall-diff=<PASS|FAIL>. Pick one:
- Proceed (only shown when both PASS) → Phase 6.
- Fix and retry → loop to Phase 4 (or to 5.c if intent YAML is wrong).
- Ignore and proceed — BLOCKED. The hard-gate invariant refuses
this path per
SKILL.md. You can abort, but you cannot bypass.
5.f — Verify criterion
ssh-checkexit 0.firewall-diffexit 0.<run-dir>/ssh-check.jsonand<run-dir>/firewall-diff.jsonsaved.
Emit:
Phase 5 done: hard-gate PASSED. Artefacts in <run-dir>/.
Proceed to Phase 6.
5.g — Non-obvious pitfalls
- sshd_config.d drop-in not loaded. Debian 12's default
/etc/ssh/sshd_configincludes the.ddirectory via anIncludedirective. We don't followIncludeon purpose (security — includes can escape the intended tree). Pass--drop-inexplicitly. - ufw status shows IPv6 rules as duplicates. Intent is IPv4-only by
default;
firewall-diff's normalisation treats(v6)rules with same port/proto as "expected" and does not flag them. If you need strict v6-only rules, open a separate intent file. MaxAuthTriesat 6 or 10 (Debian default).harden-base.shsets 3. If a previous manual edit raised it and we re-ran without rewriting, ssh-check will FAILmaxauthtries. Fix: re-runharden-base.sh.