From charlesreid1

No edit summary
 
(63 intermediate revisions by the same user not shown)
Line 1: Line 1:
This page gives an overview of how to install and set up an OpenVPN server on Ubuntu 18.04.
=Server Prep=
=Server Prep=
Note that you should run these commands as root.


==Install OpenVPN==
==Install OpenVPN==
Line 6: Line 10:


<pre>
<pre>
sudo apt update
apt update
sudo apt -y install openvpn
apt -y install openvpn
</pre>
</pre>


Line 27: Line 31:


<pre>
<pre>
export KEY_DIR="/opt/easy-rsa/keys"
export KEY_COUNTRY="US"
export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_PROVINCE="CA"
Line 62: Line 67:
sed -i 's/--interact//g' /opt/easy-rsa/build-ca
sed -i 's/--interact//g' /opt/easy-rsa/build-ca
. /opt/easy-rsa/vars;. /opt/easy-rsa/local_vars;/opt/easy-rsa/build-ca
. /opt/easy-rsa/vars;. /opt/easy-rsa/local_vars;/opt/easy-rsa/build-ca
</pre>
NOTE: This will print an error like the one below, but this is not actually a problem for generating the CA.
<pre>
Can't load /root/.rnd into RNG
140672703574464:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
</pre>
</pre>


Line 94: Line 106:
==Configure VPN Server==
==Configure VPN Server==


Here we configure the VPN so that VPN IP addresses are in the CIDR block <code>10.10.10.0/24</code>.
Here we configure the VPN so that VPN IP addresses are in the CIDR block <code>192.168.20.0/24</code>.
 
Start by making a directory for the server configuration:
 
<pre>
mkdir -p /opt/openvpn
</pre>
 
Now put the server configuration in <code>/opt/optenvpn</code>, then copy to <code>/etc/openvpn</code>.


<code>/etc/openvpn/server.conf</code>
<code>/opt/openvpn/server.conf</code>


<pre>
<pre>
port 1194
port 1194
proto udp
proto udp
dev tun
# This is LAN20 so make it dev20
server 10.101.0.0 255.255.255.0
dev tun20
server 192.168.20.0 255.255.255.0
# enable this line to tunnel all client traffic thru vpn
# enable this line to tunnel all client traffic thru vpn
#push "redirect-gateway def1"
#push "redirect-gateway def1"
# use dnsmasq as a dns server
# use dnsmasq as a dns server
push "dhcp-option DNS 10.10.10.1"
#push "dhcp-option DNS 192.168.20.1"
 
ca /opt/easy-rsa/keys/ca.crt
cert /opt/easy-rsa/keys/server.crt
key /opt/easy-rsa/keys/server.key
dh /opt/easy-rsa/keys/dh2048.pem
dh /opt/easy-rsa/keys/dh2048.pem
 
# tls auth: act as server
tls-auth statictlssecret.key 0
key-direction 0
 
# use pam for auth
# use pam for auth
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
# custom client configurations
# custom client configurations
client-config-dir /etc/openvpn/clients
client-config-dir /etc/openvpn/clients
</pre>
Copy the finalized version to the appropriate location:
<pre>
cp /opt/openvpn/server.conf /etc/openvpn/server.conf
chmod 0644 /etc/openvpn/server.conf
chown root:root /etc/openvpn/server.conf
</pre>
Now you have an easy backup of your original OpenVPN server config file.
Create the custom client configuration directory specified at the end of the file:
<pre>
mkdir -p /etc/openvpn/clients
</pre>
==Create OpenVPN Server Profile==
Finally, assemble the server config and keys into an OpenVPN profile file (.ovpn) so we can start this up with our startup service:
<pre>
(cat /opt/openvpn/server.conf
echo '<key>'
cat /opt/easy-rsa/keys/server.key
echo '</key>'
echo '<tls-auth>'
cat /opt/easy-rsa/keys/statictlssecret.key
echo '</tls-auth>'
echo '<cert>'
cat /opt/easy-rsa/keys/server.crt
echo '</cert>'
echo '<ca>'
cat /opt/easy-rsa/keys/ca.crt
echo '</ca>'
) > /etc/openvpn/server.ovpn
</pre>
</pre>


Line 126: Line 179:


<pre>
<pre>
sed -i 's|^ExecStart=.*|& --log-append /var/log/openvpn.%i.log|' /lib/systemd/system/openvpn@.service
grep "log-append" /lib/systemd/system/openvpn@.service &> /dev/null || sed -i 's|^ExecStart=.*|& --log-append /var/log/openvpn.%i.log|' /lib/systemd/system/openvpn@.service
</pre>
 
Run this command to force the OpenVPN startup service to use an .ovpn file instead of a .conf file:
 
<pre>
sed -i 's|\.conf|.ovpn|' /lib/systemd/system/openvpn@.service
</pre>
</pre>


