proxy-1 is verified after #1477/#1479: authorized_keys holds exactly
breakglass-neo-6532348 and the CI key, and CI still reaches it.
Removed with this run, per the [ssh_ca] report and the sshd journals
(no root login with any of them except id_ecdsa, the owner's old key):
- proxy-2: id_ecdsa, ansible-runner@ci-new
- netbird: id_ecdsa, ansible-runner@ci-new, krenner@fortress,
netbird-tunnel (SHA256:cNy8...), a second woodpecker-ci key
(SHA256:hQxz...); none used for a root login since 2026-08-29
- edge-1: id_ecdsa, ansible-runner@ci-new, krenner@fortress
The CI key keeps from= the pod network on edge-1 (LAN, CI arrives from
10.244.2.x) and has none on proxy-2 and netbird (CI arrives NATed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1477 locked CI out of proxy-1: the CI key got from= the pod network,
but CI reaches the internet hosts NATed from the home WAN address
(proxy-2 journal: root logins from 84.171.37.13). The from= came from
plays/configure-ssh-keys.yml, which no pipeline ever ran, so it was
never in effect on those hosts. LAN hosts do see the pod address
(edge-1 journal: 10.244.2.x).
ssh_ci_key_from is now per host: the pod CIDRs by default, empty for
[netbird_proxy] and netbird.renner.uno. The owner already removed the
from= on proxy-1 by hand; this keeps the next run from restoring it.
Restricting SSH on internet hosts to the NetBird mesh is the follow-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First host in [ssh_keys_managed]. root on proxy-1 currently has
id_ecdsa (krenner@krenner-XPS-13-9310), woodpecker-ci@homelab
(SHA256:DQWPS...) and ansible-runner@ci-new (dead since #184). After
this run: breakglass-neo-6532348 and the same CI key with its from=
pod-network restriction; people log in with CA certificates. proxy-2
is the twin, so the public edge survives a mistake here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 4 of the SSH CA, step 1. Nothing is removed from any host yet.
- ssh_admin_authorized_keys (group_vars/all) is the single list of
plain keys the admin logins may accept: the break-glass PIV key on
the offline YubiKey NEO and the CI deploy key (from= pod network).
- ssh-ca reports, on every [ssh_ca] host, the fingerprints of the
admin logins' current authorized_keys.
- configure-ssh-ca.yml gets a second play that sets the admin logins'
authorized_keys exclusively to that list on [ssh_keys_managed]. The
group is empty; hosts join one at a time after the report.
- Removed the three dead key lists: plays/configure-ssh-keys.yml (no
pipeline ran it), common_ssh_public_keys (defined, never used) and
opentofu/locals.tf common_ssh_keys (no reference). They carried
krenner@fortress, ansible-runner@ci-new and the laptop id_ecdsa key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step A is verified: Secret gitea-ssh-ca holds exactly the CA key
(SHA256:xEhuqx..., one line, 81 bytes like OpenBao's content-length),
so the webhook provider passes a text/plain body through unchanged.
- StatefulSet: SSH_TRUSTED_USER_CA_KEYS from the Secret,
SSH_AUTHORIZED_PRINCIPALS_ALLOW=username. The built-in SSH server
looks a cert's principals up as principal keys, so each user
registers their username once as a principal.
- ExternalSecret: trim the trailing newline before it reaches app.ini.
- Reloader annotation: Gitea reads env only at start, so a CA rotation
restarts it. The step A comment claimed the refresh alone reaches
Gitea; corrected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 3, step A of the SSH CA: Gitea's built-in SSH server needs the CA
key as SSH_TRUSTED_USER_CA_KEYS. A namespaced ESO webhook SecretStore
reads OpenBao's unauthenticated /v1/ssh-client-signer/public_key from
the in-cluster Service into Secret gitea-ssh-ca, so the key has one
source. Gitea is not wired to it yet; step B follows once the Secret
is verified to hold exactly the CA key (the ESO docs do not say how a
text/plain body without result.jsonPath is handled).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
netbird verified (Ubuntu, ssh.service): claude-bot cert login, sudo
journalctl allowed, systemctl restart refused. edge-1 is the last
Ansible-managed host (OpenSSH_10.6 banner like the proxies).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The netbird run (pipeline 14795) failed at the reload: 'Could not find
the requested service sshd.service'. Ubuntu's service_facts list
sshd.service as a not-found alias next to the real ssh.service
(running; disabled because ssh.socket activates it), and the role only
checked that the name exists.
The unit is now the one of sshd.service/ssh.service whose state is
running, asserted to be exactly one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
proxy-2 verified (claude-bot cert login, sudo allow/deny as on
proxy-1). netbird is the first Ubuntu host (OpenSSH_9.6p1
Ubuntu-3ubuntu13.20): unit ssh.service, which the role picks from
service_facts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.