News   Software   Hardware   Publication   Education   FAQ   Projects   Site Map 
Printable version XML version
Login
Name

Password


Join
Forgot your password?
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 script

Getting Nexedi-vpn

To 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.tgz

Nexedi-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 usage

With 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 VPNKey

The 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" :

  • everytime a device is added, it"ll run "/etc/dynamic/scripts/vpnkey.script"
  • it will copy a small shellscript to $REMOVER that simply runs "/etc/dynamic/scripts/vpnkey.script", and make the script executable, so that it's launched when the device is removed too.

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 interface

The 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 networks

Here, 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 nodes

In 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 policies

From 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

Filtering

In 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 nodes

We 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 resources

The 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 nodes

As 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 :

  • it will be the master server for the local subdomain and associated reverse records,
  • it will forward request for the company's domain or global reverse records to the center node.

The center node will be the master server for the company's domain and for its local subdomain, so it will work like this :

  • it will handle statical records in the company's domain and global reverse records,
  • it will forward requests for a specific node's subdomain or reverse records to this node's gateway (unless the specific node is the center node itself, in which case, it will answer them itself).

The dynamic DNS records

As 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 :

  • If the host defined a client identifier option in its request, and this identifier's type is zero, we use the identifier as the hostname (zero means the identifier is a hostname, else the identifier is a hardware address).
  • Else, if the host sent a hostname option in its request, we use it as the hostname for registration.
  • Else, we use the IP address in dotted decimal as the hostname ("a.b.c.d").

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 setup

To 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.


Nexedi, SARL au capital 30.000 Euros, 270, bd. Clémenceau 59700 Marcq-en-Baroeul
RCS Roubaix Tourcoing 440 047 504 - IBAN: FR76 3002 7175 3900 0410 2760 135
Email: info@nexedi.com - Tel. +33(0)6 62 05 76 14 - Fax. +33(0)3 20 72 99 25
Legal Notice
Powered by ERP5 Open Source ERP, Zope, CPS and Nexedi