mirror of
https://gitlab.com/hacklab01/pivilion.git
synced 2026-08-06 16:08:22 +00:00
Compare commits
2 commits
0f20889948
...
5a27dfeceb
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a27dfeceb | |||
| 3340108acd |
6 changed files with 147 additions and 14 deletions
|
|
@ -1,11 +1,9 @@
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
# Captive-network probes use otherwise unknown paths such as /generate_204,
|
# Serve every existing gallery file or directory without rewriting.
|
||||||
# /hotspot-detect.html, and /connecttest.txt. Redirect those temporarily so
|
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||||
# clients open their login assistant, but let the gallery serve its own PHP and
|
RewriteCond %{REQUEST_FILENAME} -d
|
||||||
# asset paths without a loop.
|
RewriteRule ^ - [END]
|
||||||
RewriteCond %{REQUEST_URI} !\.php$ [NC]
|
|
||||||
RewriteCond %{REQUEST_URI} !\.head(?:\.|$) [NC]
|
# Redirect only unknown captive-network probe paths.
|
||||||
RewriteCond %{REQUEST_URI} !^/(?:content|css|doc|docker|img|images|js|modules|tests|upload)(?:/|$) [NC]
|
RewriteRule ^ http://10.1.1.1/index.php [END,R=302,NE]
|
||||||
RewriteCond %{REQUEST_URI} !^/pi-logo_128\.png$ [NC]
|
|
||||||
RewriteRule ^ http://10.1.1.1/index.php [L,R=302,NE]
|
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,8 @@ deploy_files() {
|
||||||
local php_version
|
local php_version
|
||||||
log "Deploying Pivilion files"
|
log "Deploying Pivilion files"
|
||||||
install -d -m 0755 /usr/local/lib/pivilion /usr/local/bin /etc/systemd/system /etc/tor/torrc.d /etc/profile.d /var/lib/pivilion
|
install -d -m 0755 /usr/local/lib/pivilion /usr/local/bin /etc/systemd/system /etc/tor/torrc.d /etc/profile.d /var/lib/pivilion
|
||||||
install -m 0644 "$SOURCE_DIR/usr/local/lib/pivilion/common.sh" "$SOURCE_DIR/usr/local/lib/pivilion/hostname.sh" /usr/local/lib/pivilion/
|
install -m 0644 "$SOURCE_DIR/usr/local/lib/pivilion/common.sh" "$SOURCE_DIR/usr/local/lib/pivilion/hostname.sh" \
|
||||||
|
"$SOURCE_DIR/usr/local/lib/pivilion/networkmanager.sh" /usr/local/lib/pivilion/
|
||||||
install -m 0755 "$SOURCE_DIR/usr/local/lib/pivilion/mode.sh" "$SOURCE_DIR/usr/local/lib/pivilion/config-sync.sh" "$SOURCE_DIR/usr/local/lib/pivilion/diagnose.sh" \
|
install -m 0755 "$SOURCE_DIR/usr/local/lib/pivilion/mode.sh" "$SOURCE_DIR/usr/local/lib/pivilion/config-sync.sh" "$SOURCE_DIR/usr/local/lib/pivilion/diagnose.sh" \
|
||||||
"$SOURCE_DIR/usr/local/lib/pivilion/issue-banner.sh" /usr/local/lib/pivilion/
|
"$SOURCE_DIR/usr/local/lib/pivilion/issue-banner.sh" /usr/local/lib/pivilion/
|
||||||
install -m 0755 "$SOURCE_DIR"/usr/local/bin/* /usr/local/bin/
|
install -m 0755 "$SOURCE_DIR"/usr/local/bin/* /usr/local/bin/
|
||||||
|
|
@ -319,7 +320,7 @@ check_install() {
|
||||||
local failed=0 file
|
local failed=0 file
|
||||||
for file in /etc/pivilion.defaults /var/lib/pivilion/install-state /etc/issue.d/90-pivilion.issue \
|
for file in /etc/pivilion.defaults /var/lib/pivilion/install-state /etc/issue.d/90-pivilion.issue \
|
||||||
/usr/local/lib/pivilion/common.sh /usr/local/lib/pivilion/mode.sh /usr/local/lib/pivilion/diagnose.sh \
|
/usr/local/lib/pivilion/common.sh /usr/local/lib/pivilion/mode.sh /usr/local/lib/pivilion/diagnose.sh \
|
||||||
/usr/local/lib/pivilion/config-sync.sh /usr/local/lib/pivilion/hostname.sh \
|
/usr/local/lib/pivilion/config-sync.sh /usr/local/lib/pivilion/hostname.sh /usr/local/lib/pivilion/networkmanager.sh \
|
||||||
/usr/local/lib/pivilion/issue-banner.sh /etc/systemd/system/pivilion-mode.service \
|
/usr/local/lib/pivilion/issue-banner.sh /etc/systemd/system/pivilion-mode.service \
|
||||||
/etc/systemd/system/pivilion-config.service /etc/systemd/system/pivilion-login-banner.service; do
|
/etc/systemd/system/pivilion-config.service /etc/systemd/system/pivilion-login-banner.service; do
|
||||||
if [ ! -e "$file" ]; then echo "Missing: $file" >&2; failed=1; fi
|
if [ ! -e "$file" ]; then echo "Missing: $file" >&2; failed=1; fi
|
||||||
|
|
|
||||||
11
tests/run.sh
11
tests/run.sh
|
|
@ -28,6 +28,9 @@ systemd_syntax() {
|
||||||
}
|
}
|
||||||
|
|
||||||
captive_rules() {
|
captive_rules() {
|
||||||
|
grep -Fqx 'RewriteCond %{REQUEST_FILENAME} -f [OR]' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
||||||
|
grep -Fqx 'RewriteCond %{REQUEST_FILENAME} -d' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
||||||
|
grep -Fqx 'RewriteRule ^ - [END]' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
||||||
grep -q 'R=302' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
grep -q 'R=302' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
||||||
! grep -q 'R=301' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
! grep -q 'R=301' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
|
||||||
! grep -Eq 'dport[[:space:]]+443|redirect to :443' "$ROOT/usr/local/lib/pivilion/mode.sh"
|
! grep -Eq 'dport[[:space:]]+443|redirect to :443' "$ROOT/usr/local/lib/pivilion/mode.sh"
|
||||||
|
|
@ -79,6 +82,12 @@ same_boot_mode_contract() {
|
||||||
grep -q 'wait_for_diagnostics onion' "$ROOT/usr/local/lib/pivilion/mode.sh"
|
grep -q 'wait_for_diagnostics onion' "$ROOT/usr/local/lib/pivilion/mode.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
networkmanager_startup_contract() {
|
||||||
|
grep -q 'pivilion_nm_prepare_interface "$IFACE"' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
|
||||||
|
grep -q 'pivilion_nm_connection_up "$NM_HOTSPOT" "$IFACE"' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
|
||||||
|
grep -q 'nmcli connection up "$connection" ifname "$iface"' "$ROOT/usr/local/lib/pivilion/networkmanager.sh"
|
||||||
|
}
|
||||||
|
|
||||||
shellcheck_scripts() {
|
shellcheck_scripts() {
|
||||||
command -v shellcheck >/dev/null 2>&1 || return 0
|
command -v shellcheck >/dev/null 2>&1 || return 0
|
||||||
shellcheck "$ROOT/install.sh" "$ROOT"/usr/local/lib/pivilion/*.sh "$ROOT"/usr/local/bin/pivilion* "$ROOT/usr/local/bin/hotspot" "$ROOT/usr/local/bin/onion" "$ROOT/usr/local/bin/pikey"
|
shellcheck "$ROOT/install.sh" "$ROOT"/usr/local/lib/pivilion/*.sh "$ROOT"/usr/local/bin/pivilion* "$ROOT/usr/local/bin/hotspot" "$ROOT/usr/local/bin/onion" "$ROOT/usr/local/bin/pikey"
|
||||||
|
|
@ -95,6 +104,8 @@ run "truthful runtime state contract" state_contract
|
||||||
run "agetty login-banner contract" tty_banner_contract
|
run "agetty login-banner contract" tty_banner_contract
|
||||||
run "canonical boot configuration contract" boot_config_contract
|
run "canonical boot configuration contract" boot_config_contract
|
||||||
run "same-boot hotspot and onion activation contract" same_boot_mode_contract
|
run "same-boot hotspot and onion activation contract" same_boot_mode_contract
|
||||||
|
run "NetworkManager software-radio and delayed-device handling" "$ROOT/tests/test-networkmanager.sh"
|
||||||
|
run "NetworkManager hotspot startup contract" networkmanager_startup_contract
|
||||||
run "ShellCheck (when installed)" shellcheck_scripts
|
run "ShellCheck (when installed)" shellcheck_scripts
|
||||||
|
|
||||||
[ "$failures" -eq 0 ] || { echo "$failures test group(s) failed." >&2; exit 1; }
|
[ "$failures" -eq 0 ] || { echo "$failures test group(s) failed." >&2; exit 1; }
|
||||||
|
|
|
||||||
63
tests/test-networkmanager.sh
Executable file
63
tests/test-networkmanager.sh
Executable file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
TMP=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$TMP"' EXIT
|
||||||
|
mkdir "$TMP/bin"
|
||||||
|
export MOCK_NM_LOG="$TMP/nmcli.log"
|
||||||
|
export MOCK_NM_STATE="$TMP/nm-state"
|
||||||
|
export MOCK_NM_READS="$TMP/nm-reads"
|
||||||
|
|
||||||
|
cat > "$TMP/bin/nmcli" <<'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
printf '%s\n' "$*" >> "$MOCK_NM_LOG"
|
||||||
|
case "$*" in
|
||||||
|
'radio wifi on') printf 'on\n' > "$MOCK_NM_STATE" ;;
|
||||||
|
'device set wlan0 managed yes') ;;
|
||||||
|
'-g GENERAL.STATE device show wlan0')
|
||||||
|
[ "$(cat "$MOCK_NM_STATE")" = on ] || { echo '20 (unavailable)'; exit 0; }
|
||||||
|
reads=$(cat "$MOCK_NM_READS" 2>/dev/null || echo 0)
|
||||||
|
reads=$((reads + 1)); printf '%s\n' "$reads" > "$MOCK_NM_READS"
|
||||||
|
if [ "$reads" -lt "${MOCK_NM_AVAILABLE_AFTER:-3}" ]; then echo '20 (unavailable)'; else echo '30 (disconnected)'; fi ;;
|
||||||
|
'radio') echo 'WIFI-HW WIFI'; echo 'enabled enabled' ;;
|
||||||
|
'device status') echo 'DEVICE TYPE STATE'; echo 'wlan0 wifi unavailable' ;;
|
||||||
|
'-f GENERAL.STATE,GENERAL.REASON device show wlan0') echo 'GENERAL.STATE: 20 (unavailable)'; echo 'GENERAL.REASON: 2 (unknown)' ;;
|
||||||
|
'connection up pivilion-hotspot ifname wlan0') ;;
|
||||||
|
esac
|
||||||
|
EOF
|
||||||
|
cat > "$TMP/bin/rfkill" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
echo '0: phy0: Wireless LAN'
|
||||||
|
echo 'Soft blocked: no'
|
||||||
|
echo 'Hard blocked: no'
|
||||||
|
EOF
|
||||||
|
cat > "$TMP/bin/sleep" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod +x "$TMP/bin/"*
|
||||||
|
PATH="$TMP/bin:$PATH"
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
source "$ROOT/usr/local/lib/pivilion/networkmanager.sh"
|
||||||
|
|
||||||
|
# The mock starts software-disabled and becomes available after a delay.
|
||||||
|
PIVILION_NM_READY_TIMEOUT=5 pivilion_nm_prepare_interface wlan0
|
||||||
|
[ "$(cat "$MOCK_NM_STATE")" = on ]
|
||||||
|
[ "$(cat "$MOCK_NM_READS")" -eq 3 ]
|
||||||
|
sed -n '1p' "$MOCK_NM_LOG" | grep -Fqx 'radio wifi on'
|
||||||
|
sed -n '2p' "$MOCK_NM_LOG" | grep -Fqx 'device set wlan0 managed yes'
|
||||||
|
pivilion_nm_connection_up pivilion-hotspot wlan0
|
||||||
|
grep -Fqx 'connection up pivilion-hotspot ifname wlan0' "$MOCK_NM_LOG"
|
||||||
|
|
||||||
|
# A device that remains unavailable fails within the configured bound and logs
|
||||||
|
# the complete NetworkManager/rfkill snapshot.
|
||||||
|
: > "$MOCK_NM_LOG"
|
||||||
|
: > "$MOCK_NM_READS"
|
||||||
|
if MOCK_NM_AVAILABLE_AFTER=99 PIVILION_NM_READY_TIMEOUT=2 pivilion_nm_prepare_interface wlan0 >"$TMP/timeout.log" 2>&1; then exit 1; fi
|
||||||
|
grep -q 'Timed out after 2s' "$TMP/timeout.log"
|
||||||
|
grep -q '^-- nmcli radio --$' "$TMP/timeout.log"
|
||||||
|
grep -q '^-- nmcli device status --$' "$TMP/timeout.log"
|
||||||
|
grep -q '^-- wlan0 state and reason --$' "$TMP/timeout.log"
|
||||||
|
grep -q '^-- rfkill Wi-Fi state --$' "$TMP/timeout.log"
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
source /usr/local/lib/pivilion/common.sh
|
source /usr/local/lib/pivilion/common.sh
|
||||||
|
source /usr/local/lib/pivilion/networkmanager.sh
|
||||||
pivilion_require_root
|
pivilion_require_root
|
||||||
pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
||||||
|
|
||||||
|
|
@ -60,6 +61,7 @@ activation_error() {
|
||||||
if [ "$existing" = none ] || [ "$existing" = unknown ]; then
|
if [ "$existing" = none ] || [ "$existing" = unknown ]; then
|
||||||
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
|
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
|
||||||
fi
|
fi
|
||||||
|
if [ "$BACKEND" = networkmanager ]; then pivilion_nm_log_diagnostics "$IFACE"; fi
|
||||||
refresh_login_banner || true
|
refresh_login_banner || true
|
||||||
logger -t pivilion-mode "Failed to activate $MODE mode at line $line (exit $rc)" 2>/dev/null || true
|
logger -t pivilion-mode "Failed to activate $MODE mode at line $line (exit $rc)" 2>/dev/null || true
|
||||||
exit "$rc"
|
exit "$rc"
|
||||||
|
|
@ -208,6 +210,7 @@ update_torname() {
|
||||||
|
|
||||||
start_hotspot() {
|
start_hotspot() {
|
||||||
pivilion_write_state none "$BACKEND" activating none
|
pivilion_write_state none "$BACKEND" activating none
|
||||||
|
if [ "$BACKEND" = networkmanager ]; then pivilion_nm_prepare_interface "$IFACE"; fi
|
||||||
validate_ap_support || {
|
validate_ap_support || {
|
||||||
pivilion_write_state none "$BACKEND" failed "$IFACE does not advertise Wi-Fi AP support"
|
pivilion_write_state none "$BACKEND" failed "$IFACE does not advertise Wi-Fi AP support"
|
||||||
refresh_login_banner
|
refresh_login_banner
|
||||||
|
|
@ -235,7 +238,7 @@ start_hotspot() {
|
||||||
nmcli connection modify "$NM_HOTSPOT" 802-11-wireless.mode ap 802-11-wireless.band bg \
|
nmcli connection modify "$NM_HOTSPOT" 802-11-wireless.mode ap 802-11-wireless.band bg \
|
||||||
802-11-wireless.channel "$PIVILION_HOTSPOT_CHANNEL" \
|
802-11-wireless.channel "$PIVILION_HOTSPOT_CHANNEL" \
|
||||||
ipv4.method manual ipv4.addresses 10.1.1.1/24 ipv4.never-default yes ipv6.method disabled connection.autoconnect no
|
ipv4.method manual ipv4.addresses 10.1.1.1/24 ipv4.never-default yes ipv6.method disabled connection.autoconnect no
|
||||||
nmcli connection up "$NM_HOTSPOT"
|
pivilion_nm_connection_up "$NM_HOTSPOT" "$IFACE"
|
||||||
else
|
else
|
||||||
command -v dhcpcd >/dev/null 2>&1 && dhcpcd -k "$IFACE" 2>/dev/null || true
|
command -v dhcpcd >/dev/null 2>&1 && dhcpcd -k "$IFACE" 2>/dev/null || true
|
||||||
systemctl stop "wpa_supplicant@$IFACE.service" 2>/dev/null || true
|
systemctl stop "wpa_supplicant@$IFACE.service" 2>/dev/null || true
|
||||||
|
|
@ -259,10 +262,14 @@ start_hotspot() {
|
||||||
start_onion() {
|
start_onion() {
|
||||||
local failure=""
|
local failure=""
|
||||||
pivilion_write_state none "$BACKEND" activating none
|
pivilion_write_state none "$BACKEND" activating none
|
||||||
|
if [ "$BACKEND" = networkmanager ]; then pivilion_nm_prepare_interface "$IFACE"; fi
|
||||||
cleanup_hotspot
|
cleanup_hotspot
|
||||||
rm -f "$CAPTIVE_LIVE"
|
rm -f "$CAPTIVE_LIVE"
|
||||||
if [ "$BACKEND" = networkmanager ]; then
|
if [ "$BACKEND" = networkmanager ]; then
|
||||||
nmcli connection up pivilion-onion >/dev/null 2>&1 || failure="No saved Pivilion Wi-Fi connection; run pikey"
|
nmcli connection up pivilion-onion >/dev/null 2>&1 || {
|
||||||
|
pivilion_nm_log_diagnostics "$IFACE"
|
||||||
|
failure="No saved Pivilion Wi-Fi connection; run pikey"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
systemctl restart dhcpcd 2>/dev/null || true
|
systemctl restart dhcpcd 2>/dev/null || true
|
||||||
systemctl restart "wpa_supplicant@$IFACE.service" 2>/dev/null || systemctl restart wpa_supplicant 2>/dev/null || true
|
systemctl restart "wpa_supplicant@$IFACE.service" 2>/dev/null || systemctl restart wpa_supplicant 2>/dev/null || true
|
||||||
|
|
|
||||||
53
usr/local/lib/pivilion/networkmanager.sh
Normal file
53
usr/local/lib/pivilion/networkmanager.sh
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pivilion_nm_log_diagnostics() {
|
||||||
|
local iface=$1
|
||||||
|
{
|
||||||
|
echo "NetworkManager Wi-Fi diagnostics for $iface:"
|
||||||
|
echo "-- nmcli radio --"
|
||||||
|
LC_ALL=C nmcli radio || true
|
||||||
|
echo "-- nmcli device status --"
|
||||||
|
LC_ALL=C nmcli device status || true
|
||||||
|
echo "-- $iface state and reason --"
|
||||||
|
LC_ALL=C nmcli -f GENERAL.STATE,GENERAL.REASON device show "$iface" || true
|
||||||
|
echo "-- rfkill Wi-Fi state --"
|
||||||
|
rfkill list wifi || rfkill list || true
|
||||||
|
} >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
pivilion_nm_prepare_interface() {
|
||||||
|
local iface=$1 timeout=${PIVILION_NM_READY_TIMEOUT:-30} state i
|
||||||
|
|
||||||
|
if ! nmcli radio wifi on; then
|
||||||
|
echo "NetworkManager could not enable the Wi-Fi software radio" >&2
|
||||||
|
pivilion_nm_log_diagnostics "$iface"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! nmcli device set "$iface" managed yes; then
|
||||||
|
echo "NetworkManager could not mark $iface as managed" >&2
|
||||||
|
pivilion_nm_log_diagnostics "$iface"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for ((i = 0; i < timeout; i++)); do
|
||||||
|
state=$(LC_ALL=C nmcli -g GENERAL.STATE device show "$iface" 2>/dev/null || true)
|
||||||
|
case "$state" in
|
||||||
|
*unmanaged*|*unavailable*|'10 '*|'20 '*|'') ;;
|
||||||
|
*) return 0 ;;
|
||||||
|
esac
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Timed out after ${timeout}s waiting for $iface to leave the unavailable/unmanaged state" >&2
|
||||||
|
pivilion_nm_log_diagnostics "$iface"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pivilion_nm_connection_up() {
|
||||||
|
local connection=$1 iface=$2
|
||||||
|
if ! nmcli connection up "$connection" ifname "$iface"; then
|
||||||
|
echo "NetworkManager could not activate $connection on $iface" >&2
|
||||||
|
pivilion_nm_log_diagnostics "$iface"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue