Skip to content

.macos: Touch ID for sudo, and one touch for everything - #98

Merged
hadees merged 2 commits into
mainfrom
feat/touchid-sudo
Oct 8, 2026
Merged

hadees merged 2 commits into
mainfrom
feat/touchid-sudo

Conversation

@hadees

@hadees hadees commented Oct 6, 2026

Copy link
Copy Markdown
Owner

What

  • Touch ID for sudo through /etc/pam.d/sudo_local (Apple's hook that survives OS updates): created from the template when missing, appended when present without the line, untouched otherwise. Never writes /etc/pam.d/sudo.
  • One authorisation for every process. sudo caches a tty-less authorisation per parent process, so with Touch ID on, anything that runs sudo once per step from fresh processes (Ansible's local connection) prompts per step — ten in one run today. /etc/sudoers.d/timestamp sets Defaults timestamp_type=global (the fix) and timestamp_timeout=60 (a preference, $SUDO_TIMESTAMP_TIMEOUT overrides). The file is generated to a temp path, validated with visudo -cf there, and only then installed at 0440 root:wheel; a rejected file never reaches sudoers.d. Idempotent, including repairing a wrong mode.

Tests

tests/macos.bats: two static checks (sudo_local not /etc/pam.d/sudo; validate-before-install at 0440). tests/macos-apply.bats (macOS CI runner, MACOS_APPLY_OK=1): the line is in sudo_local, the sudoers file parses and is in effect, and a second .macos run changes nothing.

Verified on a Mac: visudo -cf validates a temp file as a normal user and rejects a bad one; BSD grep/sed on the real template.

🤖 Generated with Claude Code

Touch ID goes through /etc/pam.d/sudo_local, Apple's update-proof hook,
created from the template beside it or appended to. On its own it made
things worse for anything that runs sudo once per step from fresh
processes: sudo caches a tty-less authorisation per parent process, so
an Ansible run raised ten Touch ID prompts. A global timestamp in
/etc/sudoers.d/timestamp makes one touch cover every sudo on the
machine; the file is validated by visudo before it is installed at 0440,
and the 60-minute timeout is a preference behind SUDO_TIMESTAMP_TIMEOUT.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hadees hadees self-assigned this Oct 6, 2026
…e it

/etc/sudoers.d/timestamp is installed root:wheel 0440, so an unprivileged
grep on it dies with "Permission denied". In .macos the -s flag hid that:
the check always came back false and the file was reinstalled on every
run, the opposite of the "left alone otherwise" the comment promises. In
tests/macos-apply.bats the same grep failed loudly, which is how the
macOS CI job caught it. Both reads now go through sudo — already live in
.macos from the upfront sudo -v, and -n in the test like the visudo check
beside it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hadees
hadees merged commit 9116ea9 into main Oct 8, 2026
5 checks passed
@hadees
hadees deleted the feat/touchid-sudo branch October 8, 2026 01:19
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.

1 participant