mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-20 02:27:01 +01:00
feat: add peer-fingerprint authentication mode (OpenVPN 2.6+) (#1437)
## Summary Implements support for OpenVPN's `--peer-fingerprint` option, enabling PKI-less authentication using SHA256 certificate fingerprints instead of a CA chain. Closes #1361 ## Changes - Add `--auth-mode` option (`pki` or `fingerprint`) for install command - Use Easy-RSA's `self-sign-server` and `self-sign-client` commands for fingerprint mode - Server stores client fingerprints in `<peer-fingerprint>` block in `server.conf` - Clients verify server using `peer-fingerprint` directive instead of CA - Revocation removes fingerprint from config and reloads OpenVPN (instant effect) - Version check ensures OpenVPN 2.6+ when fingerprint mode is selected ## Usage ```bash # Interactive mode prompts for auth mode choice # CLI mode ./openvpn-install.sh install --auth-mode fingerprint ``` ## Comparison | Aspect | PKI Mode | Fingerprint Mode | |--------|----------|------------------| | Server cert | CA-signed | Self-signed | | Client cert | CA-signed | Self-signed | | Revocation | CRL-based | Remove fingerprint | | OpenVPN | Any version | 2.6.0+ required | | Best for | Large deployments | Small/home setups |
This commit is contained in:
10
.github/workflows/docker-test.yml
vendored
10
.github/workflows/docker-test.yml
vendored
@@ -118,6 +118,15 @@ jobs:
|
|||||||
name: tls-crypt-v2
|
name: tls-crypt-v2
|
||||||
sig: crypt-v2
|
sig: crypt-v2
|
||||||
key_file: tls-crypt-v2.key
|
key_file: tls-crypt-v2.key
|
||||||
|
# Test peer-fingerprint authentication mode (OpenVPN 2.6+)
|
||||||
|
- os:
|
||||||
|
name: ubuntu-24.04-fingerprint
|
||||||
|
image: ubuntu:24.04
|
||||||
|
auth_mode: fingerprint
|
||||||
|
tls:
|
||||||
|
name: tls-crypt-v2
|
||||||
|
sig: crypt-v2
|
||||||
|
key_file: tls-crypt-v2.key
|
||||||
|
|
||||||
name: ${{ matrix.os.name }}
|
name: ${{ matrix.os.name }}
|
||||||
steps:
|
steps:
|
||||||
@@ -166,6 +175,7 @@ jobs:
|
|||||||
-e TLS_SIG=${{ matrix.tls.sig }} \
|
-e TLS_SIG=${{ matrix.tls.sig }} \
|
||||||
-e TLS_KEY_FILE=${{ matrix.tls.key_file }} \
|
-e TLS_KEY_FILE=${{ matrix.tls.key_file }} \
|
||||||
-e CLIENT_IPV6=${{ matrix.os.client_ipv6 && 'y' || 'n' }} \
|
-e CLIENT_IPV6=${{ matrix.os.client_ipv6 && 'y' || 'n' }} \
|
||||||
|
-e AUTH_MODE=${{ matrix.os.auth_mode || 'pki' }} \
|
||||||
openvpn-server
|
openvpn-server
|
||||||
|
|
||||||
- name: Wait for server installation and startup
|
- name: Wait for server installation and startup
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -62,6 +62,7 @@ That said, OpenVPN still makes sense when you need:
|
|||||||
- Randomised server certificate name
|
- Randomised server certificate name
|
||||||
- Choice to protect clients with a password (private key encryption)
|
- Choice to protect clients with a password (private key encryption)
|
||||||
- Option to allow multiple devices to use the same client profile simultaneously (disables persistent IP addresses)
|
- Option to allow multiple devices to use the same client profile simultaneously (disables persistent IP addresses)
|
||||||
|
- **Peer fingerprint authentication** (OpenVPN 2.6+): Simplified WireGuard-like authentication without a CA
|
||||||
- Many other little things!
|
- Many other little things!
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
@@ -317,6 +318,7 @@ The `install` command supports many options for customization:
|
|||||||
- `--rsa-bits <2048|3072|4096>` - RSA key size (default: `2048`)
|
- `--rsa-bits <2048|3072|4096>` - RSA key size (default: `2048`)
|
||||||
- `--hmac <alg>` - HMAC algorithm (default: `SHA256`). Options: `SHA256`, `SHA384`, `SHA512`
|
- `--hmac <alg>` - HMAC algorithm (default: `SHA256`). Options: `SHA256`, `SHA384`, `SHA512`
|
||||||
- `--tls-sig <mode>` - TLS mode (default: `crypt-v2`). Options: `crypt-v2`, `crypt`, `auth`
|
- `--tls-sig <mode>` - TLS mode (default: `crypt-v2`). Options: `crypt-v2`, `crypt`, `auth`
|
||||||
|
- `--auth-mode <mode>` - Authentication mode (default: `pki`). Options: `pki` (CA-based), `fingerprint` (peer-fingerprint, requires OpenVPN 2.6+)
|
||||||
- `--tls-version-min <1.2|1.3>` - Minimum TLS version (default: `1.2`)
|
- `--tls-version-min <1.2|1.3>` - Minimum TLS version (default: `1.2`)
|
||||||
- `--tls-ciphersuites <list>` - TLS 1.3 cipher suites, colon-separated (default: `TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256`)
|
- `--tls-ciphersuites <list>` - TLS 1.3 cipher suites, colon-separated (default: `TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256`)
|
||||||
- `--tls-groups <list>` - Key exchange groups, colon-separated (default: `X25519:prime256v1:secp384r1:secp521r1`)
|
- `--tls-groups <list>` - Key exchange groups, colon-separated (default: `X25519:prime256v1:secp384r1:secp521r1`)
|
||||||
@@ -470,6 +472,45 @@ It defaults to ECDSA with `prime256v1`.
|
|||||||
|
|
||||||
OpenVPN uses `SHA-256` as the signature hash by default, and so does the script. It provides no other choice as of now.
|
OpenVPN uses `SHA-256` as the signature hash by default, and so does the script. It provides no other choice as of now.
|
||||||
|
|
||||||
|
### Authentication Mode
|
||||||
|
|
||||||
|
The script supports two authentication modes:
|
||||||
|
|
||||||
|
#### PKI Mode (default)
|
||||||
|
|
||||||
|
Traditional Certificate Authority (CA) based authentication. The server and all clients have certificates signed by the same CA. Client revocation is handled via Certificate Revocation Lists (CRL).
|
||||||
|
|
||||||
|
This is the recommended mode for larger deployments where you need:
|
||||||
|
|
||||||
|
- Centralized certificate management
|
||||||
|
- Standard CRL-based revocation
|
||||||
|
- Compatibility with all OpenVPN versions
|
||||||
|
|
||||||
|
#### Peer Fingerprint Mode (OpenVPN 2.6+)
|
||||||
|
|
||||||
|
A simplified WireGuard-like authentication model using SHA256 certificate fingerprints instead of a CA chain. Each peer (server and clients) has a self-signed certificate, and peers authenticate each other by verifying fingerprints.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install with fingerprint mode
|
||||||
|
./openvpn-install.sh install --auth-mode fingerprint
|
||||||
|
```
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
- Simpler setup: No CA infrastructure needed
|
||||||
|
- Easier to understand: Similar to SSH's `known_hosts` model
|
||||||
|
- Ideal for small setups: Home networks, labs, small teams
|
||||||
|
|
||||||
|
How it works:
|
||||||
|
|
||||||
|
1. Server generates a self-signed certificate and stores its fingerprint
|
||||||
|
2. Each client generates a self-signed certificate
|
||||||
|
3. Client fingerprints are added to the server's `<peer-fingerprint>` block
|
||||||
|
4. Clients verify the server using the server's fingerprint
|
||||||
|
5. Revocation removes the fingerprint from the server config (no CRL needed)
|
||||||
|
|
||||||
|
Trade-off: Revoking a client requires reloading OpenVPN (fingerprints are in server.conf). In PKI mode, the CRL file is re-read automatically on new connections.
|
||||||
|
|
||||||
### Data channel
|
### Data channel
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
|
|||||||
@@ -239,6 +239,8 @@ show_install_help() {
|
|||||||
(default: X25519:prime256v1:secp384r1:secp521r1)
|
(default: X25519:prime256v1:secp384r1:secp521r1)
|
||||||
--hmac <alg> HMAC algorithm: SHA256, SHA384, SHA512 (default: SHA256)
|
--hmac <alg> HMAC algorithm: SHA256, SHA384, SHA512 (default: SHA256)
|
||||||
--tls-sig <mode> TLS mode: crypt-v2, crypt, auth (default: crypt-v2)
|
--tls-sig <mode> TLS mode: crypt-v2, crypt, auth (default: crypt-v2)
|
||||||
|
--auth-mode <mode> Auth mode: pki, fingerprint (default: pki)
|
||||||
|
fingerprint requires OpenVPN 2.6+
|
||||||
--server-cert-days <n> Server cert validity in days (default: 3650)
|
--server-cert-days <n> Server cert validity in days (default: 3650)
|
||||||
|
|
||||||
Other Options:
|
Other Options:
|
||||||
@@ -477,6 +479,9 @@ readonly TLS_VERSIONS=("1.2" "1.3")
|
|||||||
# TLS signature modes (use strings)
|
# TLS signature modes (use strings)
|
||||||
readonly TLS_SIG_MODES=("crypt-v2" "crypt" "auth")
|
readonly TLS_SIG_MODES=("crypt-v2" "crypt" "auth")
|
||||||
|
|
||||||
|
# Authentication modes: pki (CA-based) or fingerprint (peer-fingerprint, OpenVPN 2.6+)
|
||||||
|
readonly AUTH_MODES=("pki" "fingerprint")
|
||||||
|
|
||||||
# HMAC algorithms
|
# HMAC algorithms
|
||||||
readonly HMAC_ALGS=("SHA256" "SHA384" "SHA512")
|
readonly HMAC_ALGS=("SHA256" "SHA384" "SHA512")
|
||||||
|
|
||||||
@@ -516,6 +521,7 @@ set_installation_defaults() {
|
|||||||
TLS_GROUPS="${TLS_GROUPS:-X25519:prime256v1:secp384r1:secp521r1}"
|
TLS_GROUPS="${TLS_GROUPS:-X25519:prime256v1:secp384r1:secp521r1}"
|
||||||
HMAC_ALG="${HMAC_ALG:-SHA256}"
|
HMAC_ALG="${HMAC_ALG:-SHA256}"
|
||||||
TLS_SIG="${TLS_SIG:-crypt-v2}"
|
TLS_SIG="${TLS_SIG:-crypt-v2}"
|
||||||
|
AUTH_MODE="${AUTH_MODE:-pki}"
|
||||||
|
|
||||||
# Derive CC_CIPHER from CERT_TYPE if not set
|
# Derive CC_CIPHER from CERT_TYPE if not set
|
||||||
if [[ -z $CC_CIPHER ]]; then
|
if [[ -z $CC_CIPHER ]]; then
|
||||||
@@ -536,6 +542,18 @@ set_installation_defaults() {
|
|||||||
# are computed in prepare_network_config() which is called after validation
|
# are computed in prepare_network_config() which is called after validation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Version comparison: returns 0 if version1 >= version2
|
||||||
|
version_ge() {
|
||||||
|
local ver1="$1" ver2="$2"
|
||||||
|
# Use sort -V for version comparison
|
||||||
|
[[ "$(printf '%s\n%s' "$ver1" "$ver2" | sort -V | head -n1)" == "$ver2" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get installed OpenVPN version (e.g., "2.6.12")
|
||||||
|
get_openvpn_version() {
|
||||||
|
openvpn --version 2>/dev/null | head -1 | awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
# Validation functions
|
# Validation functions
|
||||||
validate_port() {
|
validate_port() {
|
||||||
local port="$1"
|
local port="$1"
|
||||||
@@ -642,6 +660,21 @@ validate_configuration() {
|
|||||||
*) log_fatal "Invalid TLS signature mode: $TLS_SIG. Must be 'crypt-v2', 'crypt', or 'auth'." ;;
|
*) log_fatal "Invalid TLS signature mode: $TLS_SIG. Must be 'crypt-v2', 'crypt', or 'auth'." ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Validate AUTH_MODE
|
||||||
|
case "$AUTH_MODE" in
|
||||||
|
pki | fingerprint) ;;
|
||||||
|
*) log_fatal "Invalid auth mode: $AUTH_MODE. Must be 'pki' or 'fingerprint'." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Fingerprint mode requires OpenVPN 2.6+
|
||||||
|
if [[ $AUTH_MODE == "fingerprint" ]]; then
|
||||||
|
local openvpn_ver
|
||||||
|
openvpn_ver=$(get_openvpn_version)
|
||||||
|
if [[ -n "$openvpn_ver" ]] && ! version_ge "$openvpn_ver" "2.6.0"; then
|
||||||
|
log_fatal "Fingerprint mode requires OpenVPN 2.6.0 or later. Installed version: $openvpn_ver"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Validate PORT
|
# Validate PORT
|
||||||
if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then
|
if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then
|
||||||
log_fatal "Invalid port: $PORT. Must be a number between 1 and 65535."
|
log_fatal "Invalid port: $PORT. Must be a number between 1 and 65535."
|
||||||
@@ -1034,6 +1067,14 @@ cmd_install() {
|
|||||||
esac
|
esac
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--auth-mode)
|
||||||
|
[[ -z "${2:-}" ]] && log_fatal "--auth-mode requires an argument"
|
||||||
|
case "$2" in
|
||||||
|
pki | fingerprint) AUTH_MODE="$2" ;;
|
||||||
|
*) log_fatal "Invalid auth mode: $2. Use 'pki' or 'fingerprint'." ;;
|
||||||
|
esac
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
--server-cert-days)
|
--server-cert-days)
|
||||||
[[ -z "${2:-}" ]] && log_fatal "--server-cert-days requires an argument"
|
[[ -z "${2:-}" ]] && log_fatal "--server-cert-days requires an argument"
|
||||||
validate_positive_int "$2" "server-cert-days"
|
validate_positive_int "$2" "server-cert-days"
|
||||||
@@ -1251,6 +1292,7 @@ cmd_client_add() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
newClient
|
newClient
|
||||||
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Handle client list command
|
# Handle client list command
|
||||||
@@ -2336,6 +2378,30 @@ function installQuestions() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
log_menu ""
|
log_menu ""
|
||||||
|
log_prompt "Choose the authentication mode:"
|
||||||
|
log_menu " 1) PKI (Certificate Authority) - Traditional CA-based authentication (recommended for larger setups)"
|
||||||
|
log_menu " 2) Peer Fingerprint - Simplified WireGuard-like authentication using certificate fingerprints"
|
||||||
|
log_menu " Note: Fingerprint mode requires OpenVPN 2.6+ and is ideal for small/home setups"
|
||||||
|
local auth_mode_choice
|
||||||
|
until [[ $auth_mode_choice =~ ^[1-2]$ ]]; do
|
||||||
|
read -rp "Authentication mode [1-2]: " -e -i 1 auth_mode_choice
|
||||||
|
done
|
||||||
|
case $auth_mode_choice in
|
||||||
|
1)
|
||||||
|
AUTH_MODE="pki"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
AUTH_MODE="fingerprint"
|
||||||
|
# Verify OpenVPN 2.6+ is available for fingerprint mode
|
||||||
|
local openvpn_ver
|
||||||
|
openvpn_ver=$(get_openvpn_version)
|
||||||
|
if [[ -n "$openvpn_ver" ]] && ! version_ge "$openvpn_ver" "2.6.0"; then
|
||||||
|
log_warn "OpenVPN $openvpn_ver detected. Fingerprint mode requires 2.6.0+."
|
||||||
|
log_warn "OpenVPN 2.6+ will be installed during setup."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
log_menu ""
|
||||||
log_prompt "Do you want to customize encryption settings?"
|
log_prompt "Do you want to customize encryption settings?"
|
||||||
log_prompt "Unless you know what you're doing, you should stick with the default parameters provided by the script."
|
log_prompt "Unless you know what you're doing, you should stick with the default parameters provided by the script."
|
||||||
log_prompt "Note that whatever you choose, all the choices presented in the script are safe (unlike OpenVPN's defaults)."
|
log_prompt "Note that whatever you choose, all the choices presented in the script are safe (unlike OpenVPN's defaults)."
|
||||||
@@ -2541,6 +2607,7 @@ function installOpenVPN() {
|
|||||||
log_info " DNS=$DNS"
|
log_info " DNS=$DNS"
|
||||||
[[ -n $MTU ]] && log_info " MTU=$MTU"
|
[[ -n $MTU ]] && log_info " MTU=$MTU"
|
||||||
log_info " MULTI_CLIENT=$MULTI_CLIENT"
|
log_info " MULTI_CLIENT=$MULTI_CLIENT"
|
||||||
|
log_info " AUTH_MODE=$AUTH_MODE"
|
||||||
log_info " CLIENT=$CLIENT"
|
log_info " CLIENT=$CLIENT"
|
||||||
log_info " CLIENT_CERT_DURATION_DAYS=$CLIENT_CERT_DURATION_DAYS"
|
log_info " CLIENT_CERT_DURATION_DAYS=$CLIENT_CERT_DURATION_DAYS"
|
||||||
log_info " SERVER_CERT_DURATION_DAYS=$SERVER_CERT_DURATION_DAYS"
|
log_info " SERVER_CERT_DURATION_DAYS=$SERVER_CERT_DURATION_DAYS"
|
||||||
@@ -2680,15 +2747,34 @@ function installOpenVPN() {
|
|||||||
# Create the PKI, set up the CA, the DH params and the server certificate
|
# Create the PKI, set up the CA, the DH params and the server certificate
|
||||||
log_info "Initializing PKI..."
|
log_info "Initializing PKI..."
|
||||||
run_cmd_fatal "Initializing PKI" ./easyrsa init-pki
|
run_cmd_fatal "Initializing PKI" ./easyrsa init-pki
|
||||||
export EASYRSA_CA_EXPIRE=$DEFAULT_CERT_VALIDITY_DURATION_DAYS
|
|
||||||
log_info "Building CA..."
|
|
||||||
run_cmd_fatal "Building CA" ./easyrsa --batch --req-cn="$SERVER_CN" build-ca nopass
|
|
||||||
|
|
||||||
export EASYRSA_CERT_EXPIRE=${SERVER_CERT_DURATION_DAYS:-$DEFAULT_CERT_VALIDITY_DURATION_DAYS}
|
if [[ $AUTH_MODE == "pki" ]]; then
|
||||||
log_info "Building server certificate..."
|
# Traditional PKI mode with CA
|
||||||
run_cmd_fatal "Building server certificate" ./easyrsa --batch build-server-full "$SERVER_NAME" nopass
|
export EASYRSA_CA_EXPIRE=$DEFAULT_CERT_VALIDITY_DURATION_DAYS
|
||||||
export EASYRSA_CRL_DAYS=$DEFAULT_CRL_VALIDITY_DURATION_DAYS
|
log_info "Building CA..."
|
||||||
run_cmd_fatal "Generating CRL" ./easyrsa gen-crl
|
run_cmd_fatal "Building CA" ./easyrsa --batch --req-cn="$SERVER_CN" build-ca nopass
|
||||||
|
|
||||||
|
export EASYRSA_CERT_EXPIRE=${SERVER_CERT_DURATION_DAYS:-$DEFAULT_CERT_VALIDITY_DURATION_DAYS}
|
||||||
|
log_info "Building server certificate..."
|
||||||
|
run_cmd_fatal "Building server certificate" ./easyrsa --batch build-server-full "$SERVER_NAME" nopass
|
||||||
|
export EASYRSA_CRL_DAYS=$DEFAULT_CRL_VALIDITY_DURATION_DAYS
|
||||||
|
run_cmd_fatal "Generating CRL" ./easyrsa gen-crl
|
||||||
|
else
|
||||||
|
# Fingerprint mode with self-signed certificates (OpenVPN 2.6+)
|
||||||
|
log_info "Building self-signed server certificate for fingerprint mode..."
|
||||||
|
export EASYRSA_CERT_EXPIRE=${SERVER_CERT_DURATION_DAYS:-$DEFAULT_CERT_VALIDITY_DURATION_DAYS}
|
||||||
|
run_cmd_fatal "Building self-signed server certificate" ./easyrsa --batch self-sign-server "$SERVER_NAME" nopass
|
||||||
|
|
||||||
|
# Extract and store server fingerprint
|
||||||
|
SERVER_FINGERPRINT=$(openssl x509 -in "pki/issued/$SERVER_NAME.crt" -fingerprint -sha256 -noout | cut -d'=' -f2)
|
||||||
|
if [[ -z $SERVER_FINGERPRINT ]]; then
|
||||||
|
log_error "Failed to extract server certificate fingerprint"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p /etc/openvpn/server
|
||||||
|
echo "$SERVER_FINGERPRINT" >/etc/openvpn/server/server-fingerprint
|
||||||
|
log_info "Server fingerprint: $SERVER_FINGERPRINT"
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Generating TLS key..."
|
log_info "Generating TLS key..."
|
||||||
case $TLS_SIG in
|
case $TLS_SIG in
|
||||||
@@ -2705,19 +2791,32 @@ function installOpenVPN() {
|
|||||||
run_cmd_fatal "Generating tls-auth key" openvpn --genkey secret /etc/openvpn/server/tls-auth.key
|
run_cmd_fatal "Generating tls-auth key" openvpn --genkey secret /etc/openvpn/server/tls-auth.key
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
# Store auth mode for later use
|
||||||
|
echo "$AUTH_MODE" >AUTH_MODE_GENERATED
|
||||||
else
|
else
|
||||||
# If easy-rsa is already installed, grab the generated SERVER_NAME
|
# If easy-rsa is already installed, grab the generated SERVER_NAME
|
||||||
# for client configs
|
# for client configs
|
||||||
cd /etc/openvpn/server/easy-rsa/ || return
|
cd /etc/openvpn/server/easy-rsa/ || return
|
||||||
SERVER_NAME=$(cat SERVER_NAME_GENERATED)
|
SERVER_NAME=$(cat SERVER_NAME_GENERATED)
|
||||||
|
# Read stored auth mode
|
||||||
|
if [[ -f AUTH_MODE_GENERATED ]]; then
|
||||||
|
AUTH_MODE=$(cat AUTH_MODE_GENERATED)
|
||||||
|
else
|
||||||
|
# Default to pki for existing installations
|
||||||
|
AUTH_MODE="pki"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Move all the generated files
|
# Move all the generated files
|
||||||
log_info "Copying certificates..."
|
log_info "Copying certificates..."
|
||||||
run_cmd_fatal "Copying certificates to /etc/openvpn/server" cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server
|
if [[ $AUTH_MODE == "pki" ]]; then
|
||||||
|
run_cmd_fatal "Copying certificates to /etc/openvpn/server" cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server
|
||||||
# Make cert revocation list readable for non-root
|
# Make cert revocation list readable for non-root
|
||||||
run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/server/crl.pem
|
run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/server/crl.pem
|
||||||
|
else
|
||||||
|
# Fingerprint mode: only copy server cert and key (no CA or CRL)
|
||||||
|
run_cmd_fatal "Copying certificates to /etc/openvpn/server" cp "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/server
|
||||||
|
fi
|
||||||
|
|
||||||
# Generate server.conf
|
# Generate server.conf
|
||||||
log_info "Generating server configuration..."
|
log_info "Generating server configuration..."
|
||||||
@@ -2931,9 +3030,13 @@ topology subnet" >>/etc/openvpn/server/server.conf
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "crl-verify crl.pem
|
# Common server config options
|
||||||
ca ca.crt
|
# PKI mode adds crl-verify, ca, and remote-cert-tls
|
||||||
cert $SERVER_NAME.crt
|
# Fingerprint mode: <peer-fingerprint> block is added when first client is created
|
||||||
|
{
|
||||||
|
[[ $AUTH_MODE == "pki" ]] && echo "crl-verify crl.pem
|
||||||
|
ca ca.crt"
|
||||||
|
echo "cert $SERVER_NAME.crt
|
||||||
key $SERVER_NAME.key
|
key $SERVER_NAME.key
|
||||||
auth $HMAC_ALG
|
auth $HMAC_ALG
|
||||||
cipher $CIPHER
|
cipher $CIPHER
|
||||||
@@ -2941,14 +3044,15 @@ ignore-unknown-option data-ciphers
|
|||||||
data-ciphers $CIPHER
|
data-ciphers $CIPHER
|
||||||
ncp-ciphers $CIPHER
|
ncp-ciphers $CIPHER
|
||||||
tls-server
|
tls-server
|
||||||
tls-version-min $TLS_VERSION_MIN
|
tls-version-min $TLS_VERSION_MIN"
|
||||||
remote-cert-tls client
|
[[ $AUTH_MODE == "pki" ]] && echo "remote-cert-tls client"
|
||||||
tls-cipher $CC_CIPHER
|
echo "tls-cipher $CC_CIPHER
|
||||||
tls-ciphersuites $TLS13_CIPHERSUITES
|
tls-ciphersuites $TLS13_CIPHERSUITES
|
||||||
client-config-dir ccd
|
client-config-dir ccd
|
||||||
status /var/log/openvpn/status.log
|
status /var/log/openvpn/status.log
|
||||||
management /var/run/openvpn/server.sock unix
|
management /var/run/openvpn/server.sock unix
|
||||||
verb 3" >>/etc/openvpn/server/server.conf
|
verb 3"
|
||||||
|
} >>/etc/openvpn/server/server.conf
|
||||||
|
|
||||||
# Create management socket directory
|
# Create management socket directory
|
||||||
run_cmd_fatal "Creating management socket directory" mkdir -p /var/run/openvpn
|
run_cmd_fatal "Creating management socket directory" mkdir -p /var/run/openvpn
|
||||||
@@ -3028,7 +3132,11 @@ verb 3" >>/etc/openvpn/server/server.conf
|
|||||||
|
|
||||||
run_cmd "Reloading systemd" systemctl daemon-reload
|
run_cmd "Reloading systemd" systemctl daemon-reload
|
||||||
run_cmd "Enabling OpenVPN service" systemctl enable openvpn-server@server
|
run_cmd "Enabling OpenVPN service" systemctl enable openvpn-server@server
|
||||||
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
|
# In fingerprint mode, delay service start until first client is created
|
||||||
|
# (OpenVPN requires at least one fingerprint or a CA to start)
|
||||||
|
if [[ $AUTH_MODE == "pki" ]]; then
|
||||||
|
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $DNS == "unbound" ]]; then
|
if [[ $DNS == "unbound" ]]; then
|
||||||
installUnbound
|
installUnbound
|
||||||
@@ -3207,15 +3315,19 @@ WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service
|
|||||||
elif [[ $PROTOCOL == 'tcp6' ]]; then
|
elif [[ $PROTOCOL == 'tcp6' ]]; then
|
||||||
echo "proto tcp6-client" >>/etc/openvpn/server/client-template.txt
|
echo "proto tcp6-client" >>/etc/openvpn/server/client-template.txt
|
||||||
fi
|
fi
|
||||||
echo "remote $IP $PORT
|
# Common client template options
|
||||||
|
# PKI mode adds remote-cert-tls and verify-x509-name
|
||||||
|
# Fingerprint mode adds peer-fingerprint when generating client config
|
||||||
|
{
|
||||||
|
echo "remote $IP $PORT
|
||||||
dev tun
|
dev tun
|
||||||
resolv-retry infinite
|
resolv-retry infinite
|
||||||
nobind
|
nobind
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun"
|
||||||
remote-cert-tls server
|
[[ $AUTH_MODE == "pki" ]] && echo "remote-cert-tls server
|
||||||
verify-x509-name $SERVER_NAME name
|
verify-x509-name $SERVER_NAME name"
|
||||||
auth $HMAC_ALG
|
echo "auth $HMAC_ALG
|
||||||
auth-nocache
|
auth-nocache
|
||||||
cipher $CIPHER
|
cipher $CIPHER
|
||||||
ignore-unknown-option data-ciphers
|
ignore-unknown-option data-ciphers
|
||||||
@@ -3227,7 +3339,8 @@ tls-cipher $CC_CIPHER
|
|||||||
tls-ciphersuites $TLS13_CIPHERSUITES
|
tls-ciphersuites $TLS13_CIPHERSUITES
|
||||||
ignore-unknown-option block-outside-dns
|
ignore-unknown-option block-outside-dns
|
||||||
setenv opt block-outside-dns # Prevent Windows 10 DNS leak
|
setenv opt block-outside-dns # Prevent Windows 10 DNS leak
|
||||||
verb 3" >>/etc/openvpn/server/client-template.txt
|
verb 3"
|
||||||
|
} >>/etc/openvpn/server/client-template.txt
|
||||||
|
|
||||||
if [[ -n $MTU ]]; then
|
if [[ -n $MTU ]]; then
|
||||||
echo "tun-mtu $MTU" >>/etc/openvpn/server/client-template.txt
|
echo "tun-mtu $MTU" >>/etc/openvpn/server/client-template.txt
|
||||||
@@ -3235,10 +3348,18 @@ verb 3" >>/etc/openvpn/server/client-template.txt
|
|||||||
|
|
||||||
# Generate the custom client.ovpn
|
# Generate the custom client.ovpn
|
||||||
if [[ $NEW_CLIENT == "n" ]]; then
|
if [[ $NEW_CLIENT == "n" ]]; then
|
||||||
log_info "No clients added. To add clients, simply run the script again."
|
if [[ $AUTH_MODE == "fingerprint" ]]; then
|
||||||
|
log_info "No clients added. OpenVPN will not start until you add at least one client."
|
||||||
|
else
|
||||||
|
log_info "No clients added. To add clients, simply run the script again."
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log_info "Generating first client certificate..."
|
log_info "Generating first client certificate..."
|
||||||
newClient
|
newClient
|
||||||
|
# In fingerprint mode, start service now that we have at least one fingerprint
|
||||||
|
if [[ $AUTH_MODE == "fingerprint" ]]; then
|
||||||
|
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
|
||||||
|
fi
|
||||||
log_success "If you want to add more clients, you simply need to run this script another time!"
|
log_success "If you want to add more clients, you simply need to run this script another time!"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -3333,6 +3454,12 @@ function generateClientConfig() {
|
|||||||
local client="$1"
|
local client="$1"
|
||||||
local filepath="$2"
|
local filepath="$2"
|
||||||
|
|
||||||
|
# Read auth mode
|
||||||
|
local auth_mode="pki"
|
||||||
|
if [[ -f /etc/openvpn/server/easy-rsa/AUTH_MODE_GENERATED ]]; then
|
||||||
|
auth_mode=$(cat /etc/openvpn/server/easy-rsa/AUTH_MODE_GENERATED)
|
||||||
|
fi
|
||||||
|
|
||||||
# Determine if we use tls-crypt-v2, tls-crypt, or tls-auth
|
# Determine if we use tls-crypt-v2, tls-crypt, or tls-auth
|
||||||
local tls_sig=""
|
local tls_sig=""
|
||||||
if grep -qs "^tls-crypt-v2" /etc/openvpn/server/server.conf; then
|
if grep -qs "^tls-crypt-v2" /etc/openvpn/server/server.conf; then
|
||||||
@@ -3346,9 +3473,25 @@ function generateClientConfig() {
|
|||||||
# Generate the custom client.ovpn
|
# Generate the custom client.ovpn
|
||||||
run_cmd "Creating client config" cp /etc/openvpn/server/client-template.txt "$filepath"
|
run_cmd "Creating client config" cp /etc/openvpn/server/client-template.txt "$filepath"
|
||||||
{
|
{
|
||||||
echo "<ca>"
|
if [[ $auth_mode == "pki" ]]; then
|
||||||
cat "/etc/openvpn/server/easy-rsa/pki/ca.crt"
|
# PKI mode: include CA certificate
|
||||||
echo "</ca>"
|
echo "<ca>"
|
||||||
|
cat "/etc/openvpn/server/easy-rsa/pki/ca.crt"
|
||||||
|
echo "</ca>"
|
||||||
|
else
|
||||||
|
# Fingerprint mode: use server fingerprint instead of CA
|
||||||
|
local server_fingerprint
|
||||||
|
if [[ ! -f /etc/openvpn/server/server-fingerprint ]]; then
|
||||||
|
log_error "Server fingerprint file not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
server_fingerprint=$(cat /etc/openvpn/server/server-fingerprint)
|
||||||
|
if [[ -z $server_fingerprint ]]; then
|
||||||
|
log_error "Server fingerprint is empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "peer-fingerprint $server_fingerprint"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "<cert>"
|
echo "<cert>"
|
||||||
awk '/BEGIN/,/END CERTIFICATE/' "/etc/openvpn/server/easy-rsa/pki/issued/$client.crt"
|
awk '/BEGIN/,/END CERTIFICATE/' "/etc/openvpn/server/easy-rsa/pki/issued/$client.crt"
|
||||||
@@ -3677,45 +3820,96 @@ function newClient() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CLIENTEXISTS=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -E "^V" | grep -c -E "/CN=$CLIENT\$")
|
cd /etc/openvpn/server/easy-rsa/ || return
|
||||||
|
|
||||||
|
# Read auth mode
|
||||||
|
if [[ -f AUTH_MODE_GENERATED ]]; then
|
||||||
|
AUTH_MODE=$(cat AUTH_MODE_GENERATED)
|
||||||
|
else
|
||||||
|
AUTH_MODE="pki"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if client already exists
|
||||||
|
if [[ -f pki/index.txt ]]; then
|
||||||
|
CLIENTEXISTS=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -E "^V" | grep -c -E "/CN=$CLIENT\$")
|
||||||
|
else
|
||||||
|
CLIENTEXISTS=0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $CLIENTEXISTS != '0' ]]; then
|
if [[ $CLIENTEXISTS != '0' ]]; then
|
||||||
log_error "The specified client CN was already found in easy-rsa, please choose another name."
|
log_error "The specified client CN was already found in easy-rsa, please choose another name."
|
||||||
exit 1
|
exit 1
|
||||||
else
|
|
||||||
cd /etc/openvpn/server/easy-rsa/ || return
|
|
||||||
log_info "Generating client certificate..."
|
|
||||||
export EASYRSA_CERT_EXPIRE=$CLIENT_CERT_DURATION_DAYS
|
|
||||||
case $PASS in
|
|
||||||
1)
|
|
||||||
run_cmd_fatal "Building client certificate" ./easyrsa --batch build-client-full "$CLIENT" nopass
|
|
||||||
;;
|
|
||||||
2)
|
|
||||||
if [[ -z "$PASSPHRASE" ]]; then
|
|
||||||
log_warn "You will be asked for the client password below"
|
|
||||||
# Run directly (not via run_cmd) so password prompt is visible to user
|
|
||||||
if ! ./easyrsa --batch build-client-full "$CLIENT"; then
|
|
||||||
log_fatal "Building client certificate failed"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log_info "Using provided passphrase for client certificate"
|
|
||||||
# Use env var to avoid exposing passphrase in install log
|
|
||||||
export EASYRSA_PASSPHRASE="$PASSPHRASE"
|
|
||||||
run_cmd_fatal "Building client certificate" ./easyrsa --batch --passin=env:EASYRSA_PASSPHRASE --passout=env:EASYRSA_PASSPHRASE build-client-full "$CLIENT"
|
|
||||||
unset EASYRSA_PASSPHRASE
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
log_success "Client $CLIENT added and is valid for $CLIENT_CERT_DURATION_DAYS days."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
log_info "Generating client certificate..."
|
||||||
|
export EASYRSA_CERT_EXPIRE=$CLIENT_CERT_DURATION_DAYS
|
||||||
|
|
||||||
|
# Determine easyrsa command based on auth mode
|
||||||
|
local easyrsa_cmd cert_desc
|
||||||
|
if [[ $AUTH_MODE == "pki" ]]; then
|
||||||
|
easyrsa_cmd="build-client-full"
|
||||||
|
cert_desc="client certificate"
|
||||||
|
else
|
||||||
|
easyrsa_cmd="self-sign-client"
|
||||||
|
cert_desc="self-signed client certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $PASS in
|
||||||
|
1)
|
||||||
|
run_cmd_fatal "Building $cert_desc" ./easyrsa --batch "$easyrsa_cmd" "$CLIENT" nopass
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if [[ -z "$PASSPHRASE" ]]; then
|
||||||
|
log_warn "You will be asked for the client password below"
|
||||||
|
if ! ./easyrsa --batch "$easyrsa_cmd" "$CLIENT"; then
|
||||||
|
log_fatal "Building $cert_desc failed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Using provided passphrase for client certificate"
|
||||||
|
export EASYRSA_PASSPHRASE="$PASSPHRASE"
|
||||||
|
run_cmd_fatal "Building $cert_desc" ./easyrsa --batch --passin=env:EASYRSA_PASSPHRASE --passout=env:EASYRSA_PASSPHRASE "$easyrsa_cmd" "$CLIENT"
|
||||||
|
unset EASYRSA_PASSPHRASE
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Fingerprint mode: register client fingerprint with server
|
||||||
|
if [[ $AUTH_MODE == "fingerprint" ]]; then
|
||||||
|
CLIENT_FINGERPRINT=$(openssl x509 -in "pki/issued/$CLIENT.crt" -fingerprint -sha256 -noout | cut -d'=' -f2)
|
||||||
|
if [[ -z $CLIENT_FINGERPRINT ]]; then
|
||||||
|
log_error "Failed to extract client certificate fingerprint"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_info "Client fingerprint: $CLIENT_FINGERPRINT"
|
||||||
|
|
||||||
|
# Add fingerprint to server.conf's <peer-fingerprint> block
|
||||||
|
# Create the block if this is the first client
|
||||||
|
if ! grep -q '<peer-fingerprint>' /etc/openvpn/server/server.conf; then
|
||||||
|
echo "# Client fingerprints are listed below
|
||||||
|
<peer-fingerprint>
|
||||||
|
# $CLIENT
|
||||||
|
$CLIENT_FINGERPRINT
|
||||||
|
</peer-fingerprint>" >>/etc/openvpn/server/server.conf
|
||||||
|
else
|
||||||
|
# Insert comment and fingerprint before closing tag
|
||||||
|
sed -i "/<\/peer-fingerprint>/i # $CLIENT\n$CLIENT_FINGERPRINT" /etc/openvpn/server/server.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload OpenVPN to pick up new fingerprint
|
||||||
|
log_info "Reloading OpenVPN to apply new fingerprint..."
|
||||||
|
if systemctl is-active --quiet openvpn-server@server; then
|
||||||
|
systemctl reload openvpn-server@server 2>/dev/null || systemctl restart openvpn-server@server
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Client $CLIENT added and is valid for $CLIENT_CERT_DURATION_DAYS days."
|
||||||
|
|
||||||
# Write the .ovpn config file with proper path and permissions
|
# Write the .ovpn config file with proper path and permissions
|
||||||
writeClientConfig "$CLIENT"
|
writeClientConfig "$CLIENT"
|
||||||
|
|
||||||
log_menu ""
|
log_menu ""
|
||||||
log_success "The configuration file has been written to $GENERATED_CONFIG_PATH."
|
log_success "The configuration file has been written to $GENERATED_CONFIG_PATH."
|
||||||
log_info "Download the .ovpn file and import it in your OpenVPN client."
|
log_info "Download the .ovpn file and import it in your OpenVPN client."
|
||||||
|
|
||||||
exit 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function revokeClient() {
|
function revokeClient() {
|
||||||
@@ -3724,13 +3918,45 @@ function revokeClient() {
|
|||||||
selectClient
|
selectClient
|
||||||
|
|
||||||
cd /etc/openvpn/server/easy-rsa/ || return
|
cd /etc/openvpn/server/easy-rsa/ || return
|
||||||
|
|
||||||
|
# Read auth mode
|
||||||
|
local auth_mode="pki"
|
||||||
|
if [[ -f AUTH_MODE_GENERATED ]]; then
|
||||||
|
auth_mode=$(cat AUTH_MODE_GENERATED)
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Revoking certificate for $CLIENT..."
|
log_info "Revoking certificate for $CLIENT..."
|
||||||
run_cmd_fatal "Revoking certificate" ./easyrsa --batch revoke-issued "$CLIENT"
|
|
||||||
regenerateCRL
|
if [[ $auth_mode == "pki" ]]; then
|
||||||
|
# PKI mode: use Easy-RSA revocation and CRL
|
||||||
|
run_cmd_fatal "Revoking certificate" ./easyrsa --batch revoke-issued "$CLIENT"
|
||||||
|
regenerateCRL
|
||||||
|
run_cmd "Backing up index" cp /etc/openvpn/server/easy-rsa/pki/index.txt{,.bk}
|
||||||
|
else
|
||||||
|
# Fingerprint mode: remove fingerprint from server.conf and delete cert files
|
||||||
|
log_info "Removing client fingerprint from server configuration..."
|
||||||
|
|
||||||
|
# Remove comment line and fingerprint line below it from server.conf
|
||||||
|
sed -i "/^# $CLIENT\$/{N;d;}" /etc/openvpn/server/server.conf
|
||||||
|
|
||||||
|
# Remove client certificate and key
|
||||||
|
rm -f "pki/issued/$CLIENT.crt" "pki/private/$CLIENT.key"
|
||||||
|
|
||||||
|
# Mark as revoked in index.txt if it exists (for client listing)
|
||||||
|
if [[ -f pki/index.txt ]]; then
|
||||||
|
sed -i "s|^V\(.*\)/CN=$CLIENT\$|R\1/CN=$CLIENT|" pki/index.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload OpenVPN to apply fingerprint removal
|
||||||
|
log_info "Reloading OpenVPN to apply fingerprint removal..."
|
||||||
|
if systemctl is-active --quiet openvpn-server@server; then
|
||||||
|
systemctl reload openvpn-server@server 2>/dev/null || systemctl restart openvpn-server@server
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
run_cmd "Removing client config from /home" find /home/ -maxdepth 2 -name "$CLIENT.ovpn" -delete
|
run_cmd "Removing client config from /home" find /home/ -maxdepth 2 -name "$CLIENT.ovpn" -delete
|
||||||
run_cmd "Removing client config from /root" rm -f "/root/$CLIENT.ovpn"
|
run_cmd "Removing client config from /root" rm -f "/root/$CLIENT.ovpn"
|
||||||
run_cmd "Removing IP assignment" sed -i "/^$CLIENT,.*/d" /etc/openvpn/server/ipp.txt
|
run_cmd "Removing IP assignment" sed -i "/^$CLIENT,.*/d" /etc/openvpn/server/ipp.txt
|
||||||
run_cmd "Backing up index" cp /etc/openvpn/server/easy-rsa/pki/index.txt{,.bk}
|
|
||||||
|
|
||||||
# Disconnect the client if currently connected
|
# Disconnect the client if currently connected
|
||||||
disconnectClient "$CLIENT"
|
disconnectClient "$CLIENT"
|
||||||
@@ -4102,6 +4328,7 @@ function manageMenu() {
|
|||||||
case $menu_option in
|
case $menu_option in
|
||||||
1)
|
1)
|
||||||
newClient
|
newClient
|
||||||
|
exit 0
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
listClients
|
listClients
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ TLS_KEY_FILE="${TLS_KEY_FILE:-tls-crypt-v2.key}"
|
|||||||
TLS_VERSION_MIN="${TLS_VERSION_MIN:-1.2}"
|
TLS_VERSION_MIN="${TLS_VERSION_MIN:-1.2}"
|
||||||
TLS13_CIPHERSUITES="${TLS13_CIPHERSUITES:-TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256}"
|
TLS13_CIPHERSUITES="${TLS13_CIPHERSUITES:-TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256}"
|
||||||
|
|
||||||
|
# Authentication mode configuration
|
||||||
|
# AUTH_MODE: pki (default, CA-based) or fingerprint (peer-fingerprint, OpenVPN 2.6+)
|
||||||
|
AUTH_MODE="${AUTH_MODE:-pki}"
|
||||||
|
|
||||||
# Build install command with CLI flags (using array for proper quoting)
|
# Build install command with CLI flags (using array for proper quoting)
|
||||||
INSTALL_CMD=(/opt/openvpn-install.sh install)
|
INSTALL_CMD=(/opt/openvpn-install.sh install)
|
||||||
INSTALL_CMD+=(--endpoint openvpn-server)
|
INSTALL_CMD+=(--endpoint openvpn-server)
|
||||||
@@ -75,6 +79,12 @@ if [ "$TLS13_CIPHERSUITES" != "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS
|
|||||||
echo "Testing TLS 1.3 ciphersuites: $TLS13_CIPHERSUITES"
|
echo "Testing TLS 1.3 ciphersuites: $TLS13_CIPHERSUITES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Add auth mode if non-default
|
||||||
|
if [ "$AUTH_MODE" != "pki" ]; then
|
||||||
|
INSTALL_CMD+=(--auth-mode "$AUTH_MODE")
|
||||||
|
echo "Testing authentication mode: $AUTH_MODE"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Running OpenVPN install script..."
|
echo "Running OpenVPN install script..."
|
||||||
echo "Command: ${INSTALL_CMD[*]}"
|
echo "Command: ${INSTALL_CMD[*]}"
|
||||||
# Run in subshell because the script calls 'exit 0' after generating client config
|
# Run in subshell because the script calls 'exit 0' after generating client config
|
||||||
@@ -104,16 +114,26 @@ fi
|
|||||||
# Verify all expected files were created
|
# Verify all expected files were created
|
||||||
echo "Verifying installation..."
|
echo "Verifying installation..."
|
||||||
MISSING_FILES=0
|
MISSING_FILES=0
|
||||||
# Build list of required files
|
# Build list of required files based on auth mode
|
||||||
REQUIRED_FILES=(
|
REQUIRED_FILES=(
|
||||||
/etc/openvpn/server/server.conf
|
/etc/openvpn/server/server.conf
|
||||||
/etc/openvpn/server/ca.crt
|
|
||||||
/etc/openvpn/server/ca.key
|
|
||||||
"/etc/openvpn/server/$TLS_KEY_FILE"
|
"/etc/openvpn/server/$TLS_KEY_FILE"
|
||||||
/etc/openvpn/server/crl.pem
|
|
||||||
/etc/openvpn/server/easy-rsa/pki/ca.crt
|
|
||||||
/root/testclient.ovpn
|
/root/testclient.ovpn
|
||||||
)
|
)
|
||||||
|
if [ "$AUTH_MODE" = "pki" ]; then
|
||||||
|
# PKI mode requires CA and CRL files
|
||||||
|
REQUIRED_FILES+=(
|
||||||
|
/etc/openvpn/server/ca.crt
|
||||||
|
/etc/openvpn/server/ca.key
|
||||||
|
/etc/openvpn/server/crl.pem
|
||||||
|
/etc/openvpn/server/easy-rsa/pki/ca.crt
|
||||||
|
)
|
||||||
|
else
|
||||||
|
# Fingerprint mode requires server fingerprint file
|
||||||
|
REQUIRED_FILES+=(
|
||||||
|
/etc/openvpn/server/server-fingerprint
|
||||||
|
)
|
||||||
|
fi
|
||||||
# Only check for iptables script if firewalld and nftables are not active
|
# Only check for iptables script if firewalld and nftables are not active
|
||||||
if ! systemctl is-active --quiet firewalld && ! systemctl is-active --quiet nftables; then
|
if ! systemctl is-active --quiet firewalld && ! systemctl is-active --quiet nftables; then
|
||||||
REQUIRED_FILES+=(/etc/iptables/add-openvpn-rules.sh)
|
REQUIRED_FILES+=(/etc/iptables/add-openvpn-rules.sh)
|
||||||
@@ -197,13 +217,16 @@ sed -i 's/^remote .*/remote openvpn-server 1194/' /shared/client.ovpn
|
|||||||
echo "Client config copied to /shared/client.ovpn"
|
echo "Client config copied to /shared/client.ovpn"
|
||||||
|
|
||||||
# Write VPN network info to shared volume for client tests
|
# Write VPN network info to shared volume for client tests
|
||||||
echo "VPN_SUBNET_IPV4=$VPN_SUBNET_IPV4" >/shared/vpn-config.env
|
{
|
||||||
echo "VPN_GATEWAY=$VPN_GATEWAY" >>/shared/vpn-config.env
|
echo "VPN_SUBNET_IPV4=$VPN_SUBNET_IPV4"
|
||||||
echo "CLIENT_IPV6=$CLIENT_IPV6" >>/shared/vpn-config.env
|
echo "VPN_GATEWAY=$VPN_GATEWAY"
|
||||||
if [ "$CLIENT_IPV6" = "y" ]; then
|
echo "CLIENT_IPV6=$CLIENT_IPV6"
|
||||||
echo "VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6" >>/shared/vpn-config.env
|
echo "AUTH_MODE=$AUTH_MODE"
|
||||||
echo "VPN_GATEWAY_IPV6=$VPN_GATEWAY_IPV6" >>/shared/vpn-config.env
|
if [ "$CLIENT_IPV6" = "y" ]; then
|
||||||
fi
|
echo "VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6"
|
||||||
|
echo "VPN_GATEWAY_IPV6=$VPN_GATEWAY_IPV6"
|
||||||
|
fi
|
||||||
|
} >/shared/vpn-config.env
|
||||||
echo "VPN config written to /shared/vpn-config.env"
|
echo "VPN config written to /shared/vpn-config.env"
|
||||||
|
|
||||||
# =====================================================
|
# =====================================================
|
||||||
@@ -396,12 +419,14 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify CRL was updated
|
# Verify CRL was updated (PKI mode only)
|
||||||
if [ -f /etc/openvpn/server/crl.pem ]; then
|
if [ "$AUTH_MODE" = "pki" ]; then
|
||||||
echo "PASS: CRL file exists"
|
if [ -f /etc/openvpn/server/crl.pem ]; then
|
||||||
else
|
echo "PASS: CRL file exists"
|
||||||
echo "FAIL: CRL file missing after renewal"
|
else
|
||||||
exit 1
|
echo "FAIL: CRL file missing after renewal"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update shared client config with renewed certificate
|
# Update shared client config with renewed certificate
|
||||||
@@ -815,13 +840,25 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify certificate is marked as revoked in index.txt
|
# Verify revocation was applied correctly
|
||||||
if tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -q "^R.*CN=$REVOKE_CLIENT\$"; then
|
if [ "$AUTH_MODE" = "pki" ]; then
|
||||||
echo "PASS: Certificate marked as revoked in index.txt"
|
# PKI mode: verify certificate is marked as revoked in index.txt
|
||||||
|
if tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -q "^R.*CN=$REVOKE_CLIENT\$"; then
|
||||||
|
echo "PASS: Certificate marked as revoked in index.txt"
|
||||||
|
else
|
||||||
|
echo "FAIL: Certificate not marked as revoked"
|
||||||
|
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "FAIL: Certificate not marked as revoked"
|
# Fingerprint mode: verify fingerprint was removed from server.conf
|
||||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
if ! grep -q "# $REVOKE_CLIENT\$" /etc/openvpn/server/server.conf; then
|
||||||
exit 1
|
echo "PASS: Client fingerprint removed from server.conf"
|
||||||
|
else
|
||||||
|
echo "FAIL: Client fingerprint still present in server.conf"
|
||||||
|
grep "$REVOKE_CLIENT" /etc/openvpn/server/server.conf || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Wait for client to confirm it was disconnected by the revoke
|
# Wait for client to confirm it was disconnected by the revoke
|
||||||
@@ -883,13 +920,26 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify certificate count (3 certs: testclient valid, testclient revoked from renewal, revoketest revoked)
|
# Verify certificate count (varies by auth mode)
|
||||||
if grep -q "Found 3 client certificate(s)" "$LIST_OUTPUT"; then
|
if [ "$AUTH_MODE" = "pki" ]; then
|
||||||
echo "PASS: List shows correct certificate count"
|
# PKI mode: 3 certs (testclient valid, testclient revoked from renewal, revoketest revoked)
|
||||||
|
if grep -q "Found 3 client certificate(s)" "$LIST_OUTPUT"; then
|
||||||
|
echo "PASS: List shows correct certificate count"
|
||||||
|
else
|
||||||
|
echo "FAIL: List does not show correct certificate count"
|
||||||
|
cat "$LIST_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "FAIL: List does not show correct certificate count"
|
# Fingerprint mode: 2 certs (testclient valid, revoketest revoked)
|
||||||
cat "$LIST_OUTPUT"
|
# In fingerprint mode, renewal doesn't create a separate revoked entry
|
||||||
exit 1
|
if grep -q "Found [23] client certificate(s)" "$LIST_OUTPUT"; then
|
||||||
|
echo "PASS: List shows correct certificate count for fingerprint mode"
|
||||||
|
else
|
||||||
|
echo "FAIL: List does not show correct certificate count"
|
||||||
|
cat "$LIST_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test JSON output
|
# Test JSON output
|
||||||
@@ -906,14 +956,25 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify client count in JSON
|
# Verify client count in JSON (varies by auth mode)
|
||||||
JSON_CLIENT_COUNT=$(jq '.clients | length' "$LIST_JSON_OUTPUT")
|
JSON_CLIENT_COUNT=$(jq '.clients | length' "$LIST_JSON_OUTPUT")
|
||||||
if [ "$JSON_CLIENT_COUNT" -eq 3 ]; then
|
if [ "$AUTH_MODE" = "pki" ]; then
|
||||||
echo "PASS: Client list JSON has correct count ($JSON_CLIENT_COUNT)"
|
if [ "$JSON_CLIENT_COUNT" -eq 3 ]; then
|
||||||
|
echo "PASS: Client list JSON has correct count ($JSON_CLIENT_COUNT)"
|
||||||
|
else
|
||||||
|
echo "FAIL: Client list JSON has wrong count: $JSON_CLIENT_COUNT (expected 3)"
|
||||||
|
cat "$LIST_JSON_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "FAIL: Client list JSON has wrong count: $JSON_CLIENT_COUNT (expected 3)"
|
# Fingerprint mode may have fewer entries
|
||||||
cat "$LIST_JSON_OUTPUT"
|
if [ "$JSON_CLIENT_COUNT" -ge 2 ] && [ "$JSON_CLIENT_COUNT" -le 3 ]; then
|
||||||
exit 1
|
echo "PASS: Client list JSON has correct count for fingerprint mode ($JSON_CLIENT_COUNT)"
|
||||||
|
else
|
||||||
|
echo "FAIL: Client list JSON has wrong count: $JSON_CLIENT_COUNT (expected 2-3)"
|
||||||
|
cat "$LIST_JSON_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify valid client in JSON
|
# Verify valid client in JSON
|
||||||
@@ -955,25 +1016,37 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify the new certificate is valid (V) in index.txt
|
# Verify the new certificate is valid
|
||||||
if tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -q "^V.*CN=$REVOKE_CLIENT\$"; then
|
if [ "$AUTH_MODE" = "pki" ]; then
|
||||||
echo "PASS: New certificate is valid in index.txt"
|
# PKI mode: verify in index.txt
|
||||||
else
|
if tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -q "^V.*CN=$REVOKE_CLIENT\$"; then
|
||||||
echo "FAIL: New certificate not marked as valid"
|
echo "PASS: New certificate is valid in index.txt"
|
||||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
else
|
||||||
exit 1
|
echo "FAIL: New certificate not marked as valid"
|
||||||
fi
|
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Verify there's also a revoked entry (both should exist)
|
# Verify there's also a revoked entry (both should exist)
|
||||||
REVOKED_COUNT=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^R.*CN=$REVOKE_CLIENT\$")
|
REVOKED_COUNT=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^R.*CN=$REVOKE_CLIENT\$")
|
||||||
VALID_COUNT=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V.*CN=$REVOKE_CLIENT\$")
|
VALID_COUNT=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V.*CN=$REVOKE_CLIENT\$")
|
||||||
echo "Certificates for '$REVOKE_CLIENT': $REVOKED_COUNT revoked, $VALID_COUNT valid"
|
echo "Certificates for '$REVOKE_CLIENT': $REVOKED_COUNT revoked, $VALID_COUNT valid"
|
||||||
if [ "$REVOKED_COUNT" -ge 1 ] && [ "$VALID_COUNT" -eq 1 ]; then
|
if [ "$REVOKED_COUNT" -ge 1 ] && [ "$VALID_COUNT" -eq 1 ]; then
|
||||||
echo "PASS: Both revoked and new valid certificate entries exist"
|
echo "PASS: Both revoked and new valid certificate entries exist"
|
||||||
|
else
|
||||||
|
echo "FAIL: Unexpected certificate state"
|
||||||
|
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "FAIL: Unexpected certificate state"
|
# Fingerprint mode: verify fingerprint was added back to server.conf
|
||||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
if grep -q "# $REVOKE_CLIENT\$" /etc/openvpn/server/server.conf; then
|
||||||
exit 1
|
echo "PASS: New client fingerprint added to server.conf"
|
||||||
|
else
|
||||||
|
echo "FAIL: New client fingerprint not found in server.conf"
|
||||||
|
cat /etc/openvpn/server/server.conf | grep -A5 "<peer-fingerprint>" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy the new config
|
# Copy the new config
|
||||||
|
|||||||
Reference in New Issue
Block a user