NOV
2011
16
50 Comments
14878 Hits
Captive Portal Using PHP and iptables Firewall on Linux

This experiment will show you, how to build a simple captive portal using PHP and iptables on Linux Ubuntu.

Most linux commands used in this experiment are suitable for Debian based Linux (just like what I use in this experiment, Ubuntu Linux). Some of them may have slight differences to be implemented on any other non-Debian-based Linux distros such as Centos, RHEL, or OpenSUSE.

Warning! I'm not responsible for any damages to your computer and/or your computer network configuration that caused by following this experiment. Do this AT YOUR OWN RISK! Use this article as educational and/or experimental purpose only. This articles is not intended for production use nor public use. But if you found any information in this article is useful, I will be very grateful.

First, here the basic concepts on how the captive portal works:

  1. Every user which is connected to the wireless hotspot are connected to the internet through a gateway.
  2. The gateway itself acts as a router and a firewall (and probably also acts as a portal webserver just like in this experiment).
  3. Every HTTP data packet destined to the internet from unauthenticated users are "marked" with a special code by the firewall so it can be intercepted (redirected) to the portal webserver.
  4. Portal webserver serves an authentication web page to the user transparently and identify the computer used by them.
  5. The user accept/authenticates themself with the webserver so their computer identity (in this case their network interface MAC address) are being entered into firewall's whitelist.
  6. As long as user's network interfaces are inside the firewall's whitelist, packets sent from them are not marked for intercepting. The packets are then forwarded by the router to the internet without being intercepted again to the web portal.

In this experiment I uses:

  • An Ubuntu 10.10 x86 Desktop Linux box. I prefer GUI while experimenting rather than a single workspace on Ubuntu Server.
  • Dual Core Intel CPU with 2GB of RAM.
  • Two network interfaces, one for the main internet connection, and one for the public accessible hotspot. Yes, you need at least two network interfaces to do this experiment.
  • The software itself for building the captive portal:
    • iptables, the common firewall
    • Apache2 web server, with PHP5 installed
    • MySQL server, for managing user credentials (for authenticative portal)
    • dhcp3-server as DHCP server and bind9 as DNS server, so users don't have to configure their computer  network configuration manually when they are connected to the hotspot. But I will not explain about how to configure these servers further.
    • Plus, I uses Panada PHP Framework to make web portal development much easier.

In this point forward, if you're following to practices this articles I assumes that you already have an ubuntu box installed and ready to use and all the required packages as well.

1. Build a linux box router

Configuring the network interfaces

I have two network interfaces, eth0 and eth1. eth0 is connected to the internet, and eth1 is connected to the hotspot access point LAN. I have this configuration on /etc/network/interfaces file:

# file: /etc/network/interfaces
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet static
address 192.168.3.1
network 192.168.3.0
netmask 255.255.255.0

The eth0 is connected to the internet and it's IP address is assigned by DHCP by the network. You may have to configure it manually if it's IP address is not assigned by DHCP. However the network interface (eth1) which is connected to the hotspot LAN has IP address 192.168.3.1 on 192.168.3.0/24 network. This will be going to be the portal IP Address and acts as user's gateway IP address.

You may edit the /etc/network/interfaces file by using nano or vi from command line as root. Change your network configuration to match your network settings, save the file and restart the networking services by issuing the following command to apply changes:

$ sudo /etc/init.d/networking restart

Enable IP forwarding (routing) on Linux Ubuntu so every packets from the hotspot are forwarded to the internet

Enable the IPv4 packet forwarding by uncommenting the following line on /etc/sysctl.conf file

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
Masquerade any incoming packet on the firewall, using the following command:
$ sudo iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE

At this point, any other computer connected to the hotspot (eth1) which is using your computer as gateway (192.168.3.1) are able to connect to the internet through your computer internet connection (eth0).

2. Build a (captive) portal

Capturing user IP and MAC address

a "splash" page is used for capturing the user IP and MAC address and display the page to user when they were redirected to this page by the portal system.

Here's an example on how to capture user's IP and MAC address using PHP:

<?php
// capture their IP address
$ip = $_SERVER['REMOTE_ADDR'];

// this is the path to the arp command used to get user MAC address 
// from it's IP address in linux environment.
$arp = "/usr/sbin/arp";

// execute the arp command to get their mac address
$mac = shell_exec("sudo $arp -an " . $ip);
preg_match('/..:..:..:..:..:../',$mac , $matches);
$mac = @$matches[0];

// if MAC Address couldn't be identified.
if( $mac === NULL) { 
  echo "Access Denied.";
  exit;
}
?>

From the example above, user's IP and MAC address are stored in variables $ip and $mac respectively. For getting their MAC address, we uses 'sudo /usr/sbin/arp -an' command. Please note that for this command to work, the linux user 'www-data' (default user for Apache webserver daemon') must be in the sudoers file.

Add www-data as sudoers in linux box

First, you have to be root to add user as sudoers. Execute this command:

$ sudo visudo

Then add the following line at the bottom of the file, so that www-data user can execute arp command without entering a password

www-data ALL=NOPASSWD: /usr/sbin/arp

Save the file.

Create a simple HTML form for user to submit (and agree the portal Terms of Service if any).

Here's an example of the form in PHP:

<form method="post" action="process.php">
  <input type="hidden" name="mac" value="<?php echo $mac; ?>" />
  <input type="hidden" name="ip" value="<?php echo $ip; ?>" />
  <input type="submit" value="OK" style="padding:10px 20px;" />
</form>

User's IP and MAC address are embedded in the form as hidden field for further processing in the firewall. Save the file with name index.php in the document root directory of your web portal. So now it's accessible from a web browser. Try http://localhost or http://192.168.3.1 from your linux box web browser to test the portal page as we will redirect user to this address.

Redirecting every "unknown user" HTTP traffic to the "splash" portal

I uses a similiar logic as explained by Andy Bev in his wiki but with some little differences. Here's the command on how to redirect every HTTP traffic to portal using iptables. PLease note that you have to be root in order to be able to modify the iptables table.

1. Create a new chain named 'internet' in mangle table with this command

sudo iptables -t mangle -N internet

2. Send all HTTP traffic from eth1 to the newly created chain for further processing

sudo iptables -t mangle -A PREROUTING -i eth1 -p tcp -m tcp --dport 80 -j internet

3. Mark all traffic from internet chain with 99

sudo iptables -t mangle -A internet -j MARK --set-mark 99

4. Redirect all marked traffic to the portal 

sudo iptables -t nat -A PREROUTING -i eth1 -p tcp -m mark --mark 99 -m tcp --dport 80 -j DNAT --to-destination 192.168.3.1

OK from now on, every HTTP request from eth1 will be redirected to the portal page.

Bypass firewall redirection rules when users submit themself from the splash page

When user click the submit button from the portal splash page, their IP and MAC address will be submitted to the process.php file. In this file their MAC address will be "entered" into iptables firewall so that they won't be redirected to the portal splash page anymore. The logic is, executing the following command from PHP (process.php) so that the redirection logic in the firewall will be bypassed:

sudo iptables -t mangle -I internet 1 -m mac --mac-source USER-MAC-ADDRESS-HERE -j RETURN

And remove their "redirection" connection track. How? From Andy Bev wiki, we can do that with a simple script.
Create an executable file /usr/bin/rmtrack and put the following code inside

/usr/sbin/conntrack -L \
    |grep $1 \
    |grep ESTAB \
    |grep 'dport=80' \
    |awk \
        "{ system(\"conntrack -D --orig-src $1 --orig-dst \" \
            substr(\$6,5) \" -p tcp --orig-port-src \" substr(\$7,7) \" \
            --orig-port-dst 80\"); }"

Change the file permission to 755 or 700 for better safety with the following command:

$ chmod 755 /usr/bin/rmtrack

With those rmtrack, we can remove user's connection track with the following command

$ sudo rmtrack USER_IP_ADDRESS

Here's an example of process.php file:

<?php 
if( isset( $POST['ip'] ) && isset ( $_POST['mac'] ) ) {
   $ip = $_POST['ip'];
   $mac = $_POST['mac'];
   exec("sudo iptables -I internet 1 -t mangle -m mac --mac-source $mac -j RETURN");
   exec("sudo rmtrack " . $ip);
   sleep(1); // allowing rmtrack to be executed
   
   // OK, redirection bypassed.
   // Show the logged in message or directly redirect to other website

   echo "User logged in.";
   exit;

} else {
   echo "Access Denied"; 
   exit;
}
?>

Don't forget to add the following line to the sudoers file so that the iptables and rmtrack command can be executed by the web server:

www-data ALL=NOPASSWD: /sbin/iptables
www-data ALL=NOPASSWD: /usr/bin/rmtrack [0-9]*.[0-9]*.[0-9]*.[0-9]*

After the user had been logged in, they won't be redirected to the portal again because of the iptables bypassing rules command. That's it! Congratulations, A (very simple) captive portal using PHP and iptables has been successfully created.

How to remove the user from the iptables bypassing rules so that they have to be authenticated again?

Here's on how to do it. Delete user's bypassing iptables rules with the following command:

sudo iptables -D internet -t mangle -m mac --mac-source USER_MAC_ADDRESS -j RETURN

and remove their connection track (again):

sudo rmtrack USER_IP_ADDRESS

if you know user's IP address, both command can be easily done by a simple PHP script below:

<?php
// get the user IP address from the query string
$ip = $_GET['ip'];

// this is the path to the arp command used to get user MAC address 
// from it's IP address in linux environment.
$arp = "/usr/sbin/arp";

// execute the arp command to get their mac address
$mac = shell_exec("sudo $arp -an " . $ip);
preg_match('/..:..:..:..:..:../',$mac , $matches);
$mac = @$matches[0];

// if MAC Address couldn't be identified.
if( $mac === NULL) {
  echo "Error: Can't retrieve user's MAC address.";
  exit;
}

// Delete it from iptables bypassing rules entry.
while( $chain = shell_exec("sudo iptables -t mangle -L | grep ".strtoupper($mac) ) !== NULL ) {
 exec("sudo iptables -D internet -t mangle -m mac --mac-source ".strtoupper($mac)." -j RETURN");
}
// Why in this while loop?
// Users may have been logged through the portal several times. 
// So they may have chances to have multiple bypassing rules entry in iptables firewall.

// remove their connection track.
exec("sudo rmtrack " . $ip); // remove their connection track if any
echo "Kickin' successful.";
?>

Save it to a file named kick.php in your web server document root. Get into your web browser and put the following URL to the address bar to kick someone from the iptables bypassing rules:

http://192.168.3.1/kick.php?ip=USER_IP_ADRESS

And you're DONE!

Feel free to modify the code to suit your requirements or asks something you don't understand in making this (very simple) captive portal. I'll try to answer and explain if I able to.

Permalink
This post was last updated on: Wednesday, 16 Nov 2011 21:05 and has been read 14878 times. Get the permalink of this post or scan the QR Code.


Posted under Application, PHP, Tutorial, Panada Framework categories.
Tagged: Application, PHP, MySQL, Scripting, Panada
13 Jun 2012
10:37 AM
1
Gifari Azka Dwinanta

Thank you so much for posting this. I am very glad that someone posting php captive portal, so I can do my assignment without any hesitation of portability.

24 Jun 2012
04:26 PM
2
gash

very, very nice!!!

08 Oct 2012
03:09 PM
3
raj

Hi,

Is it possible to view the active connection in the server via php? Also can the time-out be set based on user output. E.g. If the user clicked on 1 hour package, user will be redirected to the portal page after 1 hour.

Thanks.

11 Oct 2012
01:45 PM
4
Aryo

Hi Raj,
About viewing active connections, it is possible if you log every successfull user login to somewhere (e.g. database). You may log user's MAC address, IP address, session start time, and probably user package.

You may use cron to periodically check any logged in user from log database and check whether any logged in sessions are already timed out or not. If so, remove MAC address related to the user from the iptables rule. Then users have to login again when they try to access the web.

20 Oct 2012
01:06 AM
5
john

greetings... i need to specify login page based on vlan id... is there any way to detect which vlan the connection is coming from and redirected them according to the vlan id?

cheers

17 Nov 2012
07:54 PM
6
danko

john: might you need packetfense ?

30 Nov 2012
12:01 AM
7
kabrutus

Is there a way to do this while using my own corporate router/firewall/dhcp? I

13 Dec 2012
01:24 AM
8
Rinku

Hi , very good article

Only Last part should be modified

We need a daemon that will continously keep track of machines, whether machine is alive or not via ping command .

if machine is not alive then run last part script via crond

11 Jan 2013
07:39 AM
9
vicky

Hi, Is there any way to authenticate through mysql? i need guidance for my project

20 Jan 2013
10:29 PM
10
Aryo

Hi, vicky. Sure you can do the authencation process through MySQL. Just redirect unauthenticated user to the login page. Authenticate them using MySQL and if the username and password are correct, execute the following shell script:

sudo iptables -t mangle -I internet 1 -m mac --mac-source USER-MAC-ADDRESS-HERE -j RETURN
05 Feb 2013
08:43 AM
11
stefan

hi, after the process.php is executed i can't connect to the server anymore from the client. Am i the only one with this "effect" or is it at purpose ? If its on purpose is there a way to access the site still after the process.php got executed ?

ah btw. i'm using lighttpd maybe that makes a difference...

05 Feb 2013
08:44 AM
12
stefan

oh and i forgot to say... very nice tutorial! Thank you for sharing your knowledge!

27 Feb 2013
11:45 PM
13
Ben

Hi Ayro,

Really like this tutorial here.

Just a quick question. I've followed it step by step, configured the dhcp server too.

I'm just making sure everything works as it should do at each stage.

So at this stage: '$ sudo iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE' a client PC should be able to connect to the internet right?

For some reason I can't get it to do that.

Will you be able to help me out here? It will be a big help to my project

02 Mar 2013
05:35 PM
14
comdif-telecom

Hi, Nice howto ! For lazy peoples looking for ready to use captive portal build with iptables , mysql, php you can check my project https://sourceforge.net/projects/hotelpbx/ regards

12 Mar 2013
03:57 AM
15
Valdemar

Hello! thanks for the article, very useful. I studied it but did not understand how to look my index.php? where to put the html form link to process.php? sorry for the stupid questions ...

14 Mar 2013
01:03 AM
16
Valdemar

Needed: $ _POST ['ip'] in the first line of the process.php

02 Apr 2013
06:38 PM
17
Vis

Hi, Thanks for your guide, I having been trying to implement the captive portal on a Raspberry Pi Linux box for some time. Can you please help. I have all the dependencies installed, but I do not know anything about PHP. I am at part 2, create the captive portal and I don't know where to save the text in the box. Would it be possible to give me a more detailed instruction? Can I contact you directly rather than through the comment process? Thanks, V.

05 Apr 2013
08:15 AM
18
Aryo

hi, @stefan "after the process.php is executed i can't connect to the server anymore from the client."

can you explain more about this "can't connect"? do you mean you can't connect to the portal anymore or what? After process.php is executed, the user should be able to connect directly to the internet, because their IP and mac address are added to the iptables whitelist.

05 Apr 2013
08:19 AM
19
Aryo

@Ben

"sudo iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE' a client PC should be able to connect to the internet right?"

yes you are right. those command add rule to masquerade all postrouting traffic to eth0. How can you can't get to do it? Can not execute those command or probably you don't have enough privileges while executing those command?

05 Apr 2013
08:26 AM
20
Aryo

@Valdemar The process.php will be executed when the form to enter username and password is submitted.

05 Apr 2013
08:33 AM
21
Aryo

Vis: I'm in Indonesia, you may contact me through my email address: aryoxp#at#gmail#dot#com (replace #at# with @ and replace #dot# with . (dot mark).

11 Apr 2013
04:17 AM
22
Romaan

Thanks a lot. Really appreciate your time and good will to explain how to build a captive portal. Thanks genius

Write Your Comments

Comments are parsed with Markdown.
 
Notes
* Your email is required to submit this form, and it will not be published or shared without your consent. We use your email address to show your avatar picture profile from Gravatar. Don't have one? Then sign up to gravatar and create your own here.
We also filters your comment against SPAM because we hate SPAM as much as you do. If your comment is recognized as SPAM then it will be moderated, otherwise it will shows up immediately.
Form Key: #fa45c92d118e531c71cb56d1489b82c2
Loading...