Line 133: Line 192:
Quick side note to explain <code>/lib/systemd/system/openvpn@.service</code>:
Quick side note to explain <code>/lib/systemd/system/openvpn@.service</code>:


This is a TEMPLATED startup service that allows you to run multiple startup services for multiple instances of openvpn for multiple VPNs. If you run <code>service openvpn@myvpn start</code>, it will start OpenVPN with the configuration file <code>myvpn.conf</code>.
This is a TEMPLATED startup service that allows you to run multiple startup services for multiple instances of openvpn for multiple VPNs. If you run <code>service openvpn@myvpn start</code>, it will start OpenVPN with (by default) the configuration file <code>myvpn.conf</code>. With the modifications above, it will start OpenVPN with the OpenVPN profile <code>myvpn.ovpn</code>.
 
==Configure iptables==
 
Just kidding, iptables doesn't require any configuration!
 
This VPN tunnel will not share any traffic with other networks on this server, so no need for packets on the OpenVPN tunnel to go to other devices.
 
===Side Note - DNS===
 
If you want to provide DNS to VPN clients, you can push a DNS rule to the VPN. This is useful even if clients aren't tunneling all their traffic through the VPN, because it replaces the DNS they are using.
 
You also need to use iptabels to allow packet traffic from the wifi network to the local DNS server(s). You should probably limit this to port 53 only, unless you're tunneling all traffic through the VPN.
 
You should also reconfigure the DNS server, dnsmasq, to set the upstream servers the VPN should use (or just leave it and keep using whatever nameservers are already defined in the dnsmasq config file).
 
You should also modify the OpenVPN config file - either the server or the client - to use the VPN gateway for DNS.


===Enable and Start VPN Server Service===
To push that rule to clients, put this line in the OpenVPN server configuration file:


Given that our OpenVPN server config file is in <code>server.conf</code> in <code>/etc/openvpn</code>, we can start an OpenVPN service with this config file like this:
<code>/opt/openvpn/server.conf</code>


<pre>
<pre>
systemctl enable openvpn@server.service
push "dhcp-option DNS 192.168.20.1"
systemctl start openvpn@server.service
</pre>
</pre>


==Configure iptables==
and go through the steps listed above to re-create the OpenVPN server profile.
 
The way we plan on doing this, we're just going to use the VPN tunnel to be able to reach bespin. There is no need to share networks.
 
But what DNS server will the new VPN use? Do we need a new DHCP server too? Can we handle DNS for tun1 too? Do we need to set up another dnsmasq instance?


==Configure PAM==
==Configure PAM==
Line 170: Line 240:
auth required pam_unix.so
auth required pam_unix.so
</pre>
</pre>
<code>pam_unix</code> uses Unix system accounts as the username/password to authenticate users.


===Adding MFA to VPN Access===
===Adding MFA to VPN Access===
Line 181: Line 253:
Now modify the contents of <code>/etc/pam.d/openvpn</code> to replace the Unix login with Google Authenticator as a method of authentication:
Now modify the contents of <code>/etc/pam.d/openvpn</code> to replace the Unix login with Google Authenticator as a method of authentication:


<code>/etc/pam.d/openvpn</code>
<code>/etc/pam.d/openvpn</code> (with Google Authenticator method only)
 
<pre>
auth required /lib/x86_64-linux-gnu/security/pam_google_authenticator.so secret=/etc/openvpn/google-authenticator/${USER} user=gauth
</pre>
 
Or, add both authentication methods, and make them both optional (meaning, one or the other). Also add the try first option to google authenticator to re-use the password entered by the user if it does not work as their Unix username/password.
 
<code>/etc/pam.d/openvpn</code> (with both Unix login and Google Authenticator methods)
 
<pre>
auth optional pam_unix.so
auth optional /lib/x86_64-linux-gnu/security/pam_google_authenticator.so secret=/etc/openvpn/google-authenticator/${USER} user=gauth try_first_pass
</pre>
 
Set permissions:


<pre>
<pre>
auth required /lib/x86_64-linux-gnu/security/pam_google_authenticator.so secret=/etc/openvpn/google-authenticator/${USER} user=gauth try_first_pass
chmod 0644 /etc/pam.d/openvpn
chown root:root /etc/pam.d/openvpn
</pre>
</pre>


Line 211: Line 299:


<pre>
<pre>
sudo systemctl enable openvpn@server
systemctl enable openvpn@server
sudo systemctl start openvpn@server
systemctl start openvpn@server
</pre>
</pre>


Line 218: Line 306:


<pre>
<pre>
sudo tail -f /var/log/syslog
tail -f /var/log/syslog
</pre>
</pre>


Verify the <code>tun0</code> interface came up
Verify the <code>tun0</code> interface came up and shows up with the ifconfig command:
 
