OpenVPN server

  • This how-to describes the method for setting up OpenVPN server on OpenWrt.
  • Follow OpenVPN client for client setup and OpenVPN extras for additional tuning.
  • It requires OpenWrt 21.02+ with OpenVPN 2.5+ supporting tls-crypt-v2.
    • OpenWrt 19.07 users with OpenVPN 2.4 should refer to an older revision.
  • Encrypt your internet connection to enforce security and privacy.
    • Prevent traffic leaks and spoofing on the client side.
  • Bypass regional restrictions using commercial providers.
    • Escape client side content filters and internet censorship.
  • Access your LAN services remotely without port forwarding.

Setting things up manually can be cumbersome with OpenVPN and a simple client. Use the latest all-in-one script once you've installed packages to manage things: all-in-one_openvpn_management_script

Install the required packages. Specify configuration parameters for VPN server.

# Install packages
opkg update
opkg install openvpn-openssl openvpn-easy-rsa
 
# Configuration parameters
VPN_DIR="/etc/openvpn"
VPN_PKI="/etc/easy-rsa/pki"
VPN_PORT="1194"
VPN_PROTO="udp"
VPN_POOL="192.168.9.0 255.255.255.0"
VPN_DNS="${VPN_POOL%.* *}.1"
VPN_DN="$(uci -q get dhcp.@dnsmasq[0].domain)"
 
# Fetch server address
NET_FQDN="$(uci -q get ddns.@service[0].lookup_host)"
. /lib/functions/network.sh
network_flush_cache
network_find_wan NET_IF
network_get_ipaddr NET_ADDR "${NET_IF}"
if [ -n "${NET_FQDN}" ]
then VPN_SERV="${NET_FQDN}"
else VPN_SERV="${NET_ADDR}"
fi

Use EasyRSA to manage the PKI. Utilize private key password protection if necessary.

# Configuration parameters
export EASYRSA_PKI="${VPN_PKI}"
export EASYRSA_TEMP_DIR="/tmp"
export EASYRSA_CERT_EXPIRE="3650"
export EASYRSA_BATCH="1"
 
# Remove and re-initialize PKI directory
easyrsa init-pki
 
# Generate DH parameters
easyrsa gen-dh
 
# Create a new CA
easyrsa build-ca nopass
 
# Generate server keys and certificate
easyrsa build-server-full server nopass
openvpn --genkey tls-crypt-v2-server ${EASYRSA_PKI}/private/server.pem
 
# Generate client keys and certificate
easyrsa build-client-full client nopass
openvpn --tls-crypt-v2 ${EASYRSA_PKI}/private/server.pem \
--genkey tls-crypt-v2-client ${EASYRSA_PKI}/private/client.pem

Consider VPN network as private. Assign VPN interface to LAN zone to minimize firewall setup. Allow access to VPN server from WAN zone.

# Configure firewall
uci rename firewall.@zone[0]="lan"
uci rename firewall.@zone[1]="wan"
uci del_list firewall.lan.device="tun+"
uci add_list firewall.lan.device="tun+"
uci -q delete firewall.ovpn
uci set firewall.ovpn="rule"
uci set firewall.ovpn.name="Allow-OpenVPN"
uci set firewall.ovpn.src="wan"
uci set firewall.ovpn.dest_port="${VPN_PORT}"
uci set firewall.ovpn.proto="${VPN_PROTO}"
uci set firewall.ovpn.target="ACCEPT"
uci commit firewall
service firewall restart

Configure VPN service and generate client profiles.

