The place to learn about your Mac. Tips and tutorials for novices and experts.

Set Up a Wireless Captive Portal Server

What does a captive portal server, also called a NAC (Network Access Control) do? It can sandbox any wireless connection until some form of authentication is provided. These servers are used in many cafes and public places that offer wireless internet. For example, when you try to connect to the wireless network at Starbucks, it will force your web browser to the same page - no matter what URL you enter. Until you authenticate, you can't go anywhere on the Internet.

There are some great commercial solutions out there that work really well, but implementing 802.1X port security can cost lots of money and time. Companies like Infoblox provide an appliance, and when coupled with solutions like Bigfix can provide a very robust and transparent network security model. Pitney Bowes has implemented a system were an unknown user can attach their laptop and get one of two services: Either simple port 80 access for 8 hours, or the option to register and automatically have their computer scanned patched/updated to comply with the companies policies. This is all transparent.

So what do you do if you don't have the resources or the money to buy one of these solutions? You can still implement some control over DHCP assignment. The goal is to eliminate the unknown on your network. In our case, we will use the Ethernet MAC address of the client's NIC as a way of authentication. Each network interface card has its own unique address, and it looks something like this: 00:30:65:88:01:93. When an unknown computer is placed on the network that asks for an IP through DHCP it will receive one that is restricted. No matter what web page the user tries, we will always show them a pre-defined page. When a computer that has its MAC address defined on the server asks for an IP, it will be given the correct TCP/IP settings to use the network.

Note: This is not secure, and will not prevent someone from stealing a working IP on your network. If the user is savvy enough they can find the TCP/IP info on another computer and use it. If you are looking for that kind of security, then this article is not enough for you. Check out Infoblox or another commercial solution.

Here's how to do it:

  1. Make sure the Mac you are using has all os updates and security patches applied.

  2. From the Apple menu, select System Preferences. Select Network. Turn off all unused configurations. The interface we will be using is the Built-in Ethernet.

  3. Select Built-in Ethernet and click Duplicate. Click Rename and add your dummy subnet.

  4. Edit the interfaces to have the correct subnets.

  5. Make sure you are logged in as an admin to use the sudo command. You will need to use a text editor like VI, Pico, Emacs, or TextWrangler to create and edit the config files needed.

    Download the latest version of DHCP from The latest version at the time of this article is 3.0.6. You will need to have the GCC compiler installed. Install the Xcode developer software that came with your Mac or latest Mac OS X install disks.

  6. Once you have downloaded the DHCP source, double-click on the .gz file to unzip and untar the directory. Launch the Terminal application (located in /Applications/Utilities). Use the "cd" command to change to the dhcp directory you just expanded. The easiest way is to type "cd" and drag and drop the dhcp directory into the Terminal window. Here's an example:

    cd /Users/dmc/Desktop/Downloads/dhcp-3.0.6/

  7. Now type the following and hit return.


  8. Type the following and hit return.


  9. Type the following and hit return.

    sudo make install

Setting Up DHCP

You now should have a working DHCPd package installed on the Mac. The next step is to set up the dhcpd.conf file that lives in /etc. The below example will do just that.

Here's how the thing works:

  • Hand out static IPs in the range to hosts that we have the Ethernet hardware address of (MAC address).

  • Hand out dummy IPs in the range that will force all host names to resolve back to our captive portal server. This will force any http requests to show the page we want people to see.

You should read the man page for dhcpd.conf to gain an understanding of the options and commands used here.

======================begin dhcpd.conf===========================
# dhcpd.conf
# this file should be located in /etc
default-lease-time 900;
ddns-update-style none;
deny client-updates;

