Close RAIL-HO-WP-0009 declared-state gaps; leave live 6443 prune gated
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Make the k3s API tunnel-only (ADR-005), stop declaring Flannel VXLAN
open to Anywhere, tag the base role so firewall can be scoped, and
schedule the Goss declared-vs-live check. CoulombCore sets ufw_manage
false so a converge cannot enable UFW there. T02 still needs operator
approval for make converge-firewall HOST=Railiance01.
This commit is contained in:
codex 2026-08-15 15:41:59 +02:00
parent 434121be99
commit 4d9e77c968
29 changed files with 793 additions and 99 deletions

View file

@ -131,21 +131,21 @@ backup: ## Backup S1 OS config to /opt/backup/railiance/infra/ (age-encrypted, r
# ---- Ansible ----
ansible-bootstrap: ## Run base bootstrap play (users, ssh, ufw, sops-agent, custodian-agent)
cd ansible && ansible-playbook playbooks/bootstrap.yaml -u admin
cd ansible && ansible-playbook playbooks/bootstrap.yaml $(ANSIBLE_USER_FLAG)
provision-custodian-agent: ## Deploy custodian agent SSH key to all managed hosts
@python3 -c "import yaml; d=yaml.safe_load(open('ansible/inventory/group_vars/all.yaml')); k=d.get('custodian_agent_pubkey',''); exit(0 if k else 1)" \
|| (echo "ERROR: custodian_agent_pubkey is empty. Run: cd ~/the-custodian && make custodian-keygen"; exit 1)
cd ansible && ansible-playbook playbooks/custodian-agent.yaml -u $(SSH_USER)
cd ansible && ansible-playbook playbooks/custodian-agent.yaml $(ANSIBLE_USER_FLAG)
provision-custodian-agent-host: ## Deploy custodian agent key to one host: make provision-custodian-agent-host HOST=Railiance01
@test -n "$(HOST)" || (echo "Usage: make provision-custodian-agent-host HOST=Railiance01"; exit 1)
cd ansible && ansible-playbook playbooks/custodian-agent.yaml -u $(SSH_USER) \
cd ansible && ansible-playbook playbooks/custodian-agent.yaml $(ANSIBLE_USER_FLAG) \
--limit "$(HOST)"
bootstrap-ssh-ca: ## Deploy OpenBao SSH CA trust + auth_principals: make bootstrap-ssh-ca SSH_CA_PUBKEY=/path/to/ca_user.pub
@test -n "$(SSH_CA_PUBKEY)" || (echo "Usage: make bootstrap-ssh-ca SSH_CA_PUBKEY=/path/to/ca_user.pub [HOST=Railiance01]"; exit 1)
cd ansible && ansible-playbook playbooks/bootstrap-ssh-ca.yaml -u $(SSH_USER) \
cd ansible && ansible-playbook playbooks/bootstrap-ssh-ca.yaml $(ANSIBLE_USER_FLAG) \
-e ssh_ca_pubkey_path="$(SSH_CA_PUBKEY)" \
$(if $(HOST),--limit "$(HOST)",)
@ -167,7 +167,7 @@ deploy-stack: ## Print the full S1→S5 ordered deploy sequence (operator follow
@echo "PRE-CONDITIONS"
@echo " [ ] SSH key: ~/.ssh/id_ops"
@echo " [ ] SOPS key: ~/.config/sops/age/keys.txt (or SOPS_AGE_KEY)"
@echo " [ ] ops-bridge: bridge up state-hub-coulombcore k3s-api-coulombcore"
@echo " [ ] ops-bridge: bridge up state-hub-coulombcore k3s-api-coulombcore k3s-api-railiance01"
@echo ""
@echo "S1 — Infrastructure Substrate (this repo)"
@echo " make tf-plan && make tf-apply # provision server (skip if exists)"
@ -222,7 +222,10 @@ remote-set: ## Set origin to your Gitea repo (GITEA/OWNER/REPO vars)
ANS_DIR := ansible
INV_SCRIPT := $(ANS_DIR)/inventory_from_yaml.py
PLAY := $(ANS_DIR)/playbooks/bootstrap.yaml
SSH_USER ?= admin
# Inventory servers.yaml sets ansible_user. Override only when needed:
# make converge SSH_USER=tegwick
SSH_USER ?=
ANSIBLE_USER_FLAG := $(if $(SSH_USER),-u $(SSH_USER),)
# Load your SOPS key for decryption when running playbooks (optional if you use keys.txt)
export SOPS_AGE_KEY := $(shell cat ~/.config/sops/age/keys.txt 2>/dev/null)
@ -234,50 +237,66 @@ ansible-help: ## Show common Ansible commands
@echo " make converge # run baseline convergence on all hosts"
@echo " make converge-host HOST=web-01# run on a single host"
@echo " make converge-tags TAGS=base # run only tagged tasks"
@echo " make converge-firewall HOST=Railiance01 # UFW only (RAIL-HO-WP-0009)"
@echo " make converge-check # dry-run (check mode)"
@echo " make converge-diff # show config diffs"
@echo " make verify-host HOST=Railiance01"
@echo " make goss-status # last on-host timer result"
ansible-inventory: ## Print the dynamic inventory Ansible will use
cd $(ANS_DIR) && ansible-inventory --list | head -200
ansible-ping: ## Quick connectivity check (SSH + Python availability)
cd $(ANS_DIR) && ansible all -u $(SSH_USER) -m ping
cd $(ANS_DIR) && ansible all $(ANSIBLE_USER_FLAG) -m ping
status: ## Show live security state of all hosts (UFW, fail2ban, SSH hardening)
@echo "=== Connectivity ==="
cd $(ANS_DIR) && ansible all -u $(SSH_USER) -m ping
cd $(ANS_DIR) && ansible all $(ANSIBLE_USER_FLAG) -m ping
@echo "=== UFW ==="
cd $(ANS_DIR) && ansible all -u $(SSH_USER) -m shell -a "ufw status" --become
cd $(ANS_DIR) && ansible all $(ANSIBLE_USER_FLAG) -m shell -a "ufw status" --become
@echo "=== fail2ban ==="
cd $(ANS_DIR) && ansible all -u $(SSH_USER) -m shell -a "systemctl is-active fail2ban"
cd $(ANS_DIR) && ansible all $(ANSIBLE_USER_FLAG) -m shell -a "systemctl is-active fail2ban"
@echo "=== SSH hardening ==="
cd $(ANS_DIR) && ansible all -u $(SSH_USER) -m shell -a "grep -iE '^(PermitRootLogin|PasswordAuthentication)' /etc/ssh/sshd_config" --become
cd $(ANS_DIR) && ansible all $(ANSIBLE_USER_FLAG) -m shell -a "grep -iE '^(PermitRootLogin|PasswordAuthentication)' /etc/ssh/sshd_config" --become
@echo ""
@echo "--- Hint: run 'make verify' for a structured pass/fail report ---"
verify: ## Run Goss test suite against all hosts, commit TAP reports — exits non-zero on failure
@echo "Running Goss baseline assertions..."
@cd $(ANS_DIR) && ansible-playbook playbooks/verify.yaml -u $(SSH_USER) || \
@cd $(ANS_DIR) && ansible-playbook playbooks/verify.yaml $(ANSIBLE_USER_FLAG) || \
(echo "One or more assertions FAILED — see reports/ for TAP output." && exit 1)
@echo "All assertions passed."
@git add reports/ && \
git diff --cached --quiet && echo "No new reports to commit." || \
git commit -m "chore: Goss verification reports $$(date -u +%Y-%m-%dT%H%M%SZ)"
verify-host: ## Run Goss against one host: make verify-host HOST=Railiance01
@test -n "$(HOST)" || (echo "Usage: make verify-host HOST=Railiance01"; exit 1)
@echo "Running Goss baseline assertions on $(HOST)..."
@cd $(ANS_DIR) && ansible-playbook playbooks/verify.yaml $(ANSIBLE_USER_FLAG) -l $(HOST) || \
(echo "One or more assertions FAILED — see reports/ for TAP output." && exit 1)
goss-status: ## Fetch last on-host Goss timer result (fails if FAILED flag present)
cd $(ANS_DIR) && ansible-playbook playbooks/goss-status.yaml $(ANSIBLE_USER_FLAG)
converge: ## Converge all hosts to the baseline (idempotent)
cd $(ANS_DIR) && ansible-playbook $(PLAY) -u $(SSH_USER)
cd $(ANS_DIR) && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG)
converge-host: ## Converge a single host: make converge-host HOST=core-01
@test -n "$(HOST)" || (echo "Usage: make converge-host HOST=<name>"; exit 1)
cd $(ANS_DIR) && ansible-playbook $(PLAY) -u $(SSH_USER) -l $(HOST)
cd $(ANS_DIR) && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG) -l $(HOST)
converge-tags: ## Run only certain tags: make converge-tags TAGS="base,ufw"
@test -n "$(TAGS)" || (echo "Usage: make converge-tags TAGS=tag1,tag2"; exit 1)
cd $(ANS_DIR) && ansible-playbook $(PLAY) -u $(SSH_USER) --tags "$(TAGS)"
cd $(ANS_DIR) && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG) --tags "$(TAGS)"
converge-firewall: ## Apply only UFW tasks: make converge-firewall HOST=Railiance01
@test -n "$(HOST)" || (echo "Usage: make converge-firewall HOST=Railiance01"; exit 1)
cd $(ANS_DIR) && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG) -l $(HOST) --tags firewall
converge-check: ## Dry-run (no changes), great for previews
cd $(ANS_DIR) && ansible-playbook $(PLAY) -u $(SSH_USER) --check
cd $(ANS_DIR) && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG) --check
converge-diff: ## Show file/templating diffs while applying changes
cd $(ANS_DIR) && ansible-playbook $(PLAY) -u $(SSH_USER) --diff
cd $(ANS_DIR) && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG) --diff