mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-16 00:47:02 +01:00
feat: migrate to OpenVPN 2.4+ directory structure and improve distro compatibility (#1364)
## Summary Migrates OpenVPN configuration to use the modern OpenVPN 2.4+ directory structure and improves compatibility across different Linux distributions. Close https://github.com/angristan/openvpn-install/issues/1307, close https://github.com/angristan/openvpn-install/issues/788, close https://github.com/angristan/openvpn-install/issues/605, close https://github.com/angristan/openvpn-install/pull/653, close https://github.com/angristan/openvpn-install/issues/1214 ### Directory Structure Changes - All server files now in `/etc/openvpn/server/` instead of `/etc/openvpn/` - Uses `openvpn-server@server.service` consistently across all distros - `server.conf` uses relative paths for portability ### Distro-Specific User/Group Handling Different distros configure OpenVPN differently: | Distro | User | Group | systemd handles user? | |--------|------|-------|----------------------| | Debian/Ubuntu | nobody | nogroup | No | | Fedora/RHEL/Amazon | openvpn | openvpn | No | | Arch | openvpn | network | **Yes** (via `User=` in service) | The script now: 1. Detects if an `openvpn` user exists and uses appropriate group 2. Checks if systemd service already has `User=` directive 3. Skips `user`/`group` in config when systemd handles it (avoids "double privilege drop" error on Arch) 4. Sets file ownership with `chown -R` for non-root OpenVPN users ### Other Changes - Updated FAQ.md with new paths - Added systemd service file validation in tests - Added CRL reload verification in tests
This commit is contained in:
@@ -65,12 +65,12 @@ fi
|
||||
echo "Verifying installation..."
|
||||
MISSING_FILES=0
|
||||
for f in \
|
||||
/etc/openvpn/server.conf \
|
||||
/etc/openvpn/ca.crt \
|
||||
/etc/openvpn/ca.key \
|
||||
/etc/openvpn/tls-crypt.key \
|
||||
/etc/openvpn/crl.pem \
|
||||
/etc/openvpn/easy-rsa/pki/ca.crt \
|
||||
/etc/openvpn/server/server.conf \
|
||||
/etc/openvpn/server/ca.crt \
|
||||
/etc/openvpn/server/ca.key \
|
||||
/etc/openvpn/server/tls-crypt.key \
|
||||
/etc/openvpn/server/crl.pem \
|
||||
/etc/openvpn/server/easy-rsa/pki/ca.crt \
|
||||
/etc/iptables/add-openvpn-rules.sh \
|
||||
/root/testclient.ovpn; do
|
||||
if [ ! -f "$f" ]; then
|
||||
@@ -85,9 +85,66 @@ if [ $MISSING_FILES -gt 0 ]; then
|
||||
fi
|
||||
|
||||
echo "All required files present"
|
||||
|
||||
# =====================================================
|
||||
# Verify systemd service file configuration
|
||||
# =====================================================
|
||||
echo ""
|
||||
echo "=== Verifying systemd service configuration ==="
|
||||
|
||||
# Check that the correct service file was created
|
||||
SERVICE_FILE="/etc/systemd/system/openvpn-server@.service"
|
||||
if [ -f "$SERVICE_FILE" ]; then
|
||||
echo "PASS: openvpn-server@.service exists at $SERVICE_FILE"
|
||||
else
|
||||
echo "FAIL: openvpn-server@.service not found at $SERVICE_FILE"
|
||||
echo "Contents of /etc/systemd/system/:"
|
||||
find /etc/systemd/system/ -maxdepth 1 -name '*openvpn*' -ls 2>/dev/null || echo "No openvpn service files found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the service file points to /etc/openvpn/server/ (not patched back to /etc/openvpn/)
|
||||
if grep -q "/etc/openvpn/server" "$SERVICE_FILE"; then
|
||||
echo "PASS: Service file uses correct path /etc/openvpn/server/"
|
||||
else
|
||||
echo "FAIL: Service file does not reference /etc/openvpn/server/"
|
||||
echo "Service file contents:"
|
||||
cat "$SERVICE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the service file syntax is valid (if systemd-analyze is available)
|
||||
if command -v systemd-analyze >/dev/null 2>&1; then
|
||||
echo "Validating service file syntax..."
|
||||
if systemd-analyze verify "$SERVICE_FILE" 2>&1 | tee /tmp/service-verify.log; then
|
||||
echo "PASS: Service file syntax is valid"
|
||||
else
|
||||
# systemd-analyze verify may return non-zero for warnings, check for actual errors
|
||||
if grep -qi "error" /tmp/service-verify.log; then
|
||||
echo "FAIL: Service file has syntax errors"
|
||||
cat /tmp/service-verify.log
|
||||
exit 1
|
||||
else
|
||||
echo "PASS: Service file syntax is valid (warnings only)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "SKIP: systemd-analyze not available, skipping syntax validation"
|
||||
fi
|
||||
|
||||
# Verify the old service file pattern (openvpn@.service) was NOT created
|
||||
OLD_SERVICE_FILE="/etc/systemd/system/openvpn@.service"
|
||||
if [ -f "$OLD_SERVICE_FILE" ]; then
|
||||
echo "FAIL: Legacy openvpn@.service was created (should use openvpn-server@.service)"
|
||||
exit 1
|
||||
else
|
||||
echo "PASS: Legacy openvpn@.service not present (correct)"
|
||||
fi
|
||||
|
||||
echo "=== systemd service configuration verified ==="
|
||||
echo ""
|
||||
echo "Server config:"
|
||||
cat /etc/openvpn/server.conf
|
||||
cat /etc/openvpn/server/server.conf
|
||||
|
||||
# Copy client config to shared volume
|
||||
cp /root/testclient.ovpn /shared/client.ovpn
|
||||
@@ -102,7 +159,7 @@ echo ""
|
||||
echo "=== Testing Certificate Renewal ==="
|
||||
|
||||
# Get the original certificate serial number for comparison
|
||||
ORIG_CERT_SERIAL=$(openssl x509 -in /etc/openvpn/easy-rsa/pki/issued/testclient.crt -noout -serial | cut -d= -f2)
|
||||
ORIG_CERT_SERIAL=$(openssl x509 -in /etc/openvpn/server/easy-rsa/pki/issued/testclient.crt -noout -serial | cut -d= -f2)
|
||||
echo "Original client certificate serial: $ORIG_CERT_SERIAL"
|
||||
|
||||
# Test client certificate renewal using the script
|
||||
@@ -120,7 +177,7 @@ else
|
||||
fi
|
||||
|
||||
# Verify new certificate has different serial
|
||||
NEW_CERT_SERIAL=$(openssl x509 -in /etc/openvpn/easy-rsa/pki/issued/testclient.crt -noout -serial | cut -d= -f2)
|
||||
NEW_CERT_SERIAL=$(openssl x509 -in /etc/openvpn/server/easy-rsa/pki/issued/testclient.crt -noout -serial | cut -d= -f2)
|
||||
echo "New client certificate serial: $NEW_CERT_SERIAL"
|
||||
if [ "$ORIG_CERT_SERIAL" != "$NEW_CERT_SERIAL" ]; then
|
||||
echo "PASS: Certificate serial changed (renewal created new cert)"
|
||||
@@ -131,8 +188,8 @@ fi
|
||||
|
||||
# Verify renewed certificate has correct validity period
|
||||
# The default is 3650 days, so the cert should be valid for ~10 years from now
|
||||
CLIENT_CERT_NOT_AFTER=$(openssl x509 -in /etc/openvpn/easy-rsa/pki/issued/testclient.crt -noout -enddate | cut -d= -f2)
|
||||
CLIENT_CERT_NOT_BEFORE=$(openssl x509 -in /etc/openvpn/easy-rsa/pki/issued/testclient.crt -noout -startdate | cut -d= -f2)
|
||||
CLIENT_CERT_NOT_AFTER=$(openssl x509 -in /etc/openvpn/server/easy-rsa/pki/issued/testclient.crt -noout -enddate | cut -d= -f2)
|
||||
CLIENT_CERT_NOT_BEFORE=$(openssl x509 -in /etc/openvpn/server/easy-rsa/pki/issued/testclient.crt -noout -startdate | cut -d= -f2)
|
||||
echo "Client certificate valid from: $CLIENT_CERT_NOT_BEFORE"
|
||||
echo "Client certificate valid until: $CLIENT_CERT_NOT_AFTER"
|
||||
|
||||
@@ -159,7 +216,7 @@ else
|
||||
fi
|
||||
|
||||
# Verify CRL was updated
|
||||
if [ -f /etc/openvpn/crl.pem ]; then
|
||||
if [ -f /etc/openvpn/server/crl.pem ]; then
|
||||
echo "PASS: CRL file exists"
|
||||
else
|
||||
echo "FAIL: CRL file missing after renewal"
|
||||
@@ -179,9 +236,9 @@ echo "=== Client Certificate Renewal Tests PASSED ==="
|
||||
echo ""
|
||||
echo "=== Testing Server Certificate Renewal ==="
|
||||
|
||||
# Get server certificate name and original serial
|
||||
SERVER_NAME=$(grep '^cert ' /etc/openvpn/server.conf | cut -d ' ' -f 2 | sed 's/\.crt$//')
|
||||
ORIG_SERVER_SERIAL=$(openssl x509 -in "/etc/openvpn/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
|
||||
# Get server certificate name and original serial (extract basename since path may be relative)
|
||||
SERVER_NAME=$(basename "$(grep '^cert ' /etc/openvpn/server/server.conf | cut -d ' ' -f 2)" .crt)
|
||||
ORIG_SERVER_SERIAL=$(openssl x509 -in "/etc/openvpn/server/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
|
||||
echo "Server certificate: $SERVER_NAME"
|
||||
echo "Original server certificate serial: $ORIG_SERVER_SERIAL"
|
||||
|
||||
@@ -200,7 +257,7 @@ else
|
||||
fi
|
||||
|
||||
# Verify new certificate has different serial
|
||||
NEW_SERVER_SERIAL=$(openssl x509 -in "/etc/openvpn/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
|
||||
NEW_SERVER_SERIAL=$(openssl x509 -in "/etc/openvpn/server/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
|
||||
echo "New server certificate serial: $NEW_SERVER_SERIAL"
|
||||
if [ "$ORIG_SERVER_SERIAL" != "$NEW_SERVER_SERIAL" ]; then
|
||||
echo "PASS: Server certificate serial changed (renewal created new cert)"
|
||||
@@ -210,8 +267,8 @@ else
|
||||
fi
|
||||
|
||||
# Verify renewed server certificate has correct validity period
|
||||
SERVER_CERT_NOT_AFTER=$(openssl x509 -in "/etc/openvpn/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -enddate | cut -d= -f2)
|
||||
SERVER_CERT_NOT_BEFORE=$(openssl x509 -in "/etc/openvpn/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -startdate | cut -d= -f2)
|
||||
SERVER_CERT_NOT_AFTER=$(openssl x509 -in "/etc/openvpn/server/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -enddate | cut -d= -f2)
|
||||
SERVER_CERT_NOT_BEFORE=$(openssl x509 -in "/etc/openvpn/server/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -startdate | cut -d= -f2)
|
||||
echo "Server certificate valid from: $SERVER_CERT_NOT_BEFORE"
|
||||
echo "Server certificate valid until: $SERVER_CERT_NOT_AFTER"
|
||||
|
||||
@@ -227,17 +284,17 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the new certificate was copied to /etc/openvpn/
|
||||
if [ -f "/etc/openvpn/$SERVER_NAME.crt" ]; then
|
||||
DEPLOYED_SERIAL=$(openssl x509 -in "/etc/openvpn/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
|
||||
# Verify the new certificate was copied to /etc/openvpn/server/
|
||||
if [ -f "/etc/openvpn/server/$SERVER_NAME.crt" ]; then
|
||||
DEPLOYED_SERIAL=$(openssl x509 -in "/etc/openvpn/server/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
|
||||
if [ "$NEW_SERVER_SERIAL" = "$DEPLOYED_SERIAL" ]; then
|
||||
echo "PASS: New server certificate deployed to /etc/openvpn/"
|
||||
echo "PASS: New server certificate deployed to /etc/openvpn/server/"
|
||||
else
|
||||
echo "FAIL: Deployed certificate doesn't match renewed certificate"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "FAIL: Server certificate not found in /etc/openvpn/"
|
||||
echo "FAIL: Server certificate not found in /etc/openvpn/server/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -309,11 +366,11 @@ else
|
||||
fi
|
||||
|
||||
# Verify OpenVPN pushes correct DNS
|
||||
if grep -q 'push "dhcp-option DNS 10.8.0.1"' /etc/openvpn/server.conf; then
|
||||
if grep -q 'push "dhcp-option DNS 10.8.0.1"' /etc/openvpn/server/server.conf; then
|
||||
echo "PASS: OpenVPN configured to push Unbound DNS"
|
||||
else
|
||||
echo "FAIL: OpenVPN not configured to push Unbound DNS"
|
||||
grep "dhcp-option DNS" /etc/openvpn/server.conf || echo "No DNS push found"
|
||||
grep "dhcp-option DNS" /etc/openvpn/server/server.conf || echo "No DNS push found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -346,9 +403,9 @@ if [ "$(cat /proc/sys/net/ipv4/ip_forward)" != "1" ]; then
|
||||
}
|
||||
fi
|
||||
|
||||
# Start OpenVPN in background (run from /etc/openvpn so relative paths work)
|
||||
cd /etc/openvpn
|
||||
openvpn --config /etc/openvpn/server.conf --log /var/log/openvpn-server.log &
|
||||
# Start OpenVPN in background (run from /etc/openvpn/server so relative paths work)
|
||||
cd /etc/openvpn/server
|
||||
openvpn --config /etc/openvpn/server/server.conf --log /var/log/openvpn-server.log &
|
||||
OPENVPN_PID=$!
|
||||
|
||||
# Wait for OpenVPN to start
|
||||
@@ -453,10 +510,10 @@ echo "Revoking certificate for '$REVOKE_CLIENT'..."
|
||||
REVOKE_OUTPUT="/tmp/revoke-output.log"
|
||||
# MENU_OPTION=2 is revoke, CLIENTNUMBER is dynamically determined from index.txt
|
||||
# We need to find the client number for revoketest
|
||||
REVOKE_CLIENT_NUM=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | grep -n "CN=$REVOKE_CLIENT\$" | cut -d: -f1)
|
||||
REVOKE_CLIENT_NUM=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | grep -n "CN=$REVOKE_CLIENT\$" | cut -d: -f1)
|
||||
if [ -z "$REVOKE_CLIENT_NUM" ]; then
|
||||
echo "ERROR: Could not find client number for '$REVOKE_CLIENT'"
|
||||
cat /etc/openvpn/easy-rsa/pki/index.txt
|
||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||
exit 1
|
||||
fi
|
||||
echo "Revoke client number: $REVOKE_CLIENT_NUM"
|
||||
@@ -471,11 +528,11 @@ else
|
||||
fi
|
||||
|
||||
# Verify certificate is marked as revoked in index.txt
|
||||
if tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -q "^R.*CN=$REVOKE_CLIENT\$"; then
|
||||
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/easy-rsa/pki/index.txt
|
||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -520,23 +577,23 @@ else
|
||||
fi
|
||||
|
||||
# Verify the new certificate is valid (V) in index.txt
|
||||
if tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -q "^V.*CN=$REVOKE_CLIENT\$"; then
|
||||
if tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -q "^V.*CN=$REVOKE_CLIENT\$"; then
|
||||
echo "PASS: New certificate is valid in index.txt"
|
||||
else
|
||||
echo "FAIL: New certificate not marked as valid"
|
||||
cat /etc/openvpn/easy-rsa/pki/index.txt
|
||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify there's also a revoked entry (both should exist)
|
||||
REVOKED_COUNT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c "^R.*CN=$REVOKE_CLIENT\$")
|
||||
VALID_COUNT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c "^V.*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\$")
|
||||
echo "Certificates for '$REVOKE_CLIENT': $REVOKED_COUNT revoked, $VALID_COUNT valid"
|
||||
if [ "$REVOKED_COUNT" -ge 1 ] && [ "$VALID_COUNT" -eq 1 ]; then
|
||||
echo "PASS: Both revoked and new valid certificate entries exist"
|
||||
else
|
||||
echo "FAIL: Unexpected certificate state"
|
||||
cat /etc/openvpn/easy-rsa/pki/index.txt
|
||||
cat /etc/openvpn/server/easy-rsa/pki/index.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user