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:
-
Generate a random password as shared secret between chillispot and freeradius, e.g. with “apg”.
-
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
}
-
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
-
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:
- 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. - 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 - Set the DNS domain to hand out to DHCP clients, which can be
basically anything, e.g.:
domain certu.local - Set the address of the RADIUS server, in this case localhost:
radiusserver1 127.0.0.1
radiusserver2 127.0.0.1 - Set the password necessary for connecting to it:
radiussecret <the same password as used in /etc/freeradius/clients.conf> - Set some sensible NAS ID:
radiusnasid chilli - And one of the important bits, set the interface to bind to, in this
case:
dhcpif intwlan - 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 - And optionally an information page to which clients will be
redirected first:
uamhomepage https://10.20.30.1/local/welcome.html - 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> - 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 - 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).
- Start chillispot with “/etc/init.d/chillispot start” and again make sure that it is started automatically on bootup.
- 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.
- 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:
-
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 setupinterfaces=“ipsec0=intwlan”
nat_traversal=no
uniqueids=yes
-
Make sure that the connection description also uses this address (the other parameters are specific to what you want to tunnel):
conn wlanIpsecOnlyleft=10.20.30.1
leftsubnet=0.0.0.0/0
leftcert=gibraltar.pem
leftrsasigkey=%cert
right=%any
rightrsasigkey=%certauthby=rsasig
auto=add
dpdaction=clear
rekey=yes
keyingtries=0
ikelifetime=1h
keylife=1h
-
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 MASQUERADEThe 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.
-
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.