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.