|
|
|
Login
|
Introduction to Nexedi VPN
This documents Nexedi VPN, a service which allows small and medium organizations to interconnect multiple office branches with a high level of privacy while securing Internet access. Nexedi VPN provides firewall security and can act as a secure WiFi access point.
This document will first quickly describe how to use the Nexedi-vpn script, then it will detail some aspects of the configuration we use for Nexedi VPN. How to use the Nexedi-vpn scriptGetting Nexedi-vpnTo obtain the latest version of Nexedi-vpn, the easier is to go to our CVS repository then click "Download tarball" and save it on your disk. Next, you just have to extract the archive and you can start generating configurations. Generating config.tgzNexedi-vpn allows you to automatically generate config.tgz files. This file is to be used with umibuilder's VPN target, and it helps easily customizing a box : Just copy a config.tgz onto the compact flash card or USB key and reboot, to obtain a fully configured system. To generate such a file, you need to fill an XML file with the appropriate information. You can find a sample for this file in the archive : just copy "config.xml.sample" to "config.xml", modify the latter to fit your needs, and then run :
python autoconf.py <group_name> <host_name>
The resulting config.tgz file will be in data/group_name/host_name/config.tgz The squashfs usageWith multiple zisofs and tarballs, the initrd of a system image built by umibuilder had to mount a tmpfs in "/", create multiple directories, mount zisofs for read-only directories ("/bin" ,"/usr"...) or extract tarballs for read-write directories ("/etc", "/var"...). Now, with squashfs, tmpfs is mounted in "/", squashfs is mounted in "/mnt/squashfs" and directories are simply symlinked (read-only) or copied (read-write) from "/mnt/squashfs" to "/". Furthermore, creating a shquashfs image is as simple as "mksquashfs [list_of_directories] The VPNKeyThe goal of VPNKey is to activate remote access to the vpn gateway only when the client wishes to do so : when a key is inserted, the remote access is disabled and when no key is present, remote access is possible. VPNKey is a simple usb key containing some special file that makes it recognizable among all the plugged usb keys. We use hotplug to detect if a key is plugged or unplugged. Hotplug Configuration/etc/hotplug/usb.usermap is a file that allows to run userspace utilities when a specific device is plugged or unplugged. Here, we'll add a line that specifies that we want to run a script named vpnkey everytime that a device whose interface class is 0x08 and subclass is 0x06 (an usb-storage product that uses scsi commands) is added or removed. The corresponding match_flags is set to 0x0180, according to kernel's "include/linux/mod_devicetable.h" around line 120. Thus, the line will look like :
vpnkey 0x0180 0x0000 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x08 0x06 0x00 0x00000000
Next, we create the "/etc/hotplug/usb/vpnkey" script (which corresponds to the "vpnkey" argument in "/etc/hotplug/usb.usermap"). This script is a "callback" : it's executed everytime a corresponding device is plugged, and it sees the $REMOVER variable, which points to a (not existing for the moment) file. Everytime a device is removed, if $REMOVABLE points to an executable file, the file is executed, then deleted. Thus, we'll put the vpnkey mechanism in a file namd "/etc/dynamic/scripts/vpnkey.script", and we'll juste keep two things in "/etc/hotplug/usb/vpnkey" :
The "/etc/dynamic/scripts/vpnkey.script" script simply looks for usb-storage scsi hosts in "/proc/scsi/usb-storage" and for corresponding devices in "/proc/scsi/scsi". Then, if these devices have a first partition ("part1"), the script tries to mount it and looks for the VPNKey file. This stops when a VPNKey is found, or when every usb-storage devices have been tested. Next, depending on the presence of a VPNKey and the current status of sshd, the latter is started or stopped. Please remember that hotplug takes approximately five seconds to launch these scripts when a key is plugged, so it is not recommended to plug then unplug a key too fast, or else the script won't be launched until another event (plug or unplug) happens. Also note that due to some problems related to USB keys detection at boot time (at least with kernel 2.4), it is not recommended to use VPNKey if the boot device is an USB device too. The web interfaceThe goal of the web interface is to allow a client to modify its WAN connection (which is generally its Internet ppp connection) settings, and to check its status at different levels (from kernel to ppp), because if the WAN connection is not up, we can't remotely access its vpn gateway to fix things. The web interface is actually a CGI written in python running on a thttpd server, that reads and writes the config from the nexedi-vpn tool's xml file, launches the build of a new config.tgz and copies it on the compact flash card, and checks system files and utilities (sometimes with the help of sudo) for the status of the WAN connection. A browser (currently lynx) pointing to the web interface is also spawned on the first virtual console (tty1) in the /etc/inittab file so that the client can easily report the status of the WAN connection even if its LAN conection is down. As the web interface and the nexedi-vpn tool are strongly linked to each other, they will simply be embedded into the config.tgz file for an easy distribution. Please remember that lynx doesn't support autorefresh, so it is recommended to manually refresh the page (Ctrl+R) before reading status. Using both wired and wireless networksHere, as we needed to support protocols that use broadcast IP packets or even other network layer protocols than IP, it was not possible to completely separate wired and wireless LAN in two routes subnets, which would have offered a higher security. So, we had to use a bridge between the wired and wireless interface, but as we stil wanted to be able to distinguish between wired hosts and wireless hosts, we had to set up two DHCP daemons. Say we have a /24 network, the first /25 part will be dedicated to wired hosts, and the second /25 part, to wireless hosts. When we detect a DHCP request from the wired intreface, we redirect it to the first DHCP server, and when the request comes from the wireless interface, we redirect it to the second DHCP server. We also filter the bridge, so that we only accept data from one interface if the source IP address is in the corresponding /25 network. For the moment, filtering is only there to prevent spoofing, so it's done on the source address of IP packets. It could also be used to prevent massive ethernet diffusion (ie. prevent traffic destined to a wired host to be seen by wireless hosts) by filtering destination address (without forgetting to allow broadcast address on every "subnet"). Relaying IPSec between leaf nodesIn a geographically distributed enterprise (composed of two or more sites), people want to be able to exchange data between sites (or nodes) through secured connections. We choose to use a star topology : one site will be the center node, and the other sites will be leaf nodes. We want to maintain only one connection per leaf, between the center and the leaf itself, so that when we add a new leaf, we don't need to modify every other leaves (only the center and the new leaf). To allow a leaf to communicate with other leaves, we need to make the center node act as a router between leaf nodes. IPSec policiesFrom an IPSec point of view, we need to define policies that will match the traffic we want to encapsulate in IPSec. So, for the connection between a leaf and the center, the policy on the leaf gateway will be "match traffic from the leaf's subnet and to any other node's subnet (including center) and send it to the center node's gateway", and on the center's gateway, it will be "match traffic from every nodes's subnet and to the leaf's subnet and send it to the leaf's gateway". This way, on every node, traffic for the center's subnet will directly go to the center's gateway, and traffic for another leaf's subnet will go to the center's gateway and then be routed to the destination node's gateway. The "every node's subnet" trick actualy means that the enterprise's network addresses must be choosen hierarchically, for example :
10.0.0.0/8 : all company
10.0.0.0/14 : center (10.0.0.0 - 10.3.255.255)
10.4.0.0/15 : leaf 1 (10.4.0.0 - 10.5.255.255), policy will be 10.4.0.0/15 <-> 10.0.0.0/8
10.6.0.0/15 : leaf 2 (10.6.0.0 - 10.7.255.255), policy will be 10.6.0.0/15 <-> 10.0.0.0/8
10.8.0.0/16 : leaf 3 (10.8.0.0 - 10.8.255.255), policy will be 10.8.0.0/16 <-> 10.0.0.0/8
The only problem with this technique, is that local traffic will also be matched by the policy, rendering it impossible. So, we need to add another stricter policy on every gateway, that will match traffic from local subnet to local subnet but won't encapsulate or send it, so that it'll simply remain local traffic. We will then have for example, on leaf 1 :
traffic matching 10.4.0.0/15 <-> 10.4.0.0/15 : do nothing
traffic matching 10.4.0.0/15 <-> 10.0.0.0/8 : encapsulate in IPSec and send to 10.0.0.0/14's gateway
As we wanted to keep FreeS/WAN userland utilities, instead of using racoon or isakmpd, we encountered some difficulty in setting the local to local unencrypted policy (matching 10.4.0.0/15 <-> 10.4.0.0/15 in our example) The problem is that the policy is added to the policy base only if we use at least "auto=route", but this step also modifies the routing table, which we don't want it to do for the local network. This step also modifies the routing table, which we don't want it to do for the local network. So, we tell FreeS/WAN tu use an alternate script so modify the routes, and we use a command (/bin/true) that actualy does nothing as the script :
conn exclude-local
authby=never
left=127.0.0.1 # set this to whatever you want
leftsubnet=10.4.0.0/15 # set this to local subnet
right=127.0.0.2 # set this to whatever you want,
# but a different value than the first one
rightsubnet=10.4.0.0/15 # set this to local subnet
type=passthrough # tells not to use IPSec
auto=route
leftupdown=/bin/true # don't modify the routing table
rightupdown=/bin/true # don't modify the routing table
FilteringIn kernel 2.6 IPSec, there is no vitrual interface dedicated to IPSec traffic, so, we need to be able to filter spoofed packets that look like decapsulated IPSec traffic (both will come from the public interface). Actually, When an IPSec comes in, it appears as encrypted data, then is it catched by the IPSec stack, which reemits it as unencapsulated, clear data if the authentication and integrity checks succeeded. Here, we use the iptables mark module to mark every encrypted packet with the same mark. Next, we can safely drop every packet that seems to come from the IPSec tunnel and that isn't marked, because we know it's some spoofed packet. Domain Name Service distributed between nodesWe wanted to offer Nexedi VPN a complete domain name service, each node being a different subdomain of the company's private domain. To do so, every node must have resource records for local hosts, as well as links to other name servers. Local resourcesThe node's gateway is the master server for the local subdomain, it will register hosts addresses, aliases and reverse records for the subnet associated with the node. All these records can be statically specified in Nexedi VPN configuration. About reverse records, there is one difficulty we had to handle : they are of the form "d.c.b.a.in-addr.arpa", with "a.b.c.d" being the corresponding IP address. This is okay for non-CIDR networks, which have fixed masks of 8, 16 or 24 bits. But now, it's common to see networks with masks of 12, 17 or 23 bits, and in this case we need to provide more than one reverse records zone per node. For example, the 10.4.0.0/15 network would have two reverse zones :
4.10.in-addr.arpa.
5.10.in-addr.arpa.
One limitation is that, due to these classless oriented reverse records, it's not possible to easily handle networks smaller than /24, because they would be in the same "c.b.a.in-addr.arpa". So, we decided to limit the networks size to a minimum of /24 for the moment. Links between nodesAs we want to provide an easily upgradable system, we want leaf nodes to only have links to the center node, so that only the center node needs to be modified when a new node is added. Thus, every leaf node will have two type of declarations :
The center node will be the master server for the company's domain and for its local subdomain, so it will work like this :
The dynamic DNS recordsAs we wanted not to rely on IP addresses anymore to identify and access computers, we chose to add the dynamic registration of hosts in the DNS. Then, every machine that requests an IP address by DHCP is automatically added in the DNS base by the DHCP server. The DHCP and DNS servers both allow dynamic updates in the same domains for this system to work and use a shared secret key to communicate with each other. As the DHCP server is the only one authorized to update the DNS base, this system is quite secure. The host name is choosen according to the infos the client sends to the DHCP server :
One important thing to always have in mind, is that the client identifier MUST be unique, for the DHCP mechanism to work without problems. If two hosts give the same identifier to the DHCP server, it will consider that the two requests are from the SAME host, and if a host changes its identifier five times and do five requests, the server will think the requests come from five DIFFERENT hosts. Client setupTo have a UNIX dhclient send the hostname option in the request, we must add a line in /etc/dhclient.conf, containing (for example, if we want "unix-box" as the hostname) :
hostname : send host-name "unix-box";
Alternatively, on Mandrake Linux, it's possible to simply add a line in /etc/sysconfig/network, containing :
DHCP_HOSTNAME=unix-box
On MacOS 9, in TCP/IP settings, there is a text field named "DHCP Client", that allows to set a DHCP client identifier. |
|
|