OpenVPN with password authentication on Ubuntu 15.04

OpenVPN logo

All the howtos and guides I found for setting up OpenVPN seemed to use certificate authentication of clients. In principal this is not a bad idea, but in practice such a setup is a lot more work to manage well. None of the guides I found gave any advice on how to configure and maintain CRL, which is extremely important with such an environment. In addition, all the guides advocated creating client certificates on the server, finding some way to transport a none-trivial amount of sensitive data from the server to the client (none-trivial in this case means more the can be read over the phone or easily re-typed). Both of these concerns greatly reduces the security of such a system, and in my opinion makes a username/password base authentication system a more secure option. Mind you, it’s possible to do the PKI based authentication system securely; it’s just a lot more work, and not really well documented in the guides and howtos available (or at least the once easily discoverable via Google).

Be advised: Before you take my advice on anything, you should known that I have a grand total of 3 days experience with OpenVPN. I do however have more then 20 years experience with Linux and TCP/IP networking in general. Feel free to use the comment section or to email me at if you find any mistakes or have suggestions for a better setup.

The goal of this is to set up OpenVPN with username/password authentication, in a way where every connected user can use the same OpenVPN profile-file (.ovpn-file). We will also set up OpenVPN to listen on two alternative ports, to maximise the odds of managing to connect event in the presence of strict and/or stupid firewalls. In addition to the default UDP port 1194, OpenVPN will also accept connections on UDP port 53 and TCP port 443. Both of these are extremely difficult to firewall without “collateral damage”. As a nice bonus, when using UDP port 53, you may event be able to connect from an open WiFi network “protected” behind a captive portal.

Install OpenVPN and Easy-RSA

apt-get install openvpn easy-rsa

Remove the default configuration

rm -Rf /etc/openvpn/*

Create user, group and folders

mkdir /etc/openvpn/{bin,etc,tmp,user}
groupadd openvpn
useradd -d / -g openvpn openvpn
chmod 755 /etc/openvpn/{bin,etc,user}
chown openvpn:openvpn /etc/openvpn/tmp
chmod 700 /etc/openvpn/tmp

Setup Easy-RSA environments

source /usr/share/easy-rsa/vars
export EASY_RSA="/usr/share/easy-rsa"
export KEY_CONFIG="$($EASY_RSA/whichopensslcnf $EASY_RSA)"
export KEY_DIR="/etc/openvpn/etc"
/usr/share/easy-rsa/pkitool --initca
/usr/share/easy-rsa/pkitool --server server
openssl dhparam -out /etc/openvpn/etc/dh2048.pem 2048

Create OpenVPN configuration files

We create a common configuration file for all 3 protocol/port combination, and include this from the specific configurations. I’ve set this up with 3 private network (one for each protocol/port combo): for UDP port 1194, for UDP port 53 and for TCP port 443. We will set up NAT (or what iptables calls MASQUERADE) for these ranges below. Adjust the value for local to the IP of your OpenVPN server.

cat > /etc/openvpn/etc/server-common.conf << EOF
dev tun
keepalive 10 120
ca /etc/openvpn/etc/ca.crt
cert /etc/openvpn/etc/server.crt
key /etc/openvpn/etc/server.key
dh /etc/openvpn/etc/dh2048.pem
topology subnet
client-config-dir /etc/openvpn/user
tmp-dir /etc/openvpn/tmp
user openvpn
group openvpn
verb 3
mute 20
script-security 2
client-connect /etc/openvpn/bin/event
client-disconnect /etc/openvpn/bin/event
auth-user-pass-verify /etc/openvpn/bin/auth via-file
cat > /etc/openvpn/server-1194-udp.conf << EOF
config /etc/openvpn/etc/server-common.conf
port 1194
proto udp
cat > /etc/openvpn/server-53-udp.conf << EOF
config /etc/openvpn/etc/server-common.conf
port 53
proto udp
cat > /etc/openvpn/server-443-tcp.conf << EOF
config /etc/openvpn/etc/server-common.conf
port 443
proto tcp

This script is used for authenticating users from /etc/openvpn/etc/<USERNAME>.password. Save as /etc/openvpn/bin/auth. This may look unsafe, but the OpenVPN man-page states the following concerning the username field:

To protect against a client passing a maliciously formed username or password string, the username string must consist only of these characters: alphanumeric, underbar (‘_’), +, dash (‘-’), dot (‘.’), or at (‘@’)

[ -f "$1" ] || exit 1
mapfile -t args < "$1"
[ "${user}" != "" ] || exit 1
[ "${pass}" != "" ] || exit 1
[ -f "${file}" ] || exit 1
[ "${save}" != "" ] || exit 1
[ "${save}" = "${pass}" ] && exit 0
exit 1
chmod +x /etc/openvpn/bin/auth

This script will log connect and disconnect events. You can extend it to trigger other events as needed. Save as /etc/openvpn/bin/event.

echo "$(date): ${common_name} ${script_type} ${trusted_ip}" >> \
chmod +x /etc/openvpn/bin/event

We overwrite /etc/default/openvpn to auto-start all 3 listeners at boot, reload the configuration and then manually start the 3 listeners.

echo 'AUTOSTART="server-1194-udp server-53-udp server-443-tcp"' \
 > /etc/default/openvpn
systemctl daemon-reload
service openvpn@server-1194-udp start
service openvpn@server-53-udp start
service openvpn@server-443-tcp start

Setting up NAT (MASQUERADE) for the three private networks

Run the following commands and also add them to your /etc/rc.local or equivalent to have them run at boot.

iptables -I FORWARD --match state \
iptables -A FORWARD -p tcp \
 -s  -j ACCEPT
iptables -A FORWARD -p tcp \
 -s  -j ACCEPT
iptables -A FORWARD -p tcp \
 -s  -j ACCEPT
iptables -t nat -A POSTROUTING \
 -s -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING \
 -s -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING \
 -s -o eth0 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward

Create common client profile

This profile (.ovpn-file) can be used by all clients, and contains no sensitive information. As such we make it available for download via HTTPS. This makes it easier to configure clients. /home/bbj/www/ is the webroot for, substitute for the relevant path on your own system (or copy the file to your web server if it’s on a different machine).

cat > /home/bbj/www/ << EOF
dev tun
remote 1194 udp
remote 53 udp
remote 443 tcp
$(cat /etc/openvpn/etc/ca.crt)
setenv CLIENT_CERT 0
ns-cert-type server
verb 3

If the profile is sent with the proper content-type, many platforms will automatically offer to load it into the OpenVPN client.

echo "AddType application/x-openvpn-profile .ovpn" \
 >> /home/bbj/www/

Setting up a new user

These steps are all that is required to set up av new user. To remove the user, just undo (delete the two files). Individual client options can be set in the users individual config file. Refer to the OpenVPN man-page for valid options for per-user configuration. This sets up an account with username “username” and a good random password. Execute cat /etc/openvpn/etc/username.password to see the password.

cat > /etc/openvpn/user/username << EOF
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DOMAIN"
push "dhcp-option DNS"
openssl rand -base64 18 > /etc/openvpn/etc/username.password
chown openvpn:openvpn /etc/openvpn/etc/username.pasword
chmod 400 /etc/openvpn/etc/username.password

Seems to be working quite well

iOS screenshot