Howto combine Chillispot with OpenSwan on one machine

Imagine the following setting: there is some (possibly 802.11a/b/g wireless) network, which can range from a single access point to a complete backbone network of access points working together via WDS, or even a wired network infrastructure. This (W)LAN should serve two purposes:

  • act as an open “hotspot” type network where users do not need any special client configuration to use it (other than maybe a username/password combination or some prepaid account)
  • simultaneously allow registered/special users to use it for purposes that are not open to the first public group

These are usually seen as two different use cases, and both are already in extensive use. The former expanding rapidly, with free and commercial hotspot popping up all over the place, the latter in form of secured, private networks, e.g. with 802.11i/802.1x or via VPNs like IPSec or OpenVPN (I didn’t hear somebody say PPTP, did I? No? Ok, then let’s skip this insecure “VPN” protocol).

However, I wanted to use both at the same time, because both use cases can be important for the same setting. In my development network, this is just a single 802.11g access point (it’s of course a Linksys WRT54G), which is used by two of my machines to access the private network behind it and occasionally for web access when friends visit me for a few days and bring their own notebooks. The first case is secured by running IPSec over the WLAN, the second one just uses a captive portal to let my guests log into the guest accounts I pre-create or create on demand.

Implementation

The implementation of these two cases together in one network, and moreover using just one machine, proved to be a bit tricky, so this explanation will hopefully help others with similiar needs.

Components

When you’ve read some of my other Howto’s in this category, you might have guessed already that my platform of choice for networking is Linux, and more specifically my Gibraltar firewall distribution. It acts as the gateway that provides both the captive portal and the IPSec gateway.
The choice of IPSec implementations for kernel 2.4 was quite limited, so I am using the standard openswan one (in form of the Debian package that I maintain). For the captive portal, I used NoCatAuth for a previous solution, which integrated very nicely with the Linux networking parts, especially the netfilter framework. Unfortunately, it was kind of abandoned by upstream developers and was heavy-weight (requiring perl and gnupg) and rather difficult to set up correctly anyways. So I switched to chillispot for my final solution. chillispot has its own problems, mainly that it does not integrate with netfilter and VPNs as well as NoCatAuth, but those can be overcome. And how to overcome these problems is what this page is about.

The concept

chillispot, by implementing a captive portal, basically needs to take over the network management. It does this by binding to the network interface of the gateway that connects to the WLAN, capturing all the packets it sees and responding to them. In effect, this network interface should not be used for anything else, it is better to give chillispot the full control over handling it. chillispot acts as a DHCP server, handing out addresses to DHCP clients and registering all addresses it has given out. Only those addresses that were assigned by chillispot (and the corresponding MAC addresses) can actually communicate, so if chillispot is used, this forces all clients to use DHCP; static IP addresses will not work.
After configuring their address, clients can then try to connect to web pages, and chillispot will redirect all HTTP requests to the portal page to allow users to login (or to simply click through a disclaimer page if you allow free access without login). For handling the user authentication, it builds on RADIUS and therefore needs an appropriate server.

All private use cases of the network should be properly secured, and openswan acts as the IPSec gateway for tunnels to either a special private network or even for the whole traffic of the registered clients.

Basic setup

For this setup, I assume the “outgoing” interface of the gateway, i.e. the one towards the Internet to be called “ext”, and the “incoming” interface, i.e. the one connected to the WLAN in question, to be called “intwlan”. It will of course work for other names as well, but it is nice to name network interfaces after their purpose, and Gibraltar allows you to do that.
In my case, ext is configured via DHCP (but it really doesn’t matter for the following setup), and intwlan is configured with IP address 10.20.30.1/30. The reason for this seemingly odd netmask is explained in more detail below.

Setting up chillispot

