Skip to content

Instantly share code, notes, and snippets.

@igor47
Last active May 15, 2026 23:47
Show Gist options
  • Select an option

  • Save igor47/4c6b5e917fc776896afcc2b0e768ed49 to your computer and use it in GitHub Desktop.

Select an option

Save igor47/4c6b5e917fc776896afcc2b0e768ed49 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# claude-sudo — launch Claude Code with a usable sudo cache, or manage the
# global sudo timestamp drop-in directly.
#
# Usage:
# claude-sudo [claude-args...] # default: enable global sudo, run claude, revoke on exit
# claude-sudo run [claude-args...] # explicit form of the default
# claude-sudo resume # shorthand for: run --resume
# claude-sudo enable [DURATION] # install drop-in + auto-revert timer (default 30min)
# claude-sudo status # show drop-in + pending revert timer
# claude-sudo revoke # remove drop-in, cancel timer, drop ticket
# claude-sudo indicator # tmux statusline indicator (empty if no session)
#
# Anything not matching a reserved subcommand is passed through to claude, so
# `claude-sudo --resume` and `claude-sudo -p ...` Just Work.
#
# DURATION accepts systemd time spans: 30min, 1h, 2h30min, 300s, ...
# Reserved first-arg words: run, resume, enable, status, revoke, indicator, help, --help, -h.
set -euo pipefail
USER_NAME="$(id -un)"
USER_UID="$(id -u)"
SAFE="${USER_NAME//[^a-zA-Z0-9_]/_}"
SUDOERS_FILE="/etc/sudoers.d/claude-global-timestamp-${SAFE}"
UNIT_PREFIX="claude-sudo-revert-${SAFE}"
MARKER_DIR="/run/user/${USER_UID}/claude-sudo"
info() { printf '>> %s\n' "$*" >&2; }
list_units() {
systemctl list-units --all --no-legend --plain --type=timer --type=service \
"${UNIT_PREFIX}-*.timer" "${UNIT_PREFIX}-*.service" 2>/dev/null \
| awk '{print $1}'
}
stop_existing() {
local units
units=$(list_units)
if [[ -n "$units" ]]; then
info "sudo: stopping existing revert timer(s): $units"
# shellcheck disable=SC2086
sudo systemctl stop $units 2>/dev/null || true
# shellcheck disable=SC2086
sudo systemctl reset-failed $units 2>/dev/null || true
fi
}
install_dropin() {
local tmp
tmp="$(mktemp)"
printf 'Defaults:%s timestamp_type=global\n' "$USER_NAME" >"$tmp"
info "sudo: validating sudoers fragment with visudo -cf"
sudo visudo -cf "$tmp" >/dev/null
info "sudo: installing drop-in to $SUDOERS_FILE"
sudo install -m 0440 -o root -g root "$tmp" "$SUDOERS_FILE"
rm -f "$tmp"
}
schedule_revert() {
local duration="$1"
command -v systemd-run >/dev/null || { echo "systemd-run not available; aborting." >&2; exit 1; }
stop_existing
local unit="${UNIT_PREFIX}-$(date +%s)-$$"
info "sudo: scheduling revert timer $unit.timer (fires in $duration)"
sudo systemd-run --on-active="$duration" --unit="$unit" \
/bin/rm -f "$SUDOERS_FILE" >/dev/null
printf '%s\n' "$unit"
}
remove_dropin() {
stop_existing
info "sudo: removing drop-in $SUDOERS_FILE"
sudo rm -f "$SUDOERS_FILE"
info "sudo: invalidating cached ticket (sudo -k)"
sudo -k || true
}
cmd_status() {
if [[ -e "$SUDOERS_FILE" ]]; then
echo "drop-in: $SUDOERS_FILE (present)"
else
echo "drop-in: $SUDOERS_FILE (absent)"
fi
local units
units=$(list_units)
if [[ -n "$units" ]]; then
echo "pending revert timers:"
# shellcheck disable=SC2086
systemctl list-timers --all $units 2>/dev/null || true
else
echo "pending revert timers: none"
fi
}
cmd_revoke() {
remove_dropin
echo "Revoked: $SUDOERS_FILE removed, timers cancelled, ticket invalidated."
}
cmd_enable() {
local duration="${1:-30min}"
install_dropin
local unit
unit=$(schedule_revert "$duration")
cat <<EOF
Global sudo timestamp enabled for $USER_NAME; reverts in $duration.
drop-in : $SUDOERS_FILE
timer : $unit.timer
Check: claude-sudo status
Revoke: claude-sudo revoke
EOF
}
# cmd_run state is held in script-globals so the EXIT trap can read them
# after cmd_run returns (locals would be out of scope by then under set -u).
RUN_INSTALLED_HERE=0
RUN_KEEPALIVE_PID=
RUN_MARKER_FILE=
run_cleanup() {
[[ -n $RUN_KEEPALIVE_PID ]] && kill "$RUN_KEEPALIVE_PID" 2>/dev/null || true
[[ -n $RUN_MARKER_FILE ]] && rm -f "$RUN_MARKER_FILE"
rmdir "$MARKER_DIR" 2>/dev/null || true
if [[ $RUN_INSTALLED_HERE -eq 1 ]]; then
remove_dropin || true
fi
}
cmd_run() {
trap run_cleanup EXIT
# If a drop-in is already in place (e.g. from a prior `enable`), leave it
# and its timer alone on exit — the user installed it deliberately.
# Otherwise install one for this session, with a long-horizon revert timer
# as a safety net in case the EXIT trap doesn't fire (e.g. SIGKILL).
if [[ -e "$SUDOERS_FILE" ]]; then
info "sudo: drop-in already present, leaving in place on exit"
else
install_dropin
schedule_revert "8h" >/dev/null
RUN_INSTALLED_HERE=1
fi
# Drop a session marker (start time in epoch seconds) so the tmux statusline
# — via `claude-sudo indicator` — can show that this session is still open.
mkdir -p "$MARKER_DIR"
RUN_MARKER_FILE="$MARKER_DIR/$$"
date +%s >"$RUN_MARKER_FILE"
# Prime the (now-global) ticket so the first sudo from a tool call won't prompt.
sudo -v
# The global drop-in only widens *scope* (any tty/process sees the ticket);
# it does not extend timestamp_timeout (default ~5min). Without periodic
# refresh the cache expires mid-session and claude's tty-less Bash can't
# re-prompt — so keep it warm from this tty.
(
while true; do
sudo -nv 2>/dev/null || exit
sleep 240
done
) &
RUN_KEEPALIVE_PID=$!
claude "$@"
}
cmd_indicator() {
# Emits a full status-bar separator block so the tmux config can drop its
# own `][` between this and the next section. When inactive, renders the
# plain green `][` separator. When active, splits into `] [SUDO Xm] [` so
# the SUDO label sits inside its own bracket.
local oldest now elapsed hours mins
if [[ -d $MARKER_DIR ]]; then
oldest=$(cat "$MARKER_DIR"/* 2>/dev/null | sort -n | head -n1)
fi
if [[ -z ${oldest:-} ]]; then
printf '#[fg=green]][#[default] '
return 0
fi
now=$(date +%s)
elapsed=$(( now - oldest ))
hours=$(( elapsed / 3600 ))
mins=$(( (elapsed % 3600) / 60 ))
if (( hours > 0 )); then
printf '#[fg=green]] #[fg=red,bold][SUDO %dh%02dm]#[default] #[fg=green][#[default] ' "$hours" "$mins"
else
printf '#[fg=green]] #[fg=red,bold][SUDO %dm]#[default] #[fg=green][#[default] ' "$mins"
fi
}
show_help() { sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'; }
case "${1:-}" in
enable) shift; cmd_enable "$@" ;;
status) cmd_status ;;
revoke) cmd_revoke ;;
indicator) cmd_indicator ;;
help|--help|-h) show_help ;;
run) shift; cmd_run "$@" ;;
resume) shift; cmd_run --resume "$@" ;;
*) cmd_run "$@" ;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment