Difference between revisions of "OpenVPN"

From Exterior Memory
Jump to: navigation, search
(Created page with "OpenVPN is a secure tunnel. I use it to connect to my home network. OpenVPN uses certificates to both authenticate the client with the server, and the server with the client....")
 
 
(11 intermediate revisions by the same user not shown)
Line 4: Line 4:
  
 
== Setting up a public key infrastructure ==
 
== Setting up a public key infrastructure ==
 +
 +
{{Main|Create a OpenVPN Certificate Authority}}
  
 
The use of client certificates enhances the security, but makes it harder to deploy, as you need to distribute client certificates to each host. You '''can not use an existing public key infrastructure''' (PKI); you would allow anyone with a certificate of that PKI to connect to your server (the <code>tls-remote</code> and <code>tls-verify</code> options can limits the allowed clients). You either need to to set up your own certificate infrastructure, or limit OpenVPN to use password-based authentication (see the options <code>auth-user-pass-verify</code>, <code>client-cert-not-required</code> and <code>username-as-common-name</code>).
 
The use of client certificates enhances the security, but makes it harder to deploy, as you need to distribute client certificates to each host. You '''can not use an existing public key infrastructure''' (PKI); you would allow anyone with a certificate of that PKI to connect to your server (the <code>tls-remote</code> and <code>tls-verify</code> options can limits the allowed clients). You either need to to set up your own certificate infrastructure, or limit OpenVPN to use password-based authentication (see the options <code>auth-user-pass-verify</code>, <code>client-cert-not-required</code> and <code>username-as-common-name</code>).
Line 9: Line 11:
 
