OpenVPN is a mature, battle-tested VPN solution that supports TLS-based encryption and X.509 certificate authentication, making it well-suited for enterprise environments where certificate lifecycle management and strong mutual authentication are required. Unlike WireGuard, OpenVPN can traverse firewalls over TCP port 443, which is useful when UDP is blocked. On RHEL 9, OpenVPN 2.x is available via the EPEL repository and pairs with Easy-RSA for certificate authority management. This tutorial walks through building a fully functional OpenVPN server with client certificate authentication.
Prerequisites
- A RHEL 9 server with a public IP address and root or sudo access
- Firewalld running and active
- EPEL repository enabled (
dnf install -y epel-release) - A client machine (Linux or Windows with the OpenVPN client) for testing
Step 1 — Install OpenVPN and Easy-RSA
sudo dnf install -y epel-release
sudo dnf install -y openvpn easy-rsa
Copy the Easy-RSA template to a working directory:
mkdir ~/easy-rsa
cp -r /usr/share/easy-rsa/3/* ~/easy-rsa/
cd ~/easy-rsa
Step 2 — Initialize the PKI and Build the Certificate Authority
Easy-RSA manages an entire X.509 public key infrastructure from the command line. Initialize it and create a self-signed CA certificate:
./easyrsa init-pki
./easyrsa build-ca nopass
When prompted for a Common Name, enter something descriptive like ProgressiveRobot-CA. Next, generate the server certificate request and sign it:
./easyrsa gen-req server nopass
./easyrsa sign-req server server
Confirm by typing yes when prompted. Generate Diffie-Hellman parameters (this takes a minute or two):
./easyrsa gen-dh
Generate a TLS authentication key to protect against DoS and replay attacks:
openvpn --genkey secret ~/easy-rsa/pki/ta.key
Step 3 — Copy Certificates to the OpenVPN Directory
sudo cp ~/easy-rsa/pki/ca.crt /etc/openvpn/server/
sudo cp ~/easy-rsa/pki/issued/server.crt /etc/openvpn/server/
sudo cp ~/easy-rsa/pki/private/server.key /etc/openvpn/server/
sudo cp ~/easy-rsa/pki/dh.pem /etc/openvpn/server/
sudo cp ~/easy-rsa/pki/ta.key /etc/openvpn/server/
Step 4 — Create the Server Configuration File
sudo nano /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
cipher AES-256-GCM
auth SHA256
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
# Push default gateway through VPN
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 8.8.8.8"
keepalive 10 120
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
user nobody
group nobody
Create the log directory:
sudo mkdir -p /var/log/openvpn
Step 5 — Enable IP Forwarding, NAT, and Start OpenVPN
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-openvpn.conf
sudo sysctl -p /etc/sysctl.d/99-openvpn.conf
# Open the firewall port
sudo firewall-cmd --permanent --add-port=1194/udp
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload
# Enable and start the service
sudo systemctl enable --now openvpn-server@server
sudo systemctl status openvpn-server@server
Step 6 — Generate a Client Certificate and Create the .ovpn Profile
cd ~/easy-rsa
./easyrsa gen-req client1 nopass
./easyrsa sign-req client client1
Create an inline .ovpn file that embeds all required certificates (easier to distribute to users):
cat > ~/client1.ovpn << EOF
client
dev tun
proto udp
remote YOUR_SERVER_IP 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
tls-auth [inline] 1
cipher AES-256-GCM
auth SHA256
verb 3
$(cat ~/easy-rsa/pki/ca.crt)
$(cat ~/easy-rsa/pki/issued/client1.crt)
$(cat ~/easy-rsa/pki/private/client1.key)
$(cat ~/easy-rsa/pki/ta.key)
EOF
Transfer client1.ovpn securely to the client machine (via SCP or SFTP) and import it into the OpenVPN client:
# On the client (Linux)
sudo openvpn --config client1.ovpn
Conclusion
You now have a fully operational OpenVPN server on RHEL 9 with TLS mutual certificate authentication, forward-secrecy cipher suites, and NAT masquerade for internet routing. For production deployments, consider placing the CA private key on an offline machine, implementing certificate revocation lists (CRLs) with ./easyrsa gen-crl, and adding two-factor authentication via the openvpn-auth-ldap or openvpn-plugin-auth-pam plugins. Monitor /var/log/openvpn/openvpn.log for connection events and certificate validation errors.
Next steps: How to Set Up WireGuard VPN on RHEL 9, How to Harden SSH on RHEL 9, and How to Configure SELinux on RHEL 9.