From 4d797c537dc4e058c20a36a450a666de8ae60bc4 Mon Sep 17 00:00:00 2001 From: The_miro Date: Mon, 11 May 2026 19:09:38 +0200 Subject: [PATCH] amssh: fix FIDO2 auth; add pamtester to core; add package audit script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - amssh: use dedicated /etc/pam.d/amssh service instead of login (pam_u2f was commented out in login); auto-create service and register key on first-launch FIDO selection - amssh: redirect pamtester stdout+stderr to /dev/tty so the tap prompt is visible and the success message doesn't contaminate pass=$(_get_passphrase) - amssh: split _fido_pam_available into _fido_hardware_available (for dialog gating) and _fido_pam_available (runtime — requires keys file + PAM service) - setup: add pamtester to core-packages.sh - setup: add audit-packages.sh to verify installed packages come from the expected source (pacman/AUR/flatpak) Co-Authored-By: Claude Sonnet 4.6 --- desktopenvs/hyprland/scripts/amssh | 67 ++++++++++-- setup/audit-packages.sh | 161 +++++++++++++++++++++++++++++ setup/modules/core-packages.sh | 2 +- 3 files changed, 223 insertions(+), 7 deletions(-) create mode 100755 setup/audit-packages.sh diff --git a/desktopenvs/hyprland/scripts/amssh b/desktopenvs/hyprland/scripts/amssh index c15ca96..0d65b93 100755 --- a/desktopenvs/hyprland/scripts/amssh +++ b/desktopenvs/hyprland/scripts/amssh @@ -84,6 +84,9 @@ _ask_pinentry() { } # ── FIDO2 / PAM detection ──────────────────────────────────────────────────── +PAM_SVC=amssh +PAM_SVC_FILE="/etc/pam.d/${PAM_SVC}" + _find_pam_u2f() { local f for f in /lib/security/pam_u2f.so \ @@ -96,12 +99,62 @@ _find_pam_u2f() { return 1 } -_fido_pam_available() { +_fido_keys_file() { + local f + for f in "$HOME/.config/Yubico/u2f_keys" "$HOME/.config/pam-u2f/keys"; do + [[ -f "$f" ]] && printf '%s' "$f" && return 0 + done + return 1 +} + +# Hardware + tool present — enough to offer FIDO setup in the dialog. +_fido_hardware_available() { _find_pam_u2f >/dev/null && command -v pamtester &>/dev/null } +# Fully configured — all four prerequisites ready to authenticate. +_fido_pam_available() { + _fido_hardware_available \ + && _fido_keys_file >/dev/null \ + && [[ -f "$PAM_SVC_FILE" ]] +} + +# Creates /etc/pam.d/amssh — requires sudo once. +_ensure_pam_service() { + [[ -f "$PAM_SVC_FILE" ]] && return 0 + printf '[amssh] Creating PAM service %s (requires sudo)\n' "$PAM_SVC_FILE" >&2 + sudo tee "$PAM_SVC_FILE" >/dev/null <<'PAM' +#%PAM-1.0 +auth required pam_u2f.so cue +PAM +} + +# Registers a FIDO key into ~/.config/Yubico/u2f_keys if not already present. +_register_fido_key() { + local keys_file="$HOME/.config/Yubico/u2f_keys" + if [[ -f "$keys_file" ]]; then + printf '[amssh] Key file already exists at %s\n' "$keys_file" >&2 + printf '[amssh] To add another key run: pamu2fcfg -n >> %s\n' "$keys_file" >&2 + return 0 + fi + mkdir -p "$(dirname "$keys_file")" + printf '[amssh] Insert your FIDO key, then press Enter...\n' >&2 + read -r -s < /dev/tty + printf '[amssh] Touch your FIDO key when it blinks...\n' >&2 + if pamu2fcfg > "$keys_file" 2>/dev/tty; then + printf '[amssh] Key registered at %s\n' "$keys_file" >&2 + else + rm -f "$keys_file" + printf '[amssh] Registration failed — check key and retry\n' >&2 + return 1 + fi +} + _pam_authenticate() { - pamtester login "$USER" authenticate 2>/dev/null + # Both stdout and stderr must go to /dev/tty: pamtester prints its success + # message to stdout, which would contaminate pass=$(_get_passphrase) if left + # uncaptured. stderr carries the pam_u2f tap prompt. + pamtester "$PAM_SVC" "$USER" authenticate >/dev/tty 2>&1 } # ── master auth (passphrase + optional FIDO2 PAM layer) ───────────────────── @@ -529,8 +582,8 @@ _load_auth_config() { _first_launch_dialog() { mkdir -p "$CONF_DIR" - # FIDO2 unavailable — silently default to passphrase - if ! _fido_pam_available; then + # FIDO2 hardware unavailable — silently default to passphrase + if ! _fido_hardware_available; then printf 'passphrase' > "$AUTH_CONF" return fi @@ -560,8 +613,10 @@ _first_launch_dialog() { printf '%s' "$choice" > "$AUTH_CONF" if [[ "$choice" == "fido" ]]; then - printf '\n[amssh] FIDO2 selected — your key will be required on each unlock.\n' >&2 - printf '[amssh] Ensure pam_u2f is configured for your user (pamu2fcfg).\n' >&2 + printf '\n[amssh] FIDO2 selected — setting up...\n' >&2 + _register_fido_key || { printf 'passphrase' > "$AUTH_CONF"; return; } + _ensure_pam_service || { printf 'passphrase' > "$AUTH_CONF"; return; } + printf '[amssh] FIDO2 ready — your key will be required on each unlock.\n' >&2 fi } diff --git a/setup/audit-packages.sh b/setup/audit-packages.sh new file mode 100755 index 0000000..fdf5b82 --- /dev/null +++ b/setup/audit-packages.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# Verifies installed packages come from the expected source (pacman/AUR/flatpak). +# Reports missing packages, wrong-source installs, and unexpected foreign packages. +set -euo pipefail + +RED="\e[31m"; YELLOW="\e[33m"; GREEN="\e[32m"; CYAN="\e[36m"; BOLD="\e[1m"; RESET="\e[0m" + +ok() { echo -e " ${GREEN}✔${RESET} $1"; } +warn() { echo -e " ${YELLOW}⚠${RESET} $1"; } +err() { echo -e " ${RED}✘${RESET} $1"; } +hdr() { echo -e "\n${BOLD}${CYAN}$1${RESET}"; } + +ISSUES=0 +flag() { ISSUES=$((ISSUES + 1)); } + +# --------------------------------------------------------------------------- +# Expected package sources — keep in sync with setup/modules/ +# --------------------------------------------------------------------------- + +PACMAN_PKGS=( + # core-packages.sh + 7zip arch-install-scripts atftp atool + base base-devel bind bluez btrfs-progs btop + cockpit cockpit-files cockpit-podman cronie curl + distrobox fail2ban fastfetch fd ffmpeg firefox flatpak + gcc glib2 greetd-tuigreet grub + htop inetutils iwd jq ldns less libpulse linux linux-firmware + man-db mc nano neovim networkmanager + openssh pamtester pciutils pipewire podman podman-compose + python python-pip qrencode ruby-pkg-config rust rustup + ipcalc iputils mtr net-tools nmap + smartmontools symlinks tcpdump traceroute tree + udisks2 udisks2-btrfs udiskie ufw usbutils + vim vnstat wget wireplumber wireless_tools wpa_supplicant wprs + yazi zip unzip zram-generator + # shell-setup.sh + zsh pyright bash atftp bash-language-server clang fzf hyfetch + lua-language-server micro pulsemixer z dysk glow + # hyprland.sh (official-repo portion) + hyprland hyprcursor wl-clipboard hyprpaper hyprlock wofi kitty dunst + nwg-dock-hyprland nwg-drawer nwg-menu nwg-look + cmake meson cpio pkgconf + hyprsunset hypridle ksshaskpass + nm-connection-editor network-manager-applet blueman + pipewire alsa-utils greetd-tuigreet + grim slurp gst-plugin-pipewire imagemagick + nerd-fonts otf-font-awesome + pipewire-alsa pipewire-jack pipewire-pulse + qt5-wayland qt6-wayland swww ttf-jetbrains-mono + qt6ct xdg-desktop-portal-hyprland xdg-utils + xorg-server xorg-xinit papirus-icon-theme + cool-retro-term qalculate-gtk dbus + thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman + hyprpicker pcmanfm-qt ly pinta + hyprpolkitagent pavucontrol playerctl wf-recorder sound-theme-freedesktop +) + +AUR_PKGS=( + # hyprland.sh AUR portion + hyprland-workspaces vicinae-bin bluetuith wvkbd kew iwmenu + walker-bin ulauncher bzmenu wofi-calc bri chamel + # optional apps + localsend + vesktop + onlyoffice-bin + vintagestory + wprs-git + zfs-dkms +) + +FLATPAK_PKGS=( + org.prismlauncher.PrismLauncher +) + +# --------------------------------------------------------------------------- +# Snapshot current state +# --------------------------------------------------------------------------- + +ALL_INSTALLED=$(pacman -Qq 2>/dev/null) +FOREIGN=$(pacman -Qmq 2>/dev/null) # AUR / manually installed +OFFICIAL=$(pacman -Qnq 2>/dev/null) # in a sync repo + +is_installed() { echo "$ALL_INSTALLED" | grep -qx "$1"; } +is_from_official() { echo "$OFFICIAL" | grep -qx "$1"; } +is_from_aur() { echo "$FOREIGN" | grep -qx "$1"; } + +# --------------------------------------------------------------------------- +# 1. Check pacman packages +# --------------------------------------------------------------------------- + +hdr "1/3 Official-repo packages (pacman)" + +for pkg in "${PACMAN_PKGS[@]}"; do + if ! is_installed "$pkg"; then + err "$pkg — NOT INSTALLED"; flag + elif is_from_aur "$pkg"; then + warn "$pkg — installed, but from AUR instead of official repo"; flag + else + ok "$pkg" + fi +done + +# --------------------------------------------------------------------------- +# 2. Check AUR packages +# --------------------------------------------------------------------------- + +hdr "2/3 AUR packages (yay)" + +for pkg in "${AUR_PKGS[@]}"; do + if ! is_installed "$pkg"; then + warn "$pkg — not installed (optional — may be intentional)" + elif is_from_official "$pkg"; then + warn "$pkg — installed from official repo, not AUR (may need AUR build for correct version)"; flag + else + ok "$pkg" + fi +done + +# --------------------------------------------------------------------------- +# 3. Check flatpak packages +# --------------------------------------------------------------------------- + +hdr "3/3 Flatpak packages" + +if command -v flatpak &>/dev/null; then + FLATPAK_INSTALLED=$(flatpak list --app --columns=application 2>/dev/null) + for pkg in "${FLATPAK_PKGS[@]}"; do + if echo "$FLATPAK_INSTALLED" | grep -qx "$pkg"; then + ok "$pkg" + else + warn "$pkg — not installed (optional — may be intentional)" + fi + done +else + warn "flatpak not found — skipping flatpak checks" +fi + +# --------------------------------------------------------------------------- +# 4. Flag unexpected foreign (AUR) packages not in our lists +# --------------------------------------------------------------------------- + +hdr "Bonus: foreign packages not declared in setup scripts" + +AUR_SET=$(printf "%s\n" "${AUR_PKGS[@]}") +while IFS= read -r pkg; do + if ! echo "$AUR_SET" | grep -qx "$pkg"; then + echo -e " ${CYAN}?${RESET} $pkg — foreign package not in setup scripts" + fi +done <<< "$FOREIGN" + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- + +echo +if [ "$ISSUES" -eq 0 ]; then + echo -e "${GREEN}${BOLD}All checks passed — no source mismatches or missing required packages.${RESET}" +else + echo -e "${RED}${BOLD}${ISSUES} issue(s) found — review items marked ✘ or ⚠ above.${RESET}" + exit 1 +fi diff --git a/setup/modules/core-packages.sh b/setup/modules/core-packages.sh index b0e22a8..94ce5a0 100644 --- a/setup/modules/core-packages.sh +++ b/setup/modules/core-packages.sh @@ -9,7 +9,7 @@ sudo pacman -Syu --noconfirm --needed \ gcc glib2 greetd-tuigreet grub \ htop inetutils iwd jq ldns less libpulse linux linux-firmware \ man-db mc nano neovim networkmanager \ - openssh pciutils pipewire podman podman-compose \ + openssh pamtester pciutils pipewire podman podman-compose \ python python-pip qrencode ruby-pkg-config rust rustup \ ipcalc iputils mtr net-tools nmap \ smartmontools symlinks tcpdump traceroute tree \