<pre>
ifconfig
</pre>


=Client Prep=
=Client Prep=
Line 232: Line 324:
sed -i 's/--interact//g' /opt/easy-rsa/build-key
sed -i 's/--interact//g' /opt/easy-rsa/build-key
sed -i 's+export EASY_RSA=.*+export EASY_RSA="/opt/easy-rsa"+' /opt/easy-rsa/build-key
sed -i 's+export EASY_RSA=.*+export EASY_RSA="/opt/easy-rsa"+' /opt/easy-rsa/build-key
</pre>
Add a custom client config directory:
<pre>
mkdir -p /etc/openvpn/clients
</pre>
</pre>


==Configure OpenVPN Client==
==Configure OpenVPN Client==


Configure the OopenVPN client by creating a client config file:
Configure the OpenVPN client by creating a client config file. We create this in <code>/opt/openvpn</code>, and use it to create the OpenVPN profile in <code>/etc/openvpn</code> a few steps.


<code>/opt/easy-rsa/openvpn_client.conf</code>
<code>/opt/openvpn/openvpn_client.conf</code>


<pre>
<pre>
Line 256: Line 342:
persist-tun
persist-tun
remote-cert-tls server
remote-cert-tls server
ca ca.crt
# tls auth: act as client
cert clientuser.crt
key-direction 1
key clientuser.key
tls-auth statictlssecret.key 1
auth-user-pass
auth-user-pass
</pre>
Now copy to /etc:
<pre>
cp /opt/openvpn/openvpn_client.conf /etc/openvpn/.
</pre>
</pre>


Line 292: Line 382:
cat /opt/easy-rsa/keys/${1}.key
cat /opt/easy-rsa/keys/${1}.key
echo '</key>'
echo '</key>'
echo '<tls-auth>'
cat /opt/easy-rsa/keys/statictlssecret.key
echo '</tls-auth>'
echo '<cert>'
echo '<cert>'
cat /opt/easy-rsa/keys/${1}.crt
cat /opt/easy-rsa/keys/${1}.crt
Line 323: Line 416:
/opt/easy-rsa/keys/${1}.crt
/opt/easy-rsa/keys/${1}.crt
/opt/easy-rsa/keys/openvpn_${1}.ovpn
/opt/easy-rsa/keys/openvpn_${1}.ovpn
/opt/easy-rsa/README
/opt/easy-rsa/keys/${1}_password.txt"
/opt/easy-rsa/keys/${1}_password.txt"
cat /opt/easy-rsa/README_LOGIN > /opt/easy-rsa/README
for fname in ${FILES}; do
for fname in ${FILES}; do
     test -e ${fname} || { echo "File ${fname} not found!"; exit 1; }
     test -e ${fname} || { echo "File ${fname} not found!"; exit 1; }
Line 331: Line 422:
cd /opt/easy-rsa/keys
cd /opt/easy-rsa/keys
zip -j ${1}.zip $FILES
zip -j ${1}.zip $FILES
rm -f /opt/easy-rsa/README
</pre>
</pre>


Line 358: Line 448:
/opt/easy-rsa/keys/${1}.key
/opt/easy-rsa/keys/${1}.key
/opt/easy-rsa/keys/${1}.crt
/opt/easy-rsa/keys/${1}.crt
/opt/easy-rsa/keys/openvpn_${1}.ovpn
/opt/easy-rsa/keys/openvpn_${1}.ovpn"
/opt/easy-rsa/README"
if [ -z "${MFA_DISABLED}" ]; then
if [ -z "${MFA_DISABLED}" ]; then
     FILES="${FILES}
     FILES="${FILES}
     /etc/openvpn/google-authenticator/${1}_qr.png
     /etc/openvpn/google-authenticator/${1}_qr.png
     /etc/openvpn/google-authenticator/${1}_backup_codes.txt"
     /etc/openvpn/google-authenticator/${1}_backup_codes.txt"
    cat /opt/easy-rsa/README_MFA > /opt/easy-rsa/README
else
else
     FILES="${FILES}
     FILES="${FILES}
     /opt/easy-rsa/keys/${1}_password.txt"
     /opt/easy-rsa/keys/${1}_password.txt"
    cat /opt/easy-rsa/README_LOGIN > /opt/easy-rsa/README
fi
fi
for fname in ${FILES}; do
for fname in ${FILES}; do
Line 375: Line 462:
cd /opt/easy-rsa/keys
cd /opt/easy-rsa/keys
zip -j ${1}.zip $FILES
zip -j ${1}.zip $FILES
rm -f /opt/easy-rsa/README
</pre>
</pre>


