feat(baseline): key-only admin — passwordless sudo + locked password - #22
Conversation
Make the operator's admin account (ssh_admin_user, defaults to the local $USER; not the decdn service user) fully key-only, behind a new ssh_admin_passwordless_sudo knob (default true): - write a /etc/sudoers.d/<user> NOPASSWD drop-in (0440 root:root, visudo-validated), and lock the account password (password_lock) - remove the drop-in and warn the operator to set a password when the knob is disabled This closes a latent gap: the account was in the sudo group (password required) but created with no password, so `make deploy` as that user could not escalate after ssh_hardening disables root + password login. The sudoers.d filename is sanitized because sudo's #includedir silently ignores names containing '.' or ending in '~' (e.g. deploy.bot), which combined with the password lock would be a silent sudo lockout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces the ssh_admin_passwordless_sudo configuration option to enable key-only admin accounts with passwordless sudo and locked passwords. Feedback on the changes highlights two issues in the new Ansible tasks: a critical risk where an empty admin username could lead to the deletion of the entire /etc/sudoers.d/ directory, and a dry-run failure where locking the password of a non-existent user would fail in check mode.
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.
There was a problem hiding this comment.
Pull request overview
This PR updates the baseline Ansible role to make the admin/operator SSH account key-only by default by (1) granting passwordless sudo via a sudoers drop-in and (2) locking the account password, with a new ssh_admin_passwordless_sudo toggle to fall back to classic password-based sudo.
Changes:
- Add
ssh_admin_passwordless_sudo(defaulttrue) and implement NOPASSWD sudo + password lock behavior in the baseline role. - Sanitize the sudoers drop-in filename to avoid
sudoers.dinclude-skip edge cases for usernames containing./ ending with~. - Update baseline and inventory documentation to reflect the new behavior and the sudo-rs implications.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| ansible/roles/baseline/tasks/main.yml | Adds the sudoers drop-in + password lock behavior for the admin user, gated by ssh_admin_passwordless_sudo. |
| ansible/roles/baseline/defaults/main.yml | Introduces the new ssh_admin_passwordless_sudo default and documents intent. |
| ansible/roles/baseline/README.md | Documents the key-only admin default and the new knob in the role docs. |
| ansible/inventory/hosts.yml.example | Updates the sudo-rs workaround explanation to account for default NOPASSWD sudo. |
| ansible/inventory/group_vars/all.yml | Exposes the new knob in inventory defaults/documentation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…warning Address PR review: - Add `ssh_admin_user_effective | length > 0` guard to the drop-in removal task. The lockout-guard assert already blocks an empty user, but defence-in-depth: an empty user would collapse the path to the bare /etc/sudoers.d/ directory and state:absent would recursively delete it, breaking sudo host-wide. - Extend the disabled-path warning to note that flipping true->false on an already-hardened host (connected as the admin user) breaks the current run once the drop-in is removed; set a password first and re-run with --ask-become-pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Makes the
baselinerole's admin/operator account (ssh_admin_user, which defaults to the operator's local$USER— not thedecdnservice user) fully key-only, behind a newssh_admin_passwordless_sudoknob (defaulttrue):/etc/sudoers.d/<user>NOPASSWD drop-in (0440 root:root,visudo -cfvalidated), andpassword_lock: true), so no password can authenticate.false: the drop-in is removed and a loud warning tells the operator topasswd <user>(classic password sudo).Why
Closes a latent gap: the admin account was added to the
sudogroup (so sudo needs a password) but created with no password, sosudo/ non-interactivemake deployas that user could not escalate afterssh_hardeningdisables root + SSH password login. It now works non-interactively, and no password can be used to authenticate.The
decdnservice user (nologin, no sudo) is untouched.Notable hardening detail
The
sudoers.dfilename is sanitized (regex_replace('[^A-Za-z0-9_-]', '_')): sudo's#includedirsilently ignores any file whose name contains.or ends in~(e.g.deploy.bot). Without sanitizing, such a username would get a valid-but-ignored NOPASSWD drop-in which, combined with the locked password, is a silent sudo lockout.visudo -cfvalidates content, not the filename, so it cannot catch this. (Surfaced by PR review.)Files
roles/baseline/tasks/main.yml— the gated tasks (core)roles/baseline/defaults/main.yml— new knobroles/baseline/README.md,inventory/group_vars/all.yml,inventory/hosts.yml.example— docs + reconciled the stale "no NOPASSWD" / sudo-rs noteVerification
make lint✅ ·make lint-ansible(production profile) ✅ ·make security(KICS: 0 HIGH / 0 CRITICAL) ✅ ·ansible-playbook --syntax-check✅deploy.bot→deploy_bot,john.doe~→john_doe_baselineis real-host-only and not converged there.End-to-end (
make check/make deploy) requires a real target host. On the host as the admin user, expect:sudo -n truesucceeds,passwd -S <user>reportsL,/etc/sudoers.d/<user>is440 root:root,decdnhas no sudo, and a re-run is idempotent.🤖 Generated with Claude Code