amssh: fix FIDO2 auth; add pamtester to core; add package audit script
- 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 <noreply@anthropic.com>main
parent
bdc5b55c57
commit
4d797c537d
|
|
@ -84,6 +84,9 @@ _ask_pinentry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── FIDO2 / PAM detection ────────────────────────────────────────────────────
|
# ── FIDO2 / PAM detection ────────────────────────────────────────────────────
|
||||||
|
PAM_SVC=amssh
|
||||||
|
PAM_SVC_FILE="/etc/pam.d/${PAM_SVC}"
|
||||||
|
|
||||||
_find_pam_u2f() {
|
_find_pam_u2f() {
|
||||||
local f
|
local f
|
||||||
for f in /lib/security/pam_u2f.so \
|
for f in /lib/security/pam_u2f.so \
|
||||||
|
|
@ -96,12 +99,62 @@ _find_pam_u2f() {
|
||||||
return 1
|
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
|
_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() {
|
_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) ─────────────────────
|
# ── master auth (passphrase + optional FIDO2 PAM layer) ─────────────────────
|
||||||
|
|
@ -529,8 +582,8 @@ _load_auth_config() {
|
||||||
_first_launch_dialog() {
|
_first_launch_dialog() {
|
||||||
mkdir -p "$CONF_DIR"
|
mkdir -p "$CONF_DIR"
|
||||||
|
|
||||||
# FIDO2 unavailable — silently default to passphrase
|
# FIDO2 hardware unavailable — silently default to passphrase
|
||||||
if ! _fido_pam_available; then
|
if ! _fido_hardware_available; then
|
||||||
printf 'passphrase' > "$AUTH_CONF"
|
printf 'passphrase' > "$AUTH_CONF"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
@ -560,8 +613,10 @@ _first_launch_dialog() {
|
||||||
printf '%s' "$choice" > "$AUTH_CONF"
|
printf '%s' "$choice" > "$AUTH_CONF"
|
||||||
|
|
||||||
if [[ "$choice" == "fido" ]]; then
|
if [[ "$choice" == "fido" ]]; then
|
||||||
printf '\n[amssh] FIDO2 selected — your key will be required on each unlock.\n' >&2
|
printf '\n[amssh] FIDO2 selected — setting up...\n' >&2
|
||||||
printf '[amssh] Ensure pam_u2f is configured for your user (pamu2fcfg).\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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -9,7 +9,7 @@ sudo pacman -Syu --noconfirm --needed \
|
||||||
gcc glib2 greetd-tuigreet grub \
|
gcc glib2 greetd-tuigreet grub \
|
||||||
htop inetutils iwd jq ldns less libpulse linux linux-firmware \
|
htop inetutils iwd jq ldns less libpulse linux linux-firmware \
|
||||||
man-db mc nano neovim networkmanager \
|
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 \
|
python python-pip qrencode ruby-pkg-config rust rustup \
|
||||||
ipcalc iputils mtr net-tools nmap \
|
ipcalc iputils mtr net-tools nmap \
|
||||||
smartmontools symlinks tcpdump traceroute tree \
|
smartmontools symlinks tcpdump traceroute tree \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue