From 3d263bdbb48e7616a12af26ef094e5a416f9a735 Mon Sep 17 00:00:00 2001 From: sommerfeld Date: Wed, 13 May 2026 13:43:31 +0100 Subject: feat(privesc): migrate from opendoas to sudo-rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit doas's one-shot password and absent 'sudo -v' kept wasting hour-long paru AUR builds. sudo-rs is a memory-safe Rust rewrite (ISRG/Ferrous Systems), drop-in CLI compatible, and the same one Ubuntu 25.10 ships as default. We follow the Arch wiki 'Using sudo-rs without the sudo package' recipe verbatim — no custom shims. - meta/base.txt: -doas-sudo-shim +sudo-rs - etc/sudoers-rs (mode 0440): wiki minimal config + NOPASSWD reboot/poweroff - etc/pam.d/sudo: 4-line copy of upstream sudo's PAM file - run_onchange_after_deploy-etc.sh.tmpl: use real sudo, deploy sudoers-rs at 0440, create /etc/pam.d/sudo-i and /usr/local/bin/{sudo,sudoedit, su,visudo} → sudo-rs symlinks idempotently - delete etc/doas.conf, dot_local/bin/{doasedit,sudo} - zshrc: drop sudo=doas/sudoedit=doasedit aliases; rewrite ss/gimme/ pacdiff/ssys to call sudo - justfile: s/doas/sudo/g (status/diff/restore helpers) - nvim: rename :DoasWrite → :SudoWrite (uses sudo -S) - sway config: reboot/poweroff buttons call sudo - bootstrap.sh: update step-5 comment - README/KEYBINDS/copilot-instructions: flip the privesc convention No Defaults overrides: sudo's defaults (passwd_tries=3, timestamp_timeout=5) already fix the doas pain, and paru SudoLoop (kept) refreshes the 5-min window via real sudo -v. --- dot_local/bin/executable_doasedit | 203 -------------------------------------- dot_local/bin/executable_sudo | 59 ----------- 2 files changed, 262 deletions(-) delete mode 100755 dot_local/bin/executable_doasedit delete mode 100644 dot_local/bin/executable_sudo (limited to 'dot_local') diff --git a/dot_local/bin/executable_doasedit b/dot_local/bin/executable_doasedit deleted file mode 100755 index 6483d75..0000000 --- a/dot_local/bin/executable_doasedit +++ /dev/null @@ -1,203 +0,0 @@ -#!/bin/sh -e -# shellcheck disable=SC3067 # /bin/sh on Arch is bash; -O is supported - -help() { - cat - >&2 < -# -check_doas_conf() { - if printf '%s' "${1}" | grep -q '^/etc/doas\(\.d/.*\)\?\.conf$'; then - while ! doas -C "${2}"; do - printf "doasedit: Replacing '%s' would " "$file" - printf 'introduce the above error and break doas.\n' - printf '(E)dit again, (O)verwrite anyway, (A)bort: [E/o/a]? ' - read -r choice - case "$choice" in - o | O) - return 0 - ;; - a | A) - return 1 - ;; - e | E | *) - "$editor_cmd" "$tmpfile" - ;; - esac - done - fi - return 0 -} - -error() { - printf 'doasedit: %s\n' "${@}" 1>&2 -} - -_exit() { - rm -rf "$tmpdir" - trap - EXIT HUP QUIT TERM INT ABRT - exit "${1:-0}" -} - -# no argument passed -[ "${#}" -eq 0 ] && help && exit 1 - -while [ "${#}" -ne 0 ]; do - case "${1}" in - --) - shift - break - ;; - --help | -h) - help - exit 0 - ;; - --version | -V) - printf 'doasedit version 1.0.7\n' - exit 0 - ;; - -*) - printf "doasedit: invalid option: '%s'\n" "${1}" - help - exit 1 - ;; - *) - break - ;; - esac -done - -[ "$DOAS_EDITOR" != "" ] && editor_cmd="$DOAS_EDITOR" || editor_cmd="$EDITOR" -# shellcheck disable=SC2086 -if [ "$editor_cmd" = "" ]; then - if command -v vi >/dev/null 2>&1; then - editor_cmd='vi' - else - error 'no editor specified' - exit 1 - fi -elif ! command -v "$editor_cmd" >/dev/null 2>&1; then - error "invalid editor command: '${editor_cmd}'" - exit 1 -fi - -exit_code=1 -trap '_exit "${exit_code}"' EXIT -trap '_exit 130' HUP QUIT TERM INT ABRT -tmpdir="$(mktemp -dt 'doasedit-XXXXXX')" - -for file; do - unset exists readable writable - dir="$(dirname -- "$file")" - tmpfile="${tmpdir}/${file##*/}" - tmpfile_copy="${tmpdir}/copy-of-${file##*/}" - printf '' | tee "$tmpfile" >"$tmpfile_copy" - chmod 0600 "$tmpfile" "$tmpfile_copy" - - if [ -e "$file" ]; then - if ! [ -f "$file" ]; then - error "${file}: not a regular file" - continue - fi - # -O is not POSIX, but implemented at least in GNU, *BSD and macOS test - if [ -O "$file" ]; then - error "${file}: editing your own files is not permitted" - continue - fi - exists=1 - elif doas [ -e "$file" ]; then - if ! doas [ -f "$file" ]; then - error "${file}: not a regular file" - continue - fi - exists=0 - else - # New file? - if [ -O "$dir" ]; then - error "${file}: creating files in your own directory is not permitted" - continue - elif [ -x "$dir" ] && [ -w "$dir" ]; then - error "${file}: creating files in a user-writable directory is not permitted" - continue - elif ! doas [ -e "$dir" ]; then - error "${file}: no such directory" - continue - # else: root-writable directory - fi - fi - # If this test is true, it's an existent regular file - if [ "$exists" != "" ]; then - if [ -w "$file" ]; then - writable=1 - # Check in advance to make sure that it won't fail after editing. - elif ! doas dd status=none count=0 of=/dev/null; then - error "unable to run 'doas dd'" - continue - fi - if [ -r "$file" ]; then - if [ "$writable" != "" ]; then - error "${file}: editing user-readable and -writable files is not permitted" - continue - fi - # Read file - cat -- "$file" >"$tmpfile" - # Better not suppress stderr here as there might be something of importance. - elif ! doas cat -- "$file" >"$tmpfile"; then - error "you are not permitted to call 'doas cat'" - continue - fi - cat "$tmpfile" >"$tmpfile_copy" - fi - - "$editor_cmd" "$tmpfile" - - check_doas_conf "$file" "$tmpfile" || continue - if cmp -s "$tmpfile" "$tmpfile_copy"; then - printf 'doasedit: %s: unchanged\n' "$file" - else - if [ "$writable" != "" ]; then - dd status=none if="$tmpfile" of="$file" - else - for de_tries in 2 1 0; do - if doas dd status=none if="$tmpfile" of="$file"; then - break - elif [ "$de_tries" -eq 0 ]; then - error '3 incorrect password attempts' - exit 1 - fi - done - fi - fi - - exit_code=0 -done - -# vim: shiftwidth=2 tabstop=2 noexpandtab diff --git a/dot_local/bin/executable_sudo b/dot_local/bin/executable_sudo deleted file mode 100644 index b643f34..0000000 --- a/dot_local/bin/executable_sudo +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh -# sudo → doas shim that takes precedence over /usr/bin/sudo -# (provided by doas-sudo-shim) by living in $HOME/.local/bin. -# -# Why a custom shim: opendoas does not implement `sudo -v` (extend the -# auth timestamp without running a command). paru --sudoloop relies on -# that to keep credentials fresh during long AUR builds; without it, -# building gcc-git for an hour then mistyping the password at the install -# step throws the whole build away. We translate the handful of sudo -# flags paru / common scripts use into doas equivalents and swallow the -# rest. -# -# Translations: -# -v / --validate → doas true (refresh persist timestamp) -# -k / -K → doas -L (clear persist timestamp) -# -n → doas -n -# -E -H -i -S → silently dropped -# anything else → doas "$@" - -set -eu - -forward= -for arg; do - case $arg in - -v|--validate) - exec doas true - ;; - -k|-K) - exec doas -L - ;; - -h|--help) - exec doas -h - ;; - -n) - forward="$forward -n" - ;; - -E|-H|-i|-S|--preserve-env|--set-home|--login|--stdin) - # meaningless under doas; drop - ;; - --) - shift - # shellcheck disable=SC2086 - exec doas $forward "$@" - ;; - -*) - # unknown flag — pass through and let doas complain - forward="$forward $arg" - ;; - *) - # first non-flag: rest of argv is the command - # shellcheck disable=SC2086 - exec doas $forward "$@" - ;; - esac - shift -done - -# Only flags, no command — treat as `sudo -v` semantics. -exec doas true -- cgit v1.3.1