Line 476: Line 562:
. /opt/easy-rsa/vars
. /opt/easy-rsa/vars
. /opt/easy-rsa/local_vars
. /opt/easy-rsa/local_vars
/opt/easy-rsa/revoke-full ${1}
# Delete client openvpn files
# Delete client openvpn files
rm -f /opt/easy-rsa/keys/${1}.zip
rm -f /opt/easy-rsa/keys/${1}.zip
Line 482: Line 567:
rm -f /opt/easy-rsa/keys/openvpn_${1}.ovpn
rm -f /opt/easy-rsa/keys/openvpn_${1}.ovpn
# Delete unix user
# Delete unix user
deluser --remove-home ${1}
deluser --remove-home ${1} || echo "User does not exist!"
# Revoke client
/opt/easy-rsa/revoke-full /opt/easy-rsa/keys/${1}
echo "User account ${1} has been deleted"
echo "User account ${1} has been deleted"
</pre>
</pre>
Line 506: Line 593:
. /opt/easy-rsa/vars
. /opt/easy-rsa/vars
. /opt/easy-rsa/local_vars
. /opt/easy-rsa/local_vars
/opt/easy-rsa/revoke-full ${1}
# Delete client openvpn files
# Delete client openvpn files
rm -f /opt/easy-rsa/keys/${1}.zip
rm -f /opt/easy-rsa/keys/${1}.zip
Line 517: Line 603:
rm -f /etc/openvpn/google-authenticator/${1}_backup_codes.txt
rm -f /etc/openvpn/google-authenticator/${1}_backup_codes.txt
# Delete unix user
# Delete unix user
deluser --remove-home ${1}
deluser --remove-home ${1} || echo "User does not exist!"
# Revoke cert
/opt/easy-rsa/revoke-full /etc/easy-rsa/keys/${1}
echo "User account ${1} has been deleted"
echo "User account ${1} has been deleted"
</pre>
</pre>


=Using the VPN=
=Using the VPN=
Line 527: Line 614:


* Sign up a client using the <code>gen_client.sh</code> script. You specify the username with the first argument, and that's it.
* Sign up a client using the <code>gen_client.sh</code> script. You specify the username with the first argument, and that's it.
* The gen client script will produce a zip file with the username of the client. This zip file should be securely transferred to the client.
* The gen client script will produce a zip file at <code>/opt/easy-rsa/keys</code> with the username of the client. This zip file should be securely transferred to the client.
* Clients unzip the file and use the contents to connect to the OpenVPN server. Use the .ovpn profile to connect, and use the username_password.txt file to log in.
* Clients unzip the file and use the contents to connect to the OpenVPN server. Use the .ovpn profile to connect, and use the username_password.txt file to log in.


Line 538: Line 625:
./gen_client.sh <username>
./gen_client.sh <username>
</pre>
</pre>
Now get the zip file at <code>/opt/easy-rsa/keys/USERNAME.zip</code> to the user in a secure way.


==Example: Create MFA and Non-MFA Users==
==Example: Create MFA and Non-MFA Users==
Line 552: Line 641:
MFA_DISABLED="yes" ./gen_client.sh <username>
MFA_DISABLED="yes" ./gen_client.sh <username>
</pre>
</pre>
In both cases, get the file <code>/opt/easy-rsa/keys/USERNAME.zip</code> to the client in a secure manner.




Line 558: Line 649:
[[Category:Unix]]
[[Category:Unix]]
[[Category:Ubuntu]]
[[Category:Ubuntu]]
[[Category:VPN]]
[[Category:OpenVPN]]
[[Category:OpenVPN]]
[[Category:Bespin]]

Latest revision as of 00:18, 19 August 2020

This page gives an overview of how to install and set up an OpenVPN server on Ubuntu 18.04.

Server Prep

Note that you should run these commands as root.

Install OpenVPN

Update and install, this should have been completed earlier for the PIA VPN tunnel:

apt update
apt -y install openvpn

Install EasyRSA

Obtain and install EasyRSA to create a certificate authority and certificates for the server:

wget -qO- https://github.com/OpenVPN/easy-rsa/releases/download/2.2.2/EasyRSA-2.2.2.tgz | tar xvz -C /opt/
cp -R /opt/EasyRSA-2.2.2 /opt/easy-rsa
ln -fs /opt/easy-rsa/openssl-1.0.0.cnf /opt/easy-rsa/openssl.cnf

Setup OpenVPN Server

Set local EasyRSA variables for the certificate.

/opt/easy-rsa/local_vars

export KEY_DIR="/opt/easy-rsa/keys"
export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="Santa Cruz"
export KEY_ORG="charlesreid1.com"
export KEY_OU="bespin VPN"
export KEY_EMAIL=""
export KEY_NAME="bespin VPN key"

Set permissions and ownership:

chmod 0644 /opt/easy-rsa/local_vars
chown root:root /opt/easy-rsa/local_vars