The easiest method it to use easy-rsa, as described in the [http://openvpn.net/index.php/open-source/documentation/howto.html OpenVPN How-to].
 
The easiest method it to use easy-rsa, as described in the [http://openvpn.net/index.php/open-source/documentation/howto.html OpenVPN How-to].
  
This article describes the steps taken by easy-rsa in more detail, but basically gives the same result. This article is partly based on the excellent tutorial by [http://www.phildev.net/ Phil Dibowitz] on [http://www.phildev.net/ssl/creating_ca.html creating an CA].
+
The article [[Create a OpenVPN Certificate Authority]] describes the steps in more detail, but basically gives the same result.
  
 
Certificates, when deployed correctly, are ''much'' more secure than passwords, since the secret (key) does not need to be exchanged or shared between the different hosts. However, doing so requires a security hygiene which may not be required for a small-scale deployment.
 
Certificates, when deployed correctly, are ''much'' more secure than passwords, since the secret (key) does not need to be exchanged or shared between the different hosts. However, doing so requires a security hygiene which may not be required for a small-scale deployment.
  
=== Files ===
+
==Running IPv6 through the Tunnel==
  
The following files are used by OpenSSL:
+
OpenVPN can both use a tap or a tun interface. A tap interface tunnels Ethernet traffic, so that includes both IPv4, IPv6 and NetBIOS. A tun interface tunnels IP traffic. Version 2.3 of OpenVPN supports tunnelling bot IPv4 and IPv6 traffic through the tunnel. OpenVPN 2.2 can also tunnel IPv6 traffic, but requires a custom scripts to configure the IPv6 addresses and routing tables. At the time of writing, OpenVPN 2.3 is not yet released, so the following three options are available:
 +
* Use a tap interface to bridge all Ethernet traffic, including IPv4 and IPv6 traffic. The disadvantage is that is less efficient since the tunnel contains broadcast traffic not targeted to the client;
 +
* Wait for OpenVPN 2.3 or compile a development version;
 +
* Use OpenVPN 2.2 with a custom connect- and disconnect script to set the IPv6 address of the tunnel.
  
{|
+
I choose to compile a development version.
! Filename    !! Needed by !! Purpose !! Secret
+
|-
+
| ca.cnf      || CA || CA configuration of its common name (CN) || No
+
|-
+
| ca-sign.cnf  || CA || CA configuration for Signing || No
+
|-
+
| ca.key      || CA || Root CA key || Yes
+
|-
+
| ca.crt      || CA + server + all clients || Root CA certificate || No
+
|-
+
| index.txt    || CA || List of all signed certificates || No
+
|-
+
| serial      || CA || First free serial number of certificates || No
+
|-
+
| crl.pem      || CA + server (+ all clients) || Certificate revokation list || No
+
|-
+
| crlnumber    || CA || First free serial number of CRLs || No
+
|-
+
| .rand        || CA || Root CA certificate || Yes?
+
|-
+
| dh2048.pem  || server only || Diffie Hellman parameters || No
+
|-
+
| server.cnf  || server only during certificate generation || Server configuration || No
+
|-
+
| client1.req  || CA only during certificate generation || Client1 certificate signing request || No
+
|-
+
| server.key  || server only || Server key || Yes
+
|-
+
| server.crt  || server only || Server certificate || No
+
|-
+
| client1.cnf  || client 1 only during certificate generation || Client 1 configuration || No
+
|-
+
| client1.req  || CA only during certificate generation || Client1 certificate signing request || No
+
|-
+
| client1.key  || client 1 only || Client1 key || Yes
+
|-
+
| client1.crt  || client 1 only || Client1 certificate || No
+
|-
+
| client2.cnf  || client 2 only during certificate generation || Client 2 configuration || No
+
|-
+
| client2.req  || CA only during certificate generation || Client2 certificate signing request || No
+
|-
+
| client2.key  || client 2 only || Client2 key || Yes
+
|-
+
| client2.crt  || client 2 only || Client2 certificate || No
+
|}
+
  
I'll use these directories to denote the files that are required on different host:
+
===Install Development Version===
  
{|
+
On FreeBSD:
! Directory    !! Needed by
+
cd /usr/ports/security/openvpn-devel
|-
+
sudo make install
| ca          || CA
+
|-
+
| server      || server
+
|-
+
| client1      || client 1
+
|-
+
| client2      || client 2
+
|}
+
  
This is a compromise between the completely flat file structure of easy-rsa, and the recommendations set forth by most CA tutorials, which creates directories for certificate requests (<code>.req</code>), signed certificates (<code>.crt</code>), certificate revokations (<code>crl</code>), and private keys (<code>ca.key</code>).
+
On Mac OS X:
 +
sudo port install lzo2
 +
git clone git://openvpn.git.sourceforge.net/gitroot/openvpn/openvpn.git
 +
cd openvpn
 +
git checkout 32ab329bc69c6292c205d4f33a4b8069341798d3
 +
autoreconf -i -v
 +
./configure --with-lzo-headers=/opt/local/include --with-lzo-lib=/opt/local/lib
 +
make
 +
make check
 +
sudo make install
  
If you are serious about security, you should not run these commands on the same machine, but run commands in the server, client1, and client2 directory of respectively the server and client machines, and run commands in the CA directory on a machine that is not connected to the Internet.
+
===Configure IPv6 Tunnel===
  
=== Initialize ===
+
Imagine the following setup:
 +
[[File:VPN server example.png]]
  
(roughly equivalent to <code>easy-rsa/clean-all</code>)
+
A sample server and client configuration follow. These examples could probably be reduced in size; some parameters are default or some can be combined (e.g. "<code>server</code>" can replace "<code>mode server</code>", "<code>tls-server</code>" "<code>ifconfig</code>" and "<code>ifconfig-pool</code>").
  
mkdir ca server client1 client2
+
See the [http://openvpn.net/index.php/open-source/documentation/manuals/openvpn-22.html OpenVPN man page] and Gert Döring's [http://www.greenie.net/ipv6/openvpn.html IPv6 Payload Patch] for information on the different configuration options.
cd ca
+
touch index.txt
+
  
=== CA Configuration Files ===
+
====OpenVPN server configuration====
  
Create configuration files. In our setup, <code>ca-sign.cnf</code> contains the configuration for signing certificates. We only use it in conjunction with the <code>openssl ca</code> command. It described the folder structure within the ca directory, the location of support files for the CA, as well as properties of the signed certificates (duration, restricted usage) as well as the policy for the name ("distinguished name") of signed certificates. Finally, it lists the policy for certification revocation lists.
+
# The IP to listen on for incoming VPN connections
 
+
local 192.0.2.206
  # ca-sign.cnf
+
  proto udp  # or tcp-server is UDP is blocked
# This configuration file is used by the 'ca' command, to create signed certificates.
+
  dev tun1
  [ ca ]
+
default_ca              = CA_default                    # The default ca section
+
 
   
 
   
  [ CA_default ]
+
  # PKI Stuff
  dir                    = /path/to/ca                   # Where everything is kept
+
  ca         ca.crt
  certs                  = $dir/                        # Where the issued certs are kept
+
  cert        server.crt
  crl_dir                = $dir/                        # Where the issued crl are kept
+
  key        server.key
  new_certs_dir          = $dir/                        # default place for new certs
+
dh          dh2048.pem
 +
crl-verify  crl.pem
 +
  # Option TLS shared secret for HMAC signing for additional security
 +
tls-auth    tls-auth.key
 
   
 
   
  private_key            = $dir/ca.key                  # The private key
+
  mode server
  certificate            = $dir/ca.crt                  # The CA root certificate
+
  tls-server
database                = $dir/index.txt                # List of signed certificates
+
serial                  = $dir/serial                  # The current serial number
+
crlnumber              = $dir/crlnumber                # the current crl number
+
crl                    = $dir/crl.pem                  # The current CRL
+
RANDFILE                = $dir/.rand                    # private random number file
+
 
   
 
   
  unique_subject          = no                            # allow multiple certificates with same subject.
+
  topology net30
  default_md              = default                      # Use hash algorithm specified in the request
+
  ifconfig 10.240.177.1 10.240.177.2
  default_days            = 3650                          # client certificates last 10 years
+
  ifconfig-pool 10.240.177.4 10.240.177.251
  #default_crl_days        = 30                            # How often clients should download the CRL
+
  route 10.240.177.0 255.255.255.0
 
   
 
   
  #x509_extensions        = X509_ca                      # The x509 extensions for the root certificate
+
  tun-ipv6
  #x509_extensions        = X509_server                  # The x509 extensions for a server certificate
+
  ifconfig-ipv6 2001:db8:f0:b1::1 2001:db8:f0:b1::2
  x509_extensions        = X509_client                  # The x509 extensions for a client certificate
+
  ifconfig-ipv6-pool 2001:db8:f0:b1::4/64
 +
route-ipv6 2001:db8:f0:b1::/64
 
   
 
   
  # These options control what fields from the distinguished name to show before signing.
+
  # the actual routes are 10.240.129.0/24 and 2001:db8:f0:81::/64 but this avoids
  # They are required to make sure all fields are shown.
+
  # a bug when connecting from the private network
  name_opt                = ca_default                    # Subject Name options
+
  push route 10.240.128.0 255.255.254.0
  cert_opt                = ca_default                    # Certificate field options
+
  push route-ipv6 2001:db8:f0:80::0/63
 
   
 
   
  copy_extensions        = copy                          # Copy extensions, such as subjectAltName, from the request
+
  ifconfig-pool-persist ipp.txt
 
   
 
   
  policy                  = policy_dn
+
  # server and client ping every 10 seconds, client reconnects after 60 seconds.
 +
keepalive 10 60
 +
 
 +
user nobody
 +
group nobody
 +
persist-key
 +
persist-tun
 +
ns-cert-type client
 +
remote-cert-tls client
 +
# Allow a client to log in multiple times
 +
duplicate-cn
 +
#opt-verify
 
   
 
   
  [ X509_ca ]
+
  # Logging
  # X509v3 extensions for the root certificate
+
  #
  basicConstraints        = CA:TRUE
+
  log        /var/log/openvpn-server.log
  subjectKeyIdentifier    = hash
+
  log-append  /var/log/openvpn.log
  authorityKeyIdentifier = keyid:always,issuer:always
+
  status      /var/log/openvpn-status.log
  #subjectAltName        = email:move                    # Move the email address from the DN in the request to here
+
  verb 3
  #crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
+
 
 +
====OpenVPN client configuration====
 +
 
 +
  # First try UDP
 +
  remote 145.99.1.67 1194 udp
 
   
 
   
[ X509_server ]
+
  # If UDP fails, try TCP
  # X509v3 extensions for server certificates
+
  remote 145.99.1.67 1194 tcp-client
basicConstraints        = CA:FALSE
+
nsCertType              = server
+
subjectKeyIdentifier    = hash
+
authorityKeyIdentifier  = keyid,issuer
+
  #subjectAltName        = email:move                    # Move the email address from the DN in the request to here
+
#crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
+
 
   
 
   
  [ X509_client ]
+
  dev tun
  # X509v3 extensions for client certificates
+
  nobind
  basicConstraints        = CA:FALSE
+
  tls-client
nsCertType              = client
+
  pull
  subjectKeyIdentifier    = hash
+
authorityKeyIdentifier  = keyid,issuer
+
#subjectAltName        = email:move                    # Move the email address from the DN in the request to here
+
#crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
+
 
   
 
   
  [ policy_dn ]
+
  ca      ca.crt
  countryName            = supplied                      # required parameter, any value allowed
+
  cert     cert.crt
stateOrProvinceName     = optional
+
  key     key.key
  localityName            = optional
+
  tls-auth tls-auth.key
organizationName        = match                        # required, and must match root certificate
+
organizationalUnitName  = optional
+
commonName              = supplied                      # required parameter, any value allowed
+
emailAddress            = optional                      # email address in DN is deprecated, use subjectAltName
+
 
+
<code>ca.cnf</code> defines the distinguished name for the certificate authority. It also contains the key length (2048 is recommended nowadays, over the default of 1024), and if the key should be encrypted.
+
 
+
  # ca.cnf
+
# This configuration file is used by the 'req' command when the root certificates is created.
+
[ req ]
+
default_bits            = 2048                          # default strength of client certificates
+
default_md              = sha1
+
encrypt_key            = yes                          # "no" is equivalent to -nodes
+
prompt                  = no
+
string_mask            = utf8only
+
distinguished_name      = ca_distinguished_name        # root certificate name
+
req_extensions          = req_cert_extensions
+
# attributes              = req_attributes
+
 
   
 
   
  [ ca_distinguished_name ]
+
  tun-ipv6
  # root certificate name
+
  persist-key
  countryName            = NL
+
  persist-tun
  #stateOrProvinceName    = Utrecht
+
  ns-cert-type server
localityName            = Hometown
+
organizationName        = My Organisation
+
#organizationalUnitName  = My Department Name
+
commonName              = OpenVPN-CA
+
#emailAddress            = hostmaster@example.org      # email in DN is deprecated, use subjectAltName
+
+
[ req_cert_extensions ]
+
# nsCertType              = server
+
subjectAltName          = email:hostmaster@example.org
+
  
Traditionally, the email address was part of the distinguished name, like so:
+
==Problems==
Subject: C=NL, O=MyOrganisation, CN="www.example.org", emailAddress=hostmaster@macfreek.nl
+
  
However, RFC 3850 (section 3) specifies that nowadays:
+
This section describes some of the problems I encountered and how I solved them.
:The email address SHOULD be in the subjectAltName extension, and SHOULD NOT be in the subject distinguished name.
+
  
Like so:
+
===LZO Dependency===
Subject: C=NL, O=MyOrganisation, CN="www.example.org"
+
X509v3 extensions:
+
    X509v3 Subject Alternative Name:
+
        email:hostmaster@macfreek.nl
+
  
In the above request, the email is already specified in the request. The SubjectAltName (and all other extensions) are copied by this setting:
+
====Problem====
copy_extensions = copy
+
configure: checking for LZO Library and Header files...
 
+
checking lzo/lzo1x.h usability... no
An alternative is to specify the email address in the distinguished name (DN) in the request, but copy or move it to the subjectAltName by one of these two settings:
+
  checking lzo/lzo1x.h presence... no
subjectAltName=email:copy
+
  checking for lzo/lzo1x.h... no
subjectAltName=email:move
+
  checking lzo1x.h usability... no
 
+
  checking lzo1x.h presence... no
=== Build CA certificate ===
+
checking for lzo1x.h... no
 
+
LZO headers were not found
(roughly equivalent to <code>easy-rsa/build-ca</code> or <code>pkitool --interact --initca</code>)
+
  LZO library available from http://www.oberhumer.com/opensource/lzo/
 
+
  configure: error: Or try ./configure --disable-lzo OR ./configure --enable-lzo-stub
If your CA should be valid after the year 2038, be sure to use openssl 0.9.9 or higher.
+
 
+
First create a request with the correct name, and then self-sign a certificate and create a serial number file.
+
 
+
  cd ca
+
openssl req -new -config ca.cnf -keyout ca.key -out ca.req
+
  openssl ca -config ca-sign.cnf -extensions X509_ca -days 21700 -create_serial -selfsign \
+
    -keyfile ca.key -in ca.req -out ca.crt
+
  chmod 400 ca.key
+
  chmod 444 ca.crt
+
 
+
These actions create ca.key and ca.crt. ca.key should be kept secret. ca.crt should be distributed to all servers and clients.
+
 
+
  cp ca/ca.crt server/
+
cp ca/ca.crt client1/
+
  cp ca/ca.crt client2/
+
 
+
=== Generate Prime Numbers ===
+
 
+
(roughly equivalent to <code>build-dh</code>)
+
 
+
cd server
+
openssl dhparam -out dh2048.pem 2048
+
 
+
=== Build server certificate ===
+
 
+
(roughly equivalent to <code>build-key-server myserver</code> or <code>pkitool --interact --server myserver</code>)
+
 
+
First create a configuration for the server:
+
 
+
# server.cnf
+
# This configuration file is used by the 'req' command when the server certificate is created.
+
[ req ]
+
default_bits            = 2048
+
default_keyfile        = server.key
+
default_md              = sha1
+
encrypt_key            = no
+
prompt                  = no
+
distinguished_name      = server_distinguished_name
+
req_extensions          = req_cert_extensions
+
# attributes              = req_attributes
+
x509_extensions        = usr_cert
+
+
[ server_distinguished_name ]
+
countryName            = NL
+
#stateOrProvinceName    = Utrecht
+
localityName            = Breukelen
+
organizationName        = MacFreek
+
#organizationalUnitName  = My Department Name
+
commonName              = gateway.macfreek.nl
+
emailAddress            = hostmaster@macfreek.nl
+
+
[ req_cert_extensions ]
+
nsCertType              = server
+
+
[ v3_ca ]
+
subjectAltName          = email:hostmaster@macfreek.nl
+
  
Create the server request and private key:
+
====Solution====
  cd server
+
If lzo2 library isn't yet install, install it first:
  openssl req -new -config server.cnf -keyout server.key -out server.req
+
  sudo port install lzo2
  chmod 400 server.key
+
Check where lzo2 is installed:
 +
  % locate lzo1x.h
 +
/opt/local/include/lzo/lzo1x.h
 +
and tell openvpn where to find it:
 +
  ./configure --with-lzo-headers=/opt/local/include --with-lzo-lib=/opt/local/lib
  
Copy server.req to the CA machine, and run on the CA machine:
+
===VPN from Private Network===
cp server/server.req ca/
+
There is no benefit in a connection from the private network to the VPN server. However, such connection may sometimes occur if OpenVPN is set to automatically connect to the VPN server. This leads to a problem when the VPN connection is subsequently terminated.
cd ca
+
openssl ca -config -extensions X509_server ca.cnf -in server.req -out server.crt
+
  
Copy server.crt to the client machine.
+
====Problem====
cp ca/server.crt server/
+
After the VPN connection is terminated, while the client is still in the local network, the client loses all its connectivity.
  
You can delete server.req. Only server.crt and server.key are required on the server.
+
If the VPN tunnel is established in the above example, the routes 10.240.129.0/24 and 2001:db8:f0:81::/64 are pushed to the client. Since the client is connecting from this network, the routing table already contains these networks. The OpenVPN nevertheless tries to add them, which gives the error "File exists":
  
If you have multiple servers, repeat this step for all servers.
+
route: writing to routing socket: File exists
 +
add net 2001:db8:f0:81::: gateway tun0: File exists
 +
route: writing to routing socket: File exists
 +
add net 10.240.129.0 gateway 10.240.129.5: File exists
  
=== Build client certificate ===
+
The connection proceeds as normal. The actual routing table is not changed (traffic to these ranges still takes the direct route, not the tunnel). As soon as the VPN connection is brought down, so are these routes:
  
(roughly equivalent to <code>pkitool --interact --csr myclient</code>)
+
delete net 10.240.129.0: gateway 10.240.129.5
 +
delete_route_ipv6(2001:db8:f0:81::/64)
 +
delete net 2001:db8:f0:81::: gateway tun0
 +
delete_route_ipv6(2001:db8:f0:81::/64)
  
On the client machine
+
Despite the mismatch of the gateway, Mac OS X still removes these routes from the routing table, meaning that the OS no longer knows how to make local connections.
openssl req -new -config client1.cnf -keyout client1.key -out client1.req
+
chmod go-rw client1.key
+
  
Copy client1.req to the CA machine, and run on the CA machine:
+
====Workaround====
openssl ca -config ca.cnf -out client1.crt -in client1.req
+
There are a few possible solutions:
 +
* Simply don't connect to the OpenVPN server when the laptop is the private network. This can perhaps even be automated with a script that checks the local IP address before connecting.
 +
* Create a custom connect and disconnect script that brings the routes up and down.
 +
* Make sure the route that is pushed by the OpenVPN server is different from the local subnet. What I did is push the larger subnets:
  
Copy client1.crt to the client machine.
+
# the actual routes are 10.240.129.0/24 and 2001:db8:f0:81::/64 but pushing a larger
 +
# subnet avoids a bug when connecting from the private network.
 +
push route 10.240.128.0 255.255.254.0
 +
push route-ipv6 2001:db8:f0:80::0/63
  
You can delete client1.req. Only client1.crt and client1.key are required on the client.
+
If you are using the [http://www.thesparklabs.com/viscosity Viscosity] GUI, an alternative solution is to enable the preference "Reset network interfaces on disconnect" in the Advanced preferences tab.
  
Repeat this step for all client hosts.
+
===IPv6 Tunnel is not explicitly supported by Mac OS X===
 +
====Problem====
 +
While using the above example scripts, I get the following warnings:
  
=== Revoke a client certificate ===
+
NOTE: explicit support for IPv6 tun devices is not provided for this OS
 +
...
 +
add_route_ipv6(2001:db8:f0:b1::/64 -> 2001:db8:f0:b1::4 metric -1683000573) dev tun0
 +
route: writing to routing socket: File exists
 +
add net 2001:db8:f0:b1::: gateway tun0: File exists
  
if the file crlnumber does not exist:
+
====Solution====
echo '0000000000000001' > crlnumber
+
Despite these warnings, everything works for me.
  
openssl ca -config ca.cnf -crl_reason superseded -revoke 5FE840894254A22.crt
+
If it does not for you, I recommend to:
openssl ca -config ca.cnf -gencrl -out crl.pem
+
* Update your tun/tap driver. I got mine from [http://tuntaposx.sourceforge.net/ Tuntap OS X].
 +
* Ask on the [http://sourceforge.net/mail/?group_id=48978 OpenVPN mailing list] or [https://forums.openvpn.net/ OpenVPN Forum].
  
where the reason is one of the following
+
===BF-CFC cipher not found===
* unspecified
+
====Problem====
* keyCompromise
+
% make check
* CACompromise
+
...
* affiliationChanged
+
Sat Feb 11 22:46:37 2012 OpenVPN 2.x-master x86_64-apple-darwin11.2.0 [SSL (OpenSSL)] [LZO2] [eurephia] [MH]
* superseded
+
  [PF_INET6] [IPv6 payload 20110522-1 (2.2.0)] built on Feb 11 2012
* cessationOfOperation
+
Sat Feb 11 22:46:37 2012 Cipher 'BF-CBC' uses a mode not supported by OpenVPN in your current configuration. 
* certificateHold
+
  CBC mode is always supported, while CFB and OFB modes are supported only when using SSL/TLS authentication
 +
  and key exchange mode, and when OpenVPN has been built with ALLOW_NON_CBC_CIPHERS.
 +
Sat Feb 11 22:46:37 2012 Exiting due to fatal error
 +
FAIL: t_lpback.sh
 +
...
  
Test if you receive an error (code 23):
+
The above is strange, as BF-CBC (Blowfish) seem normally compiled in:
 +
% /usr/local/sbin/openvpn --show-ciphers
 +
...
 +
BF-CBC 128 bit default key (variable)
 +
...
  
  cat ca.crt crl.pem > revoke-test.pem
+
This seem to be bug introduced in revision [https://community.openvpn.net/openvpn/changeset/670f9dd91aed7ac435b79c0e28e49fa7c256642c 670f9dd91aed7ac435b79c0e28e49fa7c256642c]. It has been fixed in revision [http://openvpn.git.sourceforge.net/git/gitweb.cgi?p=openvpn/openvpn.git;a=commit;h=6449a149f850e9e1207233f3ca642d9342fbfbaf 6449a149f850e9e1207233f3ca642d9342fbfbaf].
openssl verify -CAfile revoke-test.pem -crl_check 5FE840894254A22.crt
+
  
=== Change password ===
+
====Solution====
 +
Revert to the latest revision of OpenVPN:
 +
cd openvpn
 +
make distclean
 +
git checkout master
  
To change the password of your private key:
+
[[Category:Network Tunnel]]
openssl rsa -des3 -in ca.key -out ca_new.key
+
mv ca_new.key ca.key
+

Latest revision as of 21:27, 3 August 2012

OpenVPN is a secure tunnel. I use it to connect to my home network.

OpenVPN uses certificates to both authenticate the client with the server, and the server with the client. The only hard part about OpenVPN is setting up the certificate infrastructure. You need a root certificate (the certificate authority, CA), and certificates for each server and each client, signed by this root certificate.

Setting up a public key infrastructure

The use of client certificates enhances the security, but makes it harder to deploy, as you need to distribute client certificates to each host. You can not use an existing public key infrastructure (PKI); you would allow anyone with a certificate of that PKI to connect to your server (the tls-remote and tls-verify options can limits the allowed clients). You either need to to set up your own certificate infrastructure, or limit OpenVPN to use password-based authentication (see the options auth-user-pass-verify, client-cert-not-required and username-as-common-name).

The easiest method it to use easy-rsa, as described in the OpenVPN How-to.

The article Create a OpenVPN Certificate Authority describes the steps in more detail, but basically gives the same result.

Certificates, when deployed correctly, are much more secure than passwords, since the secret (key) does not need to be exchanged or shared between the different hosts. However, doing so requires a security hygiene which may not be required for a small-scale deployment.

Running IPv6 through the Tunnel

OpenVPN can both use a tap or a tun interface. A tap interface tunnels Ethernet traffic, so that includes both IPv4, IPv6 and NetBIOS. A tun interface tunnels IP traffic. Version 2.3 of OpenVPN supports tunnelling bot IPv4 and IPv6 traffic through the tunnel. OpenVPN 2.2 can also tunnel IPv6 traffic, but requires a custom scripts to configure the IPv6 addresses and routing tables. At the time of writing, OpenVPN 2.3 is not yet released, so the following three options are available:

  • Use a tap interface to bridge all Ethernet traffic, including IPv4 and IPv6 traffic. The disadvantage is that is less efficient since the tunnel contains broadcast traffic not targeted to the client;
  • Wait for OpenVPN 2.3 or compile a development version;
  • Use OpenVPN 2.2 with a custom connect- and disconnect script to set the IPv6 address of the tunnel.

I choose to compile a development version.

Install Development Version

On FreeBSD:

cd /usr/ports/security/openvpn-devel
sudo make install

On Mac OS X:

sudo port install lzo2
git clone git://openvpn.git.sourceforge.net/gitroot/openvpn/openvpn.git
cd openvpn
git checkout 32ab329bc69c6292c205d4f33a4b8069341798d3
autoreconf -i -v
./configure --with-lzo-headers=/opt/local/include --with-lzo-lib=/opt/local/lib
make
make check
sudo make install

Configure IPv6 Tunnel

Imagine the following setup: VPN server example.png

A sample server and client configuration follow. These examples could probably be reduced in size; some parameters are default or some can be combined (e.g. "server" can replace "mode server", "tls-server" "ifconfig" and "ifconfig-pool").

See the OpenVPN man page and Gert Döring's IPv6 Payload Patch for information on the different configuration options.

OpenVPN server configuration

# The IP to listen on for incoming VPN connections
local 192.0.2.206
proto udp   # or tcp-server is UDP is blocked
dev tun1

# PKI Stuff
ca          ca.crt
cert        server.crt
key         server.key
dh          dh2048.pem
crl-verify  crl.pem
# Option TLS shared secret for HMAC signing for additional security
tls-auth    tls-auth.key

mode server
tls-server

topology net30
ifconfig 10.240.177.1 10.240.177.2
ifconfig-pool 10.240.177.4 10.240.177.251
route 10.240.177.0 255.255.255.0

tun-ipv6
ifconfig-ipv6 2001:db8:f0:b1::1 2001:db8:f0:b1::2
ifconfig-ipv6-pool 2001:db8:f0:b1::4/64
route-ipv6 2001:db8:f0:b1::/64

# the actual routes are 10.240.129.0/24 and 2001:db8:f0:81::/64 but this avoids 
# a bug when connecting from the private network
push route 10.240.128.0 255.255.254.0
push route-ipv6 2001:db8:f0:80::0/63

ifconfig-pool-persist ipp.txt

# server and client ping every 10 seconds, client reconnects after 60 seconds.
keepalive 10 60
user nobody
group nobody
persist-key
persist-tun
ns-cert-type client
remote-cert-tls client
# Allow a client to log in multiple times
duplicate-cn
#opt-verify

# Logging 
#
log         /var/log/openvpn-server.log
log-append  /var/log/openvpn.log
status      /var/log/openvpn-status.log
verb 3

OpenVPN client configuration

# First try UDP
remote 145.99.1.67 1194 udp

# If UDP fails, try TCP
remote 145.99.1.67 1194 tcp-client

dev tun
nobind
tls-client
pull

ca       ca.crt
cert     cert.crt
key      key.key
tls-auth tls-auth.key

tun-ipv6
persist-key
persist-tun
ns-cert-type server

Problems

This section describes some of the problems I encountered and how I solved them.

LZO Dependency

Problem

configure: checking for LZO Library and Header files...
checking lzo/lzo1x.h usability... no
checking lzo/lzo1x.h presence... no
checking for lzo/lzo1x.h... no
checking lzo1x.h usability... no
checking lzo1x.h presence... no
checking for lzo1x.h... no
LZO headers were not found
LZO library available from http://www.oberhumer.com/opensource/lzo/
configure: error: Or try ./configure --disable-lzo OR ./configure --enable-lzo-stub

Solution

If lzo2 library isn't yet install, install it first:

sudo port install lzo2

Check where lzo2 is installed:

% locate lzo1x.h
/opt/local/include/lzo/lzo1x.h

and tell openvpn where to find it:

./configure --with-lzo-headers=/opt/local/include --with-lzo-lib=/opt/local/lib

VPN from Private Network

There is no benefit in a connection from the private network to the VPN server. However, such connection may sometimes occur if OpenVPN is set to automatically connect to the VPN server. This leads to a problem when the VPN connection is subsequently terminated.

Problem

After the VPN connection is terminated, while the client is still in the local network, the client loses all its connectivity.

If the VPN tunnel is established in the above example, the routes 10.240.129.0/24 and 2001:db8:f0:81::/64 are pushed to the client. Since the client is connecting from this network, the routing table already contains these networks. The OpenVPN nevertheless tries to add them, which gives the error "File exists":

route: writing to routing socket: File exists
add net 2001:db8:f0:81::: gateway tun0: File exists
route: writing to routing socket: File exists
add net 10.240.129.0 gateway 10.240.129.5: File exists

The connection proceeds as normal. The actual routing table is not changed (traffic to these ranges still takes the direct route, not the tunnel). As soon as the VPN connection is brought down, so are these routes:

delete net 10.240.129.0: gateway 10.240.129.5
delete_route_ipv6(2001:db8:f0:81::/64) 
delete net 2001:db8:f0:81::: gateway tun0
delete_route_ipv6(2001:db8:f0:81::/64)

Despite the mismatch of the gateway, Mac OS X still removes these routes from the routing table, meaning that the OS no longer knows how to make local connections.

Workaround

There are a few possible solutions:

  • Simply don't connect to the OpenVPN server when the laptop is the private network. This can perhaps even be automated with a script that checks the local IP address before connecting.
  • Create a custom connect and disconnect script that brings the routes up and down.
  • Make sure the route that is pushed by the OpenVPN server is different from the local subnet. What I did is push the larger subnets:
# the actual routes are 10.240.129.0/24 and 2001:db8:f0:81::/64 but pushing a larger 
# subnet avoids a bug when connecting from the private network.
push route 10.240.128.0 255.255.254.0
push route-ipv6 2001:db8:f0:80::0/63

If you are using the Viscosity GUI, an alternative solution is to enable the preference "Reset network interfaces on disconnect" in the Advanced preferences tab.

IPv6 Tunnel is not explicitly supported by Mac OS X

Problem

While using the above example scripts, I get the following warnings:

NOTE: explicit support for IPv6 tun devices is not provided for this OS
...
add_route_ipv6(2001:db8:f0:b1::/64 -> 2001:db8:f0:b1::4 metric -1683000573) dev tun0
route: writing to routing socket: File exists
add net 2001:db8:f0:b1::: gateway tun0: File exists

Solution

Despite these warnings, everything works for me.

If it does not for you, I recommend to:

BF-CFC cipher not found

Problem

% make check
...
Sat Feb 11 22:46:37 2012 OpenVPN 2.x-master x86_64-apple-darwin11.2.0 [SSL (OpenSSL)] [LZO2] [eurephia] [MH] 
  [PF_INET6] [IPv6 payload 20110522-1 (2.2.0)] built on Feb 11 2012
Sat Feb 11 22:46:37 2012 Cipher 'BF-CBC' uses a mode not supported by OpenVPN in your current configuration.  
  CBC mode is always supported, while CFB and OFB modes are supported only when using SSL/TLS authentication 
  and key exchange mode, and when OpenVPN has been built with ALLOW_NON_CBC_CIPHERS.
Sat Feb 11 22:46:37 2012 Exiting due to fatal error
FAIL: t_lpback.sh
...

The above is strange, as BF-CBC (Blowfish) seem normally compiled in:

% /usr/local/sbin/openvpn --show-ciphers
...
BF-CBC 128 bit default key (variable)
...

This seem to be bug introduced in revision 670f9dd91aed7ac435b79c0e28e49fa7c256642c. It has been fixed in revision 6449a149f850e9e1207233f3ca642d9342fbfbaf.

Solution

Revert to the latest revision of OpenVPN:

cd openvpn
make distclean
git checkout master