Files
pi-pm3/docs/CAPTIVE_PORTAL_DEPLOY.md
michael 4f35df1781 Initial commit - Phase 3/4
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 13:46:22 -08:00

5.7 KiB

Captive Portal Deployment Notes

Date: 2026-01-06 Status: Ready to deploy on live Pi

Summary

Added captive portal detection to nginx configuration. This enables automatic popup on devices when they connect to the Dangerous-Pi WiFi network.

What Was Changed

Files Modified

  1. pi-gen pipeline (for future builds):

    • pi-gen/stageDangerousPi/01-Wireless-AP/00-run-chroot.sh
  2. Local nginx config (reference):

    • nginx/dangerous-pi.conf

Changes Made

Added OS connectivity check endpoints to nginx:

Endpoint OS/Browser Response
/generate_204 Android/Chrome 204 No Content
/gen_204 Android alternate 204 No Content
/connectivitycheck/gstatic/generate_204 Google services 204 No Content
/hotspot-detect.html iOS/macOS 200 + HTML
/library/test/success.html iOS alternate 200 + HTML
/connecttest.txt Windows 10/11 200 + text
/ncsi.txt Windows NCSI 200 + text
/success.txt Firefox 200 + text
/kindle-wifi/wifistub.html Kindle/Amazon 200 + HTML

Deploy on Live Pi

Option 1: Copy nginx config directly

# SSH into the Pi
ssh dt@dangerous-pi.local

# Backup existing config
sudo cp /etc/nginx/sites-available/dangerous-pi.conf /etc/nginx/sites-available/dangerous-pi.conf.bak

# Copy the new config (from your dev machine)
# On dev machine:
scp nginx/dangerous-pi.conf dt@dangerous-pi.local:/tmp/

# On Pi:
sudo cp /tmp/dangerous-pi.conf /etc/nginx/sites-available/dangerous-pi.conf

# Test nginx config
sudo nginx -t

# Reload nginx
sudo systemctl reload nginx

Option 2: Manual edit on Pi

Add the following block to /etc/nginx/sites-available/dangerous-pi.conf before the location /api/ block:

    # ===========================================
    # CAPTIVE PORTAL DETECTION
    # ===========================================
    # These endpoints respond to OS connectivity checks.
    # When a device connects to the AP, the OS checks these URLs.
    # By responding correctly, we trigger the captive portal popup.

    # Android / Chrome OS connectivity check
    location = /generate_204 {
        return 204;
    }

    # Additional Android check paths
    location = /gen_204 {
        return 204;
    }

    # Google connectivity check
    location = /connectivitycheck/gstatic/generate_204 {
        return 204;
    }

    # iOS / macOS captive portal detection
    location = /hotspot-detect.html {
        return 200 '<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>';
        add_header Content-Type text/html;
    }

    # iOS alternate path
    location = /library/test/success.html {
        return 200 '<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>';
        add_header Content-Type text/html;
    }

    # Windows 10/11 connectivity check
    location = /connecttest.txt {
        return 200 'Microsoft Connect Test';
        add_header Content-Type text/plain;
    }

    # Windows NCSI check
    location = /ncsi.txt {
        return 200 'Microsoft NCSI';
        add_header Content-Type text/plain;
    }

    # Firefox / Mozilla connectivity check
    location = /success.txt {
        return 200 'success';
        add_header Content-Type text/plain;
    }

    # Kindle / Amazon devices
    location = /kindle-wifi/wifistub.html {
        return 200 '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html><head><title>Kindle</title></head><body>81ce4465-7167-4dcb-835b-dcc9e44c112a</body></html>';
        add_header Content-Type text/html;
    }

    # ===========================================
    # END CAPTIVE PORTAL DETECTION
    # ===========================================

Then reload nginx:

sudo nginx -t && sudo systemctl reload nginx

Testing

Test from a mobile device

  1. Forget the "Dangerous-Pi" network on your phone
  2. Reconnect to "Dangerous-Pi" WiFi
  3. You should see a captive portal popup automatically
  4. Clicking it should open the Dangerous Pi web interface

Test manually with curl

# Android check (should return empty 204)
curl -v http://192.168.4.1/generate_204

# iOS check (should return HTML)
curl -v http://192.168.4.1/hotspot-detect.html

# Windows check (should return "Microsoft Connect Test")
curl -v http://192.168.4.1/connecttest.txt

# Firefox check (should return "success")
curl -v http://192.168.4.1/success.txt

How It Works

  1. DNS: dnsmasq already redirects ALL DNS queries to 192.168.4.1 (address=/#/192.168.4.1)
  2. HTTP: When the OS checks connectivity URLs, nginx now responds correctly
  3. Detection: OS sees the expected response and knows internet is available
  4. Popup: Because DNS resolved to the AP but responses are correct, OS triggers captive portal UI

Troubleshooting

No popup appears

  1. Check nginx is running: sudo systemctl status nginx
  2. Check nginx config: sudo nginx -t
  3. Check dnsmasq is running: sudo systemctl status dnsmasq
  4. Verify DNS redirect: nslookup google.com (should resolve to 192.168.4.1)

Popup appears but shows error

  1. Check frontend is running: curl http://localhost:3000
  2. Check backend is running: curl http://localhost:8000/api/health
  3. Check nginx logs: sudo tail -f /var/log/nginx/error.log

Some devices don't show popup

Different devices use different detection URLs. The current config covers:

  • Android (all versions)
  • iOS / macOS
  • Windows 10/11
  • Firefox
  • Kindle

If a specific device doesn't work, check its connectivity check URL and add it to nginx.

Future Improvements

  • Add Samsung-specific detection URLs if needed
  • Consider adding a redirect for unknown hosts to / (requires if statement)
  • Monitor nginx access logs to discover new detection URLs