Prepare to generate secrets:

cd /opt/easy-rsa

Clean keys directory:

test -e /opt/easy-rsa/clean-all
. /opt/easy-rsa/vars;. /opt/easy-rsa/local_vars;/opt/easy-rsa/clean-all

Build certificate - make script non-interactive, then run:

test -e /opt/easy-rsa/build-ca
sed -i 's/--interact//g' /opt/easy-rsa/build-ca
. /opt/easy-rsa/vars;. /opt/easy-rsa/local_vars;/opt/easy-rsa/build-ca

NOTE: This will print an error like the one below, but this is not actually a problem for generating the CA.

Can't load /root/.rnd into RNG
140672703574464:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd

Build DH parameters:

test -e /opt/easy-rsa/build-dh
. /opt/easy-rsa/vars;. /opt/easy-rsa/local_vars;/opt/easy-rsa/build-dh

Build key - make script non-interactive, then run:

test -e /opt/easy-rsa/build-key-server
sed -i 's/--interact//g' /opt/easy-rsa/build-key-server
. /opt/easy-rsa/vars;. /opt/easy-rsa/local_vars;/opt/easy-rsa/build-key-server server

Make keys directory:

mkdir -p /opt/easy-rsa/keys
cd /opt/easy-rsa/keys

Generate static TLS secret:

openvpn --genkey --secret statictlssecret.key

Configure VPN Server

Here we configure the VPN so that VPN IP addresses are in the CIDR block 192.168.20.0/24.

Start by making a directory for the server configuration:

mkdir -p /opt/openvpn

Now put the server configuration in /opt/optenvpn, then copy to /etc/openvpn.

/opt/openvpn/server.conf

port 1194
proto udp
# This is LAN20 so make it dev20
dev tun20
server 192.168.20.0 255.255.255.0
# enable this line to tunnel all client traffic thru vpn
#push "redirect-gateway def1"
# use dnsmasq as a dns server
#push "dhcp-option DNS 192.168.20.1"
dh /opt/easy-rsa/keys/dh2048.pem
# tls auth: act as server
key-direction 0
# use pam for auth
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
# custom client configurations
client-config-dir /etc/openvpn/clients

Copy the finalized version to the appropriate location:

cp /opt/openvpn/server.conf /etc/openvpn/server.conf
chmod 0644 /etc/openvpn/server.conf
chown root:root /etc/openvpn/server.conf

Now you have an easy backup of your original OpenVPN server config file.

Create the custom client configuration directory specified at the end of the file:

mkdir -p /etc/openvpn/clients

Create OpenVPN Server Profile

Finally, assemble the server config and keys into an OpenVPN profile file (.ovpn) so we can start this up with our startup service:

(cat /opt/openvpn/server.conf
echo '<key>'
cat /opt/easy-rsa/keys/server.key
echo '</key>'
echo '<tls-auth>'
cat /opt/easy-rsa/keys/statictlssecret.key
echo '</tls-auth>'
echo '<cert>'
cat /opt/easy-rsa/keys/server.crt
echo '</cert>'
echo '<ca>'
cat /opt/easy-rsa/keys/ca.crt
echo '</ca>'
) > /etc/openvpn/server.ovpn

Configure VPN Server Startup Service

Run this command to update the openvpn@ startup service to send separate logs for separate openvpn networks into their own log files:

grep "log-append" /lib/systemd/system/openvpn@.service &> /dev/null || sed -i 's|^ExecStart=.*|& --log-append /var/log/openvpn.%i.log|' /lib/systemd/system/openvpn@.service

Run this command to force the OpenVPN startup service to use an .ovpn file instead of a .conf file:

sed -i 's|\.conf|.ovpn|' /lib/systemd/system/openvpn@.service

A Note on the OpenVPN Startup Service

Quick side note to explain /lib/systemd/system/openvpn@.service:

This is a TEMPLATED startup service that allows you to run multiple startup services for multiple instances of openvpn for multiple VPNs. If you run service openvpn@myvpn start, it will start OpenVPN with (by default) the configuration file myvpn.conf. With the modifications above, it will start OpenVPN with the OpenVPN profile myvpn.ovpn.

Configure iptables

Just kidding, iptables doesn't require any configuration!

This VPN tunnel will not share any traffic with other networks on this server, so no need for packets on the OpenVPN tunnel to go to other devices.

Side Note - DNS

If you want to provide DNS to VPN clients, you can push a DNS rule to the VPN. This is useful even if clients aren't tunneling all their traffic through the VPN, because it replaces the DNS they are using.

You also need to use iptabels to allow packet traffic from the wifi network to the local DNS server(s). You should probably limit this to port 53 only, unless you're tunneling all traffic through the VPN.

You should also reconfigure the DNS server, dnsmasq, to set the upstream servers the VPN should use (or just leave it and keep using whatever nameservers are already defined in the dnsmasq config file).

