This repository provides a compact, production-minded captive-portal controller that intercepts unauthenticated clients at the firewall and DNS layers and redirects them to a local web portal for authentication. It operates by applying iptables DNAT rules for HTTP/DNS and installing per-IP exceptions after successful authentication.
- Lightweight: minimal dependencies, focused on iptables/dnsmasq-based deployments.
- Deterministic: per-IP firewall exceptions avoid stateful proxy complexity.
- Portable: designed to run on Linux AP hosts (hostapd+dnsmasq) with Python 3.
- Primary techniques: iptables DNAT for HTTP/DNS, POSTROUTING MASQUERADE, per-IP FORWARD exceptions.
- Components:
main.py(controller),server.py(portal web server),firewall.py(rule manager),sessions.py(session store).
main.pyโ controller entrypoint that applies rules and coordinates authentication.server.pyโ web portal (HTTP) that serves captive pages and authentication endpoints.firewall.pyโ helpers for manipulating iptables and DNAT rules.sessions.json/sessions.pyโ session storage and lookup.
- Linux host with
hostapdanddnsmasq(AP mode recommended). - Root privileges to manage interfaces and iptables (or carefully scoped sudoers).
- Python 3.8+ (recommended). The service can run as an unprivileged user if bind capabilities are granted.
Limit the commands allowed via sudo to reduce risk. Example visudo line (replace your_user):
your_user ALL=(ALL) NOPASSWD: /usr/sbin/iptables, /sbin/sysctl, /usr/sbin/arpIf you need the portal to bind to privileged ports, grant the Python binary the capability instead of running as root:
sudo setcap 'cap_net_bind_service=+ep' $(which python3)
getcap $(which python3)
# Example output: /usr/bin/python3 = cap_net_bind_service+epThis sequence shows a typical interface and service setup used for testing the portal:
sudo iw dev wlan0 interface add wlan1 type __ap
sudo nmcli dev set wlan1 managed no
sudo ip link set wlan1 up
sudo ip addr add 192.168.100.1/24 dev wlan1
sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo iptables -A FORWARD -i wlan1 -o wlan0 -j ACCEPT
sudo iptables -A FORWARD -i wlan0 -o wlan1 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo systemctl start dnsmasq
sudo hostapd /etc/hostapd/hostapd.conf- Default policy: unauthenticated clients are blocked from forwarding to the WAN.
- HTTP interception: DNAT TCP/80 ->
portal_ip:8080so HTTP requests land on the portal server. - DNS interception: DNAT UDP/TCP/53 ->
portal_ip:53so DNS queries resolve to the portal before auth. - NAT:
POSTROUTINGMASQUERADE is used for outbound traffic via the WAN. - Authentication flow: once a client authenticates, the controller inserts per-IP FORWARD and NAT exceptions that restore normal connectivity for that client.
- Keep
dnsmasqas the DHCP server and let the firewall perform DNS interception for unauthenticated clients. - Optionally, to always return the portal IP for any DNS answer before authentication, use:
address=/#/<portal_ip>
but ensure authenticated clients receive valid upstream DNS servers via DHCP (dhcp-option=6,1.1.1.1,8.8.8.8).
- Configure your AP interface and start
dnsmasq/hostapd(see example above). - Start the portal controller from the repository root:
python3 main.py- Replace interface names (
wlan0,wlan1) with the host's actual device names. - Ensure IP forwarding is enabled:
cat /proc/sys/net/ipv4/ip_forwardshould be1. - If redirection fails, inspect NAT rules:
sudo iptables -t nat -L -n -vandsudo iptables -S. - Confirm the portal is listening on the expected port:
ss -ltnp | grep python.
- Create a virtualenv and install dependencies (if you add a
requirements.txt):
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt- Important files to inspect when modifying behavior:
main.py,server.py,firewall.py,sessions.py.
- Convert this
ReadmetoREADME.mdand add badges, quick-start scripts, and example configurations. - Add automated tests for iptables rule generation and session handling.
See the repository license for usage and contribution rules. For questions or to request changes to this README, open an issue or contact the repository owner.
-- Updated: concise technical README with clear quick-start and troubleshooting guidance.