Skip to content

Instantly share code, notes, and snippets.

@brendandebeasi
Last active February 6, 2026 19:34
Show Gist options
  • Select an option

  • Save brendandebeasi/328d13632cabb66d1af5b67870d94524 to your computer and use it in GitHub Desktop.

Select an option

Save brendandebeasi/328d13632cabb66d1af5b67870d94524 to your computer and use it in GitHub Desktop.
Touch ID for sudo in tmux (macOS) - fix with pam_reattach

Touch ID for sudo in tmux (macOS)

By default, Touch ID authentication for sudo doesn't work inside tmux sessions. This is because tmux runs in a different bootstrap namespace and can't access the Security framework's authentication context.

Fix

1. Install pam-reattach

brew install pam-reattach

2. Create /etc/pam.d/sudo_local

sudo tee /etc/pam.d/sudo_local <<'EOF'
# sudo_local: auth account password session
auth       optional       /opt/homebrew/lib/pam/pam_reattach.so ignore_ssh
auth       sufficient     pam_tid.so
EOF

3. That's it

No shell wrapper needed. No Python helper needed. Just open a new tmux pane and sudo will prompt for your fingerprint.

How it works

  • pam_reattach.so re-attaches the per-session bootstrap namespace so Touch ID works in tmux
  • ignore_ssh flag detects $SSH_CONNECTION / $SSH_CLIENT / $SSH_TTY and makes the module a no-op over SSH, so it falls through to password auth
  • pam_tid.so enables Touch ID for sudo -- marked sufficient so it gracefully falls through to password if Touch ID is unavailable
  • Using sudo_local instead of editing sudo directly means this survives macOS updates
  • /etc/pam.d/sudo already includes sudo_local by default on modern macOS (Sonoma+)

Behavior by context

Context What happens
Local terminal Touch ID prompt
tmux (local) Touch ID prompt (via pam_reattach)
SSH session Password prompt (ignore_ssh skips reattach)
tmux over SSH Password prompt (ignore_ssh skips reattach)
No Touch ID hardware Password prompt (pam_tid fails, falls through)
pam_reattach not installed Password prompt (optional, skipped by PAM)

Optional: See what's being sudo'd

The Touch ID dialog only shows "Terminal" (or your app name) - it doesn't show which command is being authorized. Add this shell wrapper to see the user, parent process, and command before the Touch ID prompt appears.

Add to your .zshrc or .bashrc:

# SUDO WRAPPER - Shows what's being run before Touch ID prompt
# Displays macOS notification + terminal output with user, parent process, and command
# Install terminal-notifier for faster notifications: brew install terminal-notifier
sudo() {
    local parent_cmd=$(ps -o comm= -p $PPID 2>/dev/null)
    local sudo_cmd="$*"
    local notif_title="Touch ID: Authorize sudo"
    local notif_subtitle="${parent_cmd:-shell} wants to run as root"
    local notif_msg="Command: $sudo_cmd"

    # macOS notification FIRST (before any output)
    if command -v terminal-notifier &>/dev/null; then
        terminal-notifier -title "$notif_title" -subtitle "$notif_subtitle" -message "$notif_msg" -timeout 5 &>/dev/null
    else
        osascript -e "display notification \"$notif_msg\" with title \"$notif_title\" subtitle \"$notif_subtitle\"" 2>/dev/null
        sleep 0.5  # Give notification time to render
    fi

    # Terminal output
    echo -e "\033[1;33m[sudo]\033[0m $notif_subtitle"
    echo -e "\033[1;33m[sudo]\033[0m $notif_msg"

    command sudo "$@"
}

What you'll see:

Terminal:

[sudo] zsh wants to run as root
[sudo] Command: rm -rf /tmp/junk

macOS Notification:

  • Title: "Touch ID: Authorize sudo"
  • Subtitle: "zsh wants to run as root" (or "claude", "node", etc.)
  • Message: "Command: rm -rf /tmp/junk"

Why this is useful:

  • Security awareness: Know exactly what you're authorizing before touching the sensor
  • AI agent visibility: When tools like Claude Code or Cursor run sudo, you'll see Parent: node or Parent: claude so you know it wasn't you
  • Audit trail: Terminal output shows the command history

Enable notifications (required):

For the macOS notification to appear, you need to allow notifications from Script Editor:

  1. Open System SettingsNotifications
  2. Scroll down to Script Editor
  3. Enable Allow Notifications
  4. Optionally set banner style to "Alerts" if you want them to persist until dismissed

If Script Editor doesn't appear in the list, run sudo once first - it will appear after osascript runs for the first time.

Faster notifications with terminal-notifier:

The built-in osascript has slight latency. For instant notifications, install terminal-notifier:

brew install terminal-notifier

The wrapper automatically uses it when available (no code changes needed).

Disable notification (terminal-only):

If you don't want the popup, use this simpler version:

sudo() {
    local parent_cmd=$(ps -o comm= -p $PPID 2>/dev/null)
    echo -e "\033[1;33m[sudo]\033[0m ${parent_cmd:-shell} wants to run as root"
    echo -e "\033[1;33m[sudo]\033[0m Command: $*"
    command sudo "$@"
}

Notes

  • On Apple Silicon Macs, the library is at /opt/homebrew/lib/pam/pam_reattach.so
  • On Intel Macs, it would be /usr/local/lib/pam/pam_reattach.so
  • pam_reattach is marked optional so sudo still works if the library is missing
  • Works in tmux, iTerm2, Terminal.app, Ghostty, and any other terminal emulator

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment