[ OpenBSD 4.3, OpenVPN 2.0.9 ]
Verified on OpenBSD 4.4 amd64 with OpenVPN 2.1 rc7; OpenBSD 4.6 i386 with OpenVPN 2.1rc22
Table of Contents
Install from Port Packages.
The base server / client software are in a single package that can simply be installed from the OpenBSD ports system. The real challenge is configuring the program to execute as we expect.
Note:
If the server is to route traffic from the OpenVPN Private Network through other networks configured on the machine, remember to enable IP Forwarding (routing) on the physical server.
In the /etc/sysctl.conf file:
net.inet.ip.forwarding=1
On the command-line
/sbin/sysctl net.inet.ip.forwarding=1
To retain consistency with how other OpenBSD applications are installed, and configured we pre-select a standard configuration for our installation as per the below structure.
The following discussion configures files in the following structure.
Directory Location | Description |
---|---|
/usr/local/sbin | Location for openvpn executable. This is the standard install directory for the OpenBSD package. |
/etc/openvpn | Configuration files, a clean install should contain few files: server.conf, openvpn.cnf, ipp.txt |
/etc/openvpn/keys | generated certificates, keys, and index information |
/usr/local/bin/openvpn | scripts used during the installation process. We are modifying the scripts and keeping them in this location ensures we can rebuild the same configuration during software updates etc. |
We’ll first create the appropriate directories and files.
$ sudo mkdir -p /usr/local/bin/openvpn $ sudo mkdir -p /var/log/openvpn $ sudo touch /var/log/openvpn/status.log $ sudo touch /var/log/openvpn/access.log $ sudo touch /var/log/openvpn/ipp.txt
Copy the sample distribution configuration files to a staging area where we can modify the files, but leave our configuration directory relatively clean.
Note: Post OpenBSD 5.x, easy-rsa is installed as a separate package into /usr/local/share/examples/easy-rsa
$ cd /usr/local/bin/openvpn $ sudo cp /usr/local/share/examples/openvpn/easy-rsa/2.0/* . $ sudo mv openssl.cnf /etc/openvpn
The default configurations from the openvpn distribution is not ‘sane’ for OpenBSD, and the following instructions are to put the deployment into a ‘sane’ configuration
Fix-up errors with the install files and default data into our working files.
# diff -u /usr/local/share/examples/openvpn/easy-rsa/2.0/vars \ /usr/local/bin/openvpn/vars
--- /usr/local/share/examples/openvpn/easy-rsa/2.0/vars Sun Mar 16 09:22:52 2008 +++ /usr/local/bin/openvpn/vars Thu Aug 21 12:55:48 2008 @@ -26,7 +26,7 @@ # This variable should point to # the openssl.cnf file included # with easy-rsa. -export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA` +export KEY_CONFIG=/etc/openvpn/openssl.cnf # Edit this variable to point to # your soon-to-be-created key @@ -36,7 +36,7 @@ # a rm -rf on this directory # so make sure you define # it correctly! -export KEY_DIR="$EASY_RSA/keys" +export KEY_DIR="/etc/openvpn/keys" # Issue rm -rf warning echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR @@ -46,7 +46,7 @@ # down TLS negotiation performance # as well as the one-time DH parms # generation process. -export KEY_SIZE=1024 +export KEY_SIZE=4096 # In how many days should the root CA key expire? export CA_EXPIRE=3650 @@ -57,8 +57,9 @@ # These are the default values for fields # which will be placed in the certificate. # Don't leave any of these fields blank. -export KEY_COUNTRY="US" -export KEY_PROVINCE="CA" -export KEY_CITY="SanFrancisco" -export KEY_ORG="Fort-Funston" -export KEY_EMAIL="me@myhost.mydomain" +export KEY_COUNTRY="AU" +export KEY_PROVINCE="NSW" +export KEY_CITY="Sydney" +export KEY_ORG="My Company Pty Ltd" +export KEY_EMAIL="vpn_admin@example.com"
We’re paranoid, so let’s just set the basis for all encryption to be 4096, even if it does take a long time to generate keys.
The following process builds the basic set of keys for operating the VPN. Keys are built (and cleared,) for this configuration, in the /etc/openvpn/keys directory. For those who can’t wait and don’t mind destroying their configuration, the annotated steps are:
_ $ sudo su a # cd /usr/local/bin/openvpn - # . ./vars - # ./clean-all b # ./build-ca c # ./build-key-server EXAMPLE.COM d # /usr/local/sbin/openvpn --genkey --secret $KEY_DIR/ta.key e # ./build-key client_username
Depending on the shell of choice, the above ‘vars’ configuration may not work correctly. ‘/usr/local/bin/openvpn/vars’ exports variables used in the build scripts. EXAMPLE.COM is the variable we added to the ./vars script above.
“Install” the ’environment’ used as the default for the whole process by sourcing the ./vars script. As the subsequent scripts need to use these environment variables, and the process needs to write files in the root only access /etc/openvpn/keys directory, we can either run the scripts from root or from sudo -E (but with this you would need to ensure that your sudoers configuration is compatible) so for simplicity we choose to install as root. When correctness prevails we’ll update this document.
$ cd /usr/local/bin/openvpn $ sudo su # . ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/keys
Following instructions, the next thing is to clear out the destination directory for our keys and certificates
# ./clean-all
Build our local Certificate Authority signing key for our domain.
# ./build-ca
Generating a 4096 bit RSA private key ............................................++ .............................................................................. .............................................................++ writing new private key to 'ca.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [NSW]: Locality Name (eg, city) [City]: Organization Name (eg, company) [Organisation Name]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) [Organisation Name CA]: Email Address [vpnmeister@example.com]:
# ls $KEY_DIR
ca.crt ca.key index.txt serial
Build and sign a certificate for our server EXAMPLE.COM. We will sign the new certificate (EXAMPLE.COM.key) using the certificate authority created in the above step ca.
# ./build-key-server EXAMPLE.COM
Generating a 4096 bit RSA private key ........................................................................................................................ .........................................................................................................................++ ....++ writing new private key to 'example.com.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [NSW]: Locality Name (eg, city) [City]: Organization Name (eg, company) [Organisation Name]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) [example.com]: Email Address [vpnmeister@example.com]: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: Using configuration from /etc/openvpn/openssl.cnf Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows countryName :PRINTABLE:'AU' stateOrProvinceName :PRINTABLE:'NSW' localityName :PRINTABLE:'City' organizationName :PRINTABLE:'Organisation Name' commonName :PRINTABLE:'example.com' emailAddress :IA5STRING:'vpnmeister@example.com' Certificate is to be certified until Jun 11 01:55:01 2018 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
# ls $KEY_DIR
01.pem ca.key example.com.csr index.txt index.txt.old serial.old ca.crt example.com.crt example.com.key index.txt.attr serial
If we’ve set up clients with their own (reverse) DNS entry, then it may be appropriate to specify clients with these domain names. Another example of where the above naming convention will be useful is when you have clients accessing from multiple locations to multiple (separate) OpenVPN servers.
To help with security he final server key to be built is the tls-auth key.
$ sudo /usr/local/sbin/openvpn --genkey --secret $KEY_DIR/ta.key $ sudo cat $KEY_DIR/ta.key
# # 2048 bit OpenVPN static key # -----BEGIN OpenVPN Static key V1----- 8ca2fef6b3453a8b08b295b4b636736d 27409ddb496ea42f2491a9b0a877679f 94c94394513deb6af5d0e39c418fcc47 fd78a401ffbcbb5c40d367dcc165bd52 8b5c0dc97fca762921afbdf6b6bd459b 0c0c0dd272d52de00f8905dd9f36b5ad c75f310ad55ab034f7bebc9f097f10a6 abd05323dcaf6204e52024f327c3cd59 9dc83889d8596cfbf1d7790fb27189ab 25f965d5ba0f9fc367b9f12ed418dbaa b9e44bc97d06a7712d6402f9868e48ac 3122e26d0700d1b71d4b51ee7e739e7b 9bd79fecc83e53a4e4faacda27fe7884 cbcf93a4327ea20c5833719d288658da cd3e4ff253484a6825c2ed4de8b5e21e 1027887af54343f9919321648f9e072b -----END OpenVPN Static key V1-----
That covers most of our server certificate requirements and we can continue with creating keys/certificates for our long list of clients.
# ./build-key client_username1
Generating a 4096 bit RSA private key .......................................................................................................................... .......................................................................................................................... .............................................................................++ ................++ writing new private key to 'client_username1.key' ----- You are about to be asked to enter information that will be i:qncorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [NSW]: Locality Name (eg, city) [City]: Organization Name (eg, company) [Organisation Name]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) [client_username1]: Email Address [vpnmeister@example.com]: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: Using configuration from /etc/openvpn/openssl.cnf DEBUG[load_index]: unique_subject = "yes" Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows countryName :PRINTABLE:'AU' stateOrProvinceName :PRINTABLE:'NSW' localityName :PRINTABLE:'City' organizationName :PRINTABLE:'Organisation Name' commonName :PRINTABLE:'client_username1' emailAddress :IA5STRING:'vpnmeister@example.com' Certificate is to be certified until Jun 11 02:08:28 2018 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
# ls $KEY_DIR
01.pem client_username1.key example.com.csr index.txt.attr.old 02.pem ca.crt example.com.key index.txt.old client_username1.crt ca.key index.txt serial client_username1.csr example.com.crt index.txt.attr serial.old
To create more client keys, run the ./build-key script again
# ./build-key client_username2
There’s a reason why this is the last key construction for the setup. Warning: The above keys, at KEY_SIZE = 4096, takes sometime to build. Building the DH key at 4096 takes a really long time to build.
# ./build-dh
The above should output a file $KEY_DIR/dh4096.pem
For our sample configuration, we are taking private IPs from a known working configuration, and it is a matter of adapting to the situation that fits your new installation.
Remember that EXAMPLE.COM is a placeholder for whatever your preferred domain name is going to be.
# File: /etc/openvpn/server.conf port 1194 proto udp dev tun0 ca /etc/openvpn/keys/ca.crt cert /etc/openvpn/keys/EXAMPLE.COM.crt key /etc/openvpn/keys/EXAMPLE.COM.key dh /etc/openvpn/keys/dh4096.pem tls-auth /etc/openvpn/keys/ta.key 0 server 10.8.0.0 255.255.255.0 push "route 10.0.2.0 255.255.255.0" push "dhcp-option DNS 10.0.2.1" client-to-client keepalive 10 120 comp-lzo user _openvpn group _openvpn persist-key persist-tun ifconfig-pool-persist /var/log/openvpn/ipp.txt status /var/log/openvpn/status.log log-append /var/log/openvpn/access.log verb 3
Further explanations of our option selection with server.conf
#server $VPN_IP $VPN_IP_MASK server 10.8.0.0 255.255.255.0
server 10.8.0.0 255.255.255.0 becomes the IP range for the virtual network. Clients connecting to our OpenVPN will be given an IP address from within this range, and the server itself will get an address from this range.
push "route 10.0.2.0 255.255.255.0" push "dhcp-option DNS 10.0.2.1"
push “route 10.0.2.0 255.255.255.0” instructs the client to route traffic bound for the stated IP Range (10.0.2.0/24), through OpenVPN.
To redirect ALL traffic through the VPN, use: push “redirect-gateway def1”. Refer OpenVPN HOWTO for scenarios and issues relevant to redirect such as all machines being on the same segment, but some being on less secure transports (such as wireless), or all clients are connected wirelessly.
Thinking through the route table facilities above, we have the Virtual LAN working on the 10.8.0.0/24 ip range, giving access to remote clients to a local 10.0.2.0/24 range. We are adding specific routes to the client to the Virtual LAN and to specific services accessible to the OpenVPN Server. We can restrict/open client access to server-side services, by controlling the routing (finegrained controls require firewall rules.)
Key aspects of our example server configuration file includes:
If the client is a server, acting as a gateway for subnets gatewayed through it, then the configuration changes to require the use of:
client-config-dir /etc/openvpn/ccd
Where /etc/openvpn/ccd is the client configuration directory. When a server/gateway client connects to the OpenVPN server, the daemon will check this directory for a file which matches the common name of the connecting client. If a matching file is found, it will be read and processed for additional configuration file directives to be applied to the named client.
Create a file hostname.example.org in the /etc/openvpn/ccd directory to contain the routing information for the external gateway, for example:
iroute 10.9.10.0 255.255.255.0
This will tell the OpenVPN server that the 10.9.10.0/24 subnet should be routed to client hostname.example.org.
Next, add the following line to the main server config file server.conf
route 10.9.10.0 255.255.255.0
dev tun0 tells OpenVPN to use the tun0 interface, and sets the session to be routing as opposed to bridging. We need to create the tun0 tunnel interface.
OpenVPN re-creates the tun(4) interface at startup; compatibility with PF is improved by starting it from hostname.if(5). For example:
# cat << EOF > /etc/hostname.tun0 up !/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf EOF
The above updated documentation improves compatibility with PF.
To start the server, restart network services with
# sh /etc/netstart
Visit the files: /var/log/openvpn/[status|access].log
The following is a sample configuration for OpenVPN.
ext_if = "em0" lan_if = "em1" vpn_if = "tun0" vpn_port = "1194" table <vpn_network> { 10.8.0.0/24 } icmp_types = "{ echoreq, echorep }" set skip on lo scrub in nat on $ext_if from $lan_if:network to any -> ($ext_if) # Default rules block log # Traffic allowed from any direction pass in inet proto icmp icmp-type $icmp_types keep state pass in inet proto tcp from any to any port ssh # External Interface pass in on $ext_if inet proto udp from any to $ext_if port $vpn_port keep state pass out on $ext_if keep state # Internal Interface pass out on $lan_if from <vpn_network> to $lan_if:network keep state # VPN IF pass in on $vpn_if inet proto { tcp, udp } \ from <vpn_network> to $lan_if:network keep state pass out on $vpn_if from $lan_if:network keep state
For a bridging example, see BlogFish post