Wireless Guest Network With Web Authentication

In a previous post titled, “Home Wireless Router” I walked through my custom built FreeBSD, wireless router at home. In this post, we’ll add web based authentication for guests. Essentially, when an unknown users connects to our network and browses the web, we’ll display our own website with a note letting them know we’re watching. They’ll have to agree to behave before they can actually browse the internet on port 80.

This will build on the previous “Home Wireless Router” post; start there first and make the appropriate changes noted below.

Using dhcpd to setup a split network:

We’ll make the network for trusted users and the network for our untrusted guests. Perhaps, in the future we can even throttle their bandwidth!

Here is what my /usr/local/etc/dhcpd.conf file now looks like.

subnet netmask {
  pool {
          option domain-name-servers;
          deny unknown-clients;
  pool {
        option domain-name-servers;
        allow unknown-clients;
  option domain-name "CANAAN";
  option routers;
  option broadcast-address;
  default-lease-time 600;
  max-lease-time 7200;
group trusted {
        host phone { hardware ethernet 00:0e:08:d5:c9:af;}
        host dell { hardware ethernet 00:11:43:75:0d:89; }
        host android1 { hardware ethernet f8:7b:7a:f0:c8:4b; }
        host android2 { hardware ethernet f8:7b:7a:f0:71:8b; }
        host ipad { hardware ethernet 10:93:e9:41:8f:26; }
        host macmini { hardware ethernet 68:a8:6d:59:9f:c9; }

I’m embarrassed, I really didn’t pay for all of those Mac products. If it isn’t obvious, you’ll need to replace the hosts and their hardware Ethernet addresses with the actual hardware Ethernet addresses of your trusted machines.

Packet Filter

Our packet filter configuration (/etc/pf.conf) is going to look a lot different than it did when we only had a wireless nat, but it should be obvious what the differences do:

allowed_out="{http, https, ssh, domain}"
table  {}
set block-policy return
nat on $ext_if from to any -> ($ext_if)
nat on $ext_if from to any port $allowed_out ->($ext_if)
no rdr on $int_if proto tcp from  to any port 80
rdr on $int_if proto tcp from to any port 80 -> port 80

In my prior wireless post, I stripped out all my fancy variables. Since we are now referring to the internal and external interfaces quite often, we might as well make our lives a bit easier by using variables.

Nat’ing works as usual for the network, however only allows the ports we’ve specified in $allowed_out. The “rdr on” rule forwards port 80 on our untrusted network to our localhost’s port 80. So now, no matter what website our guests visit, they end up on our webserver! The “no rdr” rule excludes IP addresses listed in the “goodboys” table from our subsequent “rdr on” rule.

All we need to do once a guest authenticates is run a simple command from the command line to add their IP address to the “goodboys” list. That command would look something like this:

pfctl -t goodboys -T add

Setting up the web server
It’s pretty obvious what to do next. Setup a webserver on localhost. You can write any sort of authentication script you like. I’ve placed a simple note letting my guests know that I’m not a sucker, nor a socialist and that by using my network they agree to my terms. The link “I Agree” takes them to a simple php script:

$output=exec("/usr/local/bin/sudo /sbin/pfctl -t goodboys -T add " . $_SERVER["REMOTE_ADDR"]);
header ("location: http://www.mises.org/");

It would be simple enough to do that in any other language.

But wait… pfctl needs to be run as root…

Right.. Install sudo and add this to your /usr/local/etc/sudoers file:

www ALL=NOPASSWD: /sbin/pfctl -t goodboys -T add 10.0.2.[2-254]

I used a regular expression so that a vulnerability in my script couldn’t give a crafty guest free reign over pfctl. I’m sure you could also play with the permissions of /dev/pf and make use of /etc/devfs.conf … but ehh.. I think this does the trick.

Oh.. and don’t forget to change the netmask on your gateway

Our network is now (not /24) in comparison to the previous “Home Wireless” post. You’ll need to make the change in ifconfig and rc.conf .

Maybe not so obvious pitfalls..

  • A crafty user could assign themselves an IP address on the trusted network. If this were a product and we were concerned with real authentication, we wouldn’t have a trusted “backdoor” network.
  • Only port 80 is forced into our authentication scheme. The user could still make use of any other protocol in the $allowed_out variable. Again, if this were a product, we’d keep other ports closed and open them using our “goodboys” table, which contains the list of authenticated users.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>