Fix captive portal detection and restore MOTD

This commit is contained in:
v3d 2026-06-29 17:29:00 +02:00
parent 3b7a16564b
commit 34b4c4b036
9 changed files with 153 additions and 59 deletions

View file

@ -3,21 +3,11 @@ set -euo pipefail
source /usr/local/lib/pivilion/common.sh
pivilion_load_config
mode=$(pivilion_current_mode)
torname="not generated yet"
[ ! -s "$PIVILION_DIR/torname" ] || torname=$(tr -d '\r\n' < "$PIVILION_DIR/torname")
uptime_text=$(uptime -p 2>/dev/null || true)
pivilion_print_motd
cat <<EOF
Welcome to Pivilion!
Host...............: $(uname -n)
System.............: $(uname -srmo)
Uptime.............: ${uptime_text:-unknown}
Mode after reboot..: $mode
Onion address......: $torname
Web root...........: $PIVILION_WEBROOT
Web root: $PIVILION_WEBROOT
Commands:
pivilion show this status
@ -28,4 +18,5 @@ Commands:
static convert the current gallery to static files
hotglue install or restore Hotglue
htaccess reset or remove captive redirect rules
pivilion-diagnose check captive DNS, HTTP redirects, and services
EOF

View file

@ -0,0 +1,4 @@
#!/bin/bash
set -euo pipefail
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
exec /usr/local/lib/pivilion/mode.sh diagnose

View file

