Building a captive portal with OpenBSD

There aren’t (m)any lightweight captive portal solutions out there for us, except a few clumsy heavy or badly maintained projects together with heir professional counterparts in routers better than home-grade. Captive portals are cool and useful, read on how to build one, including video demo.

If you have read http://www.bsdguides.org/2012/a-wireless-access-point-hotspot-using-openbsd/ then you already know how to build a wireless access point. If not – also fine, because i will go trough the whole process once again, except this time we will configure our system to configure on boot, instead of via a script. Of course Wifi is not required to run a captive portal, you can use this method to have it run on a regular firewall router as well.

Have a look at the movie to see it working in action before doing your own thing:

Youtube movie coming asap…

Requirements

You need a fresh install of OpenBSD 5.2 on a machine with two Network Cards. I used an older Asus EEEPC with wired + wireless NIC (alc0 & athn0).

You will also need to head over to GitHub and download the OpenBSD Captive Portal files – they contain the application and sample configuration files. They also contain a install.sh script, which helps you move everything into place – be sure to carefully understand what it does before running it though, as it will overwrite a bunch of configuration files such as /etc/pf.conf and /etc/dhcpd.conf to name a few.

The rest of this guide assumes you have extracted the Github files into a folder called /root/obsdcp

Outline

A quick overview of what a captive portal usually does and what this project wants to achieve: A user connects to a wireless access point, say in a Hotel. The access point will give an IP Address, but will not yet allow any traffic such as web or mail. When the user opens his browser, he is presented with a hotel login form of some sort. The user logs in using a password he is provided (by the front desk), and the access point informs the user that he now has internet access for a certain amount of time. The captive portal adds the Users IP address to a whitelist table that is allowed network access, and checks at regular intervals for addresses that need to be expired.

Lets get to it.

Setting up Apache

We need to configure and run our web server so we can intercept and serve web requests from non-authenticated clients. Open up your /var/www/conf/httpd.conf and make sure it has the following configuration directives:

Listen *:80
ServerName localhost
# Set a sensible contact info
ServerAdmin you@your.address.com
# Uncomment rewrite_module and expires_module
LoadModule rewrite_module ...
LoadModule expires_module ...
# Find  and set AllowOverride All
# to allow .htaccess
AllowOverride All

Because our httpd runs chrooted and our captive portal depends on Perl and CGI.pm, we need create a few folders and copy a bunch of files into our webserver directory.

mkdir /var/www/usr
mkdir /var/www/usr/bin
mkdir /var/www/usr/lib
mkdir /var/www/usr/libdata
mkdir /var/www/usr/libexec

cd /var/www/usr/bin
cp /usr/bin/perl .

cd /var/www/usr/lib
cp /usr/lib/libperl.so.* .
cp /usr/lib/libm.so.* .
cp /usr/lib/libutil.so.* .
cp /usr/lib/libc.so.* .

cd /var/www/usr/libexec
cp /usr/libexec/ld.so .

# Here i am copying a lot of possibly 
# unneeded modules for perls CGI.pm
# But it got the job done and i can use
# all of Perls base modules inside a
# chrooted httpd

cd /var/www/usr/libdata
cp -r /usr/libdata/perl5 .

Add the following line to /etc/rc.conf.local so httpd starts on boot

httpd_flags = ""

Setting up the portal application

Move needed files into place – remember the downloaded GitHub Files are at /root/obsdcp

