Console Login

OpenVPN 2.2 gateway on CentOS 6: PKI, NAT and firewall fixes

Symptoms

An administrator who manages servers from untrusted networks (public Wi-Fi, hotel LANs) wants an encrypted tunnel that ends on a machine they control. A typical OpenVPN 2.2 setup on CentOS 6 fails in one of four ways:

  • The service refuses to start and logs Cannot open TUN/TAP dev /dev/net/tun: No such file or directory.
  • The client connects and reports a successful handshake, but no page loads and no host outside the VPN answers.
  • Traffic works for a while after a manual firewall change, then breaks after the firewall service restarts or the machine reboots.
  • Building the Diffie-Hellman file looks hung.

Why it happens

No TUN device

OpenVPN in dev tun mode needs the kernel tun driver and the /dev/net/tun device node. A full virtual machine with its own kernel can load the module. A container-based guest (for example OpenVZ) shares the host kernel. It only gets the device if the host operator has loaded the module and granted the container access to it.

Forwarding and NAT are off by default

The kernel does not forward packets between interfaces until net.ipv4.ip_forward is set to 1. Even with forwarding on, VPN clients use private addresses from the tunnel subnet (here 10.8.0.0/24). Those source addresses are not routable on the internet, so replies never return unless the server rewrites the source address with NAT.

Rule order in the default CentOS 6 firewall

The stock CentOS 6 iptables ruleset ends the INPUT and FORWARD chains with a catch-all REJECT rule. A rule added with -A lands after that REJECT and never matches. This applies to the UDP 1194 allow rule and to FORWARD rules for the tunnel. Rules must be inserted with -I or placed above the REJECT line. NAT is separate: it lives in the nat table, where appending is fine.

Diffie-Hellman generation is CPU-bound

Building dh2048.pem means searching for a large safe prime. That is a computation-heavy task, not an entropy shortage, and its duration varies a lot between runs. The command is not hung; wait for it to finish.

Source of truth

The OpenVPN community documentation page Routing All Client Traffic (Including Web Traffic) Through the VPN confirms the routing part of the design:

  • By default only traffic to the VPN site passes through the tunnel; pushing redirect-gateway def1 sends all client IP traffic through the OpenVPN server.
  • The server must then be set up to handle that traffic, by NATing it to the internet or by routing it through a proxy.
  • Once the gateway is redirected, DNS queries also travel through the VPN, so the server side must be able to answer them.
  • The local flag exists for clients on the same wireless subnet as the server.

The page is current documentation, but redirect-gateway with def1 is long-standing behaviour that predates OpenVPN 2.2. The option semantics (comp-lzo, cipher, ns-cert-type, persist-key) are in the openvpn(8) manual page installed with the package; read it with man openvpn on the target version.

Fix options

DecisionOptionTrade-off
Where the tunnel runsFull VM (KVM) or dedicated machineOwn kernel; the tun module can be loaded by the guest.
Container with TUN enabled by the hostWorks, but only if the host operator grants the device and the network capability.
AuthenticationStatic pre-shared keySimple, one client only, no forward secrecy because the same key protects all sessions.
Private CA with certificates (easy-rsa 2.0)Per-client certificates, revocation possible, TLS key exchange with forward secrecy when DH is used.
TransportUDP 1194Avoids TCP-over-TCP retransmission stacking on lossy links.
TCP (for example port 443)Passes restrictive firewalls, at a cost in throughput and latency under packet loss.
RoutingRedirect the whole default gatewayProtects all client traffic; needs NAT, forwarding and DNS handling on the server.
Push only specific routesLess server load; other traffic stays on the untrusted network.

Why this fix

The setup below uses a private CA with per-client certificates, UDP, and a redirected default gateway with masquerading. The reasons:

  • The goal is to protect all traffic on a hostile network, so redirecting the gateway is the right scope.
  • Certificates let each laptop be revoked without touching the others, which a static key cannot do.
  • UDP is the transport the protocol was designed for; TCP stays as a fallback for networks that block UDP.
  • The firewall rules are inserted rather than appended, so they work with the default CentOS 6 chain layout.

Two points differ from many older tutorials. First, the cipher is switched from the default Blowfish (BF-CBC) to AES, which can use hardware AES instructions where the CPU and OpenSSL build support them. Second, a physical location does not make a VPN private. It changes latency and which laws apply, but the operator of the exit node can still see unencrypted traffic leaving it.

Step by step

The commands below are the procedure to apply as root. They are not a record of a test run, so check each result on your own system.

1. Check the TUN device

modprobe tun
ls -l /dev/net/tun

If the device node is missing inside a container, the host must enable it; no setting inside the guest fixes that.

2. Install from EPEL

Download the epel-release package for CentOS 6 and your architecture (x86_64 or i386) from the EPEL 6 directory of a Fedora project mirror, over HTTPS. Then install it and check the key fingerprint against the one published by the Fedora project before trusting the repository.

rpm -Uvh epel-release-6-*.noarch.rpm
gpg --quiet --with-fingerprint /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
yum install openvpn
yum install easy-rsa

Depending on the build, the easy-rsa 2.0 scripts come inside the openvpn package or as a separate easy-rsa package. If the second command reports that no such package exists, the first one already installed the scripts. Locate them and copy them to a directory that updates do not overwrite:

