Add ops_bridge_pubkey to group_vars/all.yaml (public key only, safe to
commit) and inject it via ansible.posix.authorized_key in the base role,
immediately after SSH hardening. This ensures ops-bridge tunnel
connectivity is available as soon as SSH infrastructure is up on any
managed host — no manual key provisioning required for new nodes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- base role: allow UFW routing direction (required for k3s flannel
pod networking to function across nodes)
- docs/deploy-stack.md: full S1→S5 ordered deploy runbook with
pre-conditions checklist and layer-by-layer steps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ansible/playbooks/custodian-agent.yaml: minimal playbook with only
the custodian_agent role — avoids loading base/sops_agent/etc when
all we need is key injection
- Makefile: use custodian-agent.yaml in provision targets; remove
--tags workaround (was fragile; standalone playbook is correct)
Manual invocation (from CoulombCore):
cd ~/railiance-infra/ansible
ansible-playbook playbooks/custodian-agent.yaml -u tegwick --limit Railiance01
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC/V9fe5MGKdhTBz9KwEvC1NE+HjdoCtQocpGxP6Pko9
Generated 2026-03-27 via make custodian-keygen. Private key at workstation
only (~/.ssh/id_custodian_agent), never committed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Establishes a dedicated SSH keypair for the Custodian automation agent:
- ansible/roles/custodian_agent/: authorized_key task (tagged custodian_agent)
- ansible/inventory/group_vars/all.yaml: custodian_agent_user/pubkey vars
- ansible/playbooks/bootstrap.yaml: custodian_agent role added
- Makefile: provision-custodian-agent / provision-custodian-agent-host targets
Keypair generation: cd ~/the-custodian && make custodian-keygen
Then deploy: cd ~/railiance-infra && make provision-custodian-agent
The private key lives at ~/.ssh/id_custodian_agent — never committed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Include time in TAP report filename (ISO 8601: date + HHmmssZ)
- Add changed_when: false to report write task — verify play now shows
changed=0 on a clean run (all green recap)
- make verify auto-commits new reports to repo after a passing run;
exits non-zero before committing if assertions fail
- Register EP-RAIL-001: report pruning extension point for future
implementation when reports/ accumulates beyond a threshold
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes found by running make verify against Railiance01:
- Fix playbook_dir paths (ansible/playbooks/ is 2 levels from repo root)
- age/sops are binary installs, not apt packages — use command checks
- Admin user is tegwick, not admin; sudoers at /etc/sudoers.d/tegwick
- sudo granted via sudoers file, not group membership — remove group assert
- Ubuntu 24.04 socket-activates SSH; assert ssh.socket not ssh.service
- SSH hardening lives in sshd_config.d/10-hardening.conf, not main config
- UFW SSH rule uses app name "OpenSSH", not port 22/tcp
- Replace /regex/i patterns with plain strings (Goss file.contents)
- Update spec/server-baseline.yaml to match all findings
All 27 assertions now pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extend base role with fail2ban, UFW k3s/Flannel rules, HISTCONTROL
- Add handlers dir for fail2ban restart
- Fix inventory script to emit correct dynamic inventory JSON format
- Add roles_path to ansible.cfg so playbook finds roles
- Add Railiance01 (92.205.62.239) to inventory/servers.yaml
- Mark workplan T03/T04/T05 as done
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>