Captive portal webpage with Bottle and Python
A basic captive portal stack aiming to do the following.
This is a commonly seen setup in public Wifi networks or hotspots.
At the heart of this stack is a simple Python Bottle application but this stack requires a lot of other configuration around it.
I’ve moved all examples from the networking:captive_portal_med_iptables">old wiki-page to the docs/examples directory.
Quick steps will setup a locally hosted dev server which will only run the sample_log plugin and log a line to captiveportal.log.
python setup.py install
rq worker -u redis://127.0.0.1:6379/
python portal.py
Now visit the URL shown in the output and you’ll be able to submit the portal form, wait for the background job to finish and be redirected to the default website (google.com).
By default only sample_log plugin is executed which logs a line to captiveportal.log when rq finishes running.
See examples in docs/examples directory, along with all the cfg examples in the repo.
First and foremost all traffic in a network must be routed through the server running this software stack.
All this is works with the portal application written in Python3 using Bottle.
At the heart is iptables doing the following.
Plugins are executed when the user clicks through the captive portal form, whether they submit data or just approve an EULA these plugins are executed.
Plugins accept data from the request of the user, as of writing this is only wsgi environ data.
Result of the plugins decide whether the user gets accepted into the portal or not. As such plugins have the potential to do anything from check the users IP against a whitelist to authenticate against a RADIUS server or Active Directory.
Sample plugins prefixed with sample_ are a good starting point for understanding the plugins.
Plugins can be made mandatory, or skipped by being disabled, see plugins.cfg for more configuration.
My primary use case for this portal would be with tens of thousands of users so already I imagined that creating firewall rules would be a blocking action.
Also with plugins there are options to connect other authentication methods like LDAP or RADIUS, and even other actions to the portal. All of which are possibly blocking. So plugins and job queues felt like a necessary technology to use. Otherwise this type of portal could be a very simple CGI script that runs a system() command.
The idea is that you could add RADIUS plugins or other services. The mandatory flag in plugins.cfg decides if a plugin must pass before a client is authenticated. So you can string several plugins together for several actions that must be performed.
Each plugin responds with JSON.
DEPRECATED IN FAVOR OF IPSET
iptables_cmd
defined in plugins.cfg, with arguments being the client IP and potentially the client MAC address.iptables_cmd
is 0, if not 0 it sets a failed flag in its JSON response.The only thing this plugin does, besides run the iptables command, is that it also attempts to lookup a HW address of the client through arp.
In the environment I made this app for the arp thing was impossible and iptables command is not concurrent so I’ve abandoned this plugin.
This plugin is also basically a wrapper around executing the ipset command but it makes no attempt to lookup a HW address in arp.