Tom Butler's programming blog

Routing all traffic through an OpenVPN client on a CentOS 7 NAT

Background

I don't usually write about networking but I couldn't find a concise guide for this online so this is for my own future reference as much as for anyone else who may find this useful.

I previously had the OpenVPN client running on my router, but the router's CPU limited my overall connection speed so I decided to place a NUC (Tiny form factor PC) with dual network ports between the modem and my router for the purposes of running a VPN client and a few web facing services. I'm using some hardware I already owned which has and a comparatively beefy CPU (Intel i7 5557u) that is is overkill for this purpose but will allow the VPN to run without being CPU limited.

Why CentOS?

I wanted something to run a VPN and a web server as well as a few other web facing services and not have to worry about. With CentOS I can leave it running without having to worry about maintenance (Thanks, yum-cron!).

Why not put the VPN behind the router?

Firstly, it's more difficult to configure all outbound traffic to be forwarded to through the VPN. Secondly, having this as part of my local network is not ideal, if one of the services running on the server was compromised then someone could probe any device on the network. By putting the web facing services on a PC on its own network, anyone who gained access to the server would still have to get through the main router to access other machines on the network.

Prerequisites

  1. A separate modem and router (or two routers), if your modem is also your router you'll need access to iptables on the router to forward the traffic to the VPN PC and back, something I won't cover here as you'll need a router more configurable than most off the consumer products.
  2. A single board computer, NUC or PC with a semi-decent CPU and two network adapters. This can be done with wireless or preferably two ethernet ports

The goal here is that the modem connects to the VPN PC, and the VPN PC connects to the router.

Preliminary setup

  1. Install CentOS 7
  2. Connect the modem to one ethernet port
  3. Connect the WAN port on your router to the second ethernet port on the VPN PC

Getting Started

On the PC that will be running the OpenVPN client, run the command ip addr

You'll see something like this:

1lo: <LOOPBACK,UP,LOWER_UPmtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UPmtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:e0:4c:68:31:a8 brd ff:ff:ff:ff:ff:ff
3enp3s0:  <BROADCAST,MULTICAST,UP,LOWER_UPmtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:e0:4c:68:31:a9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.28/24 brd 192.168.0.255 scope global noprefixroute dynamic enp3s0

Each of your adapters is listed (enp2s0 and enp3s0 for me). One of them should have been given an IP by the modem. For me it's enp3s0, this is your WAN port.

The second port connects the PC running the OpenVPN client to the router, your LAN port (for me enp2s0).

You will need to remember which is which and keep the correct cable plugged in to the correct port.

Choose your IP subnets

You will be running three distinct networks. Make a note of the IP given by your modem. In my example above it is 192.168.0.28. Plug a device into your router and check the IP (if the IP is in the same range as the modem e.g. both are 192.168.0.*) adjust your router to pick a different subnet.

Finally, choose an a different subnet for your new network. In my configuration the following subnets will exist once finished:

  1. 192.168.0.* - Configured from the modem, the VPN PC will get an IP on this network
  2. 192.168.1.* - Configured from the router
  3. 192.168.2.* - Configured by the VPN PC, choose any number between 0-255 that isn't already taken by the modem or router. The router will be given an IP on this network.

LAN Adapter configuration

Edit the file /etc/sysconfig/network-scripts/ifcfg-enp2s0 (substitute your own adapter name). This is the adapter that connects the PC to the router, not the modem.

Add the following (or amend the values if the setting is already there):

BOOTPROTO=static
IPADDR=192.168.2.1
NETMASK=255.255.255.0
ZONE=internal
NM_CONTROLLED=no
ONBOOT=yes

Keep the rest of the settings as they are and make sure to set IPADDR to an IP between 1 and 254 of your chosen subnet. Note: .0 does not work!

WAN adapter configuration

Edit the file /etc/sysconfig/network-scripts/ifcfg-enp3s0 (substitute your own adapter name). The only setting you need to add is:

PEERDNS=yes

dhcpd

To manage the subnet for the LAN install DHCPD and configure it to run on startup:

yum install dhcp
systemctl enable dhcpd.service

Create /etc/dhcp/dhcpd.conf and paste in the following:

subnet 192.168.2.0 netmask 255.255.255.0 {
        
range 192.168.2.1 192.168.2.254;
        
option domain-name-servers 192.168.0.1;
        
option routers 192.168.2.1;
}

You will need to supply the IP of your modem for the domain-name-server entry to forward DNS from the client. If that doesn't work, you can use Google's DNS 8.8.8.8 or CloudFlare's 1.1.1.1. If your subnet isn't 192.168.2.* use your chosen subnet throughout.

Finally, start dhcpd:

systemctl start dhcpd.service

At this point, you can log in to your router and check that it has been assigned an IP address on the 192.168.2.* subnet. You won't be able to access the internet from devices attached to the router yet though.

NAT

The next stage is enabling NAT so that devices connecting from the LAN can access the WAN.

Firstly, enable IP forwarding in the kernel:

sysctl -w net.ipv4.ip_forward=1

Then configure masquerading in the firewall and forward all traffic from the LAN adapter:

firewall-cmd --zone=public --add-masquerade --permanent
firewall-cmd --permanent --direct --passthrough ipv4 -I FORWARD -i enp2s0 -j ACCEPT
firewall-cmd --reload

Remember to substitute your own adapter name. At this point the NAT is configured and any device connected to the router should have internet access.

OpenVPN

Set up the epel repository if you haven't already and install openvpn:

wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -ivh epel-release-latest-7.noarch.rpm
sudo yum install openvpn

There's not much configuration needed here, get your .opvn configuration file and run:

openvpn --config /path/to/config.ovpn

If you want to set a username and password, store it in a file with the username on line 1 and password on line 2, create it as root and chmod to 600 then:

openvpn --config /path/to/config.ovpn --auth-user-pass /path/to/auth/details/file

You also have to add the tunnel to the public zone:

firewall-cmd --zone=public --add-interface=tun0 --permanent
firewall-cmd --reload

That's it, go to whatismyip.com and check that it's connecting through your VPN.

To get the VPN to connect on startup, add a service file /etc/systemd/system/vpn.service

[Unit]
Description=Connect to VPN

[Service]
Type=oneshot
ExecStart=/usr/sbin/openvpn --config /path/to/config.ovpn --auth-user-pass /path/to/authentication/file

[Install]
WantedBy=multi-user.target

You can then stop/start the VPN using systemctl start vpn and systemctl stop vpn. To start the VPN on boot run systemctl enable vpn