Skip to content

feat(baseline): deploy as yourself — auto-resolve admin user/key, add extra keys - #5

Merged
thiras merged 2 commits into
mainfrom
feat/baseline-deploy-as-yourself
Jun 6, 2026
Merged

thiras merged 2 commits into
mainfrom
feat/baseline-deploy-as-yourself

Conversation

@thiras

@thiras thiras commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

What & why

A real deploy previously required the operator to hand-set ssh_admin_pubkey (and accept the shared deploy account), with the lockout-guard assert failing on an empty key. The operator's own username and ~/.ssh key are right there on the control machine — this removes that friction and adds support for additional team keys.

baseline is now "deploy as yourself" by default:

  • ssh_admin_user: "" → resolves to the control-machine local $USER
  • ssh_admin_pubkey: "" → autodetected from ~/.ssh (id_ed25519 > ecdsa > rsa)
  • new ssh_admin_extra_pubkeys list authorizes additional keys (e.g. teammates)
  • explicit values always win

Hardened lockout guard

The guard is now an unconditional assert that runs before account creation and hardening, aborting the play unless a non-root admin user and ≥1 key resolve. This closes two footguns surfaced in review:

  • resolving the admin user to root (which ssh_hardening then locks out)
  • an unset $USER (cron/CI/sudo with env_reset) silently skipping hardening with a green exit

A debug task echoes the resolved user + key count so a wrong control machine or a silently-dropped entry is visible. Docs note that resolution reads the control node's $USER/$HOME of whoever runs ansible-playbook.

Verification

  • yamllint + ansible-lint (production profile, 23 files): pass
  • site.yml / anvil.yml --syntax-check: pass
  • Behavioral test of the guard expressions across 5 scenarios:
Scenario Result
Deploy as yourself (autodetect) ✅ PASS, 1 key
$USER=root, empty user ✅ fails loud
$USER unset, empty user ✅ fails loud
User set, no key resolvable ✅ fails loud
Explicit user+key (CI) ✅ PASS

molecule.yml drops the now-inert ssh_admin_* skip vars — converge.yml runs only anvil + caddy, so baseline is never exercised there.

🤖 Generated with Claude Code

… extra keys

Make the baseline role zero-config by default instead of requiring a manually
set ssh_admin_pubkey:

- ssh_admin_user "" -> control-machine local $USER
- ssh_admin_pubkey "" -> autodetected ~/.ssh key (id_ed25519 > ecdsa > rsa)
- new ssh_admin_extra_pubkeys list authorizes additional team keys

The lockout guard is now an unconditional assert that runs before account
creation and hardening, aborting the play unless a NON-ROOT admin user and at
least one key resolve. This closes two footguns: resolving to "root" (which
ssh_hardening then locks out) and an unset $USER silently skipping hardening
with a green exit. A debug task echoes the resolved user + key count for
visibility into what is trusted.

Docs (ansible/README.md, baseline README, group_vars/all.yml, defaults)
updated to drop the "pubkey REQUIRED" framing and document the control-node
$USER/$HOME resolution caveat. molecule.yml drops the now-inert ssh_admin_*
skip vars (converge runs only anvil + caddy; baseline is never exercised).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 6, 2026 20:26

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Ansible baseline role to support deploying "as yourself" by default. It autodetects the control machine's local user and SSH keys when ssh_admin_user and ssh_admin_pubkey are left empty, and introduces an unconditional lockout guard to prevent keyless or root-only hardening. Feedback suggests improving the robustness of the key autodetection task by using tilde expansion, falling back to /dev/null to avoid empty lookup paths, and initializing ssh_admin_detected_pubkey in the role defaults to prevent undefined variable errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread ansible/roles/baseline/tasks/main.yml Outdated
Comment thread ansible/roles/baseline/defaults/main.yml

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the baseline Ansible role to default to “deploy as yourself” by resolving the admin username and SSH key(s) from the control machine when inventory values are left empty, while adding support for authorizing additional teammate keys. It also strengthens the SSH lockout guard by making it an unconditional pre-check that aborts before hardening unless a non-root admin user and at least one key are present.

Changes:

  • Auto-resolve ssh_admin_user from control-node $USER and auto-detect a default ~/.ssh public key when ssh_admin_* values are empty (explicit values still override).
  • Add ssh_admin_extra_pubkeys to authorize additional keys for the admin user.
  • Make the lockout guard an unconditional assert that runs before account creation and SSH hardening, preventing root-only/keyless hardening.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ansible/roles/baseline/tasks/main.yml Adds control-node user/key resolution, aggregates key list (including extras), and enforces an unconditional lockout-guard assert before hardening.
ansible/roles/baseline/README.md Documents “deploy as yourself” behavior, extra keys, and the strengthened lockout guard semantics.
ansible/roles/baseline/defaults/main.yml Changes defaults to empty user/key with autodetect enabled and introduces ssh_admin_extra_pubkeys.
ansible/README.md Updates operator docs to reflect auto-resolution defaults and new extra-keys support.
ansible/molecule/default/molecule.yml Removes now-unneeded ssh_admin_* overrides since Molecule doesn’t exercise baseline.
ansible/inventory/group_vars/all.yml Updates inventory defaults/docs to match the new resolution behavior and new variables.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ansible/roles/baseline/tasks/main.yml Outdated
Address PR review:
- Copilot: the debug task labeled the primary key source "autodetected"
  whenever ssh_admin_pubkey was empty, even when autodetect found nothing and
  all keys came from ssh_admin_extra_pubkeys. Now distinguishes explicit /
  autodetected / none (extras only) by checking the resolved detected key.
- gemini: use ~/.ssh tilde paths for the autodetect candidates instead of an
  env('HOME') lookup. expanduser falls back to the passwd DB when $HOME is
  unset (cron/CI), so it's more robust; verified first_found expands ~.

Declined two suggestions: a /dev/null fallback for lookup('file') (the
existing `when: found | length > 0` guard already prevents an empty-path
lookup) and initializing ssh_admin_detected_pubkey in defaults (it's an
internal computed fact already handled by `| default('')`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiras
thiras merged commit d8ca7e0 into main Jun 6, 2026
7 checks passed
@thiras
thiras deleted the feat/baseline-deploy-as-yourself branch June 6, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants