Overview
This guide demonstrates how to configure an Ubuntu VM as a VPN gateway using ProtonVPN. This setup allows you to route traffic from other devices through the VPN connection, effectively creating a network-wide VPN gateway. This is particularly useful for:
- Protecting multiple devices behind a single VPN connection
- Creating isolated networks for testing or security purposes
- Routing specific applications or containers through VPN without system-wide VPN configuration
- Implementing split-tunnel architectures in virtualized environments
Architecture
The setup creates a NAT (Network Address Translation) gateway where:
- The Ubuntu VM establishes a VPN tunnel (tun0 interface)
- IP forwarding routes packets between your local network and the VPN tunnel
- iptables performs NAT masquerading to translate source addresses
- Other devices use the VM as their gateway to route traffic through the VPN
[Local Devices] → [Ubuntu VM Gateway] → [VPN Tunnel (tun0)] → [Internet via VPN]
↓ NAT/MASQUERADE
↓ IP Forwarding
IP forward
IP forwarding enables the Linux kernel to route packets between network interfaces. This is essential for the VM to act as a gateway.
1sudo sysctl -w net.ipv4.ip_forward=12sudo nano /etc/sysctl.conf3net.ipv4.ip_forward=1The first command enables IP forwarding immediately (runtime change). The second command makes it persistent across reboots by adding it to /etc/sysctl.conf.
Verify IP forwarding status:
1# Check current value (1 = enabled, 0 = disabled)2cat /proc/sys/net/ipv4/ip_forward3
4# Or using sysctl5sysctl net.ipv4.ip_forwardUnderstanding the parameter:
net.ipv4.ip_forward=1- Enables IPv4 packet forwarding- For IPv6 support, also enable:
net.ipv6.conf.all.forwarding=1
Iptables
iptables is a userspace utility for configuring Linux kernel firewall rules. Here we configure NAT (Network Address Translation) to masquerade outgoing traffic.
1sudo iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADEBreaking down this command:
-t nat- Specifies the NAT table-A POSTROUTING- Appends rule to POSTROUTING chain (after routing decision)-o tun0- Matches packets going out through tun0 interface (VPN tunnel)-j MASQUERADE- Replaces source IP with the interface IP (dynamic SNAT)
Additional iptables rules for comprehensive setup:
1# Allow forwarding from local network (e.g., 192.168.1.0/24) to VPN2sudo iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT3
4# Allow established and related connections back5sudo iptables -A FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT6
7# View current NAT rules8sudo iptables -t nat -L -n -v1sudo apt install iptables-persistent2sudo service netfilter-persistent save3sudo ufw allow in on tun0 from any to any4sudo ufw allow out on tun0 from any to anyWhat these commands do:
iptables-persistent- Package that saves and restores iptables rules on bootnetfilter-persistent save- Saves current iptables rules to/etc/iptables/rules.v4andrules.v6- UFW (Uncomplicated Firewall) rules allow traffic through the VPN tunnel interface
Verify saved rules:
1# View IPv4 rules2sudo cat /etc/iptables/rules.v43
4# View IPv6 rules5sudo cat /etc/iptables/rules.v66
7# Check UFW status8sudo ufw status verboseUbuntu server
By default all ports are closed. Open it
1sudo iptables -P INPUT ACCEPT2sudo iptables -P OUTPUT ACCEPT3sudo iptables -P FORWARD ACCEPT4sudo iptables-save > /etc/iptables/rules.v4Understanding default policies:
-P INPUT ACCEPT- Sets default policy for incoming traffic-P OUTPUT ACCEPT- Sets default policy for outgoing traffic-P FORWARD ACCEPT- Sets default policy for forwarded traffic (crucial for gateway)iptables-save- Exports current rules to a file
⚠️ Security Note: This configuration opens all ports. For production environments, implement more restrictive rules:
1# Alternative: Restrictive approach with specific allowances2sudo iptables -P INPUT DROP3sudo iptables -P FORWARD DROP4sudo iptables -P OUTPUT ACCEPT5
6# Allow SSH access (replace 22 with your SSH port)7sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT8
9# Allow established connections10sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT11
12# Allow loopback13sudo iptables -A INPUT -i lo -j ACCEPT14
15# Allow forwarding from specific subnet16sudo iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT17sudo iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT1sudo ufw disable2sudo ufw default allow incoming3sudo ufw default allow outgoing4sudo ufw enableUFW (Uncomplicated Firewall) management:
- Provides a simpler interface over iptables
- Default policies set before enabling to avoid lockout
- More user-friendly for basic firewall management
Troubleshooting UFW:
1# Check UFW status and rules2sudo ufw status numbered3
4# Delete specific rule by number5sudo ufw delete <number>6
7# Reset UFW to default state8sudo ufw reset9
10# Check UFW logs11sudo tail -f /var/log/ufw.logOpenVPN
OpenVPN is the open-source VPN protocol used by ProtonVPN. We need to install the client and configure DNS resolution.
1sudo apt install openvpn 2sudo apt install openresolv3sudo wget "https://raw.githubusercontent.com/ProtonVPN/scripts/master/update-resolv-conf.sh" -O "/etc/openvpn/update-resolv-conf"4sudo chmod +x "/etc/openvpn/update-resolv-conf"Component breakdown:
openvpn- Main VPN client softwareopenresolv- DNS management framework for OpenVPNupdate-resolv-conf- Script that updates DNS settings when VPN connects/disconnects
Why update-resolv-conf is important:
This script ensures DNS queries are routed through the VPN provider's DNS servers, preventing DNS leaks. It's called automatically by OpenVPN through up and down script directives.
Verify installation:
1# Check OpenVPN version2openvpn --version3
4# Verify script is executable5ls -la /etc/openvpn/update-resolv-conf6
7# Test DNS resolution after VPN connection8nslookup google.com9dig google.comOpenVPN config
The configuration file tells OpenVPN how to connect to ProtonVPN servers. Download your .ovpn file from the ProtonVPN dashboard.
1sudo mv /path/to/your/file.ovpn /etc/openvpn/client.conf2sudo systemctl enable [email protected]3sudo systemctl start [email protected]4sudo systemctl status [email protected]Understanding systemd service naming:
[email protected]- The@clientpart matchesclient.conffilename- If you named it
proton.conf, the service would be[email protected] enable- Makes the service start automatically on bootstart- Starts the service immediatelystatus- Shows current status and recent log entries
Additional systemd commands:
1# Stop the VPN connection2sudo systemctl stop [email protected]3
4# Restart the VPN connection5sudo systemctl restart [email protected]6
7# View detailed logs8sudo journalctl -u [email protected] -f9
10# Check if service is set to start on boot11sudo systemctl is-enabled [email protected]Then, modify the .ovpn file (/etc/openvpn/client.conf) to include this line:
1sudo nano /etc/openvpn/credentials.txtauth-user-pass /etc/openvpn/credentials.txt
Credentials file format:
Create /etc/openvpn/credentials.txt with two lines:
your-protonvpn-username
your-protonvpn-password
Security best practices:
1# Set restrictive permissions (only root can read)2sudo chmod 600 /etc/openvpn/credentials.txt3sudo chown root:root /etc/openvpn/credentials.txt4
5# Verify permissions6ls -la /etc/openvpn/credentials.txt7# Should show: -rw------- 1 root rootAdditional client.conf modifications:
Add these lines to /etc/openvpn/client.conf for better functionality:
1# DNS leak protection (use ProtonVPN's DNS)2script-security 23up /etc/openvpn/update-resolv-conf4down /etc/openvpn/update-resolv-conf5
6# Automatically reconnect on connection drop7persist-key8persist-tun9keepalive 10 6010
11# Log file for troubleshooting12log-append /var/log/openvpn-client.log13verb 3Monitor the log file:
1# Watch logs in real-time2sudo tail -f /var/log/openvpn-client.log3
4# Check for connection errors5sudo grep -i error /var/log/openvpn-client.logTest
1curl ip.meComprehensive testing suite:
1. Verify VPN connection is active:
1# Check tun0 interface exists2ip addr show tun03
4# Should show the VPN tunnel interface with an IP address5# Example output: inet 10.x.x.x/xx2. Check your public IP (should show VPN exit server IP):
1# Multiple services to verify IP2curl ifconfig.me3curl icanhazip.com4curl ipinfo.io/ip5
6# Get detailed info including location7curl ipinfo.io3. DNS leak test:
1# Check which DNS servers you're using2nslookup -type=txt whoami.akamai.net3
4# Or using dig5dig +short TXT whoami.akamai.net6
7# Should show ProtonVPN's DNS servers, not your ISP's8cat /etc/resolv.conf4. Verify routing through VPN:
1# Show routing table2ip route show3
4# Default route should go through tun05# Example: default via 10.x.x.x dev tun06
7# Traceroute to see the path8traceroute -n google.com9# First hops should be VPN infrastructure5. Test from another device: If you've configured other devices to use this VM as gateway:
1# From another device on your network, set gateway to this VM's IP2# Then test:3curl ipinfo.io4
5# Should show the VPN exit IP, not your real IP6. Network performance test:
1# Install speedtest-cli if needed2sudo apt install speedtest-cli3
4# Test VPN connection speed5speedtest-cli6
7# Ping test8ping -c 4 8.8.8.8Troubleshooting
VPN won't connect:
1# Check OpenVPN service status2sudo systemctl status [email protected]3
4# View detailed logs5sudo journalctl -xeu [email protected]6
7# Test configuration manually (foreground mode)8sudo openvpn --config /etc/openvpn/client.confDNS not working:
1# Check DNS resolution2systemd-resolve --status3
4# Verify update-resolv-conf script executed5cat /etc/resolv.conf6
7# Manually test DNS8nslookup google.com 8.8.8.8Routing issues:
1# Verify IP forwarding is enabled2sysctl net.ipv4.ip_forward3
4# Check NAT rules are active5sudo iptables -t nat -L -n -v6
7# Verify firewall isn't blocking8sudo iptables -L -n -v9
10# Check interface status11ip link showConnection drops frequently:
1# Add to client.conf2resolv-retry infinite3nobind4persist-key5persist-tun6keepalive 10 1207
8# Then restart service9sudo systemctl restart [email protected]Advanced Configuration
Kill switch (prevent traffic without VPN):
1# Drop all traffic not going through VPN2sudo iptables -A OUTPUT ! -o tun0 -m owner ! --uid-owner root -j DROP3
4# Allow local network5sudo iptables -I OUTPUT -o eth0 -d 192.168.0.0/16 -j ACCEPTSplit tunneling (route only specific traffic through VPN):
1# Create routing table2echo "200 vpn" | sudo tee -a /etc/iproute2/rt_tables3
4# Route specific subnet through VPN5sudo ip rule add from 192.168.100.0/24 table vpn6sudo ip route add default via 10.x.x.x dev tun0 table vpnMonitor VPN status with script:
1#!/bin/bash2# save as /usr/local/bin/vpn-status.sh3
4VPN_IP=$(curl -s ifconfig.me)5TUN0_STATUS=$(ip addr show tun0 2>/dev/null | grep -q "inet" && echo "UP" || echo "DOWN")6
7echo "VPN Status: $TUN0_STATUS"8echo "Public IP: $VPN_IP"9echo "Timestamp: $(date)"Make it executable and add to cron:
1sudo chmod +x /usr/local/bin/vpn-status.sh2# Run every 5 minutes3echo "*/5 * * * * /usr/local/bin/vpn-status.sh >> /var/log/vpn-monitor.log" | sudo crontab -