aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.github/copilot-instructions.md3
-rw-r--r--KEYBINDS.md2
-rw-r--r--README.md4
-rwxr-xr-xbootstrap.sh8
-rw-r--r--dot_config/nvim/lua/config/keymaps.lua12
-rw-r--r--dot_config/sway/config4
-rw-r--r--dot_config/zsh/dot_zshrc12
-rwxr-xr-xdot_local/bin/executable_doasedit203
-rw-r--r--dot_local/bin/executable_sudo59
-rw-r--r--etc/doas.conf3
-rw-r--r--etc/pam.d/sudo4
-rw-r--r--etc/sudoers-rs13
-rw-r--r--justfile34
-rw-r--r--meta/base.txt2
-rwxr-xr-xrun_onchange_after_deploy-etc.sh.tmpl40
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)
diff --git a/README.md b/README.md
index 6bf7c18..9c5a18c 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/justfile b/justfile
index 39992a2..b4dfc65 100644
--- a/justfile
+++ b/justfile
@@ -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