VPN System

image/svg+xml fd⛵⛵:⛵⛵⛵⛵:⛵⛵⛵⛵/48 fd☮☮:☮☮☮☮:☮☮☮☮::/48 fd☺☺:☺☺☺☺:☺☺☺☺::/48 fd☕☕:☕☕☕☕:☕☕☕☕::/48

The GNUnet VPN plugin and direct IP routing like this are intended to be complementary to each other… Note that Quagga most probably will propagate GNUnet routes without issue, though the reverse would need some code updates…

Recent developments hastened the need for things like this… and caught the attention of our ISP

Choice of numbering resources

I want to create a completely distributed Internet experience, and try to avoid hijacking numbering space wherever possible to do this. This means that we want to avoid registries where you lease naming or numbering resources, and have to return them when you cannot pay any more.

This does include Regional Internet Registries, who lease address space, and most regular domain registries, who may lease names. Also, if something wrong happens, the name or numbers can be recalled.

Other name and numberspaces are typically persistently allocated. These are much more acceptable. This would include IEEE802 MAC addresses, and numbers such as OIDS used in LDAP, X509/SSL and SNMP tend to be permanently assigned to a given purpose.

For IP addresses, I choose the RFC 4193 random self-allocation numberspace of fd00::/8. We are going to use a source of random material for the addresses, so this is fine. There is a list of some of the other prefixes in use.

For resource naming, we can use Multicast DNS, allowing decentralised naming, such as through the [ff0e::fb]:5353 address.

There isn’t a formally allocated top level domain for MDNS, but port 5353 and the multicast addresses have been allocated to a specification that references .local, so we may be able to use that. There are also reserved names such as .test, as this is an experiment.

Further, rather than be a single alternate root as it will be possible, and even desirable, for several services to be able to claim the same name, and users will choose which to resolve by digital signature.

Implementation / Quickstart

You can create your own VPN using this guide, and experiment with it to become more familiar with it, before deciding to connect with other VPNs, to form part of the collective global vpn, which we can call the GNUbone as a parody on 6bone, or the InterGNU.

Interconnection may be discussed in Main Chat

Firstly, get yourself public IPv6 addresses for your computers. Ensure that packets to fd00::/8 addresses do not escape where they should not, such as by only routing allocated numberspace to the Internet, rather than as a default route.

On Debian GNU/Linux, modprobe ip6_tunnel and add ip6_tunnel to /etc/modules to make it permanent.

Now you can pick some of your computers as VPN nodes. They will be set up essentially the same, as this is a F2F network.

On each computer

We can use Quagga to do some routing between VPN nodes.

cp /usr/share/doc/quagga/examples/vtysh.conf.sample /etc/vtysh/vtysh.conf
service integrated-vtysh-config
username root nopassword

For /etc/quagga/Quagga.conf, in principle it is this, replacing router-id 0.0.0.0 with some 4-octet number unique amongst your VPN nodes.

Choosing a unique number from only 32 bits might seem much more of a problem than the 40-bits of RFC4193, if we use OSPF across the VPN, but it is possible to separate into OSPF islands with BGP links, and redistribute routes between them. So if you use OSPF, you can peer with BGP, and not need to co-ordinate router-id numbers.

router ospf6
 router-id 0.0.0.0
 area 0.0.0.0 range fd00::/8
 interface free0 area 0.0.0.0
 interface free1 area 0.0.0.0
!

We do not need the ospf6d.conf or zebra.conf files to contain any settings, as that will just be confusing, though they do need to exist. We will symlink them to /dev/null

ln --symbolic /dev/null /etc/quagga/ospf6d.conf
ln --symbolic /dev/null /etc/quagga/zebra.conf

You can then adminsister quagga by running vtysh

I have set up racoon and setkey to handle IPsec between VPN nodes. The certificate files do not exist yet, racoon would read them when the connection is used.

In /etc/ipsec-tools.conf

flush;
spdflush;

/etc/racoon/racoon.conf

path pre_shared_key "/etc/racoon/psk.txt";
path certificate "/usr/local/lib/ssl";

remote anonymous {
        exchange_mode main;
        certificate_type x509 "../../../../var/local/lib/vpn/vpn.crt" "../../../../var/local/lib/vpn/vpn.key";
        verify_cert off;
        my_identifier asn1dn;
        lifetime time 10 second;
        peers_identifier asn1dn;
        proposal {
                encryption_algorithm 3des;
                hash_algorithm md5;
                authentication_method rsasig;
                dh_group modp1024;
        }
}

sainfo anonymous {
        pfs_group modp768;
        encryption_algorithm 3des;
        authentication_algorithm hmac_md5;
        compression_algorithm deflate;
}

Startup

Now you have racoon and quagga running in the background, you can peer your nodes.

First, a sample OpenSSL configuration file, saved to /usr/local/etc/vpnssl.cnf

Other object identifiers may be added as well.

#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#

# This definition stops the following lines choking if HOME isn't
# defined.
HOME                    = .
RANDFILE                = $ENV::HOME/.rnd

# Extra OBJECT IDENTIFIER info:
oid_file                = $ENV::HOME/.oid
oid_section             = new_oids

# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions            = 
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)

[ new_oids ]

# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6

streetAddress=2.5.4.9
postalCode=2.5.4.17
favouriteDrink=0.9.2342.19200300.100.1.5

[ policy_match ]
#countryName            = match
#stateOrProvinceName    = match
#streetAddress          = optional
#postalCode             = optional
#organizationName       = optional
#organizationalUnitName = optional
commonName              = supplied
#emailAddress           = optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
streetAddress           = optional
postalCode              = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = 1024
default_keyfile         = vpn.key

distinguished_name      = req_distinguished_name
attributes              = req_attributes