You should also modify the OpenVPN config file - either the server or the client - to use the VPN gateway for DNS.

To push that rule to clients, put this line in the OpenVPN server configuration file:

/opt/openvpn/server.conf

push "dhcp-option DNS 192.168.20.1"

and go through the steps listed above to re-create the OpenVPN server profile.

Configure PAM

PAM is the pluggable authentication module in Linux. It provides a variety of methods for authenticating users in various contexts (for example, when you log into a desktop computer).

In the OpenVPN server config file, we included the following line to use PAM for authentication:

# use pam for auth
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn

This corresponds to a configuration file /etc/pam.d/openvpn that contains directives telling PAM how OpenVPN wants to authenticate users.

We can use Unix usernames/passwords to authenticate users:

/etc/pam.d/openvpn

auth required pam_unix.so

pam_unix uses Unix system accounts as the username/password to authenticate users.

Adding MFA to VPN Access

To add MFA to the VPN access (note: this will require changes to how users are created below), first install the Google Authenticator PAM library and other utilities:

apt-get -y install libpam-google-authenticator libqrencode3 qrencode

Now modify the contents of /etc/pam.d/openvpn to replace the Unix login with Google Authenticator as a method of authentication:

/etc/pam.d/openvpn (with Google Authenticator method only)

auth required /lib/x86_64-linux-gnu/security/pam_google_authenticator.so secret=/etc/openvpn/google-authenticator/${USER} user=gauth

Or, add both authentication methods, and make them both optional (meaning, one or the other). Also add the try first option to google authenticator to re-use the password entered by the user if it does not work as their Unix username/password.

/etc/pam.d/openvpn (with both Unix login and Google Authenticator methods)

auth optional pam_unix.so
auth optional /lib/x86_64-linux-gnu/security/pam_google_authenticator.so secret=/etc/openvpn/google-authenticator/${USER} user=gauth try_first_pass

Set permissions:

chmod 0644 /etc/pam.d/openvpn
chown root:root /etc/pam.d/openvpn

Also need to create a gauth user to handle Google Authenticator stuff:

addgroup gauth && useradd -g gauth gauth

Create the Google Authenticator directory:

mkdir /etc/openvpn/google-authenticator

Set ownership and permissions:

chown gauth:gauth /etc/openvpn/google-authenticator
chmod 0700 /etc/openvpn/google-authenticator

Starting the OpenVPN Server

If your OpenVPN server config file is at /etc/openvpn/server.conf then you can enable and start the service with these commands:

systemctl enable openvpn@server
systemctl start openvpn@server

Verify the service starts okay by monitoring the syslog in another window:

tail -f /var/log/syslog

Verify the tun0 interface came up and shows up with the ifconfig command:

ifconfig

Client Prep

Setup OpenVPN Client

Fix up the build key script so that it is non-interactive and has correct paths:

sed -i 's/--interact//g' /opt/easy-rsa/build-key
sed -i 's+export EASY_RSA=.*+export EASY_RSA="/opt/easy-rsa"+' /opt/easy-rsa/build-key

Configure OpenVPN Client

Configure the OpenVPN client by creating a client config file. We create this in /opt/openvpn, and use it to create the OpenVPN profile in /etc/openvpn a few steps.

/opt/openvpn/openvpn_client.conf

client
dev tun
proto udp
remote REMOTEIPOFOPENVPNSERVERGOESHERE 1194
nobind
pull
persist-key
persist-tun
remote-cert-tls server
# tls auth: act as client
key-direction 1
auth-user-pass

Now copy to /etc:

cp /opt/openvpn/openvpn_client.conf /etc/openvpn/.

Create Utility Scripts

We are going to create a couple of scripts to accomplish the task of registering users for the OpenVPN network.

First let's install some utilities that we will use.

Install Utility Helpers

sudo apt-get -y install zip unzip

Generate OpenVPN Profile

/opt/easy-rsa/gen_ovpn_profile.sh

#!/bin/bash
# Called by gen_client.sh
# Usage: ./gen_ovpn_profile.sh [USERNAME]
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username)"; exit 1; }
test -e /opt/openvpn/openvpn_client.conf || { echo "client config openvpn_client.conf not found!"; exit 1; }
test -e /opt/easy-rsa/keys/${1}.key || { echo "client key ${1} not found!"; exit 1; }
test -e /opt/easy-rsa/keys/ca.crt || { echo "server certificate ca.crt not found!"; exit 1; }
(cat /opt/openvpn/openvpn_client.conf
echo '<key>'
cat /opt/easy-rsa/keys/${1}.key
echo '</key>'
echo '<tls-auth>'
cat /opt/easy-rsa/keys/statictlssecret.key
echo '</tls-auth>'
echo '<cert>'
cat /opt/easy-rsa/keys/${1}.crt
echo '</cert>'
echo '<ca>'
cat /opt/easy-rsa/keys/ca.crt
echo '</ca>'
) > /opt/easy-rsa/keys/openvpn_${1}.ovpn

