milton: the runner stops borrowing paul's identity #5

Open
erwin wants to merge 2 commits from erwin/milton-runner into main
First-time contributor

MERGE FIRST of the four CD PRs, and converge from the laptop before merging the workflow PRs — both workflows assume milton exists on the fleet.

act_runner runs as the bot (ed25519 key, own ssh config for the ProxyJump hop), roles/cd_bot creates milton fleet-wide (authorized_key + honest NOPASSWD sudoers), server_base AllowUsers parameterized (was: hard 'AllowUsers paul' — would have rejected milton everywhere), workstation.yml asserts against CI.

Converge order after merge: services.yml (act_runner) → cd_bot.yml; site.yml encodes it. Then verify: systemctl show -p User act_runner → milton, and a trivial CI job runs.

Adversarially reviewed; blocker (sshd AllowUsers) and majors (ProxyJump ssh config, site.yml/workstation foot-gun, chown follow) fixed.

MERGE FIRST of the four CD PRs, and converge from the laptop before merging the workflow PRs — both workflows assume milton exists on the fleet. act_runner runs as the bot (ed25519 key, own ssh config for the ProxyJump hop), roles/cd_bot creates milton fleet-wide (authorized_key + honest NOPASSWD sudoers), server_base AllowUsers parameterized (was: hard 'AllowUsers paul' — would have rejected milton everywhere), workstation.yml asserts against CI. Converge order after merge: services.yml (act_runner) → cd_bot.yml; site.yml encodes it. Then verify: systemctl show -p User act_runner → milton, and a trivial CI job runs. Adversarially reviewed; blocker (sshd AllowUsers) and majors (ProxyJump ssh config, site.yml/workstation foot-gun, chown follow) fixed.
The Forgejo Actions runner has been running as paul, so every CI job
carried his identity and could ride his SSH keys. Introduce one shared
variable cd_bot_user (milton — Schrödinger's actual cat at Oxford) in
group_vars/all and flip act_runner_user to it. The role now creates
the account (real home + bash, since host-mode jobs execute in its
environment) with an ed25519 keypair for outbound ansible-over-SSH,
and recursively chowns the data dir (follow: false — never chase a
checkout's symlinks out of the tree) so the paul→milton switch
converges cleanly on the existing host. Config dir/file ownership
already tracked act_runner_user, so those flip with the variable.

The role also manages the bot's ~/.ssh/config with the
gierz-hypervisor ProxyJump hop (HostName from inventory, its own key,
StrictHostKeyChecking accept-new): the alias otherwise only resolves
via paul's ssh config, and OpenSSH does not apply ansible's
command-line host-key options to ProxyJump hops, so the hop needs
real client config of its own.

Registration is runner-scoped, not user-scoped — vault_act_runner_*
and the vault stay untouched.

Signed-off-by: erwin <erwin@gierz.eu>
Co-Authored-By: LLM <noreply@invalid>
Claude-Session: https://claude.ai/code/session_01UVBwAUedQi9YJ6S9UJomMS
New role applied to all:!workstations (playbooks/cd_bot.yml, imported
by site.yml right after services.yml): create milton on each managed
host, install the runner's pubkey (slurped once from gierz-services
with run_once + delegate_to so the registered var broadcasts to the
play), and grant NOPASSWD:ALL via /etc/sudoers.d/milton, strict
visudo-validated. The sudo grant is an interim broad one — converge
jobs run whole playbooks that become throughout; per-job identity
downscoping (a jacamar-equivalent for Forgejo) is a possible future
project.

sshd must actually let the bot in: server_base's hardening pinned
AllowUsers to paul, which would have rejected milton on every
hardened host — including gierz-hypervisor, the ProxyJump hop for
every service VM — and killed the rollout on arrival. The line now
derives from server_base_ssh_allow_users (default: paul + the bot),
so the milton rollout requires a server_base reconverge on hardened
hosts; site.yml orders base plays before cd_bot and covers it.

Order matters: act_runner (services.yml) creates the bot + keypair on
the runner host before cd_bot distributes the pubkey — the site.yml
import order encodes that, and the role README documents it.

Inventory ansible_user stays paul (laptop converges unchanged); CI
jobs opt in with --limit 'all:!workstations' (mandatory: gierz-laptop
is ansible_connection: local, and workstation.yml now asserts against
GITHUB_ACTIONS as backstop) plus -e ansible_user=milton and
-e ansible_ssh_private_key_file=/home/milton/.ssh/id_ed25519, because
several inventory entries pin paul's key file and -e wins.

Signed-off-by: erwin <erwin@gierz.eu>
Co-Authored-By: LLM <noreply@invalid>
Claude-Session: https://claude.ai/code/session_01UVBwAUedQi9YJ6S9UJomMS
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin erwin/milton-runner:erwin/milton-runner
git switch erwin/milton-runner

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff erwin/milton-runner
git switch erwin/milton-runner
git rebase main
git switch main
git merge --ff-only erwin/milton-runner
git switch erwin/milton-runner
git rebase main
git switch main
git merge --no-ff erwin/milton-runner
git switch main
git merge --squash erwin/milton-runner
git switch main
git merge --ff-only erwin/milton-runner
git switch main
git merge erwin/milton-runner
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
homelab/infra!5
No description provided.