Stand up a basic OpenVPN server using TLS certificates generated by Easy-RSA, then validate service state and issue an initial client certificate. Focus on PKI workflow, secure key placement, and systemd service management on a RHEL-style host.
You are configuring a secure VPN tunnel for remote workers using OpenVPN with TLS-based authentication and encryption. Your goal is to bootstrap a Certificate Authority (CA), issue server credentials, start the OpenVPN instance, and confirm the server reaches an “Initialization Sequence Completed” state.
VPN is security infrastructure. Treat keys as secrets, apply least privilege to key files, and validate service behavior before distributing client configs.
/etc/openvpn/./etc/openvpn/.
openvpn@server targets /etc/openvpn/server.conf.
systemctl status output and logs.
sudo dnf install openvpn easy-rsa -y
Install the VPN service and the PKI tooling used to generate and sign certificates. On RHEL-like systems, Easy-RSA may come from an additional repository depending on your build.
make-cadir ~/openvpn-ca && cd ~/openvpn-ca
Easy-RSA packaging differs across distros. Some provide
make-cadir
while others expect you to copy the working tree from
/usr/share/easy-rsa/
into a writable directory.
./easyrsa init-pki && ./easyrsa build-ca
The CA is the trust anchor for all certificates in this VPN. Protect
pki/private/ca.key
like a production secret and keep it off shared paths.
# Confirm CA artifacts:
ls -la pki/ca.crt pki/private/ca.key
./easyrsa gen-req server nopass && ./easyrsa sign-req server server
This creates the server key and request, then signs it with your CA to produce the server certificate. Use a name that maps cleanly to the OpenVPN instance you are running.
# Confirm server artifacts:
ls -la pki/private/server.key pki/issued/server.crt
./easyrsa gen-dh
DH parameters may take time to generate. When complete, you should have
pki/dh.pem
available for the server configuration.
/etc/openvpn/.
sudo cp pki/ca.crt pki/private/server.key pki/issued/server.crt pki/dh.pem /etc/openvpn/
Lock down private keys immediately. Ensure root ownership and restrict permissions for
/etc/openvpn/server.key
before running a production workload.
# Example hardening:
sudo chown root:root /etc/openvpn/server.key
sudo chmod 600 /etc/openvpn/server.key
sudo gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz | sudo tee /etc/openvpn/server.conf > /dev/null
This gives you a starting config from the upstream sample. In production, explicitly review certificate paths, cipher negotiation, networking rules, and routing behavior before onboarding users.
# Spot-check key directives:
sudo grep -E '^(ca|cert|key|dh) ' /etc/openvpn/server.conf
sudo systemctl enable --now openvpn@server
The instance name
server
maps to
/etc/openvpn/server.conf
for the unit
openvpn@server
.
sudo systemctl status openvpn@server
The key confirmation is
Initialization Sequence Completed
in the unit status or logs. That indicates OpenVPN parsed config, loaded crypto material, and brought the tunnel interface up successfully.
# Optional log view:
sudo journalctl -u openvpn@server --no-pager
./easyrsa gen-req client1 nopass && ./easyrsa sign-req client client1
You now have CA, server cert/key, and a client cert/key. In a real deployment, the next step is to generate a client configuration file and securely distribute client credentials.
# Confirm client artifacts:
ls -la pki/private/client1.key pki/issued/client1.crt
Easy-RSA packaging differs. Locate the Easy-RSA working tree under
/usr/share/easy-rsa/
and copy it into a writable directory, then run PKI commands from there.
Validate certificate and key paths in
/etc/openvpn/server.conf
and confirm the files exist with the expected names under
/etc/openvpn/
. Then inspect unit logs via
journalctl -u openvpn@server
.
This typically points to configuration parsing errors, missing crypto material, or networking issues. Confirm the instance name matches the config file and validate the tunnel interface creation from the logs.
Treat this as a real security defect. Restrict permissions on private keys and ensure only root can read them before distributing any client material.
This lab stages keys and configuration on the host and creates a PKI working directory in your home folder. If this is a disposable lab host, stop the service and remove generated artifacts.
sudo systemctl disable --now openvpn@server
sudo rm -f /etc/openvpn/ca.crt /etc/openvpn/server.crt /etc/openvpn/server.key /etc/openvpn/dh.pem /etc/openvpn/server.conf
rm -rf ~/openvpn-ca
The OpenVPN unit is stopped and disabled, PKI artifacts are removed, and no private keys remain on disk in lab paths.
dnf install openvpn easy-rsa -y
: Installs OpenVPN and Easy-RSA tooling.
-y
: Automatically answers yes to prompts.
make-cadir <path>
: Creates an Easy-RSA working directory (packaging dependent).
./easyrsa init-pki
: Initializes the PKI directory structure under pki/.
./easyrsa build-ca
: Builds the Certificate Authority certificate and private key.
./easyrsa gen-req <name> [nopass]
: Generates a private key and certificate request for a named entity.
nopass
: Creates the private key without a passphrase.
./easyrsa sign-req <type> <name>
: Signs a certificate request with the CA.
<type>
: Request type such as server or client.
./easyrsa gen-dh
: Generates Diffie-Hellman parameters (outputs pki/dh.pem).
cp <src...> <dest>
: Copies certificate and key files into the OpenVPN configuration directory.
chmod 600 <file>
: Restricts a private key file to root-only read/write permissions.
600
: Owner read/write only.
gunzip -c <file.gz> | tee <file>
: Expands a gzipped sample config and writes it to a destination file.
-c
: Writes decompressed content to stdout.
|
: Pipes output from the left command into the right command.
systemctl enable --now openvpn@server
: Enables and starts the OpenVPN instance for server.conf.
--now
: Starts the unit immediately in addition to enabling it.
openvpn@server
: Instance unit mapped to /etc/openvpn/server.conf.
systemctl status openvpn@server
: Shows OpenVPN unit state and recent status output.
journalctl -u openvpn@server
: Shows logs for the OpenVPN instance unit.
-u
: Filters by unit name.
rm -rf <path>
: Removes a directory tree (use with caution).
-r
: Recursively removes directories and contents.
-f
: Forces removal and suppresses prompts/errors for missing files.