Privacy Tools Guide

Running your own OpenVPN server on a VPS gives you a private VPN that no commercial provider can log. This guide uses Easy-RSA for certificate management, TLS authentication to block port scans, and modern cipher selection.

Prerequisites

Step 1: Install OpenVPN and Easy-RSA

sudo apt update && sudo apt install openvpn easy-rsa
openvpn --version | head -1

Step 2: Set Up the Certificate Authority

mkdir -p ~/openvpn-ca
cp -r /usr/share/easy-rsa/* ~/openvpn-ca/
cd ~/openvpn-ca

./easyrsa init-pki
./easyrsa build-ca nopass
# Enter "OpenVPN-CA" as Common Name

Step 3: Generate Server Certificate and Keys

cd ~/openvpn-ca

./easyrsa gen-req server nopass
./easyrsa sign-req server server

# DH parameters
./easyrsa gen-dh

# TLS auth key
openvpn --genkey secret ta.key

# Copy to OpenVPN directory
sudo cp pki/ca.crt pki/issued/server.crt pki/private/server.key \
  pki/dh.pem ta.key /etc/openvpn/server/
sudo chmod 600 /etc/openvpn/server/*.key /etc/openvpn/server/*.pem

Step 4: Generate Client Certificate

cd ~/openvpn-ca
./easyrsa gen-req alice nopass
./easyrsa sign-req client alice
# Client needs: ca.crt, alice.crt, alice.key, ta.key

Step 5: Server Configuration

Create /etc/openvpn/server/server.conf:

port 1194
proto udp
dev tun

ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh.pem

tls-auth /etc/openvpn/server/ta.key 0
tls-version-min 1.2
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384

cipher AES-256-GCM
ncp-ciphers AES-256-GCM:AES-128-GCM

server 10.8.0.0 255.255.255.0

push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 1.0.0.1"

keepalive 10 120

user nobody
group nogroup
persist-key
persist-tun

status /var/log/openvpn/status.log 30
log-append /var/log/openvpn/openvpn.log
verb 3

# Compression disabled (VORACLE vulnerability)
compress

Step 6: Enable IP Forwarding and NAT

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# Get your public interface name
ip route get 8.8.8.8 | grep dev | awk '{print $5}'

# Add NAT rule (replace eth0 with your interface)
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

sudo apt install iptables-persistent
sudo netfilter-persistent save

Step 7: Start OpenVPN

sudo mkdir -p /var/log/openvpn
sudo systemctl enable --now openvpn-server@server
sudo systemctl status openvpn-server@server
sudo journalctl -u openvpn-server@server -f

Step 8: Create Client Configuration File

#!/bin/bash
# generate-client.sh
CLIENT="alice"
CA_DIR="$HOME/openvpn-ca"
SERVER_IP="YOUR_VPS_IP"

cat > ${CLIENT}.ovpn <<EOF
client
dev tun
proto udp
remote ${SERVER_IP} 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
tls-version-min 1.2
cipher AES-256-GCM
verb 3

<ca>
$(cat ${CA_DIR}/pki/ca.crt)
</ca>

<cert>
$(cat ${CA_DIR}/pki/issued/${CLIENT}.crt)
</cert>

<key>
$(cat ${CA_DIR}/pki/private/${CLIENT}.key)
</key>

<tls-auth>
$(cat ${CA_DIR}/ta.key)
</tls-auth>
key-direction 1
EOF

echo "Client config written to ${CLIENT}.ovpn"

Step 9: Revoking a Client

cd ~/openvpn-ca
./easyrsa revoke alice
./easyrsa gen-crl

sudo cp pki/crl.pem /etc/openvpn/server/
# Add to server.conf: crl-verify /etc/openvpn/server/crl.pem
sudo systemctl restart openvpn-server@server

Verify

ip addr show tun0
sudo cat /var/log/openvpn/status.log
# From client: curl https://ifconfig.me (should show VPS IP)

Split Tunneling: Route Only Specific Traffic Through VPN

Full tunnel (redirect-gateway) routes all client traffic through the VPS. Split tunneling sends only specific subnets through the VPN, leaving other traffic on the client’s local internet.

Remove the redirect-gateway push directive and replace it with specific routes:

# server.conf — split tunnel example
# Do NOT push redirect-gateway

# Route internal subnet through VPN
push "route 10.0.0.0 255.255.0.0"
push "route 192.168.1.0 255.255.255.0"

# Push DNS only for internal resolution
push "dhcp-option DNS 10.8.0.1"

On the client, the .ovpn file should NOT include redirect-gateway. Traffic to 10.0.0.0/16 goes through the VPN; everything else hits the client’s ISP directly.

Use split tunneling when:

Verify routing after connection:

# On client after connecting
ip route show
# Should see: 10.0.0.0/16 via 10.8.0.1 dev tun0
# Default route should still point to local gateway, not tun0
curl https://ifconfig.me  # Should show client's ISP IP, not VPS IP

Hardening: Block DNS Leaks and IPv6

A DNS leak occurs when the client resolves DNS through the ISP’s resolver even while connected to VPN. Push DNS servers through the tunnel and disable IPv6 to prevent leaks:

# server.conf additions
push "dhcp-option DNS 10.8.0.1"
push "dhcp-option DNS 1.1.1.1"
push "block-outside-dns"  # Windows only, prevents DNS leaks on Windows clients

# Disable IPv6 pushing to prevent v6 leaks
tun-ipv6 no

On the VPS, run an unbound or dnsmasq resolver that listens on 10.8.0.1 to handle DNS inside the tunnel:

sudo apt install unbound
# /etc/unbound/unbound.conf.d/openvpn.conf
cat <<'EOF' | sudo tee /etc/unbound/unbound.conf.d/openvpn.conf
server:
    interface: 10.8.0.1
    access-control: 10.8.0.0/24 allow
    access-control: 127.0.0.1/8 allow
    do-ip4: yes
    do-ip6: no
    hide-identity: yes
    hide-version: yes
EOF
sudo systemctl enable --now unbound

Clients verify there are no leaks with:

# Test on client after connecting
dig +short whoami.akamai.net  # Should resolve through VPS IP
# Or use https://dnsleaktest.com