EASY=$(dirname $(rpm -ql openvpn easy-rsa 2>/dev/null | grep '/build-ca$' | head -n 1))
mkdir -p /etc/openvpn/easy-rsa/2.0
cp -r "$EASY"/* /etc/openvpn/easy-rsa/2.0/
cd /etc/openvpn/easy-rsa/2.0

3. Build the CA and server certificate

Edit vars and set these values (use your own country, organisation and address):

export KEY_COUNTRY="NO"
export KEY_PROVINCE="Oslo"
export KEY_CITY="Oslo"
export KEY_ORG="ExampleOrg"
export KEY_EMAIL="admin@example.com"
export KEY_SIZE=2048

A 2048-bit key is the sensible minimum for RSA and DH here. 1024-bit keys are weak by that era's guidance, though the claim that they are routinely broken is an exaggeration. Then build the files. Run clean-all only once: it empties the keys directory.

source ./vars
./clean-all
./build-ca
./build-key-server server
./build-dh
chmod 700 keys

Keep ca.key safe. For anything beyond a lab, build the CA on a separate offline machine and copy only the signed certificates to the gateway.

4. Server configuration

Create /etc/openvpn/server.conf. The resolver address is a placeholder from the documentation range. Replace it with a caching resolver you run and that is reachable from the VPN subnet. Pushing a third-party public resolver sends every lookup of every client to that provider.

port 1194
proto udp
dev tun
ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt
cert /etc/openvpn/easy-rsa/2.0/keys/server.crt
key /etc/openvpn/easy-rsa/2.0/keys/server.key
dh /etc/openvpn/easy-rsa/2.0/keys/dh2048.pem

server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 192.0.2.53"

cipher AES-128-CBC
comp-lzo
keepalive 10 120
user nobody
group nobody
persist-key
persist-tun
status /var/log/openvpn-status.log
verb 3

The original ifconfig-pool-persist ipp.txt line is omitted. After the daemon drops privileges to nobody, it cannot write a relative path under /etc/openvpn. The dhcp-option DNS push is honoured by Windows clients. Linux clients need a helper script (an up script that rewrites resolv.conf) to apply it.

5. Forwarding, firewall and NAT

sysctl -w net.ipv4.ip_forward=1
sed -i 's/^net.ipv4.ip_forward.*/net.ipv4.ip_forward = 1/' /etc/sysctl.conf

iptables -I INPUT -p udp --dport 1194 -j ACCEPT
iptables -I FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
iptables -I FORWARD -i eth0 -o tun0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
service iptables save

Check the result with iptables -L -n -v --line-numbers and iptables -t nat -L -n. The new ACCEPT rules must appear above the REJECT lines. Replace eth0 if the public interface has another name. Saving writes the rules to /etc/sysconfig/iptables, so they survive a restart of the firewall service and a reboot.

6. Start the service

service openvpn start
chkconfig openvpn on
ip addr show tun0

The tun0 interface should carry 10.8.0.1. Errors go to /var/log/messages.

7. Issue a client certificate and profile

cd /etc/openvpn/easy-rsa/2.0
source ./vars
./build-key client-laptop

Copy ca.crt, client-laptop.crt and client-laptop.key to the laptop with scp, never by email. Use build-key-pass instead of build-key if the private key should be passphrase-protected. Create client.ovpn:

client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client-laptop.crt
key client-laptop.key
ns-cert-type server
cipher AES-128-CBC
comp-lzo
verb 3

The ns-cert-type server line makes the client reject certificates that were not issued for a server, which blocks a client certificate being used to impersonate the gateway. Start the client with:

openvpn --config client.ovpn

A successful connection ends the log with Initialization Sequence Completed. Afterwards, ip route on the client should show the default route going through the tunnel.

To test the CPU side, grep -m 1 -o aes /proc/cpuinfo prints aes if the processor exposes hardware AES instructions.

Short review

What the setup gives you

  • A per-client certificate setup that can be extended without sharing keys.
  • All client IPv4 traffic leaves through one controlled exit point.
  • Firewall rules that survive restarts.

Limits

  • OpenVPN 2.2 tunnels IPv4 only. A client with working IPv6 can leak traffic outside the tunnel unless IPv6 is disabled on the client.
  • The tunnel protects the path between laptop and gateway. Beyond the gateway, traffic is only as safe as the destination protocol, so keep using TLS and SSH on top.
  • The gateway is a single point of failure and a high-value target. Keep it patched, keep SSH on keys only, and restrict who can reach port 1194 where possible.
  • Revoking a lost laptop needs ./revoke-full client-laptop plus a crl-verify line in server.conf pointing at keys/crl.pem. This guide does not configure it.
  • Adding tls-auth with a shared HMAC key (openvpn --genkey --secret ta.key) lets the server drop unauthenticated packets early. That is useful against port scans and floods.

When to choose another method

  • Use proto tcp on port 443 only if UDP is blocked.
  • Use pushed routes instead of a redirected gateway when only a private network behind the server needs protection.
  • Use plain SSH port forwarding or a SOCKS proxy over SSH (ssh -D) for a single application, without any server-side setup beyond sshd.

Recent Searches