mirror of
https://gitlab.com/hacklab01/pivilion.git
synced 2026-08-06 16:08:22 +00:00
Compare commits
2 commits
254a9b9cf3
...
b5e19f08f6
| Author | SHA1 | Date | |
|---|---|---|---|
| b5e19f08f6 | |||
| eda151065c |
14 changed files with 514 additions and 60 deletions
20
README.md
20
README.md
|
|
@ -43,15 +43,27 @@ sudo ./install.sh --repair # repeat deployment without replacing gallery conten
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer defaults to the user that invoked `sudo`, not to a hard-coded
|
The installer defaults to the user that invoked `sudo`, not to a hard-coded
|
||||||
`pi` account. To customize paths, startup mode, hotspot SSID, or Wi-Fi
|
`pi` account. The installer automatically creates `pivilion.conf` at the
|
||||||
interface, copy `pivilion.conf.example` to `/boot/firmware/pivilion.conf` (or
|
externally visible root of the FAT boot partition, using the current system and
|
||||||
`/etc/pivilion.conf`) and edit it before installing. The hotspot is intentionally
|
saved Wi-Fi settings. Linux may mount that same root as `/boot` or
|
||||||
open and passwordless.
|
`/boot/firmware`; users never need to expose that distinction. The hotspot is
|
||||||
|
intentionally open and passwordless.
|
||||||
|
|
||||||
|
The boot file controls startup mode, upstream Wi-Fi SSID and PSK, Wi-Fi country
|
||||||
|
and interface, hotspot name and channel, hostname, Pivilion user and paths,
|
||||||
|
generator state, and PHP upload limit. Pivilion commands keep it synchronized.
|
||||||
|
An 8–63-character Wi-Fi password entered there is replaced at boot with its
|
||||||
|
derived 64-character WPA PSK. The PSK does not reveal the original text, but it
|
||||||
|
still grants network access and must be treated as a secret. Invalid offline
|
||||||
|
edits retain the root-protected last-known-good settings and create
|
||||||
|
`pivilion-error.txt` beside the boot configuration.
|
||||||
|
|
||||||
Use `pikey` to save upstream Wi-Fi credentials, then `onion` and reboot to host
|
Use `pikey` to save upstream Wi-Fi credentials, then `onion` and reboot to host
|
||||||
through Tor. Use `hotspot` and reboot to run the local captive gallery at
|
through Tor. Use `hotspot` and reboot to run the local captive gallery at
|
||||||
`http://10.1.1.1/`. HTTPS interception is intentionally not attempted because a
|
`http://10.1.1.1/`. HTTPS interception is intentionally not attempted because a
|
||||||
local captive portal cannot present valid certificates for arbitrary sites.
|
local captive portal cannot present valid certificates for arbitrary sites.
|
||||||
|
Changing `PIVILION_MODE` in the boot-partition configuration activates either
|
||||||
|
mode during the immediately following boot; no second reboot is required.
|
||||||
|
|
||||||
If a phone joins but does not open its captive-login window, run
|
If a phone joins but does not open its captive-login window, run
|
||||||
`pivilion diagnose` on the Pi to verify DHCP/DNS, Apache rewrite handling,
|
`pivilion diagnose` on the Pi to verify DHCP/DNS, Apache rewrite handling,
|
||||||
|
|
|
||||||
16
etc/systemd/system/pivilion-config.service
Normal file
16
etc/systemd/system/pivilion-config.service
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Apply Pivilion boot-partition configuration
|
||||||
|
DefaultDependencies=no
|
||||||
|
After=local-fs.target
|
||||||
|
Before=network-pre.target NetworkManager.service dhcpcd.service pivilion-mode.service
|
||||||
|
Wants=network-pre.target
|
||||||
|
RequiresMountsFor=/boot /boot/firmware
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/lib/pivilion/config-sync.sh apply
|
||||||
|
RemainAfterExit=yes
|
||||||
|
StandardOutput=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Select Pivilion hotspot or onion networking mode
|
Description=Select Pivilion hotspot or onion networking mode
|
||||||
After=network-pre.target NetworkManager.service dhcpcd.service
|
Requires=pivilion-config.service
|
||||||
Wants=network-pre.target
|
After=pivilion-config.service NetworkManager.service dhcpcd.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/usr/local/lib/pivilion/mode.sh
|
ExecStart=/usr/local/lib/pivilion/mode.sh --boot-selected
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
TimeoutStartSec=120
|
TimeoutStartSec=120
|
||||||
|
|
||||||
|
|
|
||||||
33
install.sh
33
install.sh
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
INSTALL_VERSION=2
|
INSTALL_VERSION=4
|
||||||
ACTION=install
|
ACTION=install
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
'') ;;
|
'') ;;
|
||||||
|
|
@ -15,6 +15,8 @@ esac
|
||||||
|
|
||||||
SOURCE_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
SOURCE_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||||
source "$SOURCE_DIR/usr/local/lib/pivilion/common.sh"
|
source "$SOURCE_DIR/usr/local/lib/pivilion/common.sh"
|
||||||
|
PIVILION_CONFIG_PATHS="$(pivilion_boot_config_path) /etc/pivilion.conf /etc/pivilion.defaults"
|
||||||
|
export PIVILION_CONFIG_PATHS
|
||||||
pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
||||||
|
|
||||||
cat <<'PIVILION_LOGO'
|
cat <<'PIVILION_LOGO'
|
||||||
|
|
@ -48,12 +50,13 @@ declare -a MANAGED_PATHS=(
|
||||||
/etc/apache2/conf-enabled/pivilion.conf /etc/apache2/sites-enabled/000-default.conf
|
/etc/apache2/conf-enabled/pivilion.conf /etc/apache2/sites-enabled/000-default.conf
|
||||||
/etc/tor/torrc /etc/tor/torrc.d/pivilion.conf
|
/etc/tor/torrc /etc/tor/torrc.d/pivilion.conf
|
||||||
/etc/systemd/system/pivilion-mode.service /etc/systemd/system/pivilion-dnsmasq.service
|
/etc/systemd/system/pivilion-mode.service /etc/systemd/system/pivilion-dnsmasq.service
|
||||||
|
/etc/systemd/system/pivilion-config.service
|
||||||
/etc/systemd/system/pivilion-hostapd.service /etc/systemd/system/pivilion-boot-greeter.service
|
/etc/systemd/system/pivilion-hostapd.service /etc/systemd/system/pivilion-boot-greeter.service
|
||||||
/etc/systemd/system/pivilion-login-banner.service /etc/rc.local
|
/etc/systemd/system/pivilion-login-banner.service /etc/rc.local
|
||||||
/usr/local/lib/pivilion /usr/local/bin/pivilion /usr/local/bin/pivilion-diagnose
|
/usr/local/lib/pivilion /usr/local/bin/pivilion /usr/local/bin/pivilion-diagnose
|
||||||
/usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey /usr/local/bin/generator
|
/usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey /usr/local/bin/generator
|
||||||
/usr/local/bin/static /usr/local/bin/hotglue /usr/local/bin/htaccess
|
/usr/local/bin/static /usr/local/bin/hotglue /usr/local/bin/htaccess
|
||||||
/var/lib/pivilion/install-state
|
/var/lib/pivilion/install-state /var/lib/pivilion/pivilion.conf.last-good
|
||||||
/etc/php/7.4/apache2/conf.d/99-pivilion.ini
|
/etc/php/7.4/apache2/conf.d/99-pivilion.ini
|
||||||
/etc/php/8.2/apache2/conf.d/99-pivilion.ini
|
/etc/php/8.2/apache2/conf.d/99-pivilion.ini
|
||||||
/etc/php/8.4/apache2/conf.d/99-pivilion.ini
|
/etc/php/8.4/apache2/conf.d/99-pivilion.ini
|
||||||
|
|
@ -118,10 +121,11 @@ backup_managed_files() {
|
||||||
BACKUP_DIR=$(mktemp -d /var/backups/pivilion/install-$(date +%Y%m%d-%H%M%S).XXXXXX)
|
BACKUP_DIR=$(mktemp -d /var/backups/pivilion/install-$(date +%Y%m%d-%H%M%S).XXXXXX)
|
||||||
install -d -m 0700 "$BACKUP_DIR/files"
|
install -d -m 0700 "$BACKUP_DIR/files"
|
||||||
: > "$BACKUP_DIR/present"; : > "$BACKUP_DIR/missing"; : > "$BACKUP_DIR/enabled-services"; : > "$BACKUP_DIR/active-services"
|
: > "$BACKUP_DIR/present"; : > "$BACKUP_DIR/missing"; : > "$BACKUP_DIR/enabled-services"; : > "$BACKUP_DIR/active-services"
|
||||||
for service in apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do
|
for service in apache2 pivilion-config.service pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do
|
||||||
systemctl is-enabled --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/enabled-services" || true
|
systemctl is-enabled --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/enabled-services" || true
|
||||||
systemctl is-active --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/active-services" || true
|
systemctl is-active --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/active-services" || true
|
||||||
done
|
done
|
||||||
|
MANAGED_PATHS+=("$(pivilion_boot_config_path)" "$(pivilion_boot_error_path)")
|
||||||
for path in "${MANAGED_PATHS[@]}"; do
|
for path in "${MANAGED_PATHS[@]}"; do
|
||||||
relative=${path#/}
|
relative=${path#/}
|
||||||
if [ -e "$path" ] || [ -L "$path" ]; then
|
if [ -e "$path" ] || [ -L "$path" ]; then
|
||||||
|
|
@ -147,9 +151,9 @@ rollback() {
|
||||||
relative=${path#/}; rm -rf -- "$path"; install -d -m 0755 "$(dirname "$path")"; cp -a "$BACKUP_DIR/files/$relative" "$path"
|
relative=${path#/}; rm -rf -- "$path"; install -d -m 0755 "$(dirname "$path")"; cp -a "$BACKUP_DIR/files/$relative" "$path"
|
||||||
done < "$BACKUP_DIR/present"
|
done < "$BACKUP_DIR/present"
|
||||||
systemctl daemon-reload 2>/dev/null || true
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
systemctl disable apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service tor.service tor@default.service 2>/dev/null || true
|
systemctl disable apache2 pivilion-config.service pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service tor.service tor@default.service 2>/dev/null || true
|
||||||
while IFS= read -r path; do [ -n "$path" ] && systemctl enable "$path" 2>/dev/null || true; done < "$BACKUP_DIR/enabled-services"
|
while IFS= read -r path; do [ -n "$path" ] && systemctl enable "$path" 2>/dev/null || true; done < "$BACKUP_DIR/enabled-services"
|
||||||
for service in apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do
|
for service in apache2 pivilion-config.service pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do
|
||||||
grep -Fqx "$service" "$BACKUP_DIR/active-services" || systemctl stop "$service" 2>/dev/null || true
|
grep -Fqx "$service" "$BACKUP_DIR/active-services" || systemctl stop "$service" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
while IFS= read -r service; do [ -n "$service" ] && systemctl start "$service" 2>/dev/null || true; done < "$BACKUP_DIR/active-services"
|
while IFS= read -r service; do [ -n "$service" ] && systemctl start "$service" 2>/dev/null || true; done < "$BACKUP_DIR/active-services"
|
||||||
|
|
@ -171,7 +175,7 @@ deploy_files() {
|
||||||
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" /usr/local/lib/pivilion/common.sh
|
install -m 0644 "$SOURCE_DIR/usr/local/lib/pivilion/common.sh" /usr/local/lib/pivilion/common.sh
|
||||||
install -m 0755 "$SOURCE_DIR/usr/local/lib/pivilion/mode.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/
|
||||||
install -m 0644 "$SOURCE_DIR"/etc/systemd/system/*.service /etc/systemd/system/
|
install -m 0644 "$SOURCE_DIR"/etc/systemd/system/*.service /etc/systemd/system/
|
||||||
|
|
@ -186,6 +190,14 @@ PIVILION_WEBROOT=$PIVILION_WEBROOT
|
||||||
PIVILION_MODE=$PIVILION_MODE
|
PIVILION_MODE=$PIVILION_MODE
|
||||||
PIVILION_HOTSPOT_SSID=$PIVILION_HOTSPOT_SSID
|
PIVILION_HOTSPOT_SSID=$PIVILION_HOTSPOT_SSID
|
||||||
PIVILION_WIFI_INTERFACE=$PIVILION_WIFI_INTERFACE
|
PIVILION_WIFI_INTERFACE=$PIVILION_WIFI_INTERFACE
|
||||||
|
PIVILION_WIFI_SSID=$PIVILION_WIFI_SSID
|
||||||
|
PIVILION_WIFI_PSK=$PIVILION_WIFI_PSK
|
||||||
|
PIVILION_WIFI_PSK_FOR_SSID=$PIVILION_WIFI_PSK_FOR_SSID
|
||||||
|
PIVILION_WIFI_COUNTRY=$PIVILION_WIFI_COUNTRY
|
||||||
|
PIVILION_HOTSPOT_CHANNEL=$PIVILION_HOTSPOT_CHANNEL
|
||||||
|
PIVILION_HOSTNAME=$PIVILION_HOSTNAME
|
||||||
|
PIVILION_GENERATOR_ENABLED=$PIVILION_GENERATOR_ENABLED
|
||||||
|
PIVILION_UPLOAD_MAX_SIZE=$PIVILION_UPLOAD_MAX_SIZE
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
install -d -m 0755 -o "$PIVILION_USER" -g "$PIVILION_GROUP" "$PIVILION_DIR" "$PIVILION_DIR/config"
|
install -d -m 0755 -o "$PIVILION_USER" -g "$PIVILION_GROUP" "$PIVILION_DIR" "$PIVILION_DIR/config"
|
||||||
|
|
@ -268,7 +280,7 @@ EOF
|
||||||
fi
|
fi
|
||||||
migrate_legacy_console_greeter
|
migrate_legacy_console_greeter
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable apache2 pivilion-mode.service pivilion-login-banner.service
|
systemctl enable apache2 pivilion-config.service pivilion-mode.service pivilion-login-banner.service
|
||||||
systemctl enable tor@default.service 2>/dev/null || systemctl enable tor.service
|
systemctl enable tor@default.service 2>/dev/null || systemctl enable tor.service
|
||||||
systemctl disable pivilion-dnsmasq.service pivilion-hostapd.service 2>/dev/null || true
|
systemctl disable pivilion-dnsmasq.service pivilion-hostapd.service 2>/dev/null || true
|
||||||
/usr/local/lib/pivilion/issue-banner.sh
|
/usr/local/lib/pivilion/issue-banner.sh
|
||||||
|
|
@ -287,6 +299,8 @@ validate_install() {
|
||||||
grep -q '^My IP address is ' /etc/issue.d/90-pivilion.issue || failed=1
|
grep -q '^My IP address is ' /etc/issue.d/90-pivilion.issue || failed=1
|
||||||
grep -q '^@@@@@@@@@@@@@@@@@@@' /etc/issue.d/90-pivilion.issue || failed=1
|
grep -q '^@@@@@@@@@@@@@@@@@@@' /etc/issue.d/90-pivilion.issue || failed=1
|
||||||
grep -q 'StandardOutput=null' /etc/systemd/system/pivilion-login-banner.service || failed=1
|
grep -q 'StandardOutput=null' /etc/systemd/system/pivilion-login-banner.service || failed=1
|
||||||
|
[ -s "$(pivilion_boot_config_path)" ] || failed=1
|
||||||
|
[ -s /var/lib/pivilion/pivilion.conf.last-good ] || failed=1
|
||||||
if command -v shellcheck >/dev/null 2>&1; then shellcheck /usr/local/lib/pivilion/*.sh /usr/local/bin/pivilion* /usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey || failed=1; fi
|
if command -v shellcheck >/dev/null 2>&1; then shellcheck /usr/local/lib/pivilion/*.sh /usr/local/bin/pivilion* /usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey || failed=1; fi
|
||||||
[ "$failed" -eq 0 ] || return "$PIVILION_E_INSTALL"
|
[ "$failed" -eq 0 ] || return "$PIVILION_E_INSTALL"
|
||||||
}
|
}
|
||||||
|
|
@ -305,8 +319,9 @@ 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/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-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
|
||||||
done
|
done
|
||||||
if [ "$failed" -eq 0 ]; then validate_install || failed=1; fi
|
if [ "$failed" -eq 0 ]; then validate_install || failed=1; fi
|
||||||
|
|
@ -324,9 +339,11 @@ case "$ACTION" in
|
||||||
esac
|
esac
|
||||||
|
|
||||||
backup_managed_files
|
backup_managed_files
|
||||||
|
PIVILION_COMMON_PATH="$SOURCE_DIR/usr/local/lib/pivilion/common.sh" bash "$SOURCE_DIR/usr/local/lib/pivilion/config-sync.sh" initialize
|
||||||
install_packages
|
install_packages
|
||||||
deploy_files
|
deploy_files
|
||||||
configure_services
|
configure_services
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh apply
|
||||||
validate_install
|
validate_install
|
||||||
write_install_state
|
write_install_state
|
||||||
systemctl is-active --quiet apache2 2>/dev/null && systemctl reload apache2 || true
|
systemctl is-active --quiet apache2 2>/dev/null && systemctl reload apache2 || true
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
# Copy this file to /boot/firmware/pivilion.conf or /etc/pivilion.conf.
|
# Optional template. The installer normally creates pivilion.conf automatically
|
||||||
# Values are parsed as data; this is not a shell script.
|
# at the externally visible root of the FAT boot partition.
|
||||||
PIVILION_USER=pi
|
# Values are data, not shell commands. A derived PSK is credential-equivalent.
|
||||||
PIVILION_HOME=/home/pi
|
PIVILION_USER=your-user
|
||||||
PIVILION_DIR=/home/pi/pivilion
|
PIVILION_HOME=/home/your-user
|
||||||
|
PIVILION_DIR=/home/your-user/pivilion
|
||||||
PIVILION_WEBROOT=/var/www/html/pivilion
|
PIVILION_WEBROOT=/var/www/html/pivilion
|
||||||
PIVILION_MODE=onion
|
PIVILION_MODE=onion
|
||||||
|
PIVILION_WIFI_SSID=
|
||||||
|
PIVILION_WIFI_PSK=
|
||||||
|
PIVILION_WIFI_PSK_FOR_SSID=
|
||||||
|
PIVILION_WIFI_COUNTRY=HR
|
||||||
|
PIVILION_WIFI_INTERFACE=wlan0
|
||||||
PIVILION_HOTSPOT_SSID=pivilion
|
PIVILION_HOTSPOT_SSID=pivilion
|
||||||
# PIVILION_WIFI_INTERFACE=wlan0
|
PIVILION_HOTSPOT_CHANNEL=6
|
||||||
|
PIVILION_HOSTNAME=pivilion
|
||||||
|
PIVILION_GENERATOR_ENABLED=no
|
||||||
|
PIVILION_UPLOAD_MAX_SIZE=256M
|
||||||
|
|
|
||||||
23
tests/run.sh
23
tests/run.sh
|
|
@ -46,7 +46,7 @@ state_contract() {
|
||||||
grep -q "printf 'desired_mode" "$ROOT/usr/local/lib/pivilion/common.sh" &&
|
grep -q "printf 'desired_mode" "$ROOT/usr/local/lib/pivilion/common.sh" &&
|
||||||
grep -q "printf 'active_mode" "$ROOT/usr/local/lib/pivilion/common.sh" &&
|
grep -q "printf 'active_mode" "$ROOT/usr/local/lib/pivilion/common.sh" &&
|
||||||
grep -q "printf 'health" "$ROOT/usr/local/lib/pivilion/common.sh" &&
|
grep -q "printf 'health" "$ROOT/usr/local/lib/pivilion/common.sh" &&
|
||||||
grep -q 'degraded' "$ROOT/usr/local/lib/pivilion/mode.sh"
|
grep -q 'pivilion_write_state none.*failed' "$ROOT/usr/local/lib/pivilion/mode.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
tty_banner_contract() {
|
tty_banner_contract() {
|
||||||
|
|
@ -58,6 +58,25 @@ tty_banner_contract() {
|
||||||
! grep -Eq '(^|[[:space:]])/etc/issue([[:space:]]|$)' "$ROOT/install.sh"
|
! grep -Eq '(^|[[:space:]])/etc/issue([[:space:]]|$)' "$ROOT/install.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boot_config_contract() {
|
||||||
|
grep -q 'pivilion_boot_config_path' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
|
||||||
|
grep -q 'pivilion.conf.last-good' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
|
||||||
|
grep -q 'derive_psk' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
|
||||||
|
grep -q 'Before=.*NetworkManager' "$ROOT/etc/systemd/system/pivilion-config.service" &&
|
||||||
|
grep -q 'config-sync.sh set PIVILION_MODE' "$ROOT/usr/local/bin/hotspot" &&
|
||||||
|
grep -q 'config-sync.sh set-wifi' "$ROOT/usr/local/bin/pikey"
|
||||||
|
}
|
||||||
|
|
||||||
|
same_boot_mode_contract() {
|
||||||
|
grep -q 'ExecStart=/usr/local/lib/pivilion/mode.sh --boot-selected' "$ROOT/etc/systemd/system/pivilion-mode.service" &&
|
||||||
|
grep -q 'Requires=pivilion-config.service' "$ROOT/etc/systemd/system/pivilion-mode.service" &&
|
||||||
|
grep -q 'RequiresMountsFor=/boot /boot/firmware' "$ROOT/etc/systemd/system/pivilion-config.service" &&
|
||||||
|
grep -q 'PIVILION_SELECTED_MODE_FILE' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
|
||||||
|
grep -q 'MODE=$(pivilion_selected_mode)' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
|
||||||
|
grep -q 'wait_for_diagnostics hotspot' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
|
||||||
|
grep -q 'wait_for_diagnostics onion' "$ROOT/usr/local/lib/pivilion/mode.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"
|
||||||
|
|
@ -71,6 +90,8 @@ run "temporary HTTP-only captive rules" captive_rules
|
||||||
run "transactional installer safety contract" installer_safety
|
run "transactional installer safety contract" installer_safety
|
||||||
run "truthful runtime state contract" state_contract
|
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 "same-boot hotspot and onion activation contract" same_boot_mode_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; }
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,12 @@ EOF
|
||||||
export PIVILION_CONFIG_PATHS="$TMP/primary.conf $TMP/fallback.conf"
|
export PIVILION_CONFIG_PATHS="$TMP/primary.conf $TMP/fallback.conf"
|
||||||
export PIVILION_STATE_DIR="$TMP/run"
|
export PIVILION_STATE_DIR="$TMP/run"
|
||||||
export PIVILION_STATE_FILE="$TMP/run/status.tsv"
|
export PIVILION_STATE_FILE="$TMP/run/status.tsv"
|
||||||
|
export PIVILION_SELECTED_MODE_FILE="$TMP/run/selected-mode"
|
||||||
source "$ROOT/usr/local/lib/pivilion/common.sh"
|
source "$ROOT/usr/local/lib/pivilion/common.sh"
|
||||||
|
PIVILION_BOOT_ROOT="$TMP/boot"
|
||||||
|
export PIVILION_BOOT_ROOT
|
||||||
|
[ "$(pivilion_boot_config_path)" = "$TMP/boot/pivilion.conf" ]
|
||||||
|
unset PIVILION_BOOT_ROOT
|
||||||
pivilion_load_config
|
pivilion_load_config
|
||||||
|
|
||||||
[ "$PIVILION_MODE" = hotspot ]
|
[ "$PIVILION_MODE" = hotspot ]
|
||||||
|
|
@ -32,7 +37,7 @@ pivilion_load_config
|
||||||
|
|
||||||
install -d "$PIVILION_DIR" "$PIVILION_STATE_DIR"
|
install -d "$PIVILION_DIR" "$PIVILION_STATE_DIR"
|
||||||
printf 'onion\n' > "$PIVILION_DIR/mode"
|
printf 'onion\n' > "$PIVILION_DIR/mode"
|
||||||
[ "$(pivilion_current_mode)" = onion ]
|
[ "$(pivilion_current_mode)" = hotspot ]
|
||||||
cat > "$PIVILION_STATE_FILE" <<EOF
|
cat > "$PIVILION_STATE_FILE" <<EOF
|
||||||
desired_mode onion
|
desired_mode onion
|
||||||
active_mode hotspot
|
active_mode hotspot
|
||||||
|
|
@ -43,6 +48,12 @@ EOF
|
||||||
[ "$(pivilion_state_value active_mode)" = hotspot ]
|
[ "$(pivilion_state_value active_mode)" = hotspot ]
|
||||||
[ "$(pivilion_state_value health)" = degraded ]
|
[ "$(pivilion_state_value health)" = degraded ]
|
||||||
[ "$(pivilion_state_value missing fallback)" = fallback ]
|
[ "$(pivilion_state_value missing fallback)" = fallback ]
|
||||||
|
printf 'hotspot\n' > "$PIVILION_SELECTED_MODE_FILE"
|
||||||
|
[ "$(pivilion_selected_mode)" = hotspot ]
|
||||||
|
printf 'onion\n' > "$PIVILION_SELECTED_MODE_FILE"
|
||||||
|
[ "$(pivilion_selected_mode)" = onion ]
|
||||||
|
printf 'invalid\n' > "$PIVILION_SELECTED_MODE_FILE"
|
||||||
|
if pivilion_selected_mode >/dev/null 2>&1; then exit 1; fi
|
||||||
|
|
||||||
pivilion_print_issue > "$TMP/pivilion.issue"
|
pivilion_print_issue > "$TMP/pivilion.issue"
|
||||||
grep -Fqx 'My IP address is \4{wlan-test}' "$TMP/pivilion.issue"
|
grep -Fqx 'My IP address is \4{wlan-test}' "$TMP/pivilion.issue"
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,14 @@ select choice in "Enable" "Disable"; do
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
Enable)
|
Enable)
|
||||||
backup_gallery
|
backup_gallery
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh set PIVILION_GENERATOR_ENABLED yes
|
||||||
a2ensite 000-upload.conf >/dev/null
|
a2ensite 000-upload.conf >/dev/null
|
||||||
systemctl reload apache2
|
systemctl reload apache2
|
||||||
install -d -m 2775 -o www-data -g www-data "$PIVILION_WEBROOT/gen/upload"
|
install -d -m 2775 -o www-data -g www-data "$PIVILION_WEBROOT/gen/upload"
|
||||||
echo "Generator enabled at http://<device-address>:81/"
|
echo "Generator enabled at http://<device-address>:81/"
|
||||||
break ;;
|
break ;;
|
||||||
Disable)
|
Disable)
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh set PIVILION_GENERATOR_ENABLED no
|
||||||
a2dissite 000-upload.conf >/dev/null || true
|
a2dissite 000-upload.conf >/dev/null || true
|
||||||
systemctl reload apache2
|
systemctl reload apache2
|
||||||
echo "Generator disabled."
|
echo "Generator disabled."
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@ set -euo pipefail
|
||||||
source /usr/local/lib/pivilion/common.sh
|
source /usr/local/lib/pivilion/common.sh
|
||||||
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
|
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
|
||||||
pivilion_load_config
|
pivilion_load_config
|
||||||
pivilion_set_mode hotspot
|
/usr/local/lib/pivilion/config-sync.sh set PIVILION_MODE hotspot
|
||||||
echo "Pivilion will start in open hotspot mode after the next reboot."
|
echo "Pivilion will start in open hotspot mode after the next reboot."
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ set -euo pipefail
|
||||||
source /usr/local/lib/pivilion/common.sh
|
source /usr/local/lib/pivilion/common.sh
|
||||||
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
|
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
|
||||||
pivilion_load_config
|
pivilion_load_config
|
||||||
pivilion_set_mode onion
|
/usr/local/lib/pivilion/config-sync.sh set PIVILION_MODE onion
|
||||||
echo "Pivilion will start in onion mode after the next reboot."
|
echo "Pivilion will start in onion mode after the next reboot."
|
||||||
if [ -s "$PIVILION_DIR/torname" ]; then
|
if [ -s "$PIVILION_DIR/torname" ]; then
|
||||||
printf 'Your onion domain is %s\n' "$(tr -d '\r\n' < "$PIVILION_DIR/torname")"
|
printf 'Your onion domain is %s\n' "$(tr -d '\r\n' < "$PIVILION_DIR/torname")"
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@ set -euo pipefail
|
||||||
source /usr/local/lib/pivilion/common.sh
|
source /usr/local/lib/pivilion/common.sh
|
||||||
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
|
[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@"
|
||||||
pivilion_load_config
|
pivilion_load_config
|
||||||
IFACE=$(pivilion_wifi_interface)
|
|
||||||
[ -n "$IFACE" ] || { echo "No Wi-Fi interface found." >&2; exit 1; }
|
|
||||||
|
|
||||||
read -r -p "Wi-Fi SSID: " ssid
|
read -r -p "Wi-Fi SSID: " ssid
|
||||||
read -r -s -p "Wi-Fi password: " password
|
read -r -s -p "Wi-Fi password: " password
|
||||||
printf '\n'
|
printf '\n'
|
||||||
|
|
@ -14,22 +11,6 @@ printf '\n'
|
||||||
echo "WPA password must contain 8-63 characters." >&2; exit 1;
|
echo "WPA password must contain 8-63 characters." >&2; exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if pivilion_uses_networkmanager; then
|
/usr/local/lib/pivilion/config-sync.sh set-wifi "$ssid" "$password"
|
||||||
nmcli connection delete pivilion-onion >/dev/null 2>&1 || true
|
|
||||||
nmcli connection add type wifi ifname "$IFACE" con-name pivilion-onion ssid "$ssid" >/dev/null
|
|
||||||
nmcli connection modify pivilion-onion 802-11-wireless.mode infrastructure \
|
|
||||||
802-11-wireless-security.key-mgmt wpa-psk 802-11-wireless-security.psk "$password" \
|
|
||||||
ipv4.method auto ipv6.method auto connection.autoconnect yes
|
|
||||||
else
|
|
||||||
tmp=$(mktemp)
|
|
||||||
trap 'rm -f "$tmp"' EXIT
|
|
||||||
wpa_passphrase "$ssid" "$password" | sed '/^[[:space:]]*#psk=/d' > "$tmp"
|
|
||||||
install -d -m 0755 /etc/wpa_supplicant
|
|
||||||
install -m 0600 "$tmp" /etc/wpa_supplicant/pivilion.conf
|
|
||||||
touch /etc/wpa_supplicant/wpa_supplicant.conf
|
|
||||||
grep -Fqx 'include=/etc/wpa_supplicant/pivilion.conf' /etc/wpa_supplicant/wpa_supplicant.conf || \
|
|
||||||
printf '\ninclude=/etc/wpa_supplicant/pivilion.conf\n' >> /etc/wpa_supplicant/wpa_supplicant.conf
|
|
||||||
chmod 0600 /etc/wpa_supplicant/wpa_supplicant.conf
|
|
||||||
fi
|
|
||||||
unset password
|
unset password
|
||||||
echo "Wi-Fi credentials saved. Run onion and reboot when ready."
|
echo "Wi-Fi credentials saved as a derived PSK in the boot configuration. Run onion and reboot when ready."
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
PIVILION_CONFIG=""
|
PIVILION_CONFIG=""
|
||||||
PIVILION_STATE_DIR=${PIVILION_STATE_DIR:-/run/pivilion}
|
PIVILION_STATE_DIR=${PIVILION_STATE_DIR:-/run/pivilion}
|
||||||
PIVILION_STATE_FILE=${PIVILION_STATE_FILE:-$PIVILION_STATE_DIR/status.tsv}
|
PIVILION_STATE_FILE=${PIVILION_STATE_FILE:-$PIVILION_STATE_DIR/status.tsv}
|
||||||
|
PIVILION_SELECTED_MODE_FILE=${PIVILION_SELECTED_MODE_FILE:-$PIVILION_STATE_DIR/selected-mode}
|
||||||
|
|
||||||
# Stable command exit codes. Keep these public so scripts and monitoring can
|
# Stable command exit codes. Keep these public so scripts and monitoring can
|
||||||
# distinguish configuration, hardware, service, captive portal, and Tor faults.
|
# distinguish configuration, hardware, service, captive portal, and Tor faults.
|
||||||
|
|
@ -25,7 +26,7 @@ _pivilion_trim() {
|
||||||
|
|
||||||
_pivilion_config_value() {
|
_pivilion_config_value() {
|
||||||
local key=$1 file line name value config_paths
|
local key=$1 file line name value config_paths
|
||||||
config_paths=${PIVILION_CONFIG_PATHS:-/boot/firmware/pivilion.conf /boot/pivilion.conf /etc/pivilion.conf /etc/pivilion.defaults}
|
config_paths=${PIVILION_CONFIG_PATHS:-/run/pivilion/effective.conf $(pivilion_boot_config_path) /etc/pivilion.conf /etc/pivilion.defaults}
|
||||||
for file in $config_paths; do
|
for file in $config_paths; do
|
||||||
[ -r "$file" ] || continue
|
[ -r "$file" ] || continue
|
||||||
while IFS= read -r line || [ -n "$line" ]; do
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
|
@ -68,13 +69,29 @@ pivilion_load_config() {
|
||||||
PIVILION_HOTSPOT_SSID=${configured:-pivilion}
|
PIVILION_HOTSPOT_SSID=${configured:-pivilion}
|
||||||
configured=$(_pivilion_config_value PIVILION_WIFI_INTERFACE || true)
|
configured=$(_pivilion_config_value PIVILION_WIFI_INTERFACE || true)
|
||||||
PIVILION_WIFI_INTERFACE=${configured:-}
|
PIVILION_WIFI_INTERFACE=${configured:-}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_WIFI_SSID || true)
|
||||||
|
PIVILION_WIFI_SSID=${configured:-}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_WIFI_PSK || true)
|
||||||
|
PIVILION_WIFI_PSK=${configured:-}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_WIFI_PSK_FOR_SSID || true)
|
||||||
|
PIVILION_WIFI_PSK_FOR_SSID=${configured:-}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_WIFI_COUNTRY || true)
|
||||||
|
PIVILION_WIFI_COUNTRY=${configured:-HR}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_HOTSPOT_CHANNEL || true)
|
||||||
|
PIVILION_HOTSPOT_CHANNEL=${configured:-6}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_HOSTNAME || true)
|
||||||
|
PIVILION_HOSTNAME=${configured:-$(hostname -s 2>/dev/null || uname -n)}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_GENERATOR_ENABLED || true)
|
||||||
|
PIVILION_GENERATOR_ENABLED=${configured:-no}
|
||||||
|
configured=$(_pivilion_config_value PIVILION_UPLOAD_MAX_SIZE || true)
|
||||||
|
PIVILION_UPLOAD_MAX_SIZE=${configured:-256M}
|
||||||
|
|
||||||
case "$PIVILION_MODE" in onion|hotspot) ;; *) echo "PIVILION_MODE must be onion or hotspot" >&2; return 1 ;; esac
|
case "$PIVILION_MODE" in onion|hotspot) ;; *) echo "PIVILION_MODE must be onion or hotspot" >&2; return 1 ;; esac
|
||||||
ssid_bytes=$(LC_ALL=C printf '%s' "$PIVILION_HOTSPOT_SSID" | wc -c)
|
ssid_bytes=$(LC_ALL=C printf '%s' "$PIVILION_HOTSPOT_SSID" | wc -c)
|
||||||
[ "$ssid_bytes" -ge 1 ] && [ "$ssid_bytes" -le 32 ] || {
|
[ "$ssid_bytes" -ge 1 ] && [ "$ssid_bytes" -le 32 ] || {
|
||||||
echo "PIVILION_HOTSPOT_SSID must contain 1-32 bytes" >&2; return 1;
|
echo "PIVILION_HOTSPOT_SSID must contain 1-32 bytes" >&2; return 1;
|
||||||
}
|
}
|
||||||
case "$PIVILION_HOME$PIVILION_DIR$PIVILION_WEBROOT$PIVILION_WIFI_INTERFACE$PIVILION_HOTSPOT_SSID" in
|
case "$PIVILION_HOME$PIVILION_DIR$PIVILION_WEBROOT$PIVILION_WIFI_INTERFACE$PIVILION_HOTSPOT_SSID$PIVILION_WIFI_SSID$PIVILION_WIFI_PSK" in
|
||||||
*$'\n'*|*$'\r'*) echo "Invalid control character in Pivilion configuration" >&2; return 1 ;;
|
*$'\n'*|*$'\r'*) echo "Invalid control character in Pivilion configuration" >&2; return 1 ;;
|
||||||
esac
|
esac
|
||||||
case "$PIVILION_HOME:$PIVILION_DIR:$PIVILION_WEBROOT" in
|
case "$PIVILION_HOME:$PIVILION_DIR:$PIVILION_WEBROOT" in
|
||||||
|
|
@ -86,9 +103,32 @@ pivilion_load_config() {
|
||||||
[ -z "$PIVILION_WIFI_INTERFACE" ] || [[ "$PIVILION_WIFI_INTERFACE" =~ ^[A-Za-z0-9._:-]+$ ]] || {
|
[ -z "$PIVILION_WIFI_INTERFACE" ] || [[ "$PIVILION_WIFI_INTERFACE" =~ ^[A-Za-z0-9._:-]+$ ]] || {
|
||||||
echo "Invalid PIVILION_WIFI_INTERFACE" >&2; return 1;
|
echo "Invalid PIVILION_WIFI_INTERFACE" >&2; return 1;
|
||||||
}
|
}
|
||||||
|
[[ "$PIVILION_WIFI_COUNTRY" =~ ^[A-Z]{2}$ ]] || { echo "PIVILION_WIFI_COUNTRY must be a two-letter uppercase country code" >&2; return 1; }
|
||||||
|
[[ "$PIVILION_HOTSPOT_CHANNEL" =~ ^[0-9]+$ ]] && [ "$PIVILION_HOTSPOT_CHANNEL" -ge 1 ] && [ "$PIVILION_HOTSPOT_CHANNEL" -le 13 ] || {
|
||||||
|
echo "PIVILION_HOTSPOT_CHANNEL must be between 1 and 13" >&2; return 1;
|
||||||
|
}
|
||||||
|
[[ "$PIVILION_HOSTNAME" =~ ^[A-Za-z0-9][A-Za-z0-9.-]{0,62}$ ]] || { echo "Invalid PIVILION_HOSTNAME" >&2; return 1; }
|
||||||
|
case "$PIVILION_GENERATOR_ENABLED" in yes|no) ;; *) echo "PIVILION_GENERATOR_ENABLED must be yes or no" >&2; return 1 ;; esac
|
||||||
|
[[ "$PIVILION_UPLOAD_MAX_SIZE" =~ ^[1-9][0-9]*M$ ]] || { echo "PIVILION_UPLOAD_MAX_SIZE must use a value such as 256M" >&2; return 1; }
|
||||||
|
if [ -n "$PIVILION_WIFI_PSK" ] && [[ ! "$PIVILION_WIFI_PSK" =~ ^[0-9A-Fa-f]{64}$ ]] && { [ "${#PIVILION_WIFI_PSK}" -lt 8 ] || [ "${#PIVILION_WIFI_PSK}" -gt 63 ]; }; then
|
||||||
|
echo "PIVILION_WIFI_PSK must be an 8-63 character password or 64 hexadecimal PSK" >&2; return 1
|
||||||
|
fi
|
||||||
|
[ -z "$PIVILION_WIFI_PSK" ] || [ -n "$PIVILION_WIFI_SSID" ] || { echo "PIVILION_WIFI_SSID is required when a PSK is set" >&2; return 1; }
|
||||||
export PIVILION_USER PIVILION_GROUP PIVILION_HOME PIVILION_DIR PIVILION_WEBROOT PIVILION_MODE PIVILION_HOTSPOT_SSID PIVILION_WIFI_INTERFACE
|
export PIVILION_USER PIVILION_GROUP PIVILION_HOME PIVILION_DIR PIVILION_WEBROOT PIVILION_MODE PIVILION_HOTSPOT_SSID PIVILION_WIFI_INTERFACE
|
||||||
|
export PIVILION_WIFI_SSID PIVILION_WIFI_PSK PIVILION_WIFI_PSK_FOR_SSID PIVILION_WIFI_COUNTRY PIVILION_HOTSPOT_CHANNEL
|
||||||
|
export PIVILION_HOSTNAME PIVILION_GENERATOR_ENABLED PIVILION_UPLOAD_MAX_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pivilion_boot_root() {
|
||||||
|
if [ -n "${PIVILION_BOOT_ROOT:-}" ]; then printf '%s\n' "$PIVILION_BOOT_ROOT"; return; fi
|
||||||
|
if command -v mountpoint >/dev/null 2>&1 && mountpoint -q /boot/firmware; then printf '/boot/firmware\n'; return; fi
|
||||||
|
if [ -d /boot/firmware ] && { [ -e /boot/firmware/config.txt ] || [ -e /boot/firmware/cmdline.txt ]; }; then printf '/boot/firmware\n'; return; fi
|
||||||
|
printf '/boot\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
pivilion_boot_config_path() { printf '%s/pivilion.conf\n' "$(pivilion_boot_root)"; }
|
||||||
|
pivilion_boot_error_path() { printf '%s/pivilion-error.txt\n' "$(pivilion_boot_root)"; }
|
||||||
|
|
||||||
pivilion_wifi_interface() {
|
pivilion_wifi_interface() {
|
||||||
local iface
|
local iface
|
||||||
if [ -n "${PIVILION_WIFI_INTERFACE:-}" ]; then printf '%s\n' "$PIVILION_WIFI_INTERFACE"; return; fi
|
if [ -n "${PIVILION_WIFI_INTERFACE:-}" ]; then printf '%s\n' "$PIVILION_WIFI_INTERFACE"; return; fi
|
||||||
|
|
@ -116,9 +156,13 @@ pivilion_set_mode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pivilion_current_mode() {
|
pivilion_current_mode() {
|
||||||
|
printf '%s\n' "$PIVILION_MODE"
|
||||||
|
}
|
||||||
|
|
||||||
|
pivilion_selected_mode() {
|
||||||
local mode
|
local mode
|
||||||
mode=$(head -n1 "$PIVILION_DIR/mode" 2>/dev/null || true)
|
mode=$(head -n1 "$PIVILION_SELECTED_MODE_FILE" 2>/dev/null || true)
|
||||||
case "$mode" in onion|hotspot) printf '%s\n' "$mode" ;; *) printf '%s\n' "$PIVILION_MODE" ;; esac
|
case "$mode" in hotspot|onion) printf '%s\n' "$mode" ;; *) return 1 ;; esac
|
||||||
}
|
}
|
||||||
|
|
||||||
pivilion_require_root() {
|
pivilion_require_root() {
|
||||||
|
|
|
||||||
301
usr/local/lib/pivilion/config-sync.sh
Normal file
301
usr/local/lib/pivilion/config-sync.sh
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
source "${PIVILION_COMMON_PATH:-/usr/local/lib/pivilion/common.sh}"
|
||||||
|
pivilion_require_root
|
||||||
|
|
||||||
|
BOOT_ROOT=$(pivilion_boot_root)
|
||||||
|
BOOT_CONFIG=$(pivilion_boot_config_path)
|
||||||
|
BOOT_ERROR=$(pivilion_boot_error_path)
|
||||||
|
LAST_GOOD=${PIVILION_LAST_GOOD:-/var/lib/pivilion/pivilion.conf.last-good}
|
||||||
|
LOCK_FILE=${PIVILION_CONFIG_LOCK:-/run/pivilion-config.lock}
|
||||||
|
ACTION=${1:-apply}
|
||||||
|
|
||||||
|
install -d -m 0755 "$(dirname "$LOCK_FILE")" /var/lib/pivilion
|
||||||
|
exec 9>"$LOCK_FILE"
|
||||||
|
flock -x 9
|
||||||
|
|
||||||
|
fail_boot() {
|
||||||
|
local message=$1 tmp
|
||||||
|
printf 'Pivilion configuration error: %s\n' "$message" >&2
|
||||||
|
if [ -d "$BOOT_ROOT" ] && [ -w "$BOOT_ROOT" ]; then
|
||||||
|
tmp=$(mktemp "$BOOT_ROOT/.pivilion-error.XXXXXX")
|
||||||
|
printf '%s\n' "$message" > "$tmp"
|
||||||
|
mv -f "$tmp" "$BOOT_ERROR"
|
||||||
|
sync -f "$BOOT_ROOT" 2>/dev/null || sync
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_writable_boot() {
|
||||||
|
[ -d "$BOOT_ROOT" ] && [ -w "$BOOT_ROOT" ] && touch "$BOOT_ROOT/.pivilion-write-test.$$" 2>/dev/null || {
|
||||||
|
fail_boot "The boot partition root $BOOT_ROOT is missing or read-only"
|
||||||
|
exit "$PIVILION_E_CONFIG"
|
||||||
|
}
|
||||||
|
rm -f "$BOOT_ROOT/.pivilion-write-test.$$"
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_copy() {
|
||||||
|
local source=$1 destination=$2 mode=${3:-0600} tmp
|
||||||
|
install -d -m 0700 "$(dirname "$destination")"
|
||||||
|
tmp=$(mktemp "$(dirname "$destination")/.pivilion.XXXXXX")
|
||||||
|
install -m "$mode" "$source" "$tmp"
|
||||||
|
mv -f "$tmp" "$destination"
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_psk() {
|
||||||
|
local ssid=$1 password=$2
|
||||||
|
if command -v wpa_passphrase >/dev/null 2>&1; then
|
||||||
|
wpa_passphrase "$ssid" "$password" | awk -F= '/^[[:space:]]*psk=[0-9a-f]{64}$/ {print $2; exit}'
|
||||||
|
elif command -v python3 >/dev/null 2>&1; then
|
||||||
|
printf '%s\n%s\n' "$ssid" "$password" | python3 -c 'import hashlib,sys; s=sys.stdin.buffer.readline().rstrip(b"\n"); p=sys.stdin.buffer.readline().rstrip(b"\n"); print(hashlib.pbkdf2_hmac("sha1",p,s,4096,32).hex())'
|
||||||
|
else
|
||||||
|
fail_boot "wpa_passphrase or python3 is required to sanitize the Wi-Fi password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_wifi() {
|
||||||
|
local connection file
|
||||||
|
[ -n "${PIVILION_WIFI_SSID:-}" ] && return
|
||||||
|
if command -v nmcli >/dev/null 2>&1; then
|
||||||
|
connection=$(nmcli -t -f NAME,TYPE connection show --active 2>/dev/null | awk -F: '$2 ~ /wireless|wifi/ {print $1; exit}')
|
||||||
|
if [ -n "$connection" ]; then
|
||||||
|
PIVILION_WIFI_SSID=$(nmcli -g 802-11-wireless.ssid connection show "$connection" 2>/dev/null || true)
|
||||||
|
PIVILION_WIFI_PSK=$(nmcli --show-secrets -g 802-11-wireless-security.psk connection show "$connection" 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -z "${PIVILION_WIFI_SSID:-}" ]; then
|
||||||
|
for file in /etc/wpa_supplicant/pivilion.conf /etc/wpa_supplicant/wpa_supplicant.conf; do
|
||||||
|
[ -r "$file" ] || continue
|
||||||
|
PIVILION_WIFI_SSID=$(sed -n 's/^[[:space:]]*ssid="\(.*\)"/\1/p' "$file" | head -n1)
|
||||||
|
PIVILION_WIFI_PSK=$(sed -n 's/^[[:space:]]*psk="\(.*\)"/\1/p; s/^[[:space:]]*psk=\([0-9a-fA-F]\{64\}\)$/\1/p' "$file" | head -n1)
|
||||||
|
[ -n "$PIVILION_WIFI_SSID" ] && break
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitize_wifi() {
|
||||||
|
local derived
|
||||||
|
[ -n "$PIVILION_WIFI_SSID" ] || { PIVILION_WIFI_PSK=""; PIVILION_WIFI_PSK_FOR_SSID=""; return; }
|
||||||
|
if [[ "$PIVILION_WIFI_PSK" =~ ^[0-9A-Fa-f]{64}$ ]]; then
|
||||||
|
if [ -n "$PIVILION_WIFI_PSK_FOR_SSID" ] && [ "$PIVILION_WIFI_PSK_FOR_SSID" != "$PIVILION_WIFI_SSID" ]; then
|
||||||
|
fail_boot "Wi-Fi SSID changed but its stored derived PSK belongs to a different SSID"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
PIVILION_WIFI_PSK=${PIVILION_WIFI_PSK,,}
|
||||||
|
elif [ -n "$PIVILION_WIFI_PSK" ]; then
|
||||||
|
derived=$(derive_psk "$PIVILION_WIFI_SSID" "$PIVILION_WIFI_PSK") || return 1
|
||||||
|
[ -n "$derived" ] || { fail_boot "Could not derive the Wi-Fi PSK"; return 1; }
|
||||||
|
PIVILION_WIFI_PSK=$derived
|
||||||
|
fi
|
||||||
|
PIVILION_WIFI_PSK_FOR_SSID=$PIVILION_WIFI_SSID
|
||||||
|
}
|
||||||
|
|
||||||
|
render_config() {
|
||||||
|
local active onion error
|
||||||
|
active=$(pivilion_state_value active_mode none)
|
||||||
|
onion=$(tr -d '\r\n' < "$PIVILION_DIR/torname" 2>/dev/null || true)
|
||||||
|
error=$(pivilion_state_value failure none)
|
||||||
|
cat <<EOF
|
||||||
|
# Pivilion boot configuration. Edit this file while the boot partition is mounted.
|
||||||
|
# A 64-character Wi-Fi PSK grants network access just like the original password.
|
||||||
|
# With no saved upstream network, leave the next three Wi-Fi fields empty.
|
||||||
|
# To add one offline, set SSID and an 8-63 character password in PIVILION_WIFI_PSK.
|
||||||
|
# Last applied: $(date --iso-8601=seconds)
|
||||||
|
# Active mode: $active
|
||||||
|
# Detected interface: $(pivilion_wifi_interface || printf unknown)
|
||||||
|
# Onion hostname: ${onion:-unknown}
|
||||||
|
# Last error: ${error:-none}
|
||||||
|
PIVILION_MODE=$PIVILION_MODE
|
||||||
|
PIVILION_WIFI_SSID=$PIVILION_WIFI_SSID
|
||||||
|
PIVILION_WIFI_PSK=$PIVILION_WIFI_PSK
|
||||||
|
PIVILION_WIFI_PSK_FOR_SSID=$PIVILION_WIFI_PSK_FOR_SSID
|
||||||
|
PIVILION_WIFI_COUNTRY=$PIVILION_WIFI_COUNTRY
|
||||||
|
PIVILION_WIFI_INTERFACE=$PIVILION_WIFI_INTERFACE
|
||||||
|
PIVILION_HOTSPOT_SSID=$PIVILION_HOTSPOT_SSID
|
||||||
|
PIVILION_HOTSPOT_CHANNEL=$PIVILION_HOTSPOT_CHANNEL
|
||||||
|
PIVILION_HOSTNAME=$PIVILION_HOSTNAME
|
||||||
|
PIVILION_USER=$PIVILION_USER
|
||||||
|
PIVILION_HOME=$PIVILION_HOME
|
||||||
|
PIVILION_DIR=$PIVILION_DIR
|
||||||
|
PIVILION_WEBROOT=$PIVILION_WEBROOT
|
||||||
|
PIVILION_GENERATOR_ENABLED=$PIVILION_GENERATOR_ENABLED
|
||||||
|
PIVILION_UPLOAD_MAX_SIZE=$PIVILION_UPLOAD_MAX_SIZE
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
write_canonical() {
|
||||||
|
local tmp mode_tmp
|
||||||
|
require_writable_boot
|
||||||
|
tmp=$(mktemp "$BOOT_ROOT/.pivilion-conf.XXXXXX")
|
||||||
|
render_config > "$tmp"
|
||||||
|
chmod 0600 "$tmp"
|
||||||
|
mv -f "$tmp" "$BOOT_CONFIG"
|
||||||
|
sync -f "$BOOT_ROOT" 2>/dev/null || sync
|
||||||
|
atomic_copy "$BOOT_CONFIG" "$LAST_GOOD" 0600
|
||||||
|
atomic_copy "$BOOT_CONFIG" /run/pivilion/effective.conf 0600
|
||||||
|
mode_tmp=$(mktemp)
|
||||||
|
printf '%s\n' "$PIVILION_MODE" > "$mode_tmp"
|
||||||
|
atomic_copy "$mode_tmp" "$PIVILION_SELECTED_MODE_FILE" 0644
|
||||||
|
rm -f "$mode_tmp"
|
||||||
|
rm -f "$BOOT_ERROR"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_boot_or_detect() {
|
||||||
|
if [ -e "$BOOT_CONFIG" ]; then
|
||||||
|
PIVILION_CONFIG_PATHS=$BOOT_CONFIG pivilion_load_config || return 1
|
||||||
|
else
|
||||||
|
pivilion_load_config
|
||||||
|
if [ -s "$PIVILION_DIR/mode" ]; then
|
||||||
|
case "$(head -n1 "$PIVILION_DIR/mode")" in onion|hotspot) PIVILION_MODE=$(head -n1 "$PIVILION_DIR/mode") ;; esac
|
||||||
|
fi
|
||||||
|
PIVILION_WIFI_INTERFACE=${PIVILION_WIFI_INTERFACE:-$(pivilion_wifi_interface || true)}
|
||||||
|
PIVILION_HOSTNAME=${PIVILION_HOSTNAME:-$(hostname -s)}
|
||||||
|
if command -v a2query >/dev/null 2>&1 && a2query -s 000-upload >/dev/null 2>&1; then PIVILION_GENERATOR_ENABLED=yes; fi
|
||||||
|
if command -v php >/dev/null 2>&1; then
|
||||||
|
detected_upload=$(php -r 'echo ini_get("upload_max_filesize");' 2>/dev/null || true)
|
||||||
|
[[ "$detected_upload" =~ ^[1-9][0-9]*M$ ]] && PIVILION_UPLOAD_MAX_SIZE=$detected_upload
|
||||||
|
fi
|
||||||
|
detect_wifi
|
||||||
|
fi
|
||||||
|
PIVILION_WIFI_INTERFACE=${PIVILION_WIFI_INTERFACE:-$(pivilion_wifi_interface || true)}
|
||||||
|
sanitize_wifi
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate_paths() {
|
||||||
|
local old_dir old_webroot common=${PIVILION_COMMON_PATH:-/usr/local/lib/pivilion/common.sh}
|
||||||
|
[ -r "$LAST_GOOD" ] || return 0
|
||||||
|
old_dir=$(PIVILION_CONFIG_PATHS=$LAST_GOOD bash -c 'source "$1"; pivilion_load_config >/dev/null && printf %s "$PIVILION_DIR"' _ "$common" 2>/dev/null || true)
|
||||||
|
old_webroot=$(PIVILION_CONFIG_PATHS=$LAST_GOOD bash -c 'source "$1"; pivilion_load_config >/dev/null && printf %s "$PIVILION_WEBROOT"' _ "$common" 2>/dev/null || true)
|
||||||
|
if [ -n "$old_dir" ] && [ "$old_dir" != "$PIVILION_DIR" ] && [ -d "$old_dir" ] && [ ! -e "$PIVILION_DIR" ]; then
|
||||||
|
install -d -m 0755 -o "$PIVILION_USER" -g "$PIVILION_GROUP" "$PIVILION_DIR"
|
||||||
|
cp -a "$old_dir/." "$PIVILION_DIR/"
|
||||||
|
chown -R "$PIVILION_USER:$PIVILION_GROUP" "$PIVILION_DIR"
|
||||||
|
fi
|
||||||
|
if [ -n "$old_webroot" ] && [ "$old_webroot" != "$PIVILION_WEBROOT" ] && [ -d "$old_webroot" ] && [ ! -e "$PIVILION_WEBROOT" ]; then
|
||||||
|
install -d -m 2775 -o www-data -g www-data "$PIVILION_WEBROOT"
|
||||||
|
cp -a "$old_webroot/." "$PIVILION_WEBROOT/"
|
||||||
|
fi
|
||||||
|
if [ -n "$old_webroot" ] && [ "$old_webroot" != "$PIVILION_WEBROOT" ]; then
|
||||||
|
for file in /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-upload.conf /etc/apache2/conf-available/pivilion.conf; do
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
sed "s|$old_webroot|$PIVILION_WEBROOT|g" "$file" > "$file.pivilion.tmp"
|
||||||
|
install -m 0644 "$file.pivilion.tmp" "$file"
|
||||||
|
rm -f "$file.pivilion.tmp"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_runtime() {
|
||||||
|
local php_version post_mb escaped_ssid
|
||||||
|
migrate_paths
|
||||||
|
if [ "$(hostname -s)" != "$PIVILION_HOSTNAME" ] && command -v hostnamectl >/dev/null 2>&1; then hostnamectl set-hostname "$PIVILION_HOSTNAME"; fi
|
||||||
|
command -v iw >/dev/null 2>&1 && iw reg set "$PIVILION_WIFI_COUNTRY" 2>/dev/null || true
|
||||||
|
if [ -n "$PIVILION_WIFI_SSID" ] && [ -n "$PIVILION_WIFI_PSK" ]; then
|
||||||
|
if command -v nmcli >/dev/null 2>&1 && { systemctl is-enabled --quiet NetworkManager 2>/dev/null || systemctl is-active --quiet NetworkManager 2>/dev/null; }; then
|
||||||
|
install -d -m 0700 /etc/NetworkManager/system-connections
|
||||||
|
cat > /etc/NetworkManager/system-connections/.pivilion-onion.tmp <<EOF
|
||||||
|
[connection]
|
||||||
|
id=pivilion-onion
|
||||||
|
type=wifi
|
||||||
|
interface-name=$PIVILION_WIFI_INTERFACE
|
||||||
|
autoconnect=true
|
||||||
|
|
||||||
|
[wifi]
|
||||||
|
mode=infrastructure
|
||||||
|
ssid=$PIVILION_WIFI_SSID
|
||||||
|
|
||||||
|
[wifi-security]
|
||||||
|
key-mgmt=wpa-psk
|
||||||
|
psk=$PIVILION_WIFI_PSK
|
||||||
|
|
||||||
|
[ipv4]
|
||||||
|
method=auto
|
||||||
|
|
||||||
|
[ipv6]
|
||||||
|
method=auto
|
||||||
|
EOF
|
||||||
|
install -m 0600 /etc/NetworkManager/system-connections/.pivilion-onion.tmp /etc/NetworkManager/system-connections/pivilion-onion.nmconnection
|
||||||
|
rm -f /etc/NetworkManager/system-connections/.pivilion-onion.tmp
|
||||||
|
elif command -v wpa_passphrase >/dev/null 2>&1; then
|
||||||
|
install -d -m 0755 /etc/wpa_supplicant
|
||||||
|
escaped_ssid=${PIVILION_WIFI_SSID//\\/\\\\}; escaped_ssid=${escaped_ssid//\"/\\\"}
|
||||||
|
printf 'country=%s\nnetwork={\n ssid="%s"\n psk=%s\n}\n' "$PIVILION_WIFI_COUNTRY" "$escaped_ssid" "$PIVILION_WIFI_PSK" > /etc/wpa_supplicant/.pivilion.tmp
|
||||||
|
install -m 0600 /etc/wpa_supplicant/.pivilion.tmp /etc/wpa_supplicant/pivilion.conf
|
||||||
|
rm -f /etc/wpa_supplicant/.pivilion.tmp
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
install -d -m 0755 -o "$PIVILION_USER" -g "$PIVILION_GROUP" "$PIVILION_DIR"
|
||||||
|
printf '%s\n' "$PIVILION_MODE" | install -m 0644 -o "$PIVILION_USER" -g "$PIVILION_GROUP" /dev/stdin "$PIVILION_DIR/mode"
|
||||||
|
if command -v php >/dev/null 2>&1; then
|
||||||
|
printf "<?php\ndefine('PIVILION_DIR', '%s');\ndefine('PIVILION_WEBROOT', '%s');\n" "$PIVILION_DIR" "$PIVILION_WEBROOT" | install -m 0644 /dev/stdin /etc/pivilion.php
|
||||||
|
fi
|
||||||
|
if command -v php >/dev/null 2>&1; then
|
||||||
|
php_version=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
|
||||||
|
post_mb=$((${PIVILION_UPLOAD_MAX_SIZE%M} + 16))
|
||||||
|
install -d -m 0755 "/etc/php/$php_version/apache2/conf.d"
|
||||||
|
printf 'upload_max_filesize = %s\npost_max_size = %sM\nmax_file_uploads = 100\nmax_execution_time = 300\nmemory_limit = 512M\n' "$PIVILION_UPLOAD_MAX_SIZE" "$post_mb" \
|
||||||
|
| install -m 0644 /dev/stdin "/etc/php/$php_version/apache2/conf.d/99-pivilion.ini"
|
||||||
|
fi
|
||||||
|
if command -v a2ensite >/dev/null 2>&1; then
|
||||||
|
if [ "$PIVILION_GENERATOR_ENABLED" = yes ]; then a2ensite 000-upload.conf >/dev/null; else a2dissite 000-upload.conf >/dev/null 2>&1 || true; fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_value() {
|
||||||
|
local key=${2:-} value=${3-} tmp
|
||||||
|
case "$key" in
|
||||||
|
PIVILION_MODE|PIVILION_WIFI_SSID|PIVILION_WIFI_PSK|PIVILION_WIFI_PSK_FOR_SSID|PIVILION_WIFI_COUNTRY|PIVILION_WIFI_INTERFACE|PIVILION_HOTSPOT_SSID|PIVILION_HOTSPOT_CHANNEL|PIVILION_HOSTNAME|PIVILION_USER|PIVILION_HOME|PIVILION_DIR|PIVILION_WEBROOT|PIVILION_GENERATOR_ENABLED|PIVILION_UPLOAD_MAX_SIZE) ;;
|
||||||
|
*) echo "Unsupported configuration key: $key" >&2; exit "$PIVILION_E_CONFIG" ;;
|
||||||
|
esac
|
||||||
|
[ -r "$BOOT_CONFIG" ] || { echo "Boot configuration is not initialized." >&2; exit "$PIVILION_E_CONFIG"; }
|
||||||
|
tmp=$(mktemp)
|
||||||
|
awk -v key="$key" -v value="$value" 'BEGIN{done=0} $0 ~ "^" key "=" {if(!done){print key "=" value; done=1}; next} {print} END{if(!done) print key "=" value}' "$BOOT_CONFIG" > "$tmp"
|
||||||
|
PIVILION_CONFIG_PATHS=$tmp pivilion_load_config || { rm -f "$tmp"; exit "$PIVILION_E_CONFIG"; }
|
||||||
|
sanitize_wifi || { rm -f "$tmp"; exit "$PIVILION_E_CONFIG"; }
|
||||||
|
write_canonical
|
||||||
|
rm -f "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
apply|initialize)
|
||||||
|
if [ "$ACTION" = apply ]; then
|
||||||
|
rm -f /run/pivilion/effective.conf "$PIVILION_SELECTED_MODE_FILE"
|
||||||
|
fi
|
||||||
|
if ! load_boot_or_detect; then
|
||||||
|
fail_boot "Invalid pivilion.conf; the last-known-good settings were retained"
|
||||||
|
[ -r "$LAST_GOOD" ] || exit "$PIVILION_E_CONFIG"
|
||||||
|
PIVILION_CONFIG_PATHS=$LAST_GOOD pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
||||||
|
atomic_copy "$LAST_GOOD" /run/pivilion/effective.conf 0600
|
||||||
|
mode_tmp=$(mktemp)
|
||||||
|
printf '%s\n' "$PIVILION_MODE" > "$mode_tmp"
|
||||||
|
atomic_copy "$mode_tmp" "$PIVILION_SELECTED_MODE_FILE" 0644
|
||||||
|
rm -f "$mode_tmp"
|
||||||
|
[ "$ACTION" = initialize ] || apply_runtime
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
[ "$ACTION" = initialize ] || apply_runtime
|
||||||
|
write_canonical ;;
|
||||||
|
set) set_value "$@" ;;
|
||||||
|
set-wifi)
|
||||||
|
[ "$#" -eq 3 ] || { echo "Usage: config-sync.sh set-wifi SSID PASSWORD" >&2; exit 2; }
|
||||||
|
[ -r "$BOOT_CONFIG" ] || { echo "Boot configuration is not initialized." >&2; exit "$PIVILION_E_CONFIG"; }
|
||||||
|
PIVILION_CONFIG_PATHS=$BOOT_CONFIG pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
||||||
|
case "$2$3" in *$'\n'*|*$'\r'*) echo "Wi-Fi values may not contain newlines" >&2; exit "$PIVILION_E_CONFIG" ;; esac
|
||||||
|
ssid_bytes=$(LC_ALL=C printf '%s' "$2" | wc -c)
|
||||||
|
[ "$ssid_bytes" -ge 1 ] && [ "$ssid_bytes" -le 32 ] || { echo "Wi-Fi SSID must contain 1-32 bytes" >&2; exit "$PIVILION_E_CONFIG"; }
|
||||||
|
PIVILION_WIFI_SSID=$2
|
||||||
|
PIVILION_WIFI_PSK=$3
|
||||||
|
PIVILION_WIFI_PSK_FOR_SSID=$2
|
||||||
|
sanitize_wifi || exit "$PIVILION_E_CONFIG"
|
||||||
|
write_canonical
|
||||||
|
;;
|
||||||
|
sync-status)
|
||||||
|
PIVILION_CONFIG_PATHS=$BOOT_CONFIG pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
||||||
|
write_canonical ;;
|
||||||
|
record-error)
|
||||||
|
shift
|
||||||
|
[ "$#" -gt 0 ] || { echo "Usage: config-sync.sh record-error MESSAGE" >&2; exit 2; }
|
||||||
|
fail_boot "$*" ;;
|
||||||
|
*) echo "Usage: config-sync.sh apply|initialize|set KEY VALUE|set-wifi SSID PASSWORD|sync-status|record-error MESSAGE" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
@ -5,8 +5,30 @@ source /usr/local/lib/pivilion/common.sh
|
||||||
pivilion_require_root
|
pivilion_require_root
|
||||||
pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
pivilion_load_config || exit "$PIVILION_E_CONFIG"
|
||||||
|
|
||||||
MODE=${1:-$(pivilion_current_mode)}
|
if [ "${1:---boot-selected}" = --boot-selected ]; then
|
||||||
|
MODE=$(pivilion_selected_mode) || {
|
||||||
|
echo "No validated boot mode was produced by pivilion-config.service" >&2
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh record-error "No validated boot mode was produced during startup" >/dev/null 2>&1 || true
|
||||||
|
exit "$PIVILION_E_CONFIG"
|
||||||
|
}
|
||||||
|
[ "$MODE" = "$(pivilion_current_mode)" ] || {
|
||||||
|
echo "Validated boot mode does not match the effective configuration" >&2
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh record-error "Validated boot mode does not match the effective configuration" >/dev/null 2>&1 || true
|
||||||
|
exit "$PIVILION_E_CONFIG"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MODE=$1
|
||||||
|
fi
|
||||||
case "$MODE" in hotspot|onion) ;; *) echo "Unknown Pivilion mode: $MODE" >&2; exit 2 ;; esac
|
case "$MODE" in hotspot|onion) ;; *) echo "Unknown Pivilion mode: $MODE" >&2; exit 2 ;; esac
|
||||||
|
|
||||||
|
mode_exit() {
|
||||||
|
local rc=$? failure
|
||||||
|
[ "$rc" -eq 0 ] && return
|
||||||
|
failure=$(pivilion_state_value failure "activation exited with status $rc")
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh record-error "Failed to activate $MODE mode: $failure (exit $rc)" >/dev/null 2>&1 || true
|
||||||
|
logger -t pivilion-mode "Failed to activate $MODE mode: $failure (exit $rc)" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
trap mode_exit EXIT
|
||||||
IFACE=$(pivilion_wifi_interface)
|
IFACE=$(pivilion_wifi_interface)
|
||||||
[ -n "$IFACE" ] && [ -d "/sys/class/net/$IFACE" ] || {
|
[ -n "$IFACE" ] && [ -d "/sys/class/net/$IFACE" ] || {
|
||||||
pivilion_write_state none none failed "Wi-Fi interface not found"
|
pivilion_write_state none none failed "Wi-Fi interface not found"
|
||||||
|
|
@ -32,9 +54,12 @@ refresh_login_banner() {
|
||||||
}
|
}
|
||||||
|
|
||||||
activation_error() {
|
activation_error() {
|
||||||
local rc=$? line=${BASH_LINENO[0]:-unknown}
|
local rc=$? line=${BASH_LINENO[0]:-unknown} existing
|
||||||
trap - ERR
|
trap - ERR
|
||||||
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
|
existing=$(pivilion_state_value failure none)
|
||||||
|
if [ "$existing" = none ] || [ "$existing" = unknown ]; then
|
||||||
|
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
|
||||||
|
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"
|
||||||
|
|
@ -118,7 +143,7 @@ interface=$IFACE
|
||||||
driver=nl80211
|
driver=nl80211
|
||||||
ssid=$PIVILION_HOTSPOT_SSID
|
ssid=$PIVILION_HOTSPOT_SSID
|
||||||
hw_mode=g
|
hw_mode=g
|
||||||
channel=6
|
channel=$PIVILION_HOTSPOT_CHANNEL
|
||||||
auth_algs=1
|
auth_algs=1
|
||||||
wmm_enabled=0
|
wmm_enabled=0
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -152,6 +177,15 @@ wait_for_default_route() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_diagnostics() {
|
||||||
|
local mode=$1 i
|
||||||
|
for i in $(seq 1 10); do
|
||||||
|
/usr/local/lib/pivilion/diagnose.sh --mode "$mode" --quiet && return 0
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
restart_tor() {
|
restart_tor() {
|
||||||
if systemctl cat tor@default.service >/dev/null 2>&1; then systemctl restart tor@default.service; else systemctl restart tor.service; fi
|
if systemctl cat tor@default.service >/dev/null 2>&1; then systemctl restart tor@default.service; else systemctl restart tor.service; fi
|
||||||
}
|
}
|
||||||
|
|
@ -196,8 +230,10 @@ start_hotspot() {
|
||||||
rfkill unblock wifi 2>/dev/null || true
|
rfkill unblock wifi 2>/dev/null || true
|
||||||
systemctl start apache2
|
systemctl start apache2
|
||||||
if [ "$BACKEND" = networkmanager ]; then
|
if [ "$BACKEND" = networkmanager ]; then
|
||||||
|
nmcli device disconnect "$IFACE" >/dev/null 2>&1 || true
|
||||||
nmcli connection add type wifi ifname "$IFACE" con-name "$NM_HOTSPOT" ssid "$PIVILION_HOTSPOT_SSID"
|
nmcli connection add type wifi ifname "$IFACE" con-name "$NM_HOTSPOT" ssid "$PIVILION_HOTSPOT_SSID"
|
||||||
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" \
|
||||||
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"
|
nmcli connection up "$NM_HOTSPOT"
|
||||||
else
|
else
|
||||||
|
|
@ -208,13 +244,15 @@ start_hotspot() {
|
||||||
fi
|
fi
|
||||||
systemctl restart pivilion-dnsmasq.service
|
systemctl restart pivilion-dnsmasq.service
|
||||||
apply_firewall
|
apply_firewall
|
||||||
if /usr/local/lib/pivilion/diagnose.sh --mode hotspot --quiet; then
|
if wait_for_diagnostics hotspot; then
|
||||||
pivilion_write_state hotspot "$BACKEND" healthy none
|
pivilion_write_state hotspot "$BACKEND" healthy none
|
||||||
else
|
else
|
||||||
pivilion_write_state hotspot "$BACKEND" degraded "Post-start captive portal diagnostics failed"
|
pivilion_write_state none "$BACKEND" failed "Post-start captive portal diagnostics failed"
|
||||||
logger -t pivilion-mode "Hotspot active but degraded; run pivilion diagnose" || true
|
refresh_login_banner
|
||||||
|
return "$PIVILION_E_CAPTIVE"
|
||||||
fi
|
fi
|
||||||
refresh_login_banner
|
refresh_login_banner
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh sync-status || logger -t pivilion-mode "Could not mirror hotspot status to the boot partition"
|
||||||
echo "Pivilion hotspot '$PIVILION_HOTSPOT_SSID' is active on $IFACE."
|
echo "Pivilion hotspot '$PIVILION_HOTSPOT_SSID' is active on $IFACE."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,13 +271,15 @@ start_onion() {
|
||||||
systemctl start apache2
|
systemctl start apache2
|
||||||
restart_tor
|
restart_tor
|
||||||
update_torname || failure="${failure:+$failure; }Tor did not produce a valid hostname; retained previous value"
|
update_torname || failure="${failure:+$failure; }Tor did not produce a valid hostname; retained previous value"
|
||||||
if [ -z "$failure" ] && /usr/local/lib/pivilion/diagnose.sh --mode onion --quiet; then
|
if [ -z "$failure" ] && wait_for_diagnostics onion; then
|
||||||
pivilion_write_state onion "$BACKEND" healthy none
|
pivilion_write_state onion "$BACKEND" healthy none
|
||||||
else
|
else
|
||||||
pivilion_write_state onion "$BACKEND" degraded "${failure:-Onion diagnostics failed}"
|
pivilion_write_state none "$BACKEND" failed "${failure:-Onion diagnostics failed}"
|
||||||
logger -t pivilion-mode "Onion mode active but degraded: ${failure:-diagnostics failed}" || true
|
refresh_login_banner
|
||||||
|
return "$PIVILION_E_TOR"
|
||||||
fi
|
fi
|
||||||
refresh_login_banner
|
refresh_login_banner
|
||||||
|
/usr/local/lib/pivilion/config-sync.sh sync-status || logger -t pivilion-mode "Could not mirror onion status to the boot partition"
|
||||||
echo "Pivilion onion mode activation completed."
|
echo "Pivilion onion mode activation completed."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue