diff options
| author | 2026-05-13 13:43:31 +0100 | |
|---|---|---|
| committer | 2026-05-13 13:43:31 +0100 | |
| commit | 3d263bdbb48e7616a12af26ef094e5a416f9a735 (patch) | |
| tree | 34cf90cef24496ecfc271055255f8a7596f84627 | |
| parent | 51b8af587e46d4e03b059a51253d9671e27d08e3 (diff) | |
| download | dotfiles-3d263bdbb48e7616a12af26ef094e5a416f9a735.tar.gz dotfiles-3d263bdbb48e7616a12af26ef094e5a416f9a735.tar.bz2 dotfiles-3d263bdbb48e7616a12af26ef094e5a416f9a735.zip | |
feat(privesc): migrate from opendoas to sudo-rs
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.
| -rw-r--r-- | .github/copilot-instructions.md | 3 | ||||
| -rw-r--r-- | KEYBINDS.md | 2 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rwxr-xr-x | bootstrap.sh | 8 | ||||
| -rw-r--r-- | dot_config/nvim/lua/config/keymaps.lua | 12 | ||||
| -rw-r--r-- | dot_config/sway/config | 4 | ||||
| -rw-r--r-- | dot_config/zsh/dot_zshrc | 12 | ||||
| -rwxr-xr-x | dot_local/bin/executable_doasedit | 203 | ||||
| -rw-r--r-- | dot_local/bin/executable_sudo | 59 | ||||
| -rw-r--r-- | etc/doas.conf | 3 | ||||
| -rw-r--r-- | etc/pam.d/sudo | 4 | ||||
| -rw-r--r-- | etc/sudoers-rs | 13 | ||||
| -rw-r--r-- | justfile | 34 | ||||
| -rw-r--r-- | meta/base.txt | 2 | ||||
| -rwxr-xr-x | run_onchange_after_deploy-etc.sh.tmpl | 40 |
15 files changed, 83 insertions, 320 deletions
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 31c9842..d56fa93 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -17,7 +17,6 @@ The repo root is a chezmoi source directory. Files targeting `$HOME` use chezmoi - `meta/` contains plain text package lists for Arch Linux (one package per line, `#` comments). Each `.txt` file represents a group (e.g. `base.txt`, `dev.txt`, `wayland.txt`). Install with `just pkg-apply base dev` or `just pkg-apply` (all groups). Detect drift with `just pkg-status` (or `just status` for the aggregate). - `systemd-units/` contains plain text systemd unit lists split by scope: `systemd-units/system/<group>.txt` for system units (enabled via `sudo systemctl`) and `systemd-units/user/<group>.txt` for user units (enabled via `systemctl --user`). System groups are paired by name with `meta/` groups (e.g. `systemd-units/system/base.txt` ↔ `meta/base.txt`); user groups stand alone. Units listed here are enabled by `just unit-apply` (run automatically by `just init`, walks both scopes). Inspect with `just unit-list`, detect drift with `just unit-status`. The recipe group token is `<name>` or `system:<name>` (both → `system/<name>.txt`) or `user:<name>` (→ `user/<name>.txt`). E.g. `just unit-add user:graphical kanshi.service`. - `firefox/` contains Firefox/LibreWolf hardening overrides (`user-overrides.js`) and custom CSS (`chrome/userChrome.css`). Deployed by `run_onchange_after_deploy-firefox.sh.tmpl`. -- `dot_local/bin/executable_doasedit` (deployed to `~/.local/bin/doasedit`) is a small wrapper that emulates `sudoedit` for `doas`. - `bootstrap.sh` at the repo root is a POSIX shell script that takes a fresh minimal Arch install (only `base`) to a fully deployed state. It installs prerequisites, enables `%wheel` sudoers, bootstraps `paru-bin` from the AUR, clones the repo, and runs `just init`. On EFI systems missing an Arch boot entry it prints the `efibootmgr` command to register the UKI. Designed to be curlable: `curl -fsSL .../bootstrap.sh | sh`. - `.chezmoiignore` excludes non-home files (`etc/`, `meta/`, `systemd-units/`, `firefox/`, docs) from deployment to `$HOME`. - `.githooks/` contains git hooks: `pre-commit` runs `just check` as a code quality gate (bypass with `--no-verify`); `post-commit` runs `chezmoi apply`. Activated by `just init`. @@ -44,7 +43,7 @@ Additionally, `dot_config/sh/inputrc` provides readline config for non-zsh tools ## Key conventions - **XDG compliance**: All tools are configured to respect XDG base directories. History files, caches, and data go to `$XDG_CACHE_HOME`, `$XDG_DATA_HOME`, etc. — never bare `~/` dotfiles when avoidable. -- **`doas` over `sudo`**: The system uses `doas` as the privilege escalation tool; `sudo` is aliased to `doas`. +- **`sudo-rs` (memory-safe Rust sudo), not opendoas or the C sudo**: The system uses `sudo-rs` as the privilege escalation tool. The `sudo` package is not installed; `/usr/local/bin/{sudo,su,visudo,sudoedit}` are symlinks to the `sudo-rs` binaries. Sudoers config lives in `/etc/sudoers-rs` (sudo-rs prefers that path over `/etc/sudoers`). - **GPG-signed commits**: All git commits and tags are signed. The GPG agent also handles SSH authentication. - **Secrets via `pass`**: API keys and tokens are stored in the `pass` password manager and sourced into env vars at shell init, never hardcoded. - **EditorConfig**: LF line endings, UTF-8, final newlines, trimmed trailing whitespace. Lua uses 2-space indentation with 80-char line limit. Makefiles use tabs. diff --git a/KEYBINDS.md b/KEYBINDS.md index 7bf9745..de3ccef 100644 --- a/KEYBINDS.md +++ b/KEYBINDS.md @@ -23,7 +23,7 @@ Leader: `Space` | Local leader: `,` | n | `[w` / `]w` | Prev/next warning+ diagnostic | | n | `[e` / `]e` | Prev/next error diagnostic | | n | `yp` | Yank current file path to clipboard | -| | `:DoasWrite` | Write file with doas privileges | +| | `:SudoWrite` | Write file with sudo privileges | ### Navigation (plugins/init.lua) @@ -8,7 +8,7 @@ My Arch Linux configuration, managed with [chezmoi](https://www.chezmoi.io/). - **Wayland only.** No X server, no display manager. Sway starts from `exec sway` at the end of the zsh login shell on TTY1 (autologin via a host-local `getty@tty1` drop-in that's deliberately gitignored). - **XDG everywhere.** Every tool is pushed to `$XDG_CONFIG_HOME` / `$XDG_CACHE_HOME` / `$XDG_DATA_HOME` — `~` stays clean. Zsh itself lives under `$XDG_CONFIG_HOME/zsh`, bootstrapped by a single-line `dot_zshenv`. -- **[doas](https://wiki.archlinux.org/title/Doas), not sudo.** `sudo` is aliased to `doas` so muscle memory keeps working. +- **[sudo-rs](https://github.com/trifectatechfoundation/sudo-rs), not the C sudo.** Memory-safe Rust rewrite, drop-in CLI compatible. Same one that Ubuntu 25.10 ships as default. - **GPG for everything signable.** Commits and tags are signed; the same GPG agent also serves SSH authentication — one key, one cache, one PIN entry. - **Secrets via [`pass`](https://www.passwordstore.org/).** API keys and tokens are pulled into env vars at shell init; nothing is committed. - **Plain-text over configuration-as-code.** Packages and enabled units are tracked as one-per-line `.txt` files in `meta/` and `systemd-units/`, diffed against `pacman -Qeq` and `systemctl list-unit-files`. No DSL, no state file. @@ -18,7 +18,7 @@ My Arch Linux configuration, managed with [chezmoi](https://www.chezmoi.io/). | Category | Choice | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| OS & base | [Arch Linux](https://archlinux.org/), [paru](https://github.com/Morganamilo/paru) for AUR, [doas](https://wiki.archlinux.org/title/Doas) for privilege escalation | +| OS & base | [Arch Linux](https://archlinux.org/), [paru](https://github.com/Morganamilo/paru) for AUR, [sudo-rs](https://github.com/trifectatechfoundation/sudo-rs) for privilege escalation | | Dotfile manager | [chezmoi](https://www.chezmoi.io/) (dotfiles and `/etc` both deployed via `chezmoi apply`) | | Task runner | [just](https://just.systems/) — every maintenance action is a recipe (see below) | | Shell | [zsh](https://www.zsh.org/), relocated to `$XDG_CONFIG_HOME/zsh`; plugins via [zinit](https://github.com/zdharma-continuum/zinit) | diff --git a/bootstrap.sh b/bootstrap.sh index 65a9343..ead79e6 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -67,8 +67,12 @@ else fi # 5. run just init — this deploys chezmoi, installs the 'base' meta list -# (swapping sudo for doas-sudo-shim via paru -S --ask=4), deploys -# /etc/doas.conf, and installs git hooks. +# (which pulls in sudo-rs), deploys /etc/sudoers-rs and /etc/pam.d/sudo, +# creates /usr/local/bin/{sudo,su,visudo,sudoedit} symlinks pointing at +# sudo-rs, and installs git hooks. +# The classic 'sudo' package installed in step 2 stays alongside +# sudo-rs as a safety net; remove it manually with `sudo pacman -Rns +# sudo` once you've verified `sudo --version` reports sudo-rs. cd "$DOTFILES_DIR" log 'running just init' just init diff --git a/dot_config/nvim/lua/config/keymaps.lua b/dot_config/nvim/lua/config/keymaps.lua index a0dc19e..b3362a3 100644 --- a/dot_config/nvim/lua/config/keymaps.lua +++ b/dot_config/nvim/lua/config/keymaps.lua @@ -78,15 +78,15 @@ nmap("yp", function() vim.fn.setreg("+", vim.fn.expand("%")) end, "[Y]ank [P]ath") -local doas_exec = function(_cmd) +local sudo_exec = function(_cmd) vim.fn.inputsave() local password = vim.fn.inputsecret("Password: ") vim.fn.inputrestore() if not password or #password == 0 then - vim.notify("Invalid password, doas aborted", vim.log.levels.WARN) + vim.notify("Invalid password, sudo aborted", vim.log.levels.WARN) return false end - local out = vim.fn.system(string.format("doas -S %s", _cmd), password .. "\n") + local out = vim.fn.system(string.format("sudo -S %s", _cmd), password .. "\n") if vim.v.shell_error ~= 0 then print("\r\n") vim.notify(out, vim.log.levels.ERROR) @@ -95,7 +95,7 @@ local doas_exec = function(_cmd) return true end -vim.api.nvim_create_user_command("DoasWrite", function(opts) +vim.api.nvim_create_user_command("SudoWrite", function(opts) local tmpfile = vim.fn.tempname() local filepath if #opts.fargs == 1 then @@ -116,7 +116,7 @@ vim.api.nvim_create_user_command("DoasWrite", function(opts) ) -- no need to check error as this fails the entire function vim.api.nvim_exec2(string.format("write! %s", tmpfile), { output = true }) - if doas_exec(_cmd) then + if sudo_exec(_cmd) then -- refreshes the buffer and prints the "written" message vim.cmd.checktime() -- exit command mode @@ -129,5 +129,5 @@ vim.api.nvim_create_user_command("DoasWrite", function(opts) vim.fn.delete(tmpfile) end, { nargs = "?", - desc = "Write using doas permissions", + desc = "Write using sudo permissions", }) diff --git a/dot_config/sway/config b/dot_config/sway/config index 3dba142..73d505c 100644 --- a/dot_config/sway/config +++ b/dot_config/sway/config @@ -124,8 +124,8 @@ bindsym $mod+Shift+c reload bindsym $mod+Shift+e exec swaynag -t warning -m 'Session' \ -B 'Lock' 'playerctl -a pause; swaylock -f -e -c 282828' \ -B 'Logout' 'swaymsg exit' \ - -B 'Reboot' 'doas /usr/bin/reboot' \ - -B 'Poweroff' 'doas /usr/bin/poweroff' + -B 'Reboot' 'sudo /usr/bin/reboot' \ + -B 'Poweroff' 'sudo /usr/bin/poweroff' # ── Personal keybinds ───────────────────────────────────────────────────────── diff --git a/dot_config/zsh/dot_zshrc b/dot_config/zsh/dot_zshrc index e8fca42..3bce27c 100644 --- a/dot_config/zsh/dot_zshrc +++ b/dot_config/zsh/dot_zshrc @@ -53,7 +53,7 @@ zstyle ':completion:*:functions' ignored-patterns '_*' # hide internal c zstyle ':completion:*:*:kill:*' menu yes select # interactive menu for kill completion zstyle ':completion:*:kill:*' force-list always # always show process list for kill zstyle ':completion:*:cd:*' ignore-parents parent pwd # cd never completes . or .. -zstyle ':completion::complete:*' gain-privileges 1 # use doas/sudo for privileged completions +zstyle ':completion::complete:*' gain-privileges 1 # use sudo for privileged completions zstyle -e ':completion:*:approximate:*' \ max-errors 'reply=($((($#PREFIX+$#SUFFIX)/3))numeric)' # allow 1 typo per 3 chars typed @@ -200,13 +200,11 @@ alias ip="ip -color=auto" alias lsip="ip -human -color=auto --brief address show" alias ipa="ip -stats -details -human -color=auto address show" alias ipecho='curl ipecho.net/plain' -alias ss='doas ss -tupnl' +alias ss='sudo ss -tupnl' # Privilege escalation -alias sudo='doas' -alias sudoedit='doasedit' -alias gimme='doas chown $USER:$(id -gn $USER)' -alias pacdiff='doas pacdiff' +alias gimme='sudo chown $USER:$(id -gn $USER)' +alias pacdiff='sudo pacdiff' # Pacman alias pacopt='comm -13 <(pacman -Qqdt | sort) <(pacman -Qqdtt | sort)' @@ -216,7 +214,7 @@ alias g='git' # Systemd alias sys='systemctl' -alias ssys='doas systemctl' +alias ssys='sudo systemctl' alias sysu='systemctl --user' # Navigation 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 <<EOF -doasedit - edit non-user-editable files with an unprivileged editor - -usage: doasedit -h | -V -usage: doasedit file ... - -Options: - -h, --help display help message and exit - -V, --version display version information and exit - -- stop processing command line arguments - -Environment Variables: - DOAS_EDITOR program used to edit files - EDITOR program used to edit files if DOAS_EDITOR is unset - -To work properly doasedit needs to always start a new editor instance. Some -editors, graphical ones in particular, open files in previously running -instances. If so, append a command line argument to your (DOAS_)EDITOR variable -such that the editor will always start a new instance (e. g.: 'kate -n'). - -How it works: -Every File to be edited is duplicated to a user owned file in /tmp. The editor -is then run in user context. After closing the editor the user file replaces -the original file while preserving file attributes. All this is done using doas -as little as possible. Files are edited one after another, not all at once. -EOF -} - -# Checks for syntax errors in doas' config -# -# check_doas_conf <target> <tmp_target> -# -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 diff --git a/etc/doas.conf b/etc/doas.conf deleted file mode 100644 index fad7c3c..0000000 --- a/etc/doas.conf +++ /dev/null @@ -1,3 +0,0 @@ -permit persist setenv { PATH TERM LANG LC_ALL EDITOR DIFFPROG PAGER } :wheel -permit nopass :wheel as root cmd /usr/bin/poweroff -permit nopass :wheel as root cmd /usr/bin/reboot diff --git a/etc/pam.d/sudo b/etc/pam.d/sudo new file mode 100644 index 0000000..ab053c5 --- /dev/null +++ b/etc/pam.d/sudo @@ -0,0 +1,4 @@ +#%PAM-1.0 +auth include system-auth +account include system-auth +session include system-auth diff --git a/etc/sudoers-rs b/etc/sudoers-rs new file mode 100644 index 0000000..8326b8e --- /dev/null +++ b/etc/sudoers-rs @@ -0,0 +1,13 @@ +# Keep $EDITOR / $VISUAL when running visudo. +Defaults!/usr/bin/visudo-rs env_keep += "SUDO_EDITOR EDITOR VISUAL" +Defaults!/usr/local/bin/visudo env_keep += "SUDO_EDITOR EDITOR VISUAL" + +# Sanitize PATH for elevated commands. +Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/bin" + +# Root and the wheel group can run anything (after a password prompt). +root ALL=(ALL:ALL) ALL +%wheel ALL=(ALL:ALL) ALL + +# Passwordless poweroff/reboot (parity with the previous doas.conf). +%wheel ALL=(ALL) NOPASSWD: /usr/bin/poweroff, /usr/bin/reboot @@ -603,8 +603,8 @@ etc-status: fi if [ -r "$live" ]; then cmp -s "$src" "$live" || echo " modified: $live" - elif doas test -f "$live" 2>/dev/null; then - doas cat "$live" | cmp -s "$src" - || echo " modified: $live" + elif sudo test -f "$live" 2>/dev/null; then + sudo cat "$live" | cmp -s "$src" - || echo " modified: $live" else echo " missing: $live" fi @@ -642,11 +642,11 @@ etc-diff *paths: repo_for_diff=$repo rendered= fi - # Fast path for world-readable files; doas fallback only when needed (e.g. /etc/doas.conf 0600). + # Fast path for world-readable files; sudo fallback only when needed (e.g. /etc/sudo.conf 0600). if [ -r "$live" ]; then diff -u --label "$live" --label "$repo" "$live" "$repo_for_diff" || true - elif doas test -f "$live"; then - diff -u --label "$live" --label "$repo" <(doas cat "$live") "$repo_for_diff" || true + elif sudo test -f "$live"; then + diff -u --label "$live" --label "$repo" <(sudo cat "$live") "$repo_for_diff" || true else echo "skip: $live (missing or not a regular file on host)" >&2 fi @@ -672,7 +672,7 @@ etc-upstream-diff *paths: [ -f "$cache" ] && { echo "$cache"; return 0; } done echo " fetching $pkg from mirror..." >&2 - doas pacman -Sw --noconfirm "$pkg" >/dev/null || true + sudo pacman -Sw --noconfirm "$pkg" >/dev/null || true for ext in zst xz; do cache="/var/cache/pacman/pkg/${pkg}-${ver}-${arch}.pkg.tar.${ext}" [ -f "$cache" ] && { echo "$cache"; return 0; } @@ -697,8 +697,8 @@ etc-upstream-diff *paths: path=/etc/$p if [ -r "$path" ]; then live_reader=(cat "$path") - elif doas test -f "$path"; then - live_reader=(doas cat "$path") + elif sudo test -f "$path"; then + live_reader=(sudo cat "$path") else [ "$explicit" = 1 ] && { echo "error: $path missing or unreadable" >&2; exit 1; } echo "skip: $path (missing or unreadable)" >&2; continue @@ -733,13 +733,13 @@ etc-merge *paths: live=/etc/$p repo=etc/$p [ -f "$repo" ] || { echo "skip: etc/$p not tracked" >&2; continue; } - # Prepare a readable copy of live (falling back to doas cat for restricted files). + # Prepare a readable copy of live (falling back to sudo cat for restricted files). tmp=$(mktemp) trap 'rm -f "$tmp"' EXIT if [ -r "$live" ]; then cat -- "$live" > "$tmp" - elif doas test -f "$live"; then - doas cat -- "$live" > "$tmp" + elif sudo test -f "$live"; then + sudo cat -- "$live" > "$tmp" else echo "skip: $live (missing or unreadable)" >&2 rm -f "$tmp" @@ -771,8 +771,8 @@ etc-add +paths: [ -f "$path" ] || { echo "error: $path is not a regular file (symlinks/dirs not supported)" >&2; exit 1; } dest="etc/${path#/etc/}" mkdir -p "$(dirname "$dest")" - doas cp -a "$path" "$dest" - doas chown "$USER:$USER" "$dest" + sudo cp -a "$path" "$dest" + sudo chown "$USER:$USER" "$dest" echo "added: $path -> $dest" done echo @@ -814,7 +814,7 @@ etc-re-add *paths: if [ -r "$live" ]; then cat -- "$live" > "$repo.tmp" else - doas cat -- "$live" > "$repo.tmp" + sudo cat -- "$live" > "$repo.tmp" fi if cmp -s "$repo" "$repo.tmp"; then rm -f "$repo.tmp" @@ -875,7 +875,7 @@ etc-reset +paths: done if [ -z "$cache" ]; then echo " fetching $pkg from mirror..." >&2 - doas pacman -Sw --noconfirm "$pkg" >/dev/null || true + sudo pacman -Sw --noconfirm "$pkg" >/dev/null || true for ext in zst xz; do c="/var/cache/pacman/pkg/${pkg}-${ver}-${arch}.pkg.tar.${ext}" [ -f "$c" ] && { cache="$c"; break; } @@ -916,7 +916,7 @@ etc-restore +paths: done if [ -z "$cache" ]; then echo " fetching $pkg from mirror..." >&2 - doas pacman -Sw --noconfirm "$pkg" >/dev/null || true + sudo pacman -Sw --noconfirm "$pkg" >/dev/null || true for ext in zst xz; do c="/var/cache/pacman/pkg/${pkg}-${ver}-${arch}.pkg.tar.${ext}" [ -f "$c" ] && { cache="$c"; break; } @@ -927,7 +927,7 @@ etc-restore +paths: || { echo "error: $live not present in $pkg archive" >&2; exit 1; } # Extract with -p to preserve owner/mode/mtime so pacman -Qkk sees the # file as unmodified (same metadata as install time, not just same bytes). - doas bsdtar -xpf "$cache" -C / "${live#/}" + sudo bsdtar -xpf "$cache" -C / "${live#/}" echo "restored (from $pkg): $live" done diff --git a/meta/base.txt b/meta/base.txt index 1c6a81b..9c56e52 100644 --- a/meta/base.txt +++ b/meta/base.txt @@ -8,7 +8,6 @@ choose cpupower curlie dashbinsh -doas-sudo-shim dog duf fastfetch @@ -49,6 +48,7 @@ rsync sbctl sd smartmontools +sudo-rs systemd-resolvconf tldr tlp diff --git a/run_onchange_after_deploy-etc.sh.tmpl b/run_onchange_after_deploy-etc.sh.tmpl index d82a1d6..9ba10d2 100755 --- a/run_onchange_after_deploy-etc.sh.tmpl +++ b/run_onchange_after_deploy-etc.sh.tmpl @@ -7,20 +7,30 @@ set -eu cd "$CHEZMOI_SOURCE_DIR" find etc -type f ! -name .ignore | while IFS= read -r src; do - case "$src" in - *.tmpl) - dest="/${src%.tmpl}" - tmp=$(mktemp) - chezmoi execute-template <"$src" >"$tmp" - doas install -D -m 0644 -o root -g root "$tmp" "$dest" - rm -f "$tmp" - ;; - *) - dest="/${src}" - doas install -D -m 0644 -o root -g root "$src" "$dest" - ;; - esac + case "$src" in + *.tmpl) + dest="/${src%.tmpl}" + tmp=$(mktemp) + chezmoi execute-template <"$src" >"$tmp" + sudo install -D -m 0644 -o root -g root "$tmp" "$dest" + rm -f "$tmp" + ;; + etc/sudoers-rs) + sudo install -D -m 0440 -o root -g root "$src" "/${src}" + ;; + *) + sudo install -D -m 0644 -o root -g root "$src" "/${src}" + ;; + esac done -# doas refuses to parse /etc/doas.conf unless it's 0400 root:root -doas chmod 0400 /etc/doas.conf +# sudo-rs: /etc/pam.d/sudo-i is a symlink to /etc/pam.d/sudo +sudo ln -sfT sudo /etc/pam.d/sudo-i + +# Make sudo-rs the system-wide sudo via /usr/local/bin precedence. +# Targets may not exist yet on first bootstrap (sudo-rs is installed by +# the subsequent pkg-apply step); the symlinks resolve once it lands. +sudo ln -sfT /usr/bin/sudo-rs /usr/local/bin/sudo +sudo ln -sfT /usr/bin/sudo-rs /usr/local/bin/sudoedit +sudo ln -sfT /usr/bin/su-rs /usr/local/bin/su +sudo ln -sfT /usr/bin/visudo-rs /usr/local/bin/visudo |
