Captive Portal with Dansguardian
While many methods for captive portal exist including CoovaChili, nocatip, and others; ClearOS is mostly able to perform captive portal techniques with some simple modifications by using iptables, dansguardian, webconfig, and other open source scripting and rules.
The content filter is the key. We will run the content filter as both a way to block the initial traffic, to provide the walled garden, and optionally to provide post-authentication filtering and logging.
Requirements
For this installation you will need to be running ClearOS as your gateway or in transparent gateway mode. You must have the Proxy and Content Filter modules installed and running. Set the Proxy to use transparent mode and turn the content filter on.
Installation
Firewall
In order to direct the traffic for port 80 you will need some firewall rules to accomplish this. By default, ClearOS will perform some of this when the transparent mode is selected but you will need to prevent packets from traversing the bridge. If you are running the server as a transparent bridge, you will need to install 'ebtools' and use the ebtables rules to capture and force them into the content filter.
bridge /etc/rc.d/rc.firewall.local
# This is an example ruleset that works, customize as needed. # #Flush all tables ebtables -t broute -F ebtables -F #Redirect port 80 in the bridge to the local iptables stack ebtables -t broute -A BROUTING -p IPV4 --ip-protocol 6 --ip-destination-port 80 -j redirect --redirect-target ACCEPT #These rules are commented because they allow all the traffic back in, they are here for troublshooting purposes. #ebtables -P INPUT ACCEPT #ebtables -P FORWARD ACCEPT #ebtables -P OUTPUT ACCEPT #Here are some common low level network protocols which should be ok to allow. ebtables -A INPUT -p ARP -j ACCEPT ebtables -A FORWARD -p ARP -j ACCEPT ebtables -A OUTPUT -p ARP -j ACCEPT ebtables -A INPUT -p LENGTH -j ACCEPT ebtables -A FORWARD -p LENGTH -j ACCEPT ebtables -A OUTPUT -p LENGTH -j ACCEPT ebtables -A INPUT -p IPV4 -j ACCEPT ebtables -A FORWARD -p IPV4 -j ACCEPT ebtables -A OUTPUT -p IPV4 -j ACCEPT #We will implement a couple of separate chaining rules to allow for the traffic to pass, this way we can flush the chain without restarting the entire firewall. These rules merely create the chains iptables -N captive-lite iptables -N captive-lite-chain iptables -P FORWARD DROP #Allow DHCP iptables -I FORWARD -i br0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I FORWARD -i eth0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I FORWARD -i eth1 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I INPUT -i br0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I INPUT -i eth0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I INPUT -i eth1 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I OUTPUT -i br0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I OUTPUT -i eth0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT iptables -I OUTPUT -i eth1 -p udp --dport 67:68 --sport 67:68 -j ACCEPT #Here we take the port 80 packets which are have been passed to the firewall from the bridge and push them to the content filter iptables -t nat -I PREROUTING -i br0 -p tcp --dport 80 -j REDIRECT --to-ports 8080 iptables -t nat -I PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 8080 iptables -t nat -I PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-ports 8080 #White list a server on the other side of the bridge iptables -A FORWARD -d 10.1.1.20 -j ACCEPT iptables -A FORWARD -s 10.1.1.20 -j ACCEPT #Push the subnets which are in the captive portal into the chains iptables -A FORWARD -s 10.1.1.0/24 -j captive-lite-chain iptables -A FORWARD -d 10.1.1.0/24 -j captive-lite-chain iptables -A FORWARD -s 10.1.1.0/24 -j captive-teacher-chain iptables -A FORWARD -d 10.1.1.0/24 -j captive-teacher-chain #Add pings to everywhere iptables -I FORWARD -p icmp -j ACCEPT iptables -I FORWARD -p arp -j ACCEPT #Allow the entire subnet access back and forth. Everything local is allowed. iptables -I FORWARD -p tcp -s 10.1.1.0/24 -d 10.1.1.0/24 -j ACCEPT iptables -I FORWARD -p udp -s 10.1.1.0/24 -d 10.1.1.0/24 -j ACCEPT #Allow a remote subnet iptables -I FORWARD -p tcp -s 10.1.1.0/24 -d 192.168.0.0/16 -j ACCEPT iptables -I FORWARD -p udp -s 10.1.1.0/24 -d 192.168.0.0/16 -j ACCEPT iptables -I FORWARD -p tcp -s 192.168.0.0/16 -d 10.1.1.0/24 -j ACCEPT iptables -I FORWARD -p udp -s 192.168.0.0/16 -d 10.1.1.0/24 -j ACCEPT #Allow DNS server iptables -I FORWARD -p tcp -s 10.1.1.0/24 -d 8.8.8.8/32 -j ACCEPT iptables -I FORWARD -p udp -s 10.1.1.0/24 -d 8.8.8.8/32 -j ACCEPT iptables -I FORWARD -p tcp -d 10.1.1.0/24 -d 8.8.8.8/32 -j ACCEPT iptables -I FORWARD -p udp -d 10.1.1.0/24 -d 8.8.8.8/32 -j ACCEPT #Allow all HTTPS, open this only if you want to allow all https #iptables -I FORWARD -p tcp -s 10.1.1.0/24 --dport 443 -j ACCEPT #iptables -I FORWARD -p tcp -d 10.1.1.0/24 --sport 443 -j ACCEPT ##################### captive-lite chain ################### iptables -N captive-lite iptables -F captive-lite #Add a permitted protocol iptables -A captive-lite -p tcp -d 0.0.0.0/0 --dport 22 -j ACCEPT iptables -A captive-lite -p tcp -s 0.0.0.0/0 --sport 22 -j ACCEPT # Block a site based on content in the SSL certificate, alternately you could accept based on the same criteria #iptables -A captive-lite -p tcp --sport 443 -m string --string *.youtube.com --algo bm -j REJECT #Allow 443 traffice #iptables -A captive-lite -p tcp -d 0.0.0.0/0 --dport 443 -j ACCEPT # SSH for everyone outgoing #iptables -A captive-lite -p tcp -s 0.0.0.0/0 --sport 443 -j ACCEPT # SSH for everyone incoming #Return the rule, this line is required! iptables -A captive-lite -j RETURN ##################### captive-lite-chain chain #################### iptables -N captive-lite-chain iptables -A captive-lite-chain -j RETURN
Webconfig Rights
Admittedly this bit is a hack for 5.2 and will be cleaned up in 6 when this code can be made as a module. This rule will give the Webconfig block page scripts the required permissions to add approved IPs to the firewall chain, perform some logging, and restart dansguardian.
Edit /etc/sudoers and add the following to the end of the file:
webconfig ALL=(root) NOPASSWD: /sbin/iptables-bin webconfig ALL=(root) NOPASSWD: /sbin/service webconfig ALL=(root) NOPASSWD: /sbin/arping
Content Filter
Customize Dansguardian
Uncomment the following from /etc/dansguardian-av/dansguardian.conf
authplugin = '/etc/dansguardian-av/authplugins/ip.conf'
This enables the use of filtering by IP address.
Configure and start the service
You will need to set up the content filter with a couple of filter groups. Your first and default group MUST be set for Blanket Block. You will set up a second group and it will be called captive-lite.
In the blanket block group, feel free to configure any sites in the filter policy that are part of your walled garden. Configure the 'captive-lite' group with any policies you want in force after the authentication of the captive portal is successful.
Block Page
The key to the captive portal is that the block page for the blanket block policy is altered to be an authentication page.
Add the following two files:
/var/webconfig/htdocs/public/filtered.inc.php
<?php WebHeader("CONFIGURATION ERROR", "splash"); $URL = urlencode($_GET['DENIEDURL']); print(""); WebDialogWarning("You must accept the Terms of Service to use the Internet. You will be asked to accept and trust the certificate to proceed. If you have difficulty, please contact tech support at the following number (XXX) XXX-XXXX"); WebFooter("splash"); ?>
You may be asked to provide the following IP address: {$_SERVER['REMOTE_ADDR']}."); print("
/var/webconfig/htdocs/admin/captive-lite.php
<?php $validated = false; $MyURL = $_GET['URL']; { if(isset($_POST['agree'])) $validated = true; } if($validated) { exec("sudo /sbin/iptables-bin -I captive-lite-chain -s {$_SERVER['REMOTE_ADDR']} -j captive-lite"); exec("sudo /sbin/iptables-bin -I captive-lite-chain -d {$_SERVER['REMOTE_ADDR']} -j captive-lite"); exec("touch /etc/dansguardian-av/lists/authplugins/ipgroups"); exec("echo \"{$_SERVER['REMOTE_ADDR']} = filter2\" >> /etc/dansguardian-av/lists/authplugins/ipgroups"); exec("sudo /sbin/service dansguardian-av reload"); sleep(4); header("Location: $MyURL"); die(); } else { die("You must agree to the terms of service before proceeding. Please go back and try again."); } ?>
Flushing the rules
You will want to set up a cronjob to flush the captive-lite-chain at your reset interval. This job should also reset/flush the /etc/dansguardian-av/lists/authplugins/ipgroups file and reload dansguardian-av.
Resources
Links
Special thanks for these fellow ClearOS hackers:
Definitions
- Captive Portal - a gateway which permits access to a network based on authentication via a web based portal. These portals typically 'hijack' normal web traffic and force the loading of the authentication web page.