Files
LEDMatrix/scripts/install/configure_wifi_permissions.sh
Chuck 77e9eba294 fix: overhaul WiFi captive portal for reliable setup (#296)
* fix: overhaul WiFi captive portal for reliable device detection and fast setup

The captive portal detection endpoints were returning "success" responses
that told every OS (iOS, Android, Windows, Firefox) that internet was
working — so the portal popup never appeared. This fixes the core issue
and improves the full setup flow:

- Return portal-triggering redirects when AP mode is active; normal
  success responses when not (no false popups on connected devices)
- Add lightweight self-contained setup page (9KB, no frameworks) for
  the captive portal webview instead of the full UI
- Cache AP mode check with 5s TTL (single systemctl call vs full
  WiFiManager instantiation per request)
- Stop disabling AP mode during WiFi scans (which disconnected users);
  serve cached/pre-scanned results instead
- Pre-scan networks before enabling AP mode so captive portal has
  results immediately
- Use dnsmasq.d drop-in config instead of overwriting /etc/dnsmasq.conf
  (preserves Pi-hole and other services)
- Fix manual SSID input bug that incorrectly overwrote dropdown selection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings for WiFi captive portal

- Remove orphaned comment left over from old scan_networks() finally block
- Add sudoers rules for dnsmasq drop-in copy/remove to install script
- Combine cached-network message into single showMsg call (was overwriting)
- Return (networks, was_cached) tuple from scan_networks() so API endpoint
  derives cached flag from the scan itself instead of a redundant AP check
- Narrow exception catch in AP mode cache to SubprocessError/OSError and
  log the failure for remote debugging
- Bound checkNewIP retries to 20 attempts (60s) before showing fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:50:33 -04:00

158 lines
4.9 KiB
Bash
Executable File

#!/bin/bash
# LED Matrix WiFi Management Permissions Configuration Script
# This script configures both sudo and PolicyKit permissions for WiFi management
set -e
# Cleanup function for temp files
cleanup() {
rm -f "$TEMP_SUDOERS" "$TEMP_POLKIT" 2>/dev/null || true
}
trap cleanup EXIT
echo "Configuring WiFi management permissions for LED Matrix Web Interface..."
# Get the current user (should be the user running the web interface)
WEB_USER=$(whoami)
echo "Detected web interface user: $WEB_USER"
# Check if running as root
if [ "$EUID" -eq 0 ]; then
echo "Error: This script should not be run as root."
echo "Run it as the user that will be running the web interface."
exit 1
fi
# Get the full paths to commands
NMCLI_PATH=$(which nmcli || echo "/usr/bin/nmcli")
SYSTEMCTL_PATH=$(which systemctl)
echo "Command paths:"
echo " nmcli: $NMCLI_PATH"
echo " systemctl: $SYSTEMCTL_PATH"
# Step 1: Configure sudo permissions for nmcli
echo ""
echo "Step 1: Configuring sudo permissions for nmcli..."
SUDOERS_FILE="/etc/sudoers.d/ledmatrix_wifi"
# Create a temporary sudoers file using mktemp (handles permissions better)
TEMP_SUDOERS=$(mktemp) || {
echo "✗ Failed to create temporary file"
exit 1
}
cat > "$TEMP_SUDOERS" << EOF
# LED Matrix WiFi Management passwordless sudo configuration
# This allows the web interface user to run nmcli commands without a password
# Allow $WEB_USER to run nmcli commands without a password for WiFi management
$WEB_USER ALL=(ALL) NOPASSWD: $NMCLI_PATH device wifi connect *
$WEB_USER ALL=(ALL) NOPASSWD: $NMCLI_PATH device wifi disconnect *
$WEB_USER ALL=(ALL) NOPASSWD: $NMCLI_PATH device disconnect *
$WEB_USER ALL=(ALL) NOPASSWD: $NMCLI_PATH device connect *
$WEB_USER ALL=(ALL) NOPASSWD: $NMCLI_PATH radio wifi on
$WEB_USER ALL=(ALL) NOPASSWD: $NMCLI_PATH radio wifi off
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH start hostapd
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH stop hostapd
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH restart hostapd
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH start dnsmasq
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH stop dnsmasq
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH restart dnsmasq
$WEB_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH restart NetworkManager
# Allow copying hostapd and dnsmasq config files into place
$WEB_USER ALL=(ALL) NOPASSWD: /usr/bin/cp /tmp/hostapd.conf /etc/hostapd/hostapd.conf
$WEB_USER ALL=(ALL) NOPASSWD: /usr/bin/cp /tmp/dnsmasq.conf /etc/dnsmasq.d/ledmatrix-captive.conf
$WEB_USER ALL=(ALL) NOPASSWD: /usr/bin/rm -f /etc/dnsmasq.d/ledmatrix-captive.conf
EOF
echo "Generated sudoers configuration:"
echo "--------------------------------"
cat "$TEMP_SUDOERS"
echo "--------------------------------"
# Apply the sudoers configuration
echo ""
echo "Applying sudoers configuration..."
if sudo cp "$TEMP_SUDOERS" "$SUDOERS_FILE"; then
sudo chmod 440 "$SUDOERS_FILE"
echo "✓ Sudoers configuration applied successfully!"
else
echo "✗ Failed to apply sudoers configuration"
rm -f "$TEMP_SUDOERS"
exit 1
fi
rm -f "$TEMP_SUDOERS"
# Step 2: Configure PolicyKit permissions for NetworkManager
echo ""
echo "Step 2: Configuring PolicyKit permissions for NetworkManager..."
POLKIT_RULES_DIR="/etc/polkit-1/rules.d"
POLKIT_RULE_FILE="$POLKIT_RULES_DIR/10-ledmatrix-wifi.rules"
# Create PolicyKit rule using mktemp (handles permissions better)
TEMP_POLKIT=$(mktemp) || {
echo "✗ Failed to create temporary file"
exit 1
}
cat > "$TEMP_POLKIT" << EOF
// LED Matrix WiFi Management PolicyKit rules
// This allows the web interface user to control NetworkManager without authentication
polkit.addRule(function(action, subject) {
if (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 &&
subject.user == "$WEB_USER") {
return polkit.Result.YES;
}
});
EOF
echo "Generated PolicyKit rule:"
echo "--------------------------------"
cat "$TEMP_POLKIT"
echo "--------------------------------"
# Apply the PolicyKit rule
echo ""
echo "Applying PolicyKit rule..."
if sudo cp "$TEMP_POLKIT" "$POLKIT_RULE_FILE"; then
sudo chmod 644 "$POLKIT_RULE_FILE"
echo "✓ PolicyKit rule applied successfully!"
else
echo "✗ Failed to apply PolicyKit rule"
rm -f "$TEMP_POLKIT"
exit 1
fi
rm -f "$TEMP_POLKIT"
# Step 3: Test permissions
echo ""
echo "Step 3: Testing permissions..."
# Test sudo access
if sudo -n "$NMCLI_PATH" device status > /dev/null 2>&1; then
echo "✓ nmcli device status - OK"
else
echo "✗ nmcli device status - Failed (this is expected if not connected)"
fi
echo ""
echo "Configuration complete!"
echo ""
echo "The web interface user ($WEB_USER) now has:"
echo "- Passwordless sudo access to nmcli commands"
echo "- PolicyKit permissions to control NetworkManager"
echo ""
echo "You may need to restart the web interface service for changes to take effect:"
echo " sudo systemctl restart ledmatrix-web.service"
echo ""
echo "Or if running manually, restart your Flask application."