The first necessary task is to install and configure freeradius. Using the Debian packages (Gibraltar is using version 1.0.2-3.gibraltar.1 at the time of this writing), this is easy:

  1. Generate a random password as shared secret between chillispot and freeradius, e.g. with “apg”.

  2. Put this password into /etc/freeradius/clients.conf for the parameter “secret = " in the block “client 127.0.0.1 { … “, to the effect of e.g.:
    client 127.0.0.1 {

        secret = <the secret generated above>

        shortname = localhost

        nastype = other

    }

  3. Add a first test user for authenticating (you will need this also when you don’t intend to use logins later on - then all users will just use the same account) to the end of /etc/freeradius/users:
    guest    Auth-Type := Local, User-Password == “guest”

             Class = 0702345678,

             Idle-Timeout = 600,

             Session-Timeout = 1800,

             Acct-Interim-Interval = 60,

             WISPr-Bandwidth-Max-Up = 64000,

             WISPr-Bandwidth-Max-Down = 128000

  4. Start freeradius with “/etc/init.d/freeradius start” and make sure that it starts automatically on bootup (e.g. via /etc/runlevel.conf if you use that).

There are many fields than can be set for users, the above example just shows up- and download bandwidth limits, session timeout, and idle timeout.

Now that authentication is set up, the next step is to configure chillispot in /etc/chilli.conf:

  1. Set the network to use for clients, e.g.
    net 10.20.31.0/24
    chillispot will assign itself the first address from this network, in this case 10.20.31.1. However, this address is not really usable for anything outside routing tables, particularly not for running any services on it. This address is used as the router options for DHCP.
  2. Set the DNS server address to hand out to DHCP clients. Since we can not bind the service to the address inside the chillispot-managed network, the trick is to use the address that has been assigned to the network interface itself, in this case I assume that the gateway provides a DNS resolver:
    dns1 10.20.30.1
  3. Set the DNS domain to hand out to DHCP clients, which can be basically anything, e.g.:
    domain certu.local
  4. Set the address of the RADIUS server, in this case localhost:
    radiusserver1 127.0.0.1
    radiusserver2 127.0.0.1
  5. Set the password necessary for connecting to it:
    radiussecret <the same password as used in /etc/freeradius/clients.conf>
  6. Set some sensible NAS ID:
    radiusnasid chilli
  7. And one of the important bits, set the interface to bind to, in this case:
    dhcpif intwlan
  8. Then the HTTP address with the login form. Here we apply the same trick and use the internal address assigned directly to the network interface:
    uamserver https://10.20.30.1/cgi-bin/hotspotlogin.cgi
  9. And optionally an information page to which clients will be redirected first:
    uamhomepage https://10.20.30.1/local/welcome.html
  10. Generate another random password (don’t use the same!) as shared secret between the chillispot daemon and the login script, e.g. with “apg” and use it as:
    uamsecret <the second password>
  11. And another part of the trick to make chillispot and openswan work together on one box is to allow unauthenticated access to both internal addresses, in this case:
    uamallowed 10.20.30.1,10.20.31.1
  12. If not compiled directly into the kernel, load the “tun” module with “modprobe tun” and make sure that it is loaded on bootup (e.g. by putting it into /etc/modules).
  13. Start chillispot with “/etc/init.d/chillispot start” and again make sure that it is started automatically on bootup.
  14. Set up the welcome page and the login CGI script so that it matches your local needs (this is independent of how to use openswan and chillispot together, so it will not be covered here). A good starting point is the perl version of the hotspotlogin script shipped with the chillispot upstream package, but you can also use e.g. the C version which was written for the Sveasoft firmware for the Linksys WRT54G(S) access points. The second password used for the uamsecret parameter needs to be put into this script too. When using my slighly modified perl version and running it on the same machine, it will figure it out automatically from the chillispot config file and therefore doesn’t need to be set explicitly.
  15. Set NAT and firewall rules to allow these public clients on the WLAN to access whatever they are allowed to.
    Note: All connections from authenticated clients will come from the network interface “tun0”, because chillispot forwards them in user space.

At this point you should be able to test with clients on the WLAN that they get assigned an address via DHCP and that they get redirected to the welcome page and can login normally.

 

Setting up openswan