# Configure VPN service and generate client profiles
umask go=
VPN_DH="$(cat ${VPN_PKI}/dh.pem)"
VPN_CA="$(openssl x509 -in ${VPN_PKI}/ca.crt)"
ls ${VPN_PKI}/issued \
| sed -e "s/\.\w*$//" \
| while read -r VPN_ID
do
VPN_TC="$(cat ${VPN_PKI}/private/${VPN_ID}.pem)"
VPN_KEY="$(cat ${VPN_PKI}/private/${VPN_ID}.key)"
VPN_CERT="$(openssl x509 -in ${VPN_PKI}/issued/${VPN_ID}.crt)"
VPN_EKU="$(echo "${VPN_CERT}" | openssl x509 -noout -purpose)"
case ${VPN_EKU} in
(*"SSL server : Yes"*)
VPN_CONF="${VPN_DIR}/${VPN_ID}.conf"
cat << EOF > ${VPN_CONF} ;;
user nobody
group nogroup
dev tun
port ${VPN_PORT}
proto ${VPN_PROTO}
server ${VPN_POOL}
topology subnet
client-to-client
keepalive 10 60
persist-tun
persist-key
push "dhcp-option DNS ${VPN_DNS}"
push "dhcp-option DOMAIN ${VPN_DN}"
push "redirect-gateway def1"
push "persist-tun"
push "persist-key"
<dh>
${VPN_DH}
</dh>
EOF
(*"SSL client : Yes"*)
VPN_CONF="${VPN_DIR}/${VPN_ID}.ovpn"
cat << EOF > ${VPN_CONF} ;;
user nobody
group nogroup
dev tun
nobind
client
remote ${VPN_SERV} ${VPN_PORT} ${VPN_PROTO}
auth-nocache
remote-cert-tls server
EOF
esac
cat << EOF >> ${VPN_CONF}
<tls-crypt-v2>
${VPN_TC}
</tls-crypt-v2>
<key>
${VPN_KEY}
</key>
<cert>
${VPN_CERT}
</cert>
<ca>
${VPN_CA}
</ca>
EOF
done
service openvpn restart
ls ${VPN_DIR}/*.ovpn

Basic openvpn server configuration is now complete.

  1. Perform OpenWrt backup.
  2. Either extract client profile from the archive file, or use SCP to retrieve the /etc/openvpn/client.ovpn file from the router.
  3. Review/edit the IP address for the 'remote' line contained within the client.ovpn file.
  4. Import the client.ovpn profile into your clients.

For an additional .ovpn after completing the above:

  1. Run this multi-client script.
  2. Now make a script consisting of the “Configuration parameters” of Part 1 above and all of Part 4 above and run it. Note that the “remote” line may be missing in the new ovpn (use the original as a reference for that).

Establish the VPN connection. Verify your routing with traceroute and traceroute6.

traceroute openwrt.org
traceroute6 openwrt.org

Check your IP and DNS provider.

On router:

  • Go to LuCI > Status > Wireguard and look for peer device connected with an IPv4 or IPv6 address and with a recent handshake time
  • Go to LuCI > Network > Diagnostics and ipv4 ping client device IP eg. 10.0.0.10

On client device depending on wireguard software:

  • Check transfer traffic for tx & rx
  • Ping router internal lan IP
  • Check public IP address in a browser – https://whatsmyip.com – should see public IP address of ISP for the router

Collect and analyze the following information.

# Restart services
service log restart; service openvpn restart; sleep 10
 
# Log and status
logread -e openvpn; netstat -l -n -p | grep -e openvpn
 
# Runtime configuration
pgrep -f -a openvpn
ip address show; ip route show table all
ip rule show; ip -6 rule show; nft list ruleset
 
# Persistent configuration
uci show network; uci show firewall; uci show openvpn
head -v -n -0 /etc/openvpn/*.conf

Tired of managing things piecemeal ? Use this script on the CLI to manage it all. Want to contribute or give feedback to the script, visit here: https://github.com/beadon/OpenWRTOpenVPNMgmt

Otherwise, grab the file below, it's interactive!

URL="https://openwrt.org/_export/code/docs/guide-user/services/vpn/openvpn/server"
cat << EOF > openvpn_server_management.sh
$(wget -U "" -O - "${URL}?codeblock=7")
EOF
chmod 775 openvpn_server_management.sh
#!/bin/sh

# Configuration parameters
OVPN_PKI="/etc/easy-rsa/pki"
OVPN_DIR="/root/ovpn_config_out"
OVPN_SERVER_CONF="/etc/openvpn/server.conf"
OVPN_SERVER_BACKUP="/etc/openvpn/server.conf.BAK"
export EASYRSA_PKI="${OVPN_PKI}"
export EASYRSA_BATCH="1"

# OpenVPN server configuration - EDIT THESE VALUES
OVPN_SERV="vpn.example.com"  # Your VPN server address
OVPN_PORT="1194"              # VPN port
OVPN_PROTO="udp"              # Protocol: udp or tcp
OVPN_POOL="10.8.0.0 255.255.255.0"  # VPN subnet

# Auto-detect DNS and domain from OpenWrt UCI
OVPN_DNS="${OVPN_POOL%.* *}.1"
OVPN_DOMAIN=$(uci get dhcp.@dnsmasq[0].domain 2>/dev/null || echo "lan")

# Auto-Detect DDNS configured name, Fetch server address configured elsewhere
auto_detect_fqdn() {

    echo ""                                      
    echo "Active script-selected server settings:"                     
    echo "  Port: $OVPN_PORT"                    
    echo "  Protocol: $OVPN_PROTO"               
    echo "  VPN Subnet: $OVPN_POOL"              
    echo "  DNS Server: $OVPN_DNS"
    echo "  Domain: $OVPN_DOMAIN"                
    echo "  VPN Server: $OVPN_SERV"
    echo "  VPN Pool: $OVPN_POOL"
    echo ""                                      

    NET_FQDN="$(uci -q get ddns.@service[0].lookup_host)"
    . /lib/functions/network.sh
    network_flush_cache
    network_find_wan NET_IF
    network_get_ipaddr NET_ADDR "${NET_IF}"
    if [ -n "${NET_FQDN}" ]
    then OVPN_SERV="${NET_FQDN}"
    else OVPN_SERV="${NET_ADDR}"
    fi

    echo ""                                      
    echo "Auto-Detected server settings:"                     
    echo "  Port: $OVPN_PORT"                    
    echo "  Protocol: $OVPN_PROTO"               
    echo "  VPN Subnet: $OVPN_POOL"              
    echo "  DNS Server: $OVPN_DNS"
    echo "  Domain: $OVPN_DOMAIN"                
    echo "  VPN Server: $OVPN_SERV"
    echo "  VPN Pool: $OVPN_POOL"
    echo ""                                      

}

# Ensure output directory exists
if [ ! -d "$OVPN_DIR" ]; then
    mkdir -p "$OVPN_DIR"
fi

# Function to check if OpenVPN port is open in firewall
check_firewall() {
    echo ""
    echo "=== Checking Firewall Configuration ==="
    echo ""
    echo "Checking for OpenVPN port ${OVPN_PORT}/${OVPN_PROTO} on WAN..."
    echo ""
    
    # Check if firewall config exists
    if ! uci show firewall >/dev/null 2>&1; then
        echo "WARNING: Cannot access firewall configuration"
        echo "Firewall may not be configured or UCI is not available"
        return 1
    fi
    
    port_open=0
    rule_index=0
    
    # Loop through all firewall rules
    while true; do
        rule_name=$(uci get "firewall.@rule[${rule_index}].name" 2>/dev/null)
        if [ $? -ne 0 ]; then
            # No more rules
            break
        fi
        
        # Get rule properties
        rule_src=$(uci get "firewall.@rule[${rule_index}].src" 2>/dev/null)
        rule_proto=$(uci get "firewall.@rule[${rule_index}].proto" 2>/dev/null)
        rule_dest_port=$(uci get "firewall.@rule[${rule_index}].dest_port" 2>/dev/null)
        rule_target=$(uci get "firewall.@rule[${rule_index}].target" 2>/dev/null)
        
        # Check if this rule opens our OpenVPN port
        if [ "$rule_src" = "wan" ] && \
           [ "$rule_target" = "ACCEPT" ] && \
           [ "$rule_dest_port" = "$OVPN_PORT" ]; then
            
            # Check if protocol matches (or if rule accepts all protocols)
            if [ "$rule_proto" = "$OVPN_PROTO" ] || \
               [ "$rule_proto" = "tcpudp" ] || \
               [ -z "$rule_proto" ]; then
                port_open=1
                echo "  Firewall rule found: $rule_name"
                echo "  Port ${OVPN_PORT}/${OVPN_PROTO} is OPEN on WAN"
                break
            fi
        fi
        
        rule_index=$((rule_index + 1))
    done
    
    if [ $port_open -eq 0 ]; then
        echo " WARNING: No firewall rule found!"
        echo ""
        echo "OpenVPN port ${OVPN_PORT}/${OVPN_PROTO} does not appear to be open on WAN."
        echo ""
        echo "To open the port, run these commands:"
        echo ""
        echo "  uci add firewall rule"
        echo "  uci set firewall.@rule[-1].name='Allow-OpenVPN'"
        echo "  uci set firewall.@rule[-1].src='wan'"
        echo "  uci set firewall.@rule[-1].dest_port='${OVPN_PORT}'"
        echo "  uci set firewall.@rule[-1].proto='${OVPN_PROTO}'"
        echo "  uci set firewall.@rule[-1].target='ACCEPT'"
        echo "  uci commit firewall"
        echo "  service firewall restart"
        echo ""
        read -p "Would you like to add this firewall rule now? (y/n): " add_rule
        
        if [ "$add_rule" = "y" ] || [ "$add_rule" = "Y" ]; then
            echo ""
            echo "Adding firewall rule..."
            uci add firewall rule
            uci set firewall.@rule[-1].name='Allow-OpenVPN'
            uci set firewall.@rule[-1].src='wan'
            uci set firewall.@rule[-1].dest_port="${OVPN_PORT}"
            uci set firewall.@rule[-1].proto="${OVPN_PROTO}"
            uci set firewall.@rule[-1].target='ACCEPT'
            uci commit firewall
            service firewall restart
            
            echo "✓ Firewall rule added and applied"
        else
            echo "Firewall rule not added. Remember to open port ${OVPN_PORT}/${OVPN_PROTO} manually."
        fi
    fi
    
    echo ""
}

# Function to generate/update server.conf
generate_server_conf() {
    echo ""
    echo "=== Generate/Update OpenVPN Server Configuration ==="
    echo ""
    echo "Current settings:"
    echo "  Port: $OVPN_PORT"
    echo "  Protocol: $OVPN_PROTO"
    echo "  VPN Subnet: $OVPN_POOL"
    echo "  DNS Server: $OVPN_DNS"
    echo "  Domain: $OVPN_DOMAIN"
    echo ""
    
    if [ -f "$OVPN_SERVER_CONF" ]; then
        echo "WARNING: Existing server.conf found at $OVPN_SERVER_CONF"
        echo "A backup will be created at $OVPN_SERVER_BACKUP"
        echo ""
        read -p "Continue and overwrite? (yes/no): " confirm
        
        if [ "$confirm" != "yes" ]; then
            echo "Operation cancelled."
            return 0
        fi
        
        # Create backup
        echo "Creating backup..."
        cp "$OVPN_SERVER_CONF" "$OVPN_SERVER_BACKUP"
        echo "Backup created: $OVPN_SERVER_BACKUP"
    else
        echo "No existing server.conf found. Creating new configuration."
        read -p "Continue? (y/n): " confirm
        
        if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
            echo "Operation cancelled."
            return 0
        fi
    fi
    
    echo ""
    echo "Generating server.conf..."
    
    # Ensure directory exists
    mkdir -p "$(dirname "$OVPN_SERVER_CONF")"
    
    # Generate server configuration
    cat << EOF > ${OVPN_SERVER_CONF}
# OpenVPN Server Configuration
# Generated by manage_openvpn_keys.sh

# Network settings
port ${OVPN_PORT}
proto ${OVPN_PROTO}
dev tun

# Server mode and VPN subnet
server ${OVPN_POOL}
topology subnet

# Certificate and key files
ca ${OVPN_PKI}/ca.crt
cert ${OVPN_PKI}/issued/server.crt
key ${OVPN_PKI}/private/server.key
dh ${OVPN_PKI}/dh.pem

# TLS authentication
tls-crypt-v2 ${OVPN_PKI}/private/server.pem

# Client configuration
client-to-client
keepalive 10 60

# Push routes and DNS to clients
push "redirect-gateway def1"
push "dhcp-option DNS ${OVPN_DNS}"
push "dhcp-option DOMAIN ${OVPN_DOMAIN}"
push "persist-tun"
push "persist-key"

# Privileges and security
user nobody
group nogroup
persist-tun
persist-key

# Logging
status /var/log/openvpn-status.log
log-append /var/log/openvpn.log
verb 3

# Certificate Revocation List (uncomment after first revocation)
# crl-verify ${OVPN_PKI}/crl.pem
EOF
    
    echo ""
    echo "Server configuration created: $OVPN_SERVER_CONF"
    echo ""
    echo "IMPORTANT: Review the configuration file before restarting OpenVPN"
    echo ""
    
    read -p "View the generated configuration? (y/n): " view
    if [ "$view" = "y" ] || [ "$view" = "Y" ]; then
        echo ""
        echo "=== Generated Configuration ==="
        cat "$OVPN_SERVER_CONF"
        echo "=== End of Configuration ==="
        echo ""
    fi
    
    # Check firewall
    check_firewall
    
    read -p "Restart OpenVPN daemon to apply changes? (y/n): " restart
    if [ "$restart" = "y" ] || [ "$restart" = "Y" ]; then
        /etc/init.d/openvpn restart
        echo "OpenVPN daemon restarted"
    else
        echo "Remember to restart OpenVPN daemon: /etc/init.d/openvpn restart"
    fi
}

# Function to restore server.conf from backup
restore_server_conf() {
    echo ""
    echo "=== Restore OpenVPN Server Configuration from Backup ==="
    echo ""
    
    if [ ! -f "$OVPN_SERVER_BACKUP" ]; then
        echo "Error: No backup file found at $OVPN_SERVER_BACKUP"
        return 1
    fi
    
    echo "Backup found: $OVPN_SERVER_BACKUP"
    echo ""
    echo "WARNING: This will restore the server configuration from backup"
    echo "Current configuration will be overwritten."
    echo ""
    read -p "Continue? (yes/no): " confirm
    
    if [ "$confirm" != "yes" ]; then
        echo "Restore cancelled."
        return 0
    fi
    
    echo "Restoring configuration..."
    cp "$OVPN_SERVER_BACKUP" "$OVPN_SERVER_CONF"
    
    echo "Configuration restored from backup"
    echo ""
    
    read -p "Restart OpenVPN daemon? (y/n): " restart
    if [ "$restart" = "y" ] || [ "$restart" = "Y" ]; then
        /etc/init.d/openvpn restart
        echo "OpenVPN daemon restarted"
    fi
}

# Function to list all issued clients
list_clients() {
    echo ""
    echo "=== Current OpenVPN Clients ==="
    if [ -d "${OVPN_PKI}/issued" ]; then
        for cert in ${OVPN_PKI}/issued/*.crt; do
            if [ -f "$cert" ]; then
                basename=$(basename "$cert" .crt)
                if [ "$basename" != "server" ]; then
                    echo "  - $basename"
                fi
            fi
        done
    else
        echo "No issued directory found at ${OVPN_PKI}/issued"
    fi
    echo ""
}

# Function to check certificate expiration dates
check_expiration() {
    echo ""
    echo "=== Certificate Expiration Status ==="
    echo ""
    
    if [ ! -d "${OVPN_PKI}/issued" ]; then
        echo "No issued directory found at ${OVPN_PKI}/issued"
        return 1
    fi
    
    current_date=$(date +%s)
    warning_threshold=$((30 * 24 * 60 * 60))  # 30 days in seconds
    
    for cert in ${OVPN_PKI}/issued/*.crt; do
        if [ -f "$cert" ]; then
            basename=$(basename "$cert" .crt)
            
            # Get expiration date
            not_after=$(openssl x509 -in "$cert" -noout -enddate | cut -d= -f2)
            exp_date=$(date -d "$not_after" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$not_after" +%s 2>/dev/null)
            
            if [ -n "$exp_date" ]; then
                days_left=$(( ($exp_date - $current_date) / 86400 ))
                
                if [ $days_left -lt 0 ]; then
                    echo "  [EXPIRED] $basename: $days_left days"
                elif [ $days_left -lt 30 ]; then
                    echo "  [WARNING] $basename: $days_left days left"
                elif [ $days_left -lt 90 ]; then
                    echo "  [SOON]    $basename: $days_left days left"
                else
                    echo "  [OK]      $basename: $days_left days left"
                fi
            else
                echo "  [ERROR]   $basename: Could not parse expiration date"
            fi
        fi
    done
    echo ""
}

# Function to show certificate details
show_cert_details() {
    echo ""
    echo "=== Available Certificates ==="
    
    counter=1
    if [ -d "${OVPN_PKI}/issued" ]; then
        for cert in ${OVPN_PKI}/issued/*.crt; do
            if [ -f "$cert" ]; then
                basename=$(basename "$cert" .crt)
                echo "  $counter) $basename"
                counter=$((counter + 1))
            fi
        done
    fi
    
    if [ "$counter" -eq 1 ]; then
        echo "No certificates found."
        return 1
    fi
    
    echo ""
    read -p "Enter certificate name to view details: " cert_name
    
    if [ -z "$cert_name" ]; then
        echo "Error: No certificate name entered"
        return 1
    fi
    
    cert_path="${OVPN_PKI}/issued/${cert_name}.crt"
    
    if [ ! -f "$cert_path" ]; then
        echo "Error: Certificate '$cert_name' not found"
        return 1
    fi
    
    echo ""
    echo "=== Certificate Details for: $cert_name ==="
    echo ""
    
    # Extract and display key information
    openssl x509 -in "$cert_path" -noout -subject -issuer -dates -serial -purpose
    
    echo ""
}

# Function to renew a certificate
renew_certificate() {
    echo ""
    echo "=== Renew Certificate ==="
    echo ""
    echo "Available certificates:"
    
    counter=1
    if [ -d "${OVPN_PKI}/issued" ]; then
        for cert in ${OVPN_PKI}/issued/*.crt; do
            if [ -f "$cert" ]; then
                basename=$(basename "$cert" .crt)
                if [ "$basename" != "server" ]; then
                    echo "  $counter) $basename"
                    counter=$((counter + 1))
                fi
            fi
        done
    fi
    
    if [ "$counter" -eq 1 ]; then
        echo "No client certificates found to renew."
        return 1
    fi
    
    echo ""
    read -p "Enter certificate name to renew: " cert_name
    
    if [ -z "$cert_name" ]; then
        echo "Error: No certificate name entered"
        return 1
    fi
    
    if [ ! -f "${OVPN_PKI}/issued/${cert_name}.crt" ]; then
        echo "Error: Certificate '$cert_name' not found"
        return 1
    fi
    
    echo ""
    echo "WARNING: This will renew the certificate for: $cert_name"
    echo "The old certificate will be marked as expired."
    read -p "Continue? (yes/no): " confirm
    
    if [ "$confirm" != "yes" ]; then
        echo "Renewal cancelled."
        return 0
    fi
    
    echo ""
    echo "Renewing certificate for $cert_name..."
    
    # Use easyrsa renew command (available in easyrsa 3.2.1+)
    # If renew is not available, use the expire + sign-req method
    if easyrsa help 2>&1 | grep -q "renew"; then
        easyrsa renew "$cert_name" nopass
    else
        echo "Note: Using expire + sign-req method (easyrsa < 3.2.1)"
        easyrsa expire "$cert_name" && easyrsa sign-req client "$cert_name"
    fi
    
    if [ $? -eq 0 ]; then
        echo ""
        echo "Certificate renewed successfully!"
        echo "Note: You will need to regenerate the .ovpn config file for this client."
        echo ""
        read -p "Regenerate .ovpn file now? (y/n): " regen
        if [ "$regen" = "y" ] || [ "$regen" = "Y" ]; then
            generate_single_ovpn "$cert_name"
        fi
    else
        echo "Error: Certificate renewal failed"
    fi
}

# Function to generate a single .ovpn file
generate_single_ovpn() {
    OVPN_ID="$1"
    
    if [ -z "$OVPN_ID" ]; then
        echo "Error: No client name provided"
        return 1
    fi
    
    if [ ! -f "${OVPN_PKI}/issued/${OVPN_ID}.crt" ]; then
        echo "Error: Certificate for '$OVPN_ID' not found"
        return 1
    fi
    
    echo "Generating .ovpn file for $OVPN_ID..."
    
    umask go=
    OVPN_CA="$(openssl x509 -in ${OVPN_PKI}/ca.crt)"
    OVPN_TC="$(cat ${OVPN_PKI}/private/${OVPN_ID}.pem)"
    OVPN_KEY="$(cat ${OVPN_PKI}/private/${OVPN_ID}.key)"
    OVPN_CERT="$(openssl x509 -in ${OVPN_PKI}/issued/${OVPN_ID}.crt)"
    
    OVPN_CONF="${OVPN_DIR}/${OVPN_ID}.ovpn"
    
    cat << EOF > ${OVPN_CONF}
user nobody
group nogroup
dev tun
nobind
client
remote ${OVPN_SERV} ${OVPN_PORT} ${OVPN_PROTO}
auth-nocache
remote-cert-tls server
<tls-crypt-v2>
${OVPN_TC}
</tls-crypt-v2>
<key>
${OVPN_KEY}
</key>
<cert>
${OVPN_CERT}
</cert>
<ca>
${OVPN_CA}
</ca>
EOF
    
    echo "Generated: ${OVPN_CONF}"
}

# Function to generate all .ovpn files
generate_all_ovpn() {
    echo ""
    echo "=== Generate Client Configuration Files ==="
    echo ""
    echo "This will generate .ovpn files for all client certificates."
    echo "Output directory: $OVPN_DIR"
    echo ""
    read -p "Continue? (y/n): " confirm
    
    if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
        echo "Cancelled."
        return 0
    fi
    
    echo ""
    echo "Generating configuration files..."
    echo ""
    
    umask go=
    OVPN_DH="$(cat ${OVPN_PKI}/dh.pem)"
    OVPN_CA="$(openssl x509 -in ${OVPN_PKI}/ca.crt)"
    
    ls ${OVPN_PKI}/issued/*.crt 2>/dev/null | while read -r cert_file; do
        OVPN_ID=$(basename "$cert_file" .crt)
        
        OVPN_CERT="$(openssl x509 -in ${cert_file})"
        OVPN_EKU="$(echo "${OVPN_CERT}" | openssl x509 -noout -purpose)"
        
        case ${OVPN_EKU} in
            (*"SSL server : Yes"*)
                # Skip server certificates in batch client generation
                echo "Skipping server certificate: ${OVPN_ID}"
                ;;
            (*"SSL client : Yes"*)
                # Generate client config
                OVPN_TC="$(cat ${OVPN_PKI}/private/${OVPN_ID}.pem)"
                OVPN_KEY="$(cat ${OVPN_PKI}/private/${OVPN_ID}.key)"
                
                OVPN_CONF="${OVPN_DIR}/${OVPN_ID}.ovpn"
                cat << EOF > ${OVPN_CONF}
user nobody
group nogroup
dev tun
nobind
client
remote ${OVPN_SERV} ${OVPN_PORT} ${OVPN_PROTO}
auth-nocache
remote-cert-tls server
<tls-crypt-v2>
${OVPN_TC}
</tls-crypt-v2>
<key>
${OVPN_KEY}
</key>
<cert>
${OVPN_CERT}
</cert>
<ca>
${OVPN_CA}
</ca>
EOF
                echo "Generated client config: ${OVPN_CONF}"
                ;;
        esac
    done
    
    echo ""
    echo "Configuration files generated in: $OVPN_DIR"
    echo ""
    ls -lh ${OVPN_DIR}/*.ovpn 2>/dev/null
    echo ""
}

# Function to create new client
create_client() {
    read -p "Enter client name: " NEW_CLIENT
    
    if [ -z "$NEW_CLIENT" ]; then
        echo "Error: Client name cannot be empty"
        return 1
    fi
    
    echo "Building new keys for $NEW_CLIENT"
    easyrsa build-client-full $NEW_CLIENT nopass
    openvpn --tls-crypt-v2 ${EASYRSA_PKI}/private/server.pem \
        --genkey tls-crypt-v2-client ${EASYRSA_PKI}/private/$NEW_CLIENT.pem
    
    echo ""
    read -p "Generate .ovpn config file? (y/n): " gen_ovpn
    if [ "$gen_ovpn" = "y" ] || [ "$gen_ovpn" = "Y" ]; then
        generate_single_ovpn "$NEW_CLIENT"
    fi
    
    echo ""
    read -t 10 -p "OpenVPN Daemon restart. 10s timeout. Continue? (y/n): " response
    if [ "$response" = "y" ] || [ "$response" = "Y" ]; then
        /etc/init.d/openvpn restart
        echo "OpenVPN daemon restarted"
    else
        echo "OpenVPN daemon not restarted."
        echo "Keys will not be valid until the daemon refreshes them"
    fi
}

# Function to revoke a client
revoke_client() {
    echo ""
    echo "=== Available Clients to Revoke ==="
    
    counter=1
    if [ -d "${OVPN_PKI}/issued" ]; then
        for cert in ${OVPN_PKI}/issued/*.crt; do
            if [ -f "$cert" ]; then
                basename=$(basename "$cert" .crt)
                if [ "$basename" != "server" ]; then
                    echo "  $counter) $basename"
                    counter=$((counter + 1))
                fi
            fi
        done
    fi
    
    if [ "$counter" -eq 1 ]; then
        echo "No clients found to revoke."
        return 1
    fi
    
    echo ""
    read -p "Enter client name to revoke: " CLIENT_TO_REVOKE
    
    if [ -z "$CLIENT_TO_REVOKE" ]; then
        echo "Error: No client name entered"
        return 1
    fi
    
    if [ ! -f "${OVPN_PKI}/issued/${CLIENT_TO_REVOKE}.crt" ]; then
        echo "Error: Client '$CLIENT_TO_REVOKE' not found in issued certificates"
        return 1
    fi
    
    echo ""
    echo "WARNING: You are about to revoke certificate for: $CLIENT_TO_REVOKE"
    read -p "Are you sure? (yes/no): " confirm
    
    case $confirm in
        yes)
            echo "Revoking certificate for $CLIENT_TO_REVOKE..."
            easyrsa revoke $CLIENT_TO_REVOKE
            
            echo "Generating Certificate Revocation List (CRL)..."
            easyrsa gen-crl
            
            echo ""
            echo "Certificate revoked successfully."
            echo "CRL updated at: ${OVPN_PKI}/crl.pem"
            echo ""
            echo "NOTE: Ensure 'crl-verify' is enabled in your server.conf"
            echo ""
            
            read -p "Restart OpenVPN daemon to apply changes? (y/n): " response
            if [ "$response" = "y" ] || [ "$response" = "Y" ]; then
                /etc/init.d/openvpn restart
                echo "OpenVPN daemon restarted"
            else
                echo "Remember to restart OpenVPN daemon for changes to take effect"
            fi
            ;;
        *)
            echo "Revocation cancelled."
            ;;
    esac
}

key_management_first_time() {

    # Configuration parameters
    export EASYRSA_PKI="${OVPN_PKI}"
    export EASYRSA_TEMP_DIR="/tmp"
    export EASYRSA_CERT_EXPIRE="3650"
    export EASYRSA_BATCH="1"
 
    # Remove and re-initialize PKI directory
    easyrsa init-pki
 
    # Generate DH parameters
    easyrsa gen-dh
 
    # Create a new CA
    easyrsa build-ca nopass
 
    # Generate server keys and certificate
    easyrsa build-server-full server nopass
    openvpn --genkey tls-crypt-v2-server ${EASYRSA_PKI}/private/server.pem

}

# Main menu
while true; do
    echo ""
    echo "=================================================="
    echo "   OpenVPN Key Management"
    echo "=================================================="
    echo "Server Configuration:"
    echo "  0) Auto-Detect server settings"
    echo "  1) Generate/Update server.conf"
    echo "  2) Restore server.conf from backup"
    echo "  3) Check firewall configuration"
    echo ""
    echo "Certificate Management:"
    echo "  4) Create new client certificate"
    echo "  5) List current clients"
    echo "  6) Revoke client certificate"
    echo "  7) Check certificate expiration"
    echo "  8) Renew certificate"
    echo "  9) Show certificate details"
    echo ""
    echo "Configuration Files:"
    echo " 10) Generate all .ovpn config files"
    echo " 11) Generate single .ovpn config file"
    echo ""
    echo "EasyRSA Management:"
    echo " 12) Install and initialize EasyRSA for OpenVPN"
    echo ""
    echo " 13) Exit"
    echo ""
    read -p "Select an option (0-13): " choice
    
    case $choice in
	0)
            auto_detect_fqdn
            ;;
        1)
            generate_server_conf
            ;;
        2)
            restore_server_conf
            ;;
        3)
            check_firewall
            read -p "Press Enter to continue..."
            ;;
        4)
            create_client
            ;;
        5)
            list_clients
            read -p "Press Enter to continue..."
            ;;
        6)
            revoke_client
            ;;
        7)
            check_expiration
            read -p "Press Enter to continue..."
            ;;
        8)
            renew_certificate
            ;;
        9)
            show_cert_details
            read -p "Press Enter to continue..."
            ;;
        10)
            generate_all_ovpn
            read -p "Press Enter to continue..."
            ;;
        11)
            echo ""
            read -p "Enter client name: " client_name
            if [ -n "$client_name" ]; then
                generate_single_ovpn "$client_name"
            else
                echo "Error: No client name provided"
            fi
            read -p "Press Enter to continue..."
            ;;

	12) 
            key_management_first_time
            ;;
        13)
            echo "Exiting..."
            exit 0
            ;;
        *)
            echo "Invalid option. Please select 0-13."
            ;;
    esac
done

For beginners to OpenVPN server, this PDF guide may be helpful. It is based on above cli instructions with additional note and tips. OpenVPN server setup guide for BT Home Hub 5A

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2025/11/23 19:32
  • by beadon