shared-network "mynet" {
# This is the dummy network
subnet netmask {
max-lease-time 7200;
option subnet-mask;
option broadcast-address;
option domain-name-servers;
option routers;

# This is the real network
subnet netmask {
max-lease-time 21600;
option subnet-mask;
option broadcast-address;
option routers;
option domain-name-servers;
option domain-name "";
# This is a sample reserved host entry
host example1 {
hardware ethernet 00:30:65:88:01:93;

host example2 {
hardware ethernet 00:16:cb:91:16:93;
======================end dhcpd.conf===========================

  1. Create the dhcpd.leases file using the following command:

    sudo touch /var/db/dhcpd.leases

  2. To test the the DHCP server, run the following command in a Terminal window:

    sudo /usr/sbin/dhcpd -f -d en0

    The above command runs DHCPd in the foreground using the -f option and in debug mode using the -d option. This allows you to see all actions the DHCP server is performing. Populate the dhcpd.conf with the MAC addresses of the hosts you want.

  3. Alternatively, you can also assign random IPs from the real network if you don't care who gets what IP. To do this, leave out the the "fixed-address" line of the reserved host. in the real network section add a line to deny unknown clients and add a range of addresses to hand out like so:

    # This is the real network
    subnet netmask {
    max-lease-time 21600;
    deny unknown-clients;
    option subnet-mask;
    option broadcast-address;
    option routers;
    option domain-name-servers;
    option domain-name "";

  4. Here is a launchdaemon plist file you can use to launch dhcpd at boot. Place it in /Library/LaunchDaemons.

    To load the file into launchd, type the following command into Terminal:

    /bin/launchctl load -w /Library/LaunchDaemons/org.isc.dhcpd.plist

    To unload and keep dhcpd from launching at boot, type the following command into Terminal:

    /bin/launchctl unload -w /Library/LaunchDaemons/org.isc.dhcpd.plist

Set Up the Dummy DNS Server

  1. First create a file in /etc called rndc.key. This file will keep BIND from complaining that it is missing. Since you will not be updating, the DNS we can use a generic file and not include anything in the named.conf.

    ======================begin rndc.key===========================
    key "rndc-key" {
    algorithm hmac-md5;
    secret "8E48raKxqEVCtKYFxA+loQ==";

    ======================end rndc.key===========================

  2. You will need to create the following named.conf file in /etc:

    ======================begin named.conf===========================

    acl dummynets {;; };

    options {
    directory "/var/named";
    auth-nxdomain no; # conform to RFC1035
    notify no;
    allow-query { dummynets; };
    allow-recursion { none; };
    allow-transfer { none; };
    max-ncache-ttl 60;

    logging {
    category default {

    channel _default_log {
    file "/Library/Logs/named.log";
    severity info;
    print-time yes;

    zone "." IN {
    type master;
    file "db.fakeroot";
    allow-query { dummynets; };


    ======================end named.conf===========================

    The above says to use db.fakeroot as the root dns zone file. The "acl dummynets {;; };" tells that only our dummy subnet can request queries.

  3. Now we need to create the db.fakeroot zone file in /var/named.

    ======================begin db.fakeroot===========================

    $TTL 7200

    @ IN SOA (
    1 ; Serial
    3600 ; Refresh every 1 hours
    1800 ; Retry every 30 minutes
    604800 ; Expire after 7 days
    1 ) ; TTL 1 second

    IN NS

    IN A
    * IN A

    ======================end db.fakeroot===========================

    The above zone file resolves all hostnames back to

  4. Apple has included a launch daemon plist that can be used to start and stop the named service. Unfortunately, it doesn’t work, because it launches named before all the network stuff is loaded at boot. Until we get a fix, we will use the old style startup item. Download this file, unzip it and place the BIND folder in /Library/Startupitems/. This will make sure named is launched at startup.

  5. Test the DNS service by typing the following command on a host computer that has been given one of the dummy IPs:


    It should resolve to

Set Up Apache to Redirect Missing Paths

The line you want to edit in your httpd.conf file looks like the following:

#ErrorDocument 404 /missing.html

Change it to:

ErrorDocument 404 /

Create a simple index.html page that tells the user to contact you for proper access and save it to /Library/webserver/Documents. Turn on personal websharing in the sharing system prefs pane to start Apache.

You can get pretty creative using CGI and have users fill out a form that gives you their information.

Meet Your Macinstructor

David Miller has been supporting Macs since the mid '90s. He has worked for large ISPs and DOE sites, and he's currently working as a Unix systems administrator. He has graciously allowed Macinstruct to reprint this tutorial, which was originally written for his personal website:


Copyright © 2016 Macinstruct. This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.