From aadead9f38f38cde8f99ac29de82f8c0b20d4fd5 Mon Sep 17 00:00:00 2001 From: Pathfinder Date: Tue, 27 Oct 2026 11:37:12 +0100 Subject: [PATCH] System settings: optional SELinux disable; btrfs snapper + zstd compression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New section between base and DE menus: - 'Disable SELinux permanently?' — default no; writes SELINUX=disabled with a backup, warns about the security tradeoff. - When / is btrfs: 'snapper + grub-btrfs + compression?' — default yes. Shows subvolume list + fstab, appends ,compress=zstd:1 to btrfs fstab entries that lack a compress= option (idempotent, fstab backed up), then runs the SysGuides sysguides-snapper-fedora installer (snapper root+home, grub-btrfs, DNF5 pre/post snapshot actions). - Flags for unattended runs: --selinux=keep|disabled, --snapper=yes|no. - Fix found by the harness: the flag initializers sat after the arg loop and wiped parsed values; --snapper=no was silently ignored. Initializers moved before the loop. README + section renumbering updated. --- README.md | 20 +++++++++++++ lib/common.sh | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ setup.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 178 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cba42a0..74519ca 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,26 @@ compositors — do not expect the same config story from it. Wayland compositors *and* X11 sessions in one menu. * Either way `graphical.target` becomes the default. + +## System settings + +Between base setup and the DE menu, the script asks two questions: + +1. **Disable SELinux permanently?** — default **no**. Writes `SELINUX=disabled` + to `/etc/selinux/config` (backup kept); a reboot applies it. Recommended + answer is no. +2. **Snapper + grub-btrfs + zstd compression** — only asked when `/` is btrfs, + default **yes**. Runs, in order: + - `btrfs subvolume list /` and the fstab btrfs entries are shown + - every fstab btrfs entry with a `subvol=` but no `compress=` gets + `,compress=zstd:1` appended (fstab backed up first; re-runs cannot stack + duplicate options) + - the [SysGuides installer](https://github.com/SysGuides/sysguides-snapper-fedora) + sets up snapper (root + home), grub-btrfs, and DNF5 pre/post snapshot hooks, + so every `dnf` transaction is snapshotted and `snapper undochange` works. + +Unattended equivalents: `--selinux=keep|disabled` and `--snapper=yes|no`. + ## Design rules 1. **Fail loudly, early.** `set -e` is on unless `--debug`. Unsupported distro, diff --git a/lib/common.sh b/lib/common.sh index ae1a754..0ea6874 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -201,3 +201,85 @@ install_nerd_font() { fc-cache -f "$dest" >/dev/null 2>&1 || true ok "Nerd Font ${family} installed to $dest" } + + +#------------------------------------------ +# System: SELinux, Btrfs, snapper +#------------------------------------------ +# Permanently disable SELinux. Destructive-ish: warn loudly, and remind the +# user a reboot is needed for the change to take effect. +disable_selinux() { + # FSTAB/SELINUX file overridable for testing on non-Fedora hosts + local cfg="${SELINUX_CONFIG:-/etc/selinux/config}" + if [[ -f "$cfg" ]] && grep -q '^SELINUX=disabled' "$cfg"; then + ok "SELinux already disabled in $cfg." + return 0 + fi + warn "Disabling SELinux permanently. This removes a significant layer of" + warn "system protection. Only do this if you specifically need it." + if [[ -f "$cfg" ]]; then + sudo cp "$cfg" "${cfg}.bkp.$(date +%F)" + sudo sed -i 's/^SELINUX=.*/SELINUX=disabled/' "$cfg" + else + printf 'SELINUX=disabled\nSELINUXTYPE=targeted\n' | sudo tee "$cfg" >/dev/null + fi + ok "SELINUX=disabled written to $cfg (backup saved). Reboot to apply." +} + +# Show the btrfs layout, then make sure every btrfs fstab entry compresses +# with zstd. The sed variant from the SysGuides guide appends ,compress=zstd:1 +# after the subvol= option; we guard it so re-runs cannot stack duplicates. +ensure_btrfs_compression() { + local fstab="${FSTAB_FILE:-/etc/fstab}" + info "Current btrfs subvolumes on /:" + sudo btrfs subvolume list / 2>/dev/null | sed 's/^/ /' || warn "btrfs subvolume list failed (not btrfs?)" + + info "fstab btrfs entries:" + grep ' btrfs ' "$fstab" | sed 's/^/ /' || true + + local fixed=0 line + # Only touch btrfs lines that have a subvol= option and no compress= yet. + while IFS= read -r line; do + if grep -q ' btrfs ' <<<"$line" && grep -q 'subvol=' <<<"$line" && ! grep -q 'compress=' <<<"$line"; then + fixed=$((fixed+1)) + fi + done < "$fstab" + + if (( fixed == 0 )); then + ok "All btrfs fstab entries already carry a compress= option (or none exist)." + return 0 + fi + + info "Adding compress=zstd:1 to ${fixed} btrfs fstab entrie(s)..." + # Backup once per run, then apply the SysGuides sed to every qualifying line. + sudo cp "$fstab" "$fstab.bkp.$(date +%F-%H%M)" + sudo sed -i '/ btrfs / s/subvol=[^ ,]*/&,compress=zstd:1/' "$fstab" + ok "fstab updated. Remount or reboot for compression to take effect:" + info " sudo mount -o remount / && sudo mount -o remount /home" +} + +# Full snapshot setup via the SysGuides installer (snapper + grub-btrfs + +# DNF5 pre/post snapshot actions). The installer re-validates btrfs itself. +setup_snapper() { + if pkg_installed snapper && [[ -d /.snapshots ]]; then + ok "Snapper already configured (config for / exists)." + return 0 + fi + if ! findmnt -n -o FSTYPE / 2>/dev/null | grep -q btrfs; then + warn "Root filesystem is not btrfs; skipping snapshot setup." + return 0 + fi + info "Setting up snapper + grub-btrfs (SysGuides installer)..." + local tmp + tmp=$(mktemp -d) + git clone --depth 1 https://github.com/SysGuides/sysguides-snapper-fedora "$tmp/sysguides" \ + || { err "Could not clone the SysGuides installer."; rm -rf "$tmp"; return 1; } + ( cd "$tmp/sysguides" && ./install.sh ) + local rc=$? + rm -rf "$tmp" + if (( rc == 0 )); then + ok "Snapper + grub-btrfs installed. Every dnf transaction now gets a pre/post snapshot." + info "Try it: snapper ls | snapper status 1..2 | sudo snapper undochange 1..2" + fi + return $rc +} diff --git a/setup.sh b/setup.sh index ac493a6..0555ae0 100755 --- a/setup.sh +++ b/setup.sh @@ -19,6 +19,8 @@ DEBUG_MODE=false ASSUME_YES=false DE="" TWM="" +SELINUX_OPT="" +SNAPPER_OPT="" usage() { cat <<'EOF' @@ -32,6 +34,8 @@ Usage: Options: --de=NAME gnome | plasma | xfce | none --twm=NAME chadwm | hyprland | mango | niri | none + --selinux=X keep | disabled (asked, default keep) + --snapper=X yes | no (asked when / is btrfs, default yes) -y, --yes never prompt (requires --de and --twm) -d, --debug pause after each section instead of aborting on error -h, --help show this help @@ -53,6 +57,8 @@ for arg in "$@"; do -d|--debug) DEBUG_MODE=true ;; -y|--yes) ASSUME_YES=true ;; --de=*) DE="${arg#*=}" ;; + --selinux=*) SELINUX_OPT="${arg#*=}" ;; + --snapper=*) SNAPPER_OPT="${arg#*=}" ;; --twm=*) TWM="${arg#*=}" ;; -h|--help) usage; exit 0 ;; *) @@ -201,8 +207,62 @@ ask_choice() { done } +# Ask a yes/no question; $2 is the default (yes|no). Honours --yes. +ask_yes_no() { + local prompt="$1" default="$2" answer + if [[ "$ASSUME_YES" == "true" ]]; then + echo "$default" + return 0 + fi + while true; do + read -t 60 -rp "$prompt [y/n] (default: $default): " answer || printf "\n" + answer="${answer,,}" + [[ -z "$answer" ]] && answer="$default" + case "$answer" in + y|yes) echo "yes"; return 0 ;; + n|no) echo "no"; return 0 ;; + esac + err "Please answer y or n." + done +} + #------------------------------------------ -# 3 Desktop environment selection +# 3 System settings +#------------------------------------------ +SELINUX_DISABLED=false +BTRFS_SNAPPER=false + +if [[ -z "$SELINUX_OPT" ]]; then + step "3 System settings" + rule + printf "\n" + printf "\n" + choice=$(ask_yes_no "Disable SELinux permanently? (not recommended)" "no") + [[ "$choice" == "yes" ]] && SELINUX_DISABLED=true + printf "\n" + if findmnt -n -o FSTYPE / 2>/dev/null | grep -q btrfs; then + ok "Root filesystem is btrfs — snapshot support is available." + choice=$(ask_yes_no "Set up snapper + grub-btrfs snapshots and zstd compression?" "yes") + [[ "$choice" == "yes" ]] && BTRFS_SNAPPER=true + else + info "Root filesystem is not btrfs; skipping snapshot setup." + fi +fi + +case "${SELINUX_OPT:-}" in + disabled) SELINUX_DISABLED=true ;; + keep|"") : ;; + *) err "Unknown --selinux value: '$SELINUX_OPT' (keep|disabled)"; exit 2 ;; +esac +case "${SNAPPER_OPT:-}" in + yes) BTRFS_SNAPPER=true ;; + no|"") : ;; + *) err "Unknown --snapper value: '$SNAPPER_OPT' (yes|no)"; exit 2 ;; +esac +pause_if_debug + +#------------------------------------------ +# 4 Desktop environment selection #------------------------------------------ if [[ -z "$DE" ]]; then step "3 Desktop environment" @@ -232,10 +292,10 @@ esac pause_if_debug #------------------------------------------ -# 4 Tiling window manager selection +# 5 Tiling window manager selection #------------------------------------------ if [[ -z "$TWM" ]]; then - step "4 Tiling window manager" + step "5 Tiling window manager" rule printf "\n" echo " 1) Niri (Wayland, scrollable)" @@ -290,6 +350,17 @@ run_module() { step "5 Installation" rule +if [[ "$SELINUX_DISABLED" == "true" ]]; then + disable_selinux + pause_if_debug +fi + +if [[ "$BTRFS_SNAPPER" == "true" ]]; then + ensure_btrfs_compression + setup_snapper + pause_if_debug +fi + if [[ "$DE" != "none" ]]; then run_module "$SCRIPT_DIR/modules/de/${DE}.sh" pause_if_debug @@ -303,10 +374,11 @@ fi #------------------------------------------ # 6 Done #------------------------------------------ -step "6 Summary" +step "7 Summary" rule ok "DE: $DE" ok "TWM: $TWM" +ok "SELinux: $([[ "$SELINUX_DISABLED" == "true" ]] && echo disabled || echo keeping)"; ok "Btrfs snapshots: $([[ "$BTRFS_SNAPPER" == "true" ]] && echo yes || echo skipped)" DM=$(detect_display_manager) ok "Display manager: ${DM:-none}"