Sample Header Ad - 728x90

Captive Portal w/ nginx, hostapd, nftables, dnsmasq

0 votes
1 answer
153 views
I'm trying to make captive portal with nginx, hostapd, nftables, dnsmasq and python-flask. I have two main problems 1) I'm not getting a popup on Android, but am on Iphone/OSX. 2) I'm not sure how to redirect the user after the connection. I have a nftables command, but I need an IP address for this. Since nginx is formwarding from port 80 to 8080 (python app) I don't know how to get this. Here's the nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;

        if ($request_method !~ ^(GET|HEAD|POST)$) { return 444; }

        # Handle iOS
        if ($http_user_agent ~* (CaptiveNetworkSupport) ) {
            return 302 http://go.portal ;
        }

        # Handle Android captive portal detection
        location = /generate_204 {
            return 302 http://go.portal ;
        }

        location = /gen_204 {
            return 302 http://go.portal ;
        }

        # Default redirect for any unexpected requests to trigger captive portal
        # sign in screen on device.
        location / {
            return 302 http://go.portal ;
        }
    }

    server {
        listen 80;
        listen [::]:80;
        server_name go.portal;

        # Only allow GET, HEAD, POST
        if ($request_method !~ ^(GET|HEAD|POST)$) { return 444; }

        root /var/www;

        index index.html;

        location /api/ {
            proxy_pass http://127.0.0.1:8080/api/ ;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location / {
            try_files $uri $uri/ =404;
        }

        # Redirect these errors to the home page.
        error_page 401 403 404 =200 /index.html;
    }
}
dnsmasq.conf
listen-address=192.168.2.1
no-hosts
# log-queries
log-facility=/var/log/dnsmasq.log
dhcp-range=192.168.2.2,192.168.2.254,72h
dhcp-option=option:router,192.168.2.1
dhcp-authoritative
dhcp-option=114,http://go.portal/index.html 

# Resolve captive portal check domains to a "fake" external IP
address=/connectivitycheck.gstatic.com/10.45.12.1
address=/connectivitycheck.android.com/10.45.12.1
address=/clients3.google.com/10.45.12.1
address=/clients.l.google.com/10.45.12.1
address=/play.googleapis.com/10.45.12.1

# Resolve everything to the portal's IP address.
address=/#/192.168.2.1
Here's the bash that starts everything.
INET_NIC=$(cat /run/inet_nic 2>/dev/null) || { echo "Connect to WiFi first"; exit 1; }
AP_NIC=$(cat /run/ap_nic 2>/dev/null) || { echo "Create AP first"; exit 1; }

echo 1 > /proc/sys/net/ipv4/ip_forward

nft flush ruleset

# Set up the filter table (Mode 1)
nft add table ip filter
nft add chain ip filter input  '{ type filter hook input priority 0; policy accept; }'
nft add chain ip filter forward '{ type filter hook forward priority 0; policy accept; }'
nft add chain ip filter output '{ type filter hook output priority 0; policy accept; }'

# Set up the NAT table and chain for masquerading (Mode 2)
nft add table ip nat
nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }'

kill -9 $(pidof dnsmasq) 2>/dev/null
dnsmasq -C /etc/dnsmasq.conf -d 2>&1 > $LOG_F &

kill -9 $(pidof nginx) 2>/dev/null
mkdir /var/log/nginx 2>/dev/null
nginx &

kill -9 $(pidof evil_portal) 2>/dev/null
ip link set lo up
/usr/bin/evil_portal &
And here's the command I would issue when the user accepts the terms.
nft add rule ip nat postrouting oifname wlan1 ip saddr 192.168.2.217 masquerade
I won't share the python/html stuff because that's all working fine. Basically I'm getting the users button push, and my python function is calling. But python is telling me the IP is 127.0.0.1 because nginx if forwarding the traffic from port 80 to 8080 Thanks :)
Asked by user3666672 (11 rep)
Mar 5, 2025, 07:45 PM
Last activity: Mar 6, 2025, 01:02 AM