Set up a production-grade OpenVPN server with Easy-RSA certificate authority, automated client configuration generation, and certificate revocation management for secure remote access.
Prerequisites
- Root or sudo access
- Server with public IP address
- Open port 1194/UDP
What this solves
OpenVPN provides secure remote access to your private network, but manual certificate management becomes unwieldy as you scale. This tutorial sets up an OpenVPN server with automated certificate generation, client configuration bundling, and proper certificate revocation handling for production environments.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of OpenVPN and dependencies.
sudo apt update && sudo apt upgrade -y
Install OpenVPN and Easy-RSA
Install OpenVPN server and Easy-RSA for certificate management. Easy-RSA provides scripts for building a certificate authority and managing certificates.
sudo apt install -y openvpn easy-rsa iptables-persistent
Set up Easy-RSA certificate authority
Create a dedicated directory for the certificate authority and initialize it with Easy-RSA templates.
sudo mkdir -p /etc/openvpn/easy-rsa
sudo cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
cd /etc/openvpn/easy-rsa
sudo ./easyrsa init-pki
Configure Easy-RSA variables
Create the vars file to define certificate defaults and organizational information for your CA.
set_var EASYRSA_REQ_COUNTRY "US"
set_var EASYRSA_REQ_PROVINCE "CA"
set_var EASYRSA_REQ_CITY "San Francisco"
set_var EASYRSA_REQ_ORG "Example Corp"
set_var EASYRSA_REQ_EMAIL "admin@example.com"
set_var EASYRSA_REQ_OU "IT Department"
set_var EASYRSA_KEY_SIZE 2048
set_var EASYRSA_ALGO rsa
set_var EASYRSA_CA_EXPIRE 3650
set_var EASYRSA_CERT_EXPIRE 365
Build the certificate authority
Generate the root CA certificate that will sign all server and client certificates. Use a strong passphrase to protect the CA private key.
cd /etc/openvpn/easy-rsa
sudo ./easyrsa build-ca nopass
Generate server certificate and key
Create the server certificate and private key. The server certificate identifies your OpenVPN server to clients.
sudo ./easyrsa gen-req server nopass
sudo ./easyrsa sign-req server server
Generate Diffie-Hellman parameters
Generate DH parameters for perfect forward secrecy. This process may take several minutes depending on your server's CPU.
sudo ./easyrsa gen-dh
Generate TLS authentication key
Create an additional shared secret key to protect against DoS attacks and unauthorized connections.
sudo openvpn --genkey secret /etc/openvpn/easy-rsa/pki/ta.key
Copy certificates to OpenVPN directory
Move the generated certificates and keys to the OpenVPN configuration directory with proper permissions.
sudo cp /etc/openvpn/easy-rsa/pki/ca.crt /etc/openvpn/server/
sudo cp /etc/openvpn/easy-rsa/pki/issued/server.crt /etc/openvpn/server/
sudo cp /etc/openvpn/easy-rsa/pki/private/server.key /etc/openvpn/server/
sudo cp /etc/openvpn/easy-rsa/pki/dh.pem /etc/openvpn/server/dh2048.pem
sudo cp /etc/openvpn/easy-rsa/pki/ta.key /etc/openvpn/server/
Configure OpenVPN server
Create the main OpenVPN server configuration with secure defaults, routing, and certificate paths.
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
tls-auth ta.key 0
cipher AES-256-GCM
auth SHA256
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 120
comp-lzo
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
explicit-exit-notify 1
crl-verify /etc/openvpn/server/crl.pem
Create log directory
Create the logging directory and set proper permissions for the OpenVPN service.
sudo mkdir -p /var/log/openvpn
sudo chown nobody:nogroup /var/log/openvpn
Generate initial certificate revocation list
Create an empty CRL file that OpenVPN will use to check for revoked certificates.
cd /etc/openvpn/easy-rsa
sudo ./easyrsa gen-crl
sudo cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/server/
sudo chmod 644 /etc/openvpn/server/crl.pem
Enable IP forwarding
Configure the kernel to forward packets between the VPN network and your local network.
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Configure firewall and NAT
Set up iptables rules to allow VPN traffic and enable NAT for internet access. Replace eth0 with your actual network interface.
sudo iptables -A INPUT -i tun+ -j ACCEPT
sudo iptables -A FORWARD -i tun+ -j ACCEPT
sudo iptables -A INPUT -i eth0 -p udp --dport 1194 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -o tun+ -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i tun+ -o eth0 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo iptables-save | sudo tee /etc/iptables/rules.v4
ip route | grep default to find it.Create client certificate generation script
Create an automated script to generate client certificates and configuration files.
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 "
exit 1
fi
CLIENT_NAME="$1"
EASY_RSA_DIR="/etc/openvpn/easy-rsa"
CLIENT_DIR="/etc/openvpn/clients"
SERVER_IP=$(curl -s ifconfig.me)
Create client directory
mkdir -p "$CLIENT_DIR"
Generate client certificate
cd "$EASY_RSA_DIR"
./easyrsa gen-req "$CLIENT_NAME" nopass
./easyrsa sign-req client "$CLIENT_NAME"
Create client configuration
cat > "$CLIENT_DIR/$CLIENT_NAME.ovpn" <
$(cat $EASY_RSA_DIR/pki/ca.crt)
$(cat $EASY_RSA_DIR/pki/issued/$CLIENT_NAME.crt)
$(cat $EASY_RSA_DIR/pki/private/$CLIENT_NAME.key)
$(cat $EASY_RSA_DIR/pki/ta.key)
EOF
echo "Client configuration created: $CLIENT_DIR/$CLIENT_NAME.ovpn"
echo "Download this file to your client device."
EOF
Create certificate revocation script
Create a script to revoke client certificates and update the certificate revocation list.
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 "
exit 1
fi
CLIENT_NAME="$1"
EASY_RSA_DIR="/etc/openvpn/easy-rsa"
Revoke the certificate
cd "$EASY_RSA_DIR"
./easyrsa revoke "$CLIENT_NAME"
Generate new CRL
./easyrsa gen-crl
Copy updated CRL to OpenVPN directory
cp "$EASY_RSA_DIR/pki/crl.pem" /etc/openvpn/server/
chmod 644 /etc/openvpn/server/crl.pem
Restart OpenVPN to reload CRL
systemctl restart openvpn-server@server
echo "Certificate for $CLIENT_NAME has been revoked and CRL updated."
echo "Client will be disconnected within 10 minutes."
Make scripts executable
Set proper permissions on the client management scripts.
sudo chmod +x /usr/local/bin/openvpn-client-gen
sudo chmod +x /usr/local/bin/openvpn-client-revoke
Start and enable OpenVPN service
Enable the OpenVPN service to start automatically on boot and start it now.
sudo systemctl enable --now openvpn-server@server
sudo systemctl status openvpn-server@server
Generate your first client certificate
Create a test client
Generate your first client certificate and configuration file using the automation script.
sudo /usr/local/bin/openvpn-client-gen client1
Download client configuration
The client configuration file contains all necessary certificates and keys embedded inline.
sudo ls -la /etc/openvpn/clients/
sudo cat /etc/openvpn/clients/client1.ovpn
Certificate management operations
List all certificates
View all issued certificates and their expiration dates.
cd /etc/openvpn/easy-rsa
sudo ./easyrsa show-cert client1
Revoke a client certificate
Use the revocation script to immediately disable a client's access.
sudo /usr/local/bin/openvpn-client-revoke client1
Renew server certificate
Renew the server certificate before it expires. Clients don't need updates when only the server certificate is renewed.
cd /etc/openvpn/easy-rsa
sudo ./easyrsa renew server nopass
sudo cp /etc/openvpn/easy-rsa/pki/issued/server.crt /etc/openvpn/server/
sudo systemctl restart openvpn-server@server
Monitoring and maintenance
Monitor active connections
Check which clients are currently connected to your VPN server.
sudo cat /var/log/openvpn/openvpn-status.log
sudo journalctl -u openvpn-server@server -f
Set up automatic CRL updates
Create a cron job to periodically refresh the certificate revocation list.
echo '0 2 * cd /etc/openvpn/easy-rsa && ./easyrsa gen-crl && cp pki/crl.pem /etc/openvpn/server/ && systemctl reload openvpn-server@server' | sudo crontab -
Verify your setup
sudo systemctl status openvpn-server@server
sudo ss -tuln | grep 1194
sudo iptables -L -n | grep 1194
sudo cat /var/log/openvpn/openvpn.log | tail -20
Performance optimization
For high-traffic VPN servers, consider implementing network stack optimizations. Our guide on Linux network performance tuning covers advanced kernel parameters that can improve VPN throughput.
You can also integrate OpenVPN with monitoring solutions covered in our Prometheus and Grafana monitoring tutorial to track connection metrics and performance.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Service fails to start | Certificate path errors | Check file paths in server.conf match actual certificate locations |
| Clients can't connect | Firewall blocking port 1194 | Verify iptables rules and cloud security groups allow UDP 1194 |
| No internet access through VPN | IP forwarding disabled | Check sysctl net.ipv4.ip_forward returns 1 |
| Certificate errors on client | Clock skew or expired cert | Sync server time with NTP, check cert expiry with easyrsa show-cert |
| DNS resolution fails | DNS push options incorrect | Verify DNS servers in push directives are reachable |
| Permission denied on CRL | Wrong file permissions | Use chmod 644 /etc/openvpn/server/crl.pem |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# OpenVPN Server Installation and Configuration Script
# Supports Ubuntu/Debian and RHEL-based distributions
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default values
COUNTRY="${1:-US}"
PROVINCE="${2:-CA}"
CITY="${3:-San Francisco}"
ORG="${4:-Example Corp}"
EMAIL="${5:-admin@example.com}"
OU="${6:-IT Department}"
# Usage message
usage() {
echo "Usage: $0 [COUNTRY] [PROVINCE] [CITY] [ORG] [EMAIL] [OU]"
echo "Example: $0 US CA 'San Francisco' 'My Company' 'admin@mycompany.com' 'IT'"
exit 1
}
# Error handling and cleanup
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
systemctl stop openvpn-server@server 2>/dev/null || true
systemctl disable openvpn-server@server 2>/dev/null || true
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Auto-detect distribution
echo -e "${YELLOW}[1/15] Detecting distribution...${NC}"
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
FIREWALL_CMD="ufw"
OPENVPN_SERVICE="openvpn-server@server"
NOBODY_GROUP="nogroup"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
FIREWALL_CMD="firewall-cmd"
OPENVPN_SERVICE="openvpn-server@server"
NOBODY_GROUP="nobody"
if ! command -v dnf &> /dev/null; then
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
fi
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
FIREWALL_CMD="firewall-cmd"
OPENVPN_SERVICE="openvpn-server@server"
NOBODY_GROUP="nobody"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
# Update system packages
echo -e "${YELLOW}[2/15] Updating system packages...${NC}"
$PKG_UPDATE
# Install OpenVPN and Easy-RSA
echo -e "${YELLOW}[3/15] Installing OpenVPN and Easy-RSA...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL openvpn easy-rsa iptables-persistent
else
$PKG_INSTALL openvpn easy-rsa iptables-services
systemctl enable iptables
fi
# Set up Easy-RSA certificate authority
echo -e "${YELLOW}[4/15] Setting up Easy-RSA certificate authority...${NC}"
mkdir -p /etc/openvpn/easy-rsa
cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/ 2>/dev/null || cp -r /usr/share/easy-rsa/3/* /etc/openvpn/easy-rsa/
cd /etc/openvpn/easy-rsa
./easyrsa init-pki
# Configure Easy-RSA variables
echo -e "${YELLOW}[5/15] Configuring Easy-RSA variables...${NC}"
cat > /etc/openvpn/easy-rsa/vars << EOF
set_var EASYRSA_REQ_COUNTRY "$COUNTRY"
set_var EASYRSA_REQ_PROVINCE "$PROVINCE"
set_var EASYRSA_REQ_CITY "$CITY"
set_var EASYRSA_REQ_ORG "$ORG"
set_var EASYRSA_REQ_EMAIL "$EMAIL"
set_var EASYRSA_REQ_OU "$OU"
set_var EASYRSA_KEY_SIZE 2048
set_var EASYRSA_ALGO rsa
set_var EASYRSA_CA_EXPIRE 3650
set_var EASYRSA_CERT_EXPIRE 365
EOF
# Build the certificate authority
echo -e "${YELLOW}[6/15] Building certificate authority...${NC}"
cd /etc/openvpn/easy-rsa
./easyrsa build-ca nopass
# Generate server certificate and key
echo -e "${YELLOW}[7/15] Generating server certificate and key...${NC}"
./easyrsa gen-req server nopass
./easyrsa sign-req server server
# Generate Diffie-Hellman parameters
echo -e "${YELLOW}[8/15] Generating Diffie-Hellman parameters...${NC}"
./easyrsa gen-dh
# Generate TLS authentication key
echo -e "${YELLOW}[9/15] Generating TLS authentication key...${NC}"
openvpn --genkey secret /etc/openvpn/easy-rsa/pki/ta.key
# Create OpenVPN server directory
mkdir -p /etc/openvpn/server
# Copy certificates to OpenVPN directory
echo -e "${YELLOW}[10/15] Copying certificates to OpenVPN directory...${NC}"
cp /etc/openvpn/easy-rsa/pki/ca.crt /etc/openvpn/server/
cp /etc/openvpn/easy-rsa/pki/issued/server.crt /etc/openvpn/server/
cp /etc/openvpn/easy-rsa/pki/private/server.key /etc/openvpn/server/
cp /etc/openvpn/easy-rsa/pki/dh.pem /etc/openvpn/server/dh2048.pem
cp /etc/openvpn/easy-rsa/pki/ta.key /etc/openvpn/server/
# Set proper permissions
chmod 600 /etc/openvpn/server/server.key
chmod 600 /etc/openvpn/server/ta.key
chmod 644 /etc/openvpn/server/*.crt
chmod 644 /etc/openvpn/server/dh2048.pem
# Create log directory
echo -e "${YELLOW}[11/15] Creating log directory...${NC}"
mkdir -p /var/log/openvpn
chown nobody:$NOBODY_GROUP /var/log/openvpn
chmod 755 /var/log/openvpn
# Generate initial certificate revocation list
echo -e "${YELLOW}[12/15] Generating initial certificate revocation list...${NC}"
cd /etc/openvpn/easy-rsa
./easyrsa gen-crl
cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/server/
chmod 644 /etc/openvpn/server/crl.pem
# Configure OpenVPN server
echo -e "${YELLOW}[13/15] Configuring OpenVPN server...${NC}"
cat > /etc/openvpn/server/server.conf << 'EOF'
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
tls-auth ta.key 0
cipher AES-256-GCM
auth SHA256
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 120
comp-lzo
user nobody
group nobody
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
explicit-exit-notify 1
crl-verify crl.pem
EOF
# Fix group name for Debian-based systems
if [[ "$PKG_MGR" == "apt" ]]; then
sed -i 's/group nobody/group nogroup/' /etc/openvpn/server/server.conf
fi
# Configure IP forwarding
echo -e "${YELLOW}[14/15] Configuring IP forwarding and firewall...${NC}"
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p
# Configure firewall rules
if [[ "$FIREWALL_CMD" == "ufw" ]]; then
ufw allow 1194/udp
# Add NAT rules for ufw
sed -i '1i# START OPENVPN RULES\n# NAT table rules\n*nat\n:POSTROUTING ACCEPT [0:0]\n-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE\nCOMMIT\n# END OPENVPN RULES\n' /etc/ufw/before.rules
ufw --force enable
else
# For RHEL-based systems
if systemctl is-active firewalld &>/dev/null; then
firewall-cmd --permanent --add-service=openvpn
firewall-cmd --permanent --add-masquerade
firewall-cmd --permanent --direct --passthrough ipv4 -t nat -A POSTROUTING -s 10.8.0.0/8 -j MASQUERADE
firewall-cmd --reload
fi
fi
# Enable and start OpenVPN service
echo -e "${YELLOW}[15/15] Starting OpenVPN service...${NC}"
systemctl enable $OPENVPN_SERVICE
systemctl start $OPENVPN_SERVICE
# Verification
echo -e "${YELLOW}Verifying installation...${NC}"
if systemctl is-active --quiet $OPENVPN_SERVICE; then
echo -e "${GREEN}✓ OpenVPN service is running${NC}"
else
echo -e "${RED}✗ OpenVPN service failed to start${NC}"
exit 1
fi
if ss -ulpn | grep -q :1194; then
echo -e "${GREEN}✓ OpenVPN is listening on port 1194${NC}"
else
echo -e "${RED}✗ OpenVPN is not listening on port 1194${NC}"
exit 1
fi
echo -e "${GREEN}OpenVPN server installation completed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Generate client certificates using: cd /etc/openvpn/easy-rsa && ./easyrsa gen-req client1 nopass && ./easyrsa sign-req client client1"
echo "2. Create client configuration files"
echo "3. Configure port forwarding on your router if needed"
Review the script before running. Execute with: bash install.sh