railiance-infra/goss/baseline.yaml.j2
codex 95e3361598
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Tighten the Goss firewall assertion and render it from the declaration
The conformance checker existed but had not run since 2026-03-09, and its
firewall assertion matched /6443\/tcp.*ALLOW/ - asserting the port was allowed
but not from whom. It passed identically whether the API was restricted to one
operator address or open to the entire internet, which is precisely the drift
that went undetected.

goss/baseline.yaml is now a template rendered per host from the same inventory
declaration that converges the host, so the assertion cannot drift from what it
checks. It asserts each declared source is present, that no revoked source
retains access, and that 6443 is never ALLOW Anywhere. verify.yaml gained the
vars_files the bootstrap play already had - without it the firewall assertions
rendered empty and silently asserted nothing, which is worse than no check.

Result on Railiance01: 32 assertions, 31 pass. The one failure is a real find
and is recorded as T06 - Flannel VXLAN is declared open to Anywhere with no
source restriction, the same defect pattern as the k3s rule. It is currently
absent from the host, so converging would INTRODUCE the exposure rather than
fix drift. Must be resolved before RAIL-BS-WP-0007 multi-node HA needs working
VXLAN.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 03:28:35 +02:00

96 lines
2.7 KiB
Django/Jinja

# Goss baseline assertions for railiance managed nodes.
# Derived from spec/server-baseline.yaml — keep in sync.
# Run: goss -g /etc/goss/baseline.yaml validate
#
# THIS IS A TEMPLATE. It is rendered per host from inventory variables, so the
# firewall assertions below are generated from the SAME declaration that
# converges the host (k3s_api_allowed_sources in group_vars/all.yaml). That is
# deliberate: a hand-written assertion drifts from the declaration it is meant
# to check, which is exactly how RAIL-HO-WP-0009 happened.
package:
ufw:
installed: true
fail2ban:
installed: true
git:
installed: true
curl:
installed: true
vim:
installed: true
htop:
installed: true
# age and sops are binary installs, not apt packages — checked via command below
service:
ufw:
enabled: true
running: true
fail2ban:
enabled: true
running: true
# Ubuntu 24.04 uses socket activation: ssh.service is disabled by design,
# ssh.socket keeps it running. Assert the socket is enabled.
ssh.socket:
enabled: true
running: true
file:
/etc/ssh/sshd_config.d/10-hardening.conf:
exists: true
contents:
- "PermitRootLogin no"
- "PasswordAuthentication no"
- "PubkeyAuthentication yes"
user:
tegwick:
exists: true
# sudo access is via /etc/sudoers.d/tegwick (NOPASSWD), not group membership
shell: /bin/bash
command:
"ufw status":
exit-status: 0
stdout:
- "Status: active"
- /OpenSSH.*ALLOW/
{% for src in k3s_api_allowed_sources | default([]) %}
- '/6443\/tcp\s+ALLOW\s+{{ src.address | regex_escape }}/'
{% endfor %}
- /8472\/udp.*ALLOW/
# The k3s API must never be reachable from Anywhere. The previous assertion
# matched /6443\/tcp.*ALLOW/, which passes identically whether the API is
# restricted to one operator address or open to the entire internet — it
# asserted that the port was allowed, not from whom.
"ufw status | grep -Ec '6443/tcp[[:space:]]+ALLOW[[:space:]]+Anywhere' || true":
exit-status: 0
stdout:
- "0"
{% for src in k3s_api_revoked_sources | default([]) %}
# Revoked operator source must not retain access: {{ src.comment | default('') }}
"ufw status | grep -Ec '6443/tcp[[:space:]]+ALLOW[[:space:]]+{{ src.address }}' || true":
exit-status: 0
stdout:
- "0"
{% endfor %}
"grep NOPASSWD /etc/sudoers.d/tegwick":
exit-status: 0
stdout:
- "NOPASSWD"
"grep -r HISTCONTROL /etc/profile.d/":
exit-status: 0
stdout:
- "ignorespace"
"fail2ban-client status sshd":
exit-status: 0
stdout:
- "Status for the jail: sshd"
"test -x /usr/local/bin/age":
exit-status: 0
"test -x /usr/local/bin/sops":
exit-status: 0