sudo apt install easy-rsa
sudo cp /usr/share/easy-rsa /usr/local/
cd /usr/local/easy-rsa/
sudo cp vars.example vars
sudo editor vars

Edit in vars:

set_var EASYRSA_KEY_SIZE        4096
set_var EASYRSA_CERT_EXPIRE     <<time in days>>
set_var EASYRSA_CERT_RENEW      <<time in days>>
cd ..
chown <<user>> easy-rsa
./easy-rsa init-pki
./easyrsa build-ca

Password needed for new certs. Store well. Of course you can always install a new ca.crt on the openVPN server and sign all client certificates again.

In this example, all keys and signing requests are doene on the same system and then distributed to all servers and clients. Can only be done this way, if you have physical access to all systems and can use an usb-stick or similar to load keys on all machines.

./easyrsa build-server-full <keyname> nopass
./easyrsa build-client-full <keyname>
./easyrsa gen-dh

The last command will take quite a while. The build-client-full above generates client keys with password protection. If your client is unable to store that password (for example in the machines password storage), you may want to use the nopass option for client keys as well.

Certificates will be in ./pki/issued/
Private Keys will be in ./pki/private/
Each client will need the ca.crt, its own crt and its own key file.
The server will need the ca.crt, its own crt and key file and the dh.pem


(Text from community.openvpn.net/openvpn/wiki/Hardening) The --tls-auth option uses a static pre-shared key (PSK) that must be generated in advance and shared among all peers. Generate a PSK with:

openvpn --genkey --secret ta.key

And reference it in the configs as such. The 0/1 value is arbitrary and must be the opposite between peers (or omitted entirely.)

# server-example
--tls-auth ta.key 0
# client-example
--tls-auth ta.key 1


This is a bridge setup with tap device. Like this, clients will get an IP in the same range as other clients on the lan the server’s in. Works better with bonjour/zeroconf/mDNS clients on the network. Basically, the client will not see any difference whether it is connected over VPN or in the LAN.

Make sure that the local DHCP server’s IP address range and the VPN server’s IP address range don’t overlap.

The following config goes to openVPN’s config file. On debian this will be something like /etc/openvpn/server.conf. Start the server with systemctl start openvpn@<<config file name prefix>>. If the config is name server.conf, this will become systemctl start openvpn@server

 Sample OpenVPN 2.0 config file with tap interface
 needed for .local-domain names to work on vpn
 only non-default options are included
 of a many-clients <-> one-server
 Comments are preceded with '#' or ';'
port <<port vpn should be on>>
proto udp
dev tap0
ca <<location ca.crt>>
cert <<location server crt>>
key <<location server key>> # This file should be kept secret
dh .<<location diffie-hellman pem>>
tls-auth <<location tls-auth-key>> 0 # This file is secret

ifconfig-pool-persist ipp.txt
keepalive 10 120
compress lzo
user <<vpn user>>
group <<vpn group>>
status openvpn-status.log
verb 3

Network interfaces for bridging need to be set up in /etc/network/interfaces. The setup below is for a system with one ethernet card. Don’t use the start bridging or stop bridging scripts mentioned in many openvpn manuals.

# The loopback network interface
 auto lo
 iface lo inet loopback
# The primary network interface will be bridged 
# and so set to manually
 iface eth0 inet manual
 iface tap0 inet manual
# set bridge, auto brings it up at boot
 auto br0
 iface br0 inet dhcp
     bridge_ports eth0 tap0
 bridge_hw aa:38:d4:8b:12:5a 
# set a mac address for the bridge. 
# Needed to give the server a fixed IP 
# from a DHCP server in the network

Firewall setup

Use nftables on recent Debian. You don’t want all crap from your VPN going right into your LAN. So you should set up the firewall to only let packages pass that are needed for services you actually want to provide.

TUN-setup (thanks to Apple)

As Apple announced removal of kernel extensions, the tap setup above will soon cease to work with Tunnelblick. So, a new approach is needed using a tun interface. The idea is to use dnsmasq on the server to provide the hostnames otherwise provided by multicast dns.

openVPN setup

Server config file:

port 443
proto udp
dev tun
ca ./cadir/ca.crt
cert ./cadir/name.crt
key ./cadir/name.key # This file should be kept secret
dh ./cadir/dh.pem
tls-auth ta.key 0 # This file is secret
# set server address give clients addresses in the same subnet
# use the servers dnsmaq to promote fileserver.local to clients
push "dhcp-option DNS <<Server IP as seen by client, in our case>>"

ifconfig-pool-persist ipp.txt
keepalive 10 120
compress lzo
user openvpn
group openvpn
status openvpn-status.log
verb 3

For Tunnelblick on Mac client side config also needs:

dhcp-option DNS <<>>

dnsmasq setup

This is the tricky part, if you want to use your server as fileserver with samba. First we don’t want dnsmasq to forward queries to *.local domains to another domain server, so set local=/local/. We need to not only send replies for A records, but also SOA queries. That’s what the auth-* lines are here for. We also want dnsmasq to only answer to queries from openVPN clients, so we set interface=tun0, etc. if we had more than four clients, more lines would be required with further tun devices.

set /etc/dnsmasq.conf as follows:


Because of no-hosts, dnsmasq doesn’t read from /etc/hosts. I don’t want to mix in more dnsmasq domains in that file. Instead, we use /etc/hosts.dnsmasq: <<mDNSserver/Bonjourserver>>.local <<mDNSserver/Bonjourserver>>

For debugging, start dnsmasq from command line:

 sudo dnsmasq --no-daemon --log-queries