Set ownership and permissions:

chmod 0700 /opt/easy-rsa/gen_ovpn_profile.sh
chown root:root /opt/easy-rsa/gen_ovpn_profile.sh

Zip Client Files

/opt/easy-rsa/zip_client_files.sh

#!/bin/bash
# Called by gen_client.sh
# Usage: ./zip_client_files.sh [USERNAME]
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username)"; exit 1; }
FILES="/opt/easy-rsa/keys/ca.crt
/opt/easy-rsa/keys/statictlssecret.key
/opt/easy-rsa/keys/${1}.key
/opt/easy-rsa/keys/${1}.crt
/opt/easy-rsa/keys/openvpn_${1}.ovpn
/opt/easy-rsa/keys/${1}_password.txt"
for fname in ${FILES}; do
    test -e ${fname} || { echo "File ${fname} not found!"; exit 1; }
done
cd /opt/easy-rsa/keys
zip -j ${1}.zip $FILES

Set ownership and permissions:

chmod 0700 /opt/easy-rsa/zip_client_files.sh
chown root:root /opt/easy-rsa/zip_client_files.sh

Zip Client Files MFA Version

If you are setting up MFA, change the scripts as follows:

/opt/easy-rsa/zip_client_files.sh

#!/bin/bash
# Called by gen_client.sh
# Usage: ./zip_client_files.sh [USERNAME]
# Set the MFA_DISABLED env var to any value to use script for non-MFA clients
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username)"; exit 1; }
FILES="/opt/easy-rsa/keys/ca.crt
/opt/easy-rsa/keys/statictlssecret.key
/opt/easy-rsa/keys/${1}.key
/opt/easy-rsa/keys/${1}.crt
/opt/easy-rsa/keys/openvpn_${1}.ovpn"
if [ -z "${MFA_DISABLED}" ]; then
    FILES="${FILES}
    /etc/openvpn/google-authenticator/${1}_qr.png
    /etc/openvpn/google-authenticator/${1}_backup_codes.txt"
else
    FILES="${FILES}
    /opt/easy-rsa/keys/${1}_password.txt"
fi
for fname in ${FILES}; do
    test -e ${fname} || { echo "File ${fname} not found!"; exit 1; }
done
cd /opt/easy-rsa/keys
zip -j ${1}.zip $FILES

Generate and Delete Client Scripts

Generate Client Script

/opt/easy-rsa/gen_client.sh

#!/bin/bash
# Create new client and files required by new client.
# Usage: ./gen_client.sh [USERNAME]
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username)"; exit 1; }
# Run build-key
test -e /opt/easy-rsa/build-key || { echo "build-key not found!"; exit 1; }
test -e /opt/easy-rsa/gen_ovpn_profile.sh || { echo "gen_ovpn_profile.sh not found!"; exit 1; }
. /opt/easy-rsa/vars
. /opt/easy-rsa/local_vars
/opt/easy-rsa/build-key ${1}
# Generate .ovpn profile
test -e /opt/easy-rsa/keys/${1}.crt || { echo "client certificate ${1} not found!"; exit 1; }
/opt/easy-rsa/gen_ovpn_profile.sh ${1}
# Register unix user and set a password
useradd -s /bin/nologin "${1}"
# Login user needs password
head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 > /opt/easy-rsa/keys/${1}_password.txt
echo "${1}:$(cat /opt/easy-rsa/keys/${1}_password.txt)" | chpasswd
# Zip up all the client's files
/opt/easy-rsa/zip_client_files.sh ${1}
rm -f /opt/easy-rsa/keys/${1}_password.txt

Set ownership and permissions:

chmod 0700 /opt/easy-rsa/gen_client.sh
chown root:root /opt/easy-rsa/gen_client.sh

Generate Client Script MFA Version

If you are using MFA to protect the VPN, change the generate client script to the following:

/opt/easy-rsa/gen_client.sh

#!/bin/bash
# Create new client and files required by new client.
# Usage: ./gen_client.sh [USERNAME]
# Set the MFA_DISABLED env var to any value to use script for non-MFA clients
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username)"; exit 1; }
# Run build-key
test -e /opt/easy-rsa/build-key || { echo "build-key not found!"; exit 1; }
test -e /opt/easy-rsa/gen_ovpn_profile.sh || { echo "gen_ovpn_profile.sh not found!"; exit 1; }
. /opt/easy-rsa/vars
. /opt/easy-rsa/local_vars
/opt/easy-rsa/build-key ${1}
# Generate .ovpn profile
test -e /opt/easy-rsa/keys/${1}.crt || { echo "client certificate ${1} not found!"; exit 1; }
/opt/easy-rsa/gen_ovpn_profile.sh ${1}
# Register unix user and set a password
useradd -s /bin/nologin "${1}"
if [ -n "${MFA_DISABLED}" ]; then
    # Login user needs password
    head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 > /opt/easy-rsa/keys/${1}_password.txt
    echo "${1}:$(cat /opt/easy-rsa/keys/${1}_password.txt)" | chpasswd
