Preliminaries
This tutorial is inspired by the Linode documentation.
First of all, we have to install the OpenVPN package and some extra tools.
sudo apt update
sudo apt upgrade
sudo apt install -y iptables-persistent openvpn easy-rsa
echo 1 > /proc/sys/net/ipv4/ip_forward
Edit vim /etc/sysctl.conf
and add net.ipv4.ip_forward = 1
Firewall rules
Lets consider that the ssh
server listens the 65522
port and 65494
for the VPN server.
We have to define firewall rules in the file /etc/iptables/rules.v4
:
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Forward the VPN traffic to eth0
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMIT
*filter
# Allow all loopback (lo) traffic and reject anything
# to localhost that does not originate from lo.
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT
-A OUTPUT -o lo -j ACCEPT
# Allow ping and ICMP error returns.
-A INPUT -p icmp -m state --state NEW --icmp-type 8 -j ACCEPT
-A INPUT -p icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT
# Allow SSH.
-A INPUT -i eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 65522 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state ESTABLISHED --sport 65522 -j ACCEPT
# Allow UDP traffic.
-A INPUT -i eth0 -p udp -m state --state NEW,ESTABLISHED --dport 65494 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m state --state ESTABLISHED --sport 65494 -j ACCEPT
# Allow DNS resolution and limited HTTP/S on eth0.
# Necessary for updating the server and keeping time.
-A INPUT -i eth0 -p udp -m state --state ESTABLISHED --sport 53 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m state --state NEW,ESTABLISHED --dport 53 -j ACCEPT
-A INPUT -i eth0 -p tcp -m state --state ESTABLISHED --sport 80 -j ACCEPT
-A INPUT -i eth0 -p tcp -m state --state ESTABLISHED --sport 443 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 80 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 443 -j ACCEPT
# Allow traffic on the TUN interface.
-A INPUT -i tun0 -j ACCEPT
-A OUTPUT -o tun0 -j ACCEPT
# Log any packets which don't fit the rules above...
# (optional but useful)
-A INPUT -m limit --limit 3/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 4
-A FORWARD -m limit --limit 3/min -j LOG --log-prefix "iptables_FORWARD_denied: " --log-level 4
-A OUTPUT -m limit --limit 3/min -j LOG --log-prefix "iptables_OUTPUT_denied: " --log-level 4
# then reject them.
-A INPUT -j REJECT
-A OUTPUT -j REJECT
COMMIT
Be sure to adapt these rules to your needs before applying them.
sudo iptables-restore < /etc/iptables/rules.v4
We can check if the rules are correctly implied:
sudo iptables -L
Disable ipv6
In sysctl
In the file /etc/sysctl.d/99-sysctl.conf
, add the following lines:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1
and then apply the new configuration
sudo sysctl -p
... and reject with iptables
Remove ipv6
lines in /etc/hosts
:
#::1 localhost ip6-localhost ip6-loopback
Reject ipv6
traffic by editing the file /etc/iptables/rules.v6
, it must contains:
*filter
-A INPUT -j REJECT
-A FORWARD -j REJECT
-A OUTPUT -j REJECT
COMMIT
and apply:
sudo ip6tables-restore < /etc/iptables/rules.v6
OpenVPN configuration
Prepare server configuration environment
The OpenVPN configuration file is /etc/openvpn/server.conf
.
su
gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.conf
make-cadir /etc/openvpn/easy-rsa && cd /etc/openvpn/easy-rsa/
ln -s openssl-1.0.0.cnf openssl.cnf
mkdir keys
chmod 700 keys/
Configure certificates, keys, ... path
In /etc/openvpn/server.conf
, we have to specify the correct paths for the certificate, keys, etc.:
ca /etc/openvpn/easy-rsa/keys/ca.crt
cert /etc/openvpn/easy-rsa/keys/server.crt
key /etc/openvpn/easy-rsa/keys/server.key
dh /etc/openvpn/dh4096.pem
Configure certificate properties
In the file /etc/openvpn/easy-rsa/vars
, we have to specify the certificate properties like:
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_OU="MyOrganizationalUnit"
and source these variables:
cd /etc/openvpn/easy-rsa && source ./vars
./clean-all
Generate Diffie-Hellman PEM
openssl dhparam 4096 > /etc/openvpn/dh4096.pem
Harden OpenVPN
HMAC signature during TLS handshake
In the file /etc/openvpn/server.conf
, we have to change the TLS configuration:
tls-auth /etc/openvpn/easy-rsa/keys/ta.key 0
and generate the HMAC key file:
openvpn --genkey --secret /etc/openvpn/easy-rsa/keys/ta.key
Limited user
We have to create a user for our OpenVPN. This user has limited privileges:
adduser --system --shell /usr/sbin/nologin --no-create-home openvpn_server
and specify the user in the OpenVPN configuration:
user openvpn_server
group nogroup
Ciphers and digests
In the file /etc/openvpn/server.conf
, we have to specify the ciphers and digest we want to use:
cipher AES-256-CBC
auth SHA512
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA
Certificate and Key Pairs
Server side
cd /etc/openvpn/easy-rsa
./build-ca
./build-key-server server
Client side
cd /etc/openvpn/easy-rsa && source ./vars && ./build-key client1