Now the interesting bit, the addition of IPSec over the hotspot-managed WLAN. The crucial part to get this to work is to bind openswan (or strongswan, for that case) not to the chillispot-managed address, but to the address of the underlying network interface! As mentioned above, the address that chillispot binds to the tun0 device is not really usable for anything but routing and the HTTP-based hotspot login process. After about a week of playing with it, I found out that openswan will happily intercept the ESP/IKE packets directly from the underlying network interface, so it can be bound to its address. So, to make it work with chillispot, set:

  1. Make sure that openswan’s (to be specific, KLIPS’s) virtual network interface grabs the address of the underlying network interface, in thise case intwlan:
    config setup

            interfaces=“ipsec0=intwlan”

            nat_traversal=no

            uniqueids=yes

  2. Make sure that the connection description also uses this address (the other parameters are specific to what you want to tunnel):
    conn wlanIpsecOnly

            left=10.20.30.1

            leftsubnet=0.0.0.0/0

            leftcert=gibraltar.pem

            leftrsasigkey=%cert

            right=%any
            rightrsasigkey=%cert

            authby=rsasig

            auto=add

            dpdaction=clear

            rekey=yes

            keyingtries=0

            ikelifetime=1h

            keylife=1h

  3. Set firewall rules to allow all of the necessary packets through. This was also a bit tricky and took me quite some time to get right (i.e. allow only those packets that are strictly necessary for the solution to work, but to get all of them):
    iptables -t filter -A INPUT -i tun0 -s 10.20.31.0/24 -d 10.20.31.1 -p tcp –dport 3990 -j ACCEPT # chillispot daemon (for redirect)

    iptables -t filter -A INPUT -i tun0 -s 10.20.31.0/24 -d 10.20.30.1 -p udp –dport 53 -j ACCEPT # we act as DNS

    iptables -t filter -A INPUT -i tun0 -s 10.20.31.0/24 -d 10.20.30.1 -p udp –dport 500 -j ACCEPT # allow IPSec

    iptables -t filter -A INPUT -i tun0 -s 10.20.31.0/24 -d 10.20.30.1 -p esp -j ACCEPT # dt.

    # openswan is bound directly to this IP, so some of the packets appear to

    # come directly from intwlan instead of from tun0 via chilli. Don’t know

    # exactly what’s going on here, but this way it works…

    iptables -t filter -A INPUT -i intwlan -s 10.20.31.0/24 -d 10.20.30.1 -p udp –dport 500 -j ACCEPT # allow IPSec

    iptables -t filter -A INPUT -i intwlan -s 10.20.31.0/24 -d 10.20.30.1 -p esp -j ACCEPT # dt.

    # but no other plaintext packets are allowed

    iptables -t filter -A INPUT -i tun0 -j DROP

    iptables -t filter -A INPUT -i intwlan -j DROP

    # after establishing IPSec, it will also be used for DNS

    iptables -t filter -A INPUT -i ipsec0 -s 10.20.31.0/24 -p udp –dport 53 -j ACCEPT
    # if there are no other services offered by this box, then don’t allow more
    iptables -t filter -A INPUT -j LOG
    iptables -t filter -A INPUT -j DROP

    # and allow only authenticated/secured packets to the outside
    iptables -t filter -A FORWARD -i ipsec0 -o ext -s 10.20.31.0/24 -j ACCEPT
    iptables -t filter -A FORWARD -j LOG
    iptables -t filter -A FORWARD -j DROP

    # if ext is not using an official address, need to masquerade
    iptables -t nat -A POSTROUTING -o ext -s 10.20.31.0/24 -j MASQUERADE

    The forwarding and NAT rules will of course be specific to the respective needs, the above rules just reflect a basic example where authenticated clients are allowed to connect to everything that interface ext leads to (maybe, um, the Internet). Another example would be access to a private network attached to another interface dmz, while public hotspot users are only allowed access to the Internet.

  4. Make all registered/special clients use the address set above as the IPSec tunnel gateway (in this case 10.20.30.1) and match the other connection parameters (most importantly the subnet and authentication).

I certainly recommend not to use PSK authentication for the IPSec clients, but to use X.509 certificates with a proper CRL integration. This way, access from a specific client can be revoked or can just time out after a pre-set validity period of the certificate. With PSK, every client would need to use the same password, which would require to change the password for all of the remaining ones if access is to be revoked for just one. An alternative is to use the Microsoft-favored L2TP-over-IPSec combination with an additional level of username/password authentication. But I prefer the IPSec-only variant due to less overhead.

René Mayrhofer
René Mayrhofer
Professor of Networks and Security & Director of Engineering at Android Platform Security; pacifist, privacy fan, recovering hypocrite; generally here to question and learn