cd /root/obsdcp
cp var/www/htdocs/* /var/www/htdocs/
cp var/www/conf/Obsdcp_config.pm /var/www/conf/
cp usr/local/bin/obsdcp /usr/local/bin/
cp etc/rc.d/obsdcp /etc/rc.d/obsdcp
touch /var/www/conf/obsdcp_queue.txt
touch /var/www/conf/obsdcp_allow.txt

obsdcp does the heavy lifting: It checks for new IPs to add to the whitelist, and existing IPs to expire after their time has run out. Add the following line to the end of /etc/rc.conf.local so obsdcp starts on boot

pkg_scripts="obsdcp"

Now we need to adjust a few file permissions:

cd /var/www/conf/
chown www:daemon *.txt
chmod 755 *.txt

# Set permissions
cd /var/www/htdocs/
chown www:daemon .htaccess
chown www:daemon *
chmod 755 .htaccess
chmod 755 *

Network configuration

Lets setup our wired and wireless interfaces. Find out your interface names by running ifconfig. Mine are as follows:

wired: alc0
wireless: athn0

Create /etc/hostname.alc0 so your wired interface is configured automatically at each boot. We need to give it a fixed IP and add your LAN’s default Router so it can access the Internet.

inet 10.0.1.254 255.255.255.0 10.0.1.255

Adding the default Router to /etc/mygate

10.0.1.2

Now lets setup our wireless interface to be a wireless hotspot; create /etc/hostname.athn0. It will setup your open wireless hotspot using the SSID FreeWifi on channel 11. Note the below is all on one line.

inet 10.0.3.1 255.255.255.0 10.0.3.255 \
media autoselect mediaopt hostap \
nwid FreeWifi chan 11 up

Configuring your Firewall

Firstly, set the sysctl to allow passing of packets between interfaces by adjusting /etc/sysctl.conf:

net.inet.ip.forwarding=1

Now copy the provided pf.conf to /etc/pf.conf

cd /root/obsdcp
cp /etc/pf.conf /etc/pf.conf.bak
cp etc/pf.conf /etc/pf.conf

I suggest you open up /etc/pf.conf and have a look at how it works; quote possible that you need to adjust the network interface names at the top to match your hardware.

Our pf.conf uses a table called whitelist to keep track of who is allowed internet access.

touch /var/db/whitelist

Add the following line to /etc/rc.conf.local so PF starts on boot

pf="YES"

Setup DHCPD

You will need to run dhcpd to provide network configuration to clients connecting to your access point. Copy the provided file, but adjust as you need. It’s not good to blindly copy stuff, always have a look inside and try to understand what it does.

cd ocbsdcp
cp /etc/dhcpd.conf /etc/dhcpd.conf.bak
cp etc/dhcpd.conf /etc/

Add the following line to /etc/rc.conf.local so dhcpd starts on boot

dhcpd_flags="athn0"

Configure BIND

Yes, we want DNS as well; its not strictly needed but we can do a lot of nice things with it such as url filtering or ad blocking.

cd ocbsdcp
cp -r /var/named /var/named-bak
cp var/named/etc/named.conf /var/named/etc/
cp var/named/etc/rndc.conf /var/named/etc/
cp var/named/etc/rndc.key /var/named/etc/
cp etc/resolv.conf /etc/resolv.conf

Add the following line to /etc/rc.conf.local so named starts on boot

named_flags=""

Check and reboot

Now its time to make sure everything works as expected. Reboot your access point and check that:

  • You can ping your Router
  • You can ping bsdguides.org
  • Apache is running
  • dhcpd is running for your wireless NIC
  • named is running
  • PF is enabled
  • obsdcp is running
    • Check that relevant services are running by using ps -ax and netstat -an, also check your /var/log/messages and /var/log/daemon logfiles for any potential error messages. For any issues, troubleshoot, reboot, repeat.

      Conclusion

      Given that you followed the above guidelines and neither of us made any mistakes, you now have an access point with a captive portal. Connect to the “FreeWifi” access point by using your mobile or laptop, and try to browse the Internet. Are you asked to provide credentials before being allowed access? Great! Simple configuration and account management can be done inside /var/www/htdocs/Obsdcp_config.pm – you probably want to change those default username and passwords.

      I chose to keep the index.cgi as simple as possible so everything is in base; though i am considering a more feature rich frontend as well. For the moment, this does the job just fine, and runs happily inside a coffee shop i frequently visit.

      Let me know what can be improved, post your issues or suggestions in the comments.

Speak Your Mind

*