else
    # MFA user does not need password
    echo "${1}:$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13)" | chpasswd
    # Generate MFA client info
    MFA_LABEL="Dockstore VPN"
    sudo -H -u gauth google-authenticator -t -w3 -e10 -d -r3 -R30 -f -l "${MFA_LABEL}" -s /etc/openvpn/google-authenticator/${1} > /etc/openvpn/google-authenticator/${1}.log
    # Text file with MFA backup codes
    tail -n10 /etc/openvpn/google-authenticator/${1} > /etc/openvpn/google-authenticator/${1}_backup_codes.txt
    # Generate QR image
    AUTH_ID="$( head -n1 /etc/openvpn/google-authenticator/${1} )"
    qrencode -o /etc/openvpn/google-authenticator/${1}_qr.png -d 300 -s 10 "otpauth://totp/${1}?secret=${AUTH_ID}&issuer=${MFA_LABEL}"
fi
# Zip up all the client's files
/opt/easy-rsa/zip_client_files.sh ${1}
if [ -n "${MFA_DISABLED}" ]; then
    rm -f /opt/easy-rsa/keys/${1}_password.txt
fi

Delete Client Script

/opt/easy-rsa/del_client.sh

#!/bin/bash
# Delete a client by deleting their client files and unix user.
# Usage: ./del_client.sh [USERNAME]
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username"; exit 1; }
# Revoke client files
. /opt/easy-rsa/vars
. /opt/easy-rsa/local_vars
# Delete client openvpn files
rm -f /opt/easy-rsa/keys/${1}.zip
rm -f /opt/easy-rsa/keys/${1}.key
rm -f /opt/easy-rsa/keys/openvpn_${1}.ovpn
# Delete unix user
deluser --remove-home ${1} || echo "User does not exist!"
# Revoke client
/opt/easy-rsa/revoke-full /opt/easy-rsa/keys/${1}
echo "User account ${1} has been deleted"

Set ownership and permissions:

chmod 0700 /opt/easy-rsa/del_client.sh
chown root:root /opt/easy-rsa/del_client.sh

Delete Client Script MFA Version

/opt/easy-rsa/del_client.sh

#!/bin/bash
# Delete a client by deleting their client files and unix user.
# Usage: ./del_client.sh [USERNAME]
set -e
test "$#" -eq "1" || { echo "Provide 1 argument (username"; exit 1; }
# Revoke client files
. /opt/easy-rsa/vars
. /opt/easy-rsa/local_vars
# Delete client openvpn files
rm -f /opt/easy-rsa/keys/${1}.zip
rm -f /opt/easy-rsa/keys/${1}.key
rm -f /opt/easy-rsa/keys/openvpn_${1}.ovpn
# Delete client MFA files (if they exist)
rm -f /etc/openvpn/google-authenticator/${1}
rm -f /etc/openvpn/google-authenticator/${1}.log
rm -f /etc/openvpn/google-authenticator/${1}_qr.png
rm -f /etc/openvpn/google-authenticator/${1}_backup_codes.txt
# Delete unix user
deluser --remove-home ${1} || echo "User does not exist!"
# Revoke cert
/opt/easy-rsa/revoke-full /etc/easy-rsa/keys/${1}
echo "User account ${1} has been deleted"

Using the VPN

Process for clients to use the VPN:

  • Sign up a client using the gen_client.sh script. You specify the username with the first argument, and that's it.
  • The gen client script will produce a zip file at /opt/easy-rsa/keys with the username of the client. This zip file should be securely transferred to the client.
  • Clients unzip the file and use the contents to connect to the OpenVPN server. Use the .ovpn profile to connect, and use the username_password.txt file to log in.

Example: Create a New User

To create a new user:

cd /opt/easy-rsa/
./gen_client.sh <username>

Now get the zip file at /opt/easy-rsa/keys/USERNAME.zip to the user in a secure way.

Example: Create MFA and Non-MFA Users

If you have used the MFA versions of the scripts above, you can create an MFA user like so:

MFA_DISABLED="" ./gen_client.sh <username>

Similarly you can create a non-MFA user like so:

MFA_DISABLED="yes" ./gen_client.sh <username>

In both cases, get the file /opt/easy-rsa/keys/USERNAME.zip to the client in a secure manner.