@ -111,3 +111,30 @@ pivilion_current_mode() {
pivilion_require_root() {
[ "$(id -u)" -eq 0 ] || { echo "This operation must run as root (use sudo)." >&2; return 1; }
}
pivilion_print_motd() {
local heading=${1:-Welcome to Pivilion!}
local uptime_text memory_free memory_total load_one load_five load_fifteen processes torname mode
uptime_text=$(uptime -p 2>/dev/null || printf 'unknown')
memory_free=$(awk '/^MemAvailable:/ {print $2; exit}' /proc/meminfo 2>/dev/null)
memory_total=$(awk '/^MemTotal:/ {print $2; exit}' /proc/meminfo 2>/dev/null)
read -r load_one load_five load_fifteen _ < /proc/loadavg
processes=$(ps ax --no-headers 2>/dev/null | wc -l)
torname=not-generated-yet
[ ! -s "$PIVILION_DIR/torname" ] || torname=$(tr -d '\r\n' < "$PIVILION_DIR/torname")
mode=$(pivilion_current_mode)
printf '\n%s\n' "$heading"
cat <<EOF
@@@@@@@@@@@@@@@@@@@ $(date '+%A, %e %B %Y, %r')
@@@@@@, &@& *@@@@@@ $(uname -srnmo)
@@@@@@@. .@@@@@@@ Uptime.............: $uptime_text
@@@@ .@@@. @@@@ Memory.............: ${memory_free:-unknown}kB (Available) / ${memory_total:-unknown}kB (Total)
@ &@@@@.@@@@# @ Load Averages......: $load_one, $load_five, $load_fifteen (1, 5, 15 min)
@ @ . #@@@# . @ @ Running Processes..: $processes
@ @@@, # ,@@@ @ Manual / more info.: pivilion.net
@@, @ ,@@ Onion Address......: $torname
@@@@@#@@@@@@@#@@@@@ Running in $mode mode
EOF
}

View file

@ -11,6 +11,77 @@ IFACE=$(pivilion_wifi_interface)
RUN_DIR=/run/pivilion
DNSMASQ_CONF=/etc/dnsmasq.d/pivilion-runtime.conf
NM_HOTSPOT=pivilion-hotspot
CAPTIVE_TEMPLATE=$PIVILION_DIR/config/html/.htaccess
CAPTIVE_LIVE=$PIVILION_WEBROOT/gen/.htaccess
install_captive_redirect() {
local tmp
[ -r "$CAPTIVE_TEMPLATE" ] || {
echo "Captive redirect template is missing: $CAPTIVE_TEMPLATE" >&2
return 1
}
[ -d "$PIVILION_WEBROOT/gen" ] || {
echo "Gallery webroot is missing: $PIVILION_WEBROOT/gen" >&2
return 1
}
tmp=$(mktemp "$PIVILION_WEBROOT/gen/.htaccess.XXXXXX")
if ! install -m 0644 -o root -g www-data "$CAPTIVE_TEMPLATE" "$tmp"; then
rm -f "$tmp"
return 1
fi
mv -f "$tmp" "$CAPTIVE_LIVE"
}
diagnostic_result() {
local label=$1
shift
if "$@"; then
printf ' [ok] %s\n' "$label"
return 0
fi
printf ' [fail] %s\n' "$label" >&2
return 1
}
probe_redirect() {
local host=$1 path=$2 headers
headers=$(curl --silent --show-error --max-time 5 --dump-header - --output /dev/null \
--header "Host: $host" "http://10.1.1.1$path") || return 1
grep -Eq '^HTTP/[0-9.]+ 302([[:space:]]|$)' <<<"$headers" &&
grep -Eiq '^Location:[[:space:]]*http://10\.1\.1\.1/index\.php\r?$' <<<"$headers"
}
dns_points_to_portal() {
local host=$1 answer
answer=$(dig +short A @10.1.1.1 "$host" 2>/dev/null | tail -n1)
[ "$answer" = 10.1.1.1 ]
}
apache_rewrite_enabled() {
apache2ctl -M 2>/dev/null | grep -q 'rewrite_module'
}
nft_portal_table_active() {
nft list table inet pivilion >/dev/null 2>&1
}
hotspot_diagnostics() {
local failures=0
echo "Pivilion hotspot diagnostics:"
diagnostic_result "Apache is active" systemctl is-active --quiet apache2 || failures=$((failures + 1))
diagnostic_result "Apache rewrite module is enabled" apache_rewrite_enabled || failures=$((failures + 1))
diagnostic_result "live captive redirect matches its template" cmp -s "$CAPTIVE_TEMPLATE" "$CAPTIVE_LIVE" || failures=$((failures + 1))
diagnostic_result "Pivilion dnsmasq is active" systemctl is-active --quiet pivilion-dnsmasq.service || failures=$((failures + 1))
diagnostic_result "captive DNS resolves Android probe" dns_points_to_portal connectivitycheck.gstatic.com || failures=$((failures + 1))
diagnostic_result "Pivilion nftables table is active" nft_portal_table_active || failures=$((failures + 1))
diagnostic_result "Android probe receives portal redirect" probe_redirect connectivitycheck.gstatic.com /generate_204 || failures=$((failures + 1))
diagnostic_result "Apple probe receives portal redirect" probe_redirect captive.apple.com /hotspot-detect.html || failures=$((failures + 1))
diagnostic_result "Microsoft probe receives portal redirect" probe_redirect www.msftconnecttest.com /connecttest.txt || failures=$((failures + 1))
[ "$failures" -eq 0 ] || {
echo "$failures hotspot diagnostic check(s) failed." >&2
return 1
}
}
stop_hotspot() {
systemctl stop pivilion-dnsmasq.service 2>/dev/null || true
@ -56,6 +127,7 @@ EOF
start_hotspot() {
stop_hotspot
install_captive_redirect
rfkill unblock wifi 2>/dev/null || true
systemctl start apache2
if pivilion_uses_networkmanager; then
@ -87,6 +159,7 @@ EOF
dnsmasq --test --conf-file="$DNSMASQ_CONF"
systemctl restart pivilion-dnsmasq.service
start_firewall
hotspot_diagnostics
echo "Pivilion hotspot '$PIVILION_HOTSPOT_SSID' is active on $IFACE (10.1.1.1)."
}
@ -120,7 +193,7 @@ start_onion() {
systemctl restart dhcpcd 2>/dev/null || true
systemctl restart "wpa_supplicant@$IFACE.service" 2>/dev/null || systemctl restart wpa_supplicant 2>/dev/null || true
fi
rm -f "$PIVILION_WEBROOT/gen/.htaccess"
rm -f "$CAPTIVE_LIVE"
systemctl start apache2
if systemctl cat tor@default.service >/dev/null 2>&1; then
systemctl restart tor@default.service
@ -134,5 +207,6 @@ start_onion() {
case "$MODE" in
hotspot) start_hotspot ;;
onion) start_onion ;;
diagnose) hotspot_diagnostics ;;
*) echo "Unknown Pivilion mode: $MODE" >&2; exit 2 ;;
esac