# Use extensions on requests and certificates
utf8                    = yes
x509_extensions         = v3_crt
req_extensions          = v3_req

# only want UTF-8 strings
string_mask = utf8only

# we can have many LDAP style attributes
# http://www.alvestrand.no/objectid/2.5.4.html

[ req_distinguished_name ]
commonName                      = Common Name (eg, YOUR name)
commonName_max                  = 64
commonName_default              = …

[ req_attributes ]

[ v3_req ]

# Extensions to add to a certificate requests

basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_crt ]

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true

Now, the main event, /usr/local/sbin/vpn

Adjust by identifying the interface with your public IPv6 address on it, so the number is copied to MYPUB.

The addresses of your peers go replace the example 2001:db8:d0be:f00d::1 2001:db8:d0be:f00d::2 2001:db8:d0be:f00d::3 addresses.

#!/bin/bash

MYPUB=$(ifconfig eth0 | grep Scope:Global | cut -f2- -d":"|cut -f1 -d"/")
MYADDR=2001:db8:d0be:f00d::1 2001:db8:d0be:f00d::2 2001:db8:d0be:f00d::3

if test "$1" == "init"
then
        CRT=/var/local/lib/vpn/vpn.crt
        KEY=/var/local/lib/vpn/vpn.key

        openssl req -config /usr/local/etc/vpnssl.cnf -new -utf8 -x509 -newkey rsa:1024 -out $CRT -keyout $KEY -nodes -days 10203 -set_serial 0 -batch
        #openssl x509 -text -noout -nameopt utf8,sep_multiline,oid -in $CRT

        function c() { echo fd${1:62:3}${1:65:2}${1:68:3}${1:71:2}${1:74:2}::/128; };
        MYPREFIX=$(c "$(openssl x509 -in $CRT -noout -sha1 -fingerprint)")

        I=0
        for N in $MYADDR
        do
                if test $N != $MYPUB
                then
                        ip -6 tunnel add free$I mode ip6ip6 remote $N local $MYPUB
                        ip -6 addr add $MYPREFIX dev free$I
                        ip link set dev free$I up

                        # want mDNS to work here as well…
                        ifconfig free$I multicast
                        I=$((0${I} + 1))
                fi
        done

        # Use IPsec to cipher all incoming and outgoing data…
        setkey -c <<<"
                spdadd $MYPREFIX fd00::/8 any -P out ipsec esp/transport//require ah/transport//require;
                spdadd fd00::/8 $MYPREFIX any -P in  ipsec esp/transport//require ah/transport//require;
        "
fi

if test "$1" == "away"
then
	I=0
	for N in $MYADDR
	do
                if test $N != $MYPUB
		then
		        ip -6 tunnel del free${I}
			I=$((0${I} + 1))
		fi
	done
fi

Run as vpn init to connect, and vpn away to drop connection. Quagga should detect that links have gone up and add routes as needed. When ping6ing across the VPN, racoon should detect it, and they will then have some protection with IPSec.

As SSL files are generated randomly, deriving the IP address from the key fingerprint, which is hash of the public key, gives some degree of authority over the address selected.

Multicast DNS

It is possible to persuade Avahi to serve names over the VPN connections, with allow-point-to-point, and may consider using the dns reflector utilities in there as well.

IP6Tables

On nodes facing peers, you will want to firewall, so that only packets from and to fd00::/8 will be routed, with possibly some of ff00::/8 as needed.

This should prevent your node being used as an exit node by surprise.

If your default target is DROP, and may want to further adjust the rules such as to require IPsec where you act as a bearer between other parties.

LL=$'fe80::/64'
MC=$'ff00::/8'
FC=$'fd00::/8'

for N in free0 free1 free2
do
        ip6tables -A INPUT   -i $N -s ${FC} -d ${FC} -j ACCEPT
        ip6tables -A OUTPUT  -o $N -s ${FC} -d ${FC} -j ACCEPT
        ip6tables -A INPUT   -i $N -s ${LL} -d ${MC} -j ACCEPT
        ip6tables -A OUTPUT  -o $N -s ${LL} -d ${MC} -j ACCEPT
        ip6tables -A INPUT   -i $N -s ${LL} -d ${LL} -j ACCEPT
        ip6tables -A OUTPUT  -o $N -s ${LL} -d ${LL} -j ACCEPT
        for J in free0 free1 free2
        do
                if test $N != $J
                then
                        ip6tables -A FORWARD -i $N -o $J -s ${FC} -d ${FC} --protocol esp -m ipv6header --header ah,esp -j ACCEPT
                fi
        done
done

Extra Things

Add connection to wlan mesh 802.11s

This connection I use for the Engenius EPI-3601S card (Atheros PCI). You need a mac address for each virtual device, so can use the mac address from your local ethernet adaptor or bluetooth device to guarantee uniqueness. Otherwise can pick an address from a virtualisation range such as VirtualBox: 08:00:27:??:??:??, it may not be unique but gives better mesh anonymity if that is desired.

iface mesh inet6 manual
        pre-up iw phy phy0 interface add $IFACE type mp
        #pre-up ifconfig $IFACE hw ether $(ifconfig eth0|grep HWaddr|cut -f3 -d"r")
        up ip -6 addr add \
$( c() { echo fd${1:62:3}${1:65:2}${1:68:3}${1:71:2}${1:74:2}:1:${6:0:1}$(printf %x $((${6:1:1}^2)))${6:3:2}:${6:6:2}ff:fe${6:9:2}:${6:12:2}${6:15:2}/64; }; \
c "$(openssl x509 -in /var/local/lib/vpn/vpn.crt -noout -fingerprint)" $(ifconfig $IFACE)) dev $IFACE
        post-down iw dev $IFACE del