feat: Debian prep hardening, verify script, and quality pass
- 00-system-prep.sh: bootstrap sudo when run as root; on Debian enable contrib/non-free-firmware (SKIP_NONFREE to opt out) + install needrestart - scripts/verify-debian.sh: non-destructive post-install sanity checker (exit 0/1) covering OS, tools, Debian renames, upstream tooling, .NET/Podman/Postgres, groups, flatpak, dotfile symlinks - 01-package-install.sh: auto-detect current Kubernetes minor from upstream (fallback v1.36, override K8S_MINOR) instead of stale v1.32 pin - fix shellcheck findings (SC2155, SC2207, SC2088); all scripts clean at warning severity - docs: fold decisions/status into plan; README lists new tools
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 00-system-prep.sh
|
||||
# Updates system and sets up Flathub.
|
||||
# Updates system, bootstraps sudo, enables Debian extra components,
|
||||
# and sets up Flathub.
|
||||
# Relies on DISTRO and PACKAGE_MANAGER being set by the caller.
|
||||
|
||||
echo "--- Starting System Preparation ---"
|
||||
@@ -11,6 +12,27 @@ if [ -z "$DISTRO" ] || [ -z "$PACKAGE_MANAGER" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure 'sudo' exists so all later scripts can use it.
|
||||
# - Running as root on a fresh Debian: installing sudo lets root invoke
|
||||
# `sudo ...` directly (root is not subject to sudoers).
|
||||
# - Non-root users without sudo are handled by main-setup.sh's preflight.
|
||||
if ! command -v sudo &>/dev/null; then
|
||||
echo "sudo not found. Bootstrapping it..."
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
if [ "$PACKAGE_MANAGER" == "apt" ]; then
|
||||
apt-get update && apt-get install -y sudo
|
||||
elif [ "$PACKAGE_MANAGER" == "dnf" ]; then
|
||||
dnf install -y sudo
|
||||
fi
|
||||
echo "sudo installed. Add your normal user to the sudo group if desired:"
|
||||
echo " usermod -aG sudo <your-user> (then log out/in)"
|
||||
else
|
||||
echo "ERROR: 'sudo' is required but missing, and you are not root."
|
||||
echo "As root, run: apt install -y sudo && usermod -aG sudo \$USER"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update system before installing packages
|
||||
echo "Updating system packages..."
|
||||
if [ "$PACKAGE_MANAGER" == "dnf" ]; then
|
||||
@@ -21,6 +43,66 @@ else
|
||||
echo "WARNING: Unknown package manager '$PACKAGE_MANAGER'. Skipping system update."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Debian extras (only on Debian; idempotent)
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ "$DISTRO" == "debian" ]; then
|
||||
|
||||
# Enable contrib + non-free-firmware components on the main archive.
|
||||
# Needed for some WiFi/GPU firmware and non-free tooling. Skip with SKIP_NONFREE=1.
|
||||
if [ -z "$SKIP_NONFREE" ]; then
|
||||
echo "Enabling 'contrib' and 'non-free-firmware' apt components..."
|
||||
_enable_debian_components() {
|
||||
local f="/etc/apt/sources.list.d/debian.sources"
|
||||
local tmp
|
||||
# deb822 format used by Debian 12+ (trixie ships debian.sources)
|
||||
if [ -f "$f" ]; then
|
||||
tmp="$(mktemp)"
|
||||
if awk '
|
||||
/^URIs:[[:space:]]/ { in_archive = ($0 ~ /deb\.debian\.org\/debian/) }
|
||||
in_archive && /^Components:/ && $0 !~ /contrib/ {
|
||||
print $0 " contrib non-free-firmware"
|
||||
in_archive = 0
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$f" > "$tmp"; then
|
||||
if ! diff -q "$f" "$tmp" >/dev/null; then
|
||||
sudo cp "$f" "${f}.bak"
|
||||
sudo mv "$tmp" "$f"
|
||||
echo " -> updated ${f}"
|
||||
else
|
||||
rm -f "$tmp"
|
||||
echo " -> components already present in ${f}"
|
||||
fi
|
||||
else
|
||||
rm -f "$tmp"
|
||||
echo "WARNING: could not parse ${f}; skipping component changes."
|
||||
fi
|
||||
fi
|
||||
# Legacy single-line /etc/apt/sources.list fallback
|
||||
if [ -f /etc/apt/sources.list ] \
|
||||
&& grep -qE '^deb[[:space:]]+https?://deb\.debian\.org/debian' /etc/apt/sources.list \
|
||||
&& ! grep -qE '^deb[[:space:]]+https?://deb\.debian\.org/debian[^#]*non-free-firmware' /etc/apt/sources.list; then
|
||||
sudo sed -i -E 's/^(deb[[:space:]]+https?:\/\/deb\.debian\.org\/debian[^#]*main)([[:space:]]|$)/\1 contrib non-free-firmware/' /etc/apt/sources.list
|
||||
echo " -> updated /etc/apt/sources.list"
|
||||
fi
|
||||
sudo apt update
|
||||
}
|
||||
_enable_debian_components
|
||||
else
|
||||
echo "SKIP_NONFREE is set - leaving apt components unchanged."
|
||||
fi
|
||||
|
||||
# needrestart prompts (or auto-restarts) services after library upgrades.
|
||||
if ! dpkg-query -W -f='${Status}' needrestart 2>/dev/null | grep -q "ok installed"; then
|
||||
echo "Installing needrestart..."
|
||||
sudo apt install -y needrestart
|
||||
else
|
||||
echo "needrestart already installed."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Setup Flatpak
|
||||
echo "Setting up Flathub repository..."
|
||||
if command -v flatpak &> /dev/null; then
|
||||
|
||||
@@ -76,7 +76,7 @@ fi
|
||||
|
||||
# Combine package lists
|
||||
PACKAGE_LIST=("${BASE_PACKAGE_LIST[@]}" "${DISTRO_SPECIFIC_PACKAGES[@]}")
|
||||
PACKAGE_LIST=($(printf "%s\n" "${PACKAGE_LIST[@]}" | LC_ALL=C sort -u))
|
||||
mapfile -t PACKAGE_LIST < <(printf "%s\n" "${PACKAGE_LIST[@]}" | LC_ALL=C sort -u)
|
||||
|
||||
# --- apt helpers ---------------------------------------------------------
|
||||
_apt_is_installed() {
|
||||
@@ -210,10 +210,21 @@ _apt_add_hashicorp_repo() {
|
||||
| sudo tee /etc/apt/sources.list.d/hashicorp.list >/dev/null
|
||||
}
|
||||
|
||||
# Kubernetes apt repo. Bump K8S_MINOR to track the current stable minor.
|
||||
K8S_MINOR="v1.32"
|
||||
# Kubernetes apt repo. Auto-detects the latest stable minor from the
|
||||
# kubernetes/kubernetes GitHub release feed, falling back to a pinned minor
|
||||
# if the API is unreachable. Override explicitly with K8S_MINOR=...
|
||||
K8S_MINOR_FALLBACK="v1.36"
|
||||
_k8s_latest_minor() {
|
||||
local tag minor
|
||||
tag="$(curl -fsSL --max-time 10 https://api.github.com/repos/kubernetes/kubernetes/releases/latest \
|
||||
| grep '"tag_name"' | cut -d'"' -f4)" || return 1
|
||||
minor="${tag#v}" # e.g. 1.36.0
|
||||
minor="${minor%.*}" # strip patch -> 1.36
|
||||
echo "v${minor}"
|
||||
}
|
||||
_apt_add_kubernetes_repo() {
|
||||
[ -f /etc/apt/sources.list.d/kubernetes.list ] && { echo "Kubernetes repo already configured."; return 0; }
|
||||
K8S_MINOR="${K8S_MINOR:-$(_k8s_latest_minor || echo "$K8S_MINOR_FALLBACK")}"
|
||||
echo "Adding Kubernetes apt repository (${K8S_MINOR})..."
|
||||
curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
|
||||
| sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
|
||||
|
||||
176
scripts/verify-debian.sh
Executable file
176
scripts/verify-debian.sh
Executable file
@@ -0,0 +1,176 @@
|
||||
#!/bin/bash
|
||||
|
||||
# verify-debian.sh
|
||||
# Non-destructive sanity checks for a freshly set-up Debian 13 machine.
|
||||
# Verifies the pieces that 00-03 + 04-config-symlinks.sh are expected to leave behind.
|
||||
# Exit code: 0 = all critical checks passed; 1 = at least one critical check failed.
|
||||
#
|
||||
# Usage: bash scripts/verify-debian.sh (no root needed)
|
||||
|
||||
# --- output helpers -------------------------------------------------------
|
||||
if [ -t 1 ]; then
|
||||
C_GREEN=$'\e[32m'; C_RED=$'\e[31m'; C_YELLOW=$'\e[33m'; C_BOLD=$'\e[1m'; C_OFF=$'\e[0m'
|
||||
else
|
||||
C_GREEN=""; C_RED=""; C_YELLOW=""; C_BOLD=""; C_OFF=""
|
||||
fi
|
||||
|
||||
PASS=0
|
||||
WARN=0
|
||||
FAIL=0
|
||||
|
||||
ok() { PASS=$((PASS+1)); echo "${C_GREEN}✔${C_OFF} $1"; }
|
||||
warn() { WARN=$((WARN+1)); echo "${C_YELLOW}⚠${C_OFF} $1"; }
|
||||
bad() { FAIL=$((FAIL+1)); echo "${C_RED}✘${C_OFF} $1"; }
|
||||
|
||||
# --- helpers --------------------------------------------------------------
|
||||
check_cmd() { # check_cmd "label" "command-name"
|
||||
if command -v "$2" &>/dev/null; then ok "$1: found ($(command -v "$2"))"; else bad "$1: NOT FOUND"; fi
|
||||
}
|
||||
|
||||
check_symlink_target() { # check_symlink_target "label" "link-path" "expected-target"
|
||||
if [ -L "$2" ]; then
|
||||
local target; target="$(readlink -f "$2")"
|
||||
if [ "$target" = "$3" ] || [ "$(basename "$target")" = "$(basename "$3")" ]; then
|
||||
ok "$1: $2 -> $target"
|
||||
else
|
||||
warn "$1: $2 points to $target (expected $3)"
|
||||
fi
|
||||
else
|
||||
warn "$1: $2 is not a symlink"
|
||||
fi
|
||||
}
|
||||
|
||||
check_service() { # check_service "label" "unit"
|
||||
if systemctl is-active --quiet "$2" 2>/dev/null; then ok "$1: active"; else warn "$1: not active"; fi
|
||||
}
|
||||
|
||||
# --- 1. OS ------------------------------------------------------------------
|
||||
echo "${C_BOLD}== OS ==${C_OFF}"
|
||||
. /etc/os-release
|
||||
echo " distro: $PRETTY_NAME (id=$ID, codename=${VERSION_CODENAME:-unknown})"
|
||||
if [ "$ID" = "debian" ]; then
|
||||
ok "Debian detected"
|
||||
else
|
||||
warn "Not Debian (id=$ID) - this script targets Debian 13"
|
||||
fi
|
||||
|
||||
# --- 2. Package manager ------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Package manager ==${C_OFF}"
|
||||
if command -v apt-get &>/dev/null; then ok "apt present"; else bad "apt missing"; fi
|
||||
if command -v dpkg-query &>/dev/null; then :; else bad "dpkg missing"; fi
|
||||
|
||||
# --- 3. Core packages ---------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Core tools ==${C_OFF}"
|
||||
for c in git curl wget jq ripgrep tmux zsh fzf zoxide eza unzip btop direnv tldr socat mtr nmap whois traceroute tcpdump iperf3 httpie ansible virt-manager flatpak python3; do
|
||||
check_cmd "$c" "$c"
|
||||
done
|
||||
|
||||
# --- 4. Debian binary renames -------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Debian binary renames ==${C_OFF}"
|
||||
if command -v batcat &>/dev/null; then
|
||||
check_symlink_target "bat -> batcat" "/usr/local/bin/bat" "/usr/bin/batcat"
|
||||
elif command -v bat &>/dev/null; then
|
||||
ok "bat present directly"
|
||||
else
|
||||
bad "bat/batcat not found"
|
||||
fi
|
||||
if command -v fdfind &>/dev/null; then
|
||||
check_symlink_target "fd -> fdfind" "/usr/local/bin/fd" "/usr/bin/fdfind"
|
||||
elif command -v fd &>/dev/null; then
|
||||
ok "fd present directly"
|
||||
else
|
||||
bad "fd/fdfind not found"
|
||||
fi
|
||||
if command -v dig &>/dev/null; then ok "dig present"; else bad "dig missing (install bind9-dnsutils)"; fi
|
||||
|
||||
# --- 5. Upstream-only tools ----------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Upstream tooling ==${C_OFF}"
|
||||
for c in gh terraform kubectl k9s minikube; do check_cmd "$c" "$c"; done
|
||||
|
||||
# --- 6. Development tools -------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Development tools ==${C_OFF}"
|
||||
for c in nvim go rustc cargo oh-my-posh helm kubectx kubens stern dive trivy aws; do check_cmd "$c" "$c"; done
|
||||
|
||||
# Go binary path sanity
|
||||
if command -v go &>/dev/null; then ok "GOPATH=$(go env GOPATH 2>/dev/null)"; fi
|
||||
|
||||
# pynvim (PEP 668 note: system pip is externally managed on Debian 13)
|
||||
if /usr/bin/python3 -m pip show pynvim &>/dev/null; then
|
||||
ok "pynvim installed for python3"
|
||||
else
|
||||
warn "pynvim not found for /usr/bin/python3 (install with --break-system-packages if wanted)"
|
||||
fi
|
||||
|
||||
# --- 7. .NET / backend -----------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== .NET / backend services ==${C_OFF}"
|
||||
if command -v dotnet &>/dev/null; then
|
||||
ok "dotnet: $(dotnet --version 2>/dev/null)"
|
||||
else
|
||||
bad "dotnet not on PATH (install via 03-dotnet-setup.sh or add ~/.dotnet to PATH)"
|
||||
fi
|
||||
check_cmd "podman" "podman"
|
||||
if command -v docker &>/dev/null; then
|
||||
ok "docker shim present ($(readlink -f "$(command -v docker)" 2>/dev/null || echo docker))"
|
||||
else
|
||||
warn "docker shim missing (install podman-docker)"
|
||||
fi
|
||||
if command -v podman-compose &>/dev/null; then ok "podman-compose present"; else warn "podman-compose missing"; fi
|
||||
check_service "postgresql" "postgresql"
|
||||
check_service "libvirtd" "libvirtd"
|
||||
|
||||
# --- 8. Groups -------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Group membership ==${C_OFF}"
|
||||
for g in libvirt kvm; do
|
||||
if id -nG 2>/dev/null | tr ' ' '\n' | grep -qx "$g"; then
|
||||
ok "in group '$g'"
|
||||
else
|
||||
warn "not in group '$g' (needed for virt-manager/KVM; log out/in after usermod)"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- 9. Flatpak --------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Flatpak ==${C_OFF}"
|
||||
if command -v flatpak &>/dev/null && flatpak remotes 2>/dev/null | grep -q flathub; then
|
||||
ok "flathub remote present"
|
||||
else
|
||||
warn "flathub remote not configured"
|
||||
fi
|
||||
|
||||
# --- 10. Shell rc symlinks ----------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}== Dotfile symlinks ==${C_OFF}"
|
||||
for d in bashrc zshrc gitconfig aliases.bash aliases.zsh; do
|
||||
if [ -e "$HOME/.$d" ] || [ -L "$HOME/.$d" ]; then
|
||||
# shellcheck disable=SC2088
|
||||
ok "~/.$d linked"
|
||||
else
|
||||
# shellcheck disable=SC2088
|
||||
warn "~/.$d missing"
|
||||
fi
|
||||
done
|
||||
|
||||
check_symlink_target "oh-my-posh theme" "$HOME/.config/oh-my-posh/theme.omp.json" "theme.omp.json"
|
||||
check_symlink_target "nvim config" "$HOME/.config/nvim" "nvim"
|
||||
check_symlink_target "nushell config" "$HOME/.config/nushell" "nushell"
|
||||
|
||||
# --- Summary ------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "${C_BOLD}========================================${C_OFF}"
|
||||
echo "${C_GREEN}✔${C_OFF} ${PASS} passed ${C_YELLOW}⚠${C_OFF} ${WARN} warnings ${C_RED}✘${C_OFF} ${FAIL} failed"
|
||||
echo "${C_BOLD}========================================${C_OFF}"
|
||||
echo ""
|
||||
if [ "$FAIL" -gt 0 ]; then
|
||||
echo "Some critical checks failed. See ✘ items above."
|
||||
exit 1
|
||||
else
|
||||
echo "All critical checks passed. Warnings above are optional/suggested."
|
||||
exit 0
|
||||
fi
|
||||
Reference in New Issue
Block a user