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:
Stanislas
2025-12-12 22:09:18 +01:00
committed by GitHub
parent 3bc52d245b
commit 44c995df8e
3 changed files with 273 additions and 187 deletions

8
FAQ.md
View File

@@ -71,7 +71,7 @@ down /usr/share/openvpn/contrib/pull-resolv-conf/client.down
- AES CBC - AES CBC
- tls-auth - tls-auth
If your client is <2.3.3, remove `tls-version-min 1.2` from your `/etc/openvpn/server.conf` and `.ovpn` files. If your client is <2.3.3, remove `tls-version-min 1.2` from your `/etc/openvpn/server/server.conf` and `.ovpn` files.
--- ---
@@ -117,7 +117,7 @@ Sysctl options are at `/etc/sysctl.d/99-openvpn.conf`
**Q:** How can I access computers the OpenVPN server's remote LAN? **Q:** How can I access computers the OpenVPN server's remote LAN?
**A:** Add a route with the subnet of the remote network to `/etc/openvpn/server.conf` and restart OpenVPN. Example: `push "route 192.168.1.0 255.255.255.0"` if the server's LAN is `192.168.1.0/24` **A:** Add a route with the subnet of the remote network to `/etc/openvpn/server/server.conf` and restart OpenVPN. Example: `push "route 192.168.1.0 255.255.255.0"` if the server's LAN is `192.168.1.0/24`
--- ---
@@ -145,13 +145,13 @@ done < users.txt
**Q:** How do I change the default `.ovpn` file created for future clients? **Q:** How do I change the default `.ovpn` file created for future clients?
**A:** You can edit the template out of which `.ovpn` files are created by editing `/etc/openvpn/client-template.txt` **A:** You can edit the template out of which `.ovpn` files are created by editing `/etc/openvpn/server/client-template.txt`
--- ---
**Q:** For my clients - I want to set my internal network to pass through the VPN and the rest to go through my internet? **Q:** For my clients - I want to set my internal network to pass through the VPN and the rest to go through my internet?
**A:** You would need to edit the `.ovpn` file. You can edit the template out of which those files are created by editing `/etc/openvpn/client-template.txt` file and adding **A:** You would need to edit the `.ovpn` file. You can edit the template out of which those files are created by editing `/etc/openvpn/server/client-template.txt` file and adding
```sh ```sh
route-nopull route-nopull

View File

@@ -970,7 +970,7 @@ function installOpenVPN() {
# If OpenVPN isn't installed yet, install it. This script is more-or-less # If OpenVPN isn't installed yet, install it. This script is more-or-less
# idempotent on multiple runs, but will only install OpenVPN from upstream # idempotent on multiple runs, but will only install OpenVPN from upstream
# the first time. # the first time.
if [[ ! -e /etc/openvpn/server.conf ]]; then if [[ ! -e /etc/openvpn/server/server.conf ]]; then
log_header "Installing OpenVPN" log_header "Installing OpenVPN"
# Setup official OpenVPN repository for latest versions # Setup official OpenVPN repository for latest versions
@@ -1015,21 +1015,45 @@ function installOpenVPN() {
log_info "Data Channel Offload (DCO) is not available (requires OpenVPN 2.6+ and kernel support)" log_info "Data Channel Offload (DCO) is not available (requires OpenVPN 2.6+ and kernel support)"
fi fi
# Create the server directory (OpenVPN 2.4+ directory structure)
run_cmd "Creating server directory" mkdir -p /etc/openvpn/server
# An old version of easy-rsa was available by default in some openvpn packages # An old version of easy-rsa was available by default in some openvpn packages
if [[ -d /etc/openvpn/easy-rsa/ ]]; then if [[ -d /etc/openvpn/server/easy-rsa/ ]]; then
run_cmd "Removing old Easy-RSA" rm -rf /etc/openvpn/easy-rsa/ run_cmd "Removing old Easy-RSA" rm -rf /etc/openvpn/server/easy-rsa/
fi fi
fi fi
# Find out if the machine uses nogroup or nobody for the permissionless group # Determine which user/group OpenVPN should run as
if grep -qs "^nogroup:" /etc/group; then # - Fedora/RHEL/Amazon create 'openvpn' user with 'openvpn' group
NOGROUP=nogroup # - Arch creates 'openvpn' user with 'network' group
# - Debian/Ubuntu/openSUSE don't create a dedicated user, use 'nobody'
#
# Also check if the systemd service file already handles user/group switching.
# If so, we shouldn't add user/group to config (would cause double privilege drop).
SYSTEMD_HANDLES_USER=false
for service_file in /usr/lib/systemd/system/openvpn-server@.service /lib/systemd/system/openvpn-server@.service; do
if [[ -f "$service_file" ]] && grep -q "^User=" "$service_file"; then
SYSTEMD_HANDLES_USER=true
break
fi
done
if id openvpn &>/dev/null; then
OPENVPN_USER=openvpn
# Get the openvpn user's primary group (e.g., 'openvpn' on Fedora, 'network' on Arch)
OPENVPN_GROUP=$(id -gn openvpn 2>/dev/null || echo openvpn)
else else
NOGROUP=nobody OPENVPN_USER=nobody
if grep -qs "^nogroup:" /etc/group; then
OPENVPN_GROUP=nogroup
else
OPENVPN_GROUP=nobody
fi
fi fi
# Install the latest version of easy-rsa from source, if not already installed. # Install the latest version of easy-rsa from source, if not already installed.
if [[ ! -d /etc/openvpn/easy-rsa/ ]]; then if [[ ! -d /etc/openvpn/server/easy-rsa/ ]]; then
run_cmd "Downloading Easy-RSA v${EASYRSA_VERSION}" curl -fL --retry 5 -o ~/easy-rsa.tgz "https://github.com/OpenVPN/easy-rsa/releases/download/v${EASYRSA_VERSION}/EasyRSA-${EASYRSA_VERSION}.tgz" run_cmd "Downloading Easy-RSA v${EASYRSA_VERSION}" curl -fL --retry 5 -o ~/easy-rsa.tgz "https://github.com/OpenVPN/easy-rsa/releases/download/v${EASYRSA_VERSION}/EasyRSA-${EASYRSA_VERSION}.tgz"
log_info "Verifying Easy-RSA checksum..." log_info "Verifying Easy-RSA checksum..."
CHECKSUM_OUTPUT=$(echo "${EASYRSA_SHA256} $HOME/easy-rsa.tgz" | sha256sum -c 2>&1) || { CHECKSUM_OUTPUT=$(echo "${EASYRSA_SHA256} $HOME/easy-rsa.tgz" | sha256sum -c 2>&1) || {
@@ -1038,11 +1062,11 @@ function installOpenVPN() {
log_fatal "SHA256 checksum verification failed for easy-rsa download!" log_fatal "SHA256 checksum verification failed for easy-rsa download!"
} }
_log_to_file "[CHECKSUM] $CHECKSUM_OUTPUT" _log_to_file "[CHECKSUM] $CHECKSUM_OUTPUT"
run_cmd "Creating Easy-RSA directory" mkdir -p /etc/openvpn/easy-rsa run_cmd "Creating Easy-RSA directory" mkdir -p /etc/openvpn/server/easy-rsa
run_cmd "Extracting Easy-RSA" tar xzf ~/easy-rsa.tgz --strip-components=1 --no-same-owner --directory /etc/openvpn/easy-rsa run_cmd "Extracting Easy-RSA" tar xzf ~/easy-rsa.tgz --strip-components=1 --no-same-owner --directory /etc/openvpn/server/easy-rsa
run_cmd "Cleaning up archive" rm -f ~/easy-rsa.tgz run_cmd "Cleaning up archive" rm -f ~/easy-rsa.tgz
cd /etc/openvpn/easy-rsa/ || return cd /etc/openvpn/server/easy-rsa/ || return
case $CERT_TYPE in case $CERT_TYPE in
1) 1)
echo "set_var EASYRSA_ALGO ec" >vars echo "set_var EASYRSA_ALGO ec" >vars
@@ -1081,55 +1105,58 @@ function installOpenVPN() {
case $TLS_SIG in case $TLS_SIG in
1) 1)
# Generate tls-crypt key # Generate tls-crypt key
run_cmd "Generating tls-crypt key" openvpn --genkey --secret /etc/openvpn/tls-crypt.key run_cmd "Generating tls-crypt key" openvpn --genkey --secret /etc/openvpn/server/tls-crypt.key
;; ;;
2) 2)
# Generate tls-auth key # Generate tls-auth key
run_cmd "Generating tls-auth key" openvpn --genkey --secret /etc/openvpn/tls-auth.key run_cmd "Generating tls-auth key" openvpn --genkey --secret /etc/openvpn/server/tls-auth.key
;; ;;
esac esac
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/easy-rsa/ || return cd /etc/openvpn/server/easy-rsa/ || return
SERVER_NAME=$(cat SERVER_NAME_GENERATED) SERVER_NAME=$(cat SERVER_NAME_GENERATED)
fi fi
# Move all the generated files # Move all the generated files
log_info "Copying certificates..." log_info "Copying certificates..."
run_cmd "Copying certificates to /etc/openvpn" cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn run_cmd "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 [[ $DH_TYPE == "2" ]]; then if [[ $DH_TYPE == "2" ]]; then
run_cmd "Copying DH parameters" cp dh.pem /etc/openvpn run_cmd "Copying DH parameters" cp dh.pem /etc/openvpn/server
fi fi
# 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/crl.pem run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/server/crl.pem
# Generate server.conf # Generate server.conf
log_info "Generating server configuration..." log_info "Generating server configuration..."
echo "port $PORT" >/etc/openvpn/server.conf echo "port $PORT" >/etc/openvpn/server/server.conf
if [[ $IPV6_SUPPORT == 'n' ]]; then if [[ $IPV6_SUPPORT == 'n' ]]; then
echo "proto $PROTOCOL" >>/etc/openvpn/server.conf echo "proto $PROTOCOL" >>/etc/openvpn/server/server.conf
elif [[ $IPV6_SUPPORT == 'y' ]]; then elif [[ $IPV6_SUPPORT == 'y' ]]; then
echo "proto ${PROTOCOL}6" >>/etc/openvpn/server.conf echo "proto ${PROTOCOL}6" >>/etc/openvpn/server/server.conf
fi fi
if [[ $MULTI_CLIENT == "y" ]]; then if [[ $MULTI_CLIENT == "y" ]]; then
echo "duplicate-cn" >>/etc/openvpn/server.conf echo "duplicate-cn" >>/etc/openvpn/server/server.conf
fi fi
echo "dev tun echo "dev tun" >>/etc/openvpn/server/server.conf
user nobody # Only add user/group if systemd doesn't handle it (avoids double privilege drop)
group $NOGROUP if [[ $SYSTEMD_HANDLES_USER == "false" ]]; then
persist-key echo "user $OPENVPN_USER
group $OPENVPN_GROUP" >>/etc/openvpn/server/server.conf
fi
echo "persist-key
persist-tun persist-tun
keepalive 10 120 keepalive 10 120
topology subnet topology subnet
server 10.8.0.0 255.255.255.0" >>/etc/openvpn/server.conf server 10.8.0.0 255.255.255.0" >>/etc/openvpn/server/server.conf
# ifconfig-pool-persist is incompatible with duplicate-cn # ifconfig-pool-persist is incompatible with duplicate-cn
if [[ $MULTI_CLIENT != "y" ]]; then if [[ $MULTI_CLIENT != "y" ]]; then
echo "ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server.conf echo "ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server/server.conf
fi fi
# DNS resolvers # DNS resolvers
@@ -1146,64 +1173,64 @@ server 10.8.0.0 255.255.255.0" >>/etc/openvpn/server.conf
sed -ne 's/^nameserver[[:space:]]\+\([^[:space:]]\+\).*$/\1/p' $RESOLVCONF | while read -r line; do sed -ne 's/^nameserver[[:space:]]\+\([^[:space:]]\+\).*$/\1/p' $RESOLVCONF | while read -r line; do
# Copy, if it's a IPv4 |or| if IPv6 is enabled, IPv4/IPv6 does not matter # Copy, if it's a IPv4 |or| if IPv6 is enabled, IPv4/IPv6 does not matter
if [[ $line =~ ^[0-9.]*$ ]] || [[ $IPV6_SUPPORT == 'y' ]]; then if [[ $line =~ ^[0-9.]*$ ]] || [[ $IPV6_SUPPORT == 'y' ]]; then
echo "push \"dhcp-option DNS $line\"" >>/etc/openvpn/server.conf echo "push \"dhcp-option DNS $line\"" >>/etc/openvpn/server/server.conf
fi fi
done done
;; ;;
2) # Self-hosted DNS resolver (Unbound) 2) # Self-hosted DNS resolver (Unbound)
echo 'push "dhcp-option DNS 10.8.0.1"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 10.8.0.1"' >>/etc/openvpn/server/server.conf
if [[ $IPV6_SUPPORT == 'y' ]]; then if [[ $IPV6_SUPPORT == 'y' ]]; then
echo 'push "dhcp-option DNS fd42:42:42:42::1"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS fd42:42:42:42::1"' >>/etc/openvpn/server/server.conf
fi fi
;; ;;
3) # Cloudflare 3) # Cloudflare
echo 'push "dhcp-option DNS 1.0.0.1"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 1.0.0.1"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 1.1.1.1"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 1.1.1.1"' >>/etc/openvpn/server/server.conf
;; ;;
4) # Quad9 4) # Quad9
echo 'push "dhcp-option DNS 9.9.9.9"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 9.9.9.9"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 149.112.112.112"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 149.112.112.112"' >>/etc/openvpn/server/server.conf
;; ;;
5) # Quad9 uncensored 5) # Quad9 uncensored
echo 'push "dhcp-option DNS 9.9.9.10"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 9.9.9.10"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 149.112.112.10"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 149.112.112.10"' >>/etc/openvpn/server/server.conf
;; ;;
6) # FDN 6) # FDN
echo 'push "dhcp-option DNS 80.67.169.40"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 80.67.169.40"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 80.67.169.12"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 80.67.169.12"' >>/etc/openvpn/server/server.conf
;; ;;
7) # DNS.WATCH 7) # DNS.WATCH
echo 'push "dhcp-option DNS 84.200.69.80"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 84.200.69.80"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 84.200.70.40"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 84.200.70.40"' >>/etc/openvpn/server/server.conf
;; ;;
8) # OpenDNS 8) # OpenDNS
echo 'push "dhcp-option DNS 208.67.222.222"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 208.67.222.222"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 208.67.220.220"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 208.67.220.220"' >>/etc/openvpn/server/server.conf
;; ;;
9) # Google 9) # Google
echo 'push "dhcp-option DNS 8.8.8.8"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 8.8.8.8"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 8.8.4.4"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 8.8.4.4"' >>/etc/openvpn/server/server.conf
;; ;;
10) # Yandex Basic 10) # Yandex Basic
echo 'push "dhcp-option DNS 77.88.8.8"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 77.88.8.8"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 77.88.8.1"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 77.88.8.1"' >>/etc/openvpn/server/server.conf
;; ;;
11) # AdGuard DNS 11) # AdGuard DNS
echo 'push "dhcp-option DNS 94.140.14.14"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 94.140.14.14"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 94.140.15.15"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 94.140.15.15"' >>/etc/openvpn/server/server.conf
;; ;;
12) # NextDNS 12) # NextDNS
echo 'push "dhcp-option DNS 45.90.28.167"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 45.90.28.167"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 45.90.30.167"' >>/etc/openvpn/server.conf echo 'push "dhcp-option DNS 45.90.30.167"' >>/etc/openvpn/server/server.conf
;; ;;
13) # Custom DNS 13) # Custom DNS
echo "push \"dhcp-option DNS $DNS1\"" >>/etc/openvpn/server.conf echo "push \"dhcp-option DNS $DNS1\"" >>/etc/openvpn/server/server.conf
if [[ $DNS2 != "" ]]; then if [[ $DNS2 != "" ]]; then
echo "push \"dhcp-option DNS $DNS2\"" >>/etc/openvpn/server.conf echo "push \"dhcp-option DNS $DNS2\"" >>/etc/openvpn/server/server.conf
fi fi
;; ;;
esac esac
echo 'push "redirect-gateway def1 bypass-dhcp"' >>/etc/openvpn/server.conf echo 'push "redirect-gateway def1 bypass-dhcp"' >>/etc/openvpn/server/server.conf
# IPv6 network settings if needed # IPv6 network settings if needed
if [[ $IPV6_SUPPORT == 'y' ]]; then if [[ $IPV6_SUPPORT == 'y' ]]; then
@@ -1211,26 +1238,26 @@ server 10.8.0.0 255.255.255.0" >>/etc/openvpn/server.conf
tun-ipv6 tun-ipv6
push tun-ipv6 push tun-ipv6
push "route-ipv6 2000::/3" push "route-ipv6 2000::/3"
push "redirect-gateway ipv6"' >>/etc/openvpn/server.conf push "redirect-gateway ipv6"' >>/etc/openvpn/server/server.conf
fi fi
if [[ $COMPRESSION_ENABLED == "y" ]]; then if [[ $COMPRESSION_ENABLED == "y" ]]; then
echo "compress $COMPRESSION_ALG" >>/etc/openvpn/server.conf echo "compress $COMPRESSION_ALG" >>/etc/openvpn/server/server.conf
fi fi
if [[ $DH_TYPE == "1" ]]; then if [[ $DH_TYPE == "1" ]]; then
echo "dh none" >>/etc/openvpn/server.conf echo "dh none" >>/etc/openvpn/server/server.conf
echo "ecdh-curve $DH_CURVE" >>/etc/openvpn/server.conf echo "ecdh-curve $DH_CURVE" >>/etc/openvpn/server/server.conf
elif [[ $DH_TYPE == "2" ]]; then elif [[ $DH_TYPE == "2" ]]; then
echo "dh dh.pem" >>/etc/openvpn/server.conf echo "dh dh.pem" >>/etc/openvpn/server/server.conf
fi fi
case $TLS_SIG in case $TLS_SIG in
1) 1)
echo "tls-crypt tls-crypt.key" >>/etc/openvpn/server.conf echo "tls-crypt tls-crypt.key" >>/etc/openvpn/server/server.conf
;; ;;
2) 2)
echo "tls-auth tls-auth.key 0" >>/etc/openvpn/server.conf echo "tls-auth tls-auth.key 0" >>/etc/openvpn/server/server.conf
;; ;;
esac esac
@@ -1247,15 +1274,23 @@ tls-server
tls-version-min 1.2 tls-version-min 1.2
remote-cert-tls client remote-cert-tls client
tls-cipher $CC_CIPHER tls-cipher $CC_CIPHER
client-config-dir /etc/openvpn/ccd client-config-dir ccd
status /var/log/openvpn/status.log status /var/log/openvpn/status.log
verb 3" >>/etc/openvpn/server.conf verb 3" >>/etc/openvpn/server/server.conf
# Create client-config-dir dir # Create client-config-dir dir
run_cmd "Creating client config directory" mkdir -p /etc/openvpn/ccd run_cmd "Creating client config directory" mkdir -p /etc/openvpn/server/ccd
# Create log dir # Create log dir
run_cmd "Creating log directory" mkdir -p /var/log/openvpn run_cmd "Creating log directory" mkdir -p /var/log/openvpn
# On distros that use a dedicated OpenVPN user (not "nobody"), e.g., Fedora, RHEL, Arch,
# set ownership so OpenVPN can read config/certs and write to log directory
if [[ $OPENVPN_USER != "nobody" ]]; then
log_info "Setting ownership for OpenVPN user..."
chown -R "$OPENVPN_USER:$OPENVPN_GROUP" /etc/openvpn/server
chown "$OPENVPN_USER:$OPENVPN_GROUP" /var/log/openvpn
fi
# Enable routing # Enable routing
log_info "Enabling IP forwarding..." log_info "Enabling IP forwarding..."
run_cmd "Creating sysctl.d directory" mkdir -p /etc/sysctl.d run_cmd "Creating sysctl.d directory" mkdir -p /etc/sysctl.d
@@ -1276,33 +1311,41 @@ verb 3" >>/etc/openvpn/server.conf
fi fi
# Finally, restart and enable OpenVPN # Finally, restart and enable OpenVPN
# OpenVPN 2.4+ uses openvpn-server@.service with config in /etc/openvpn/server/
log_info "Configuring OpenVPN service..." log_info "Configuring OpenVPN service..."
if [[ $OS == 'arch' || $OS == 'fedora' || $OS == 'centos' || $OS == 'oracle' || $OS == 'amzn2023' ]]; then
# Don't modify package-provided service
run_cmd "Copying OpenVPN service file" cp /usr/lib/systemd/system/openvpn-server@.service /etc/systemd/system/openvpn-server@.service
# Workaround to fix OpenVPN service on OpenVZ # Find the service file (location and name vary by distro)
run_cmd "Patching service file (LimitNPROC)" sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn-server@.service # Modern distros: openvpn-server@.service in /usr/lib/systemd/system/ or /lib/systemd/system/
# Another workaround to keep using /etc/openvpn/ # openSUSE: openvpn@.service (old-style) that we need to adapt
run_cmd "Patching service file (paths)" sed -i 's|/etc/openvpn/server|/etc/openvpn|' /etc/systemd/system/openvpn-server@.service if [[ -f /usr/lib/systemd/system/openvpn-server@.service ]]; then
SERVICE_SOURCE="/usr/lib/systemd/system/openvpn-server@.service"
run_cmd "Reloading systemd" systemctl daemon-reload elif [[ -f /lib/systemd/system/openvpn-server@.service ]]; then
run_cmd "Enabling OpenVPN service" systemctl enable openvpn-server@server SERVICE_SOURCE="/lib/systemd/system/openvpn-server@.service"
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server elif [[ -f /usr/lib/systemd/system/openvpn@.service ]]; then
# openSUSE uses old-style service, we'll create our own openvpn-server@.service
SERVICE_SOURCE="/usr/lib/systemd/system/openvpn@.service"
elif [[ -f /lib/systemd/system/openvpn@.service ]]; then
SERVICE_SOURCE="/lib/systemd/system/openvpn@.service"
else else
# Don't modify package-provided service log_fatal "Could not find openvpn-server@.service or openvpn@.service file"
run_cmd "Copying OpenVPN service file" cp /lib/systemd/system/openvpn\@.service /etc/systemd/system/openvpn\@.service
# Workaround to fix OpenVPN service on OpenVZ
run_cmd "Patching service file (LimitNPROC)" sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn\@.service
# Another workaround to keep using /etc/openvpn/
run_cmd "Patching service file (paths)" sed -i 's|/etc/openvpn/server|/etc/openvpn|' /etc/systemd/system/openvpn\@.service
run_cmd "Reloading systemd" systemctl daemon-reload
run_cmd "Enabling OpenVPN service" systemctl enable openvpn@server
run_cmd "Starting OpenVPN service" systemctl restart openvpn@server
fi fi
# Don't modify package-provided service, copy to /etc/systemd/system/
run_cmd "Copying OpenVPN service file" cp "$SERVICE_SOURCE" /etc/systemd/system/openvpn-server@.service
# Workaround to fix OpenVPN service on OpenVZ
run_cmd "Patching service file (LimitNPROC)" sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn-server@.service
# Ensure the service uses /etc/openvpn/server/ as working directory
# This is needed for openSUSE which uses old-style paths by default
if grep -q "cd /etc/openvpn/" /etc/systemd/system/openvpn-server@.service; then
run_cmd "Patching service file (paths)" sed -i 's|/etc/openvpn/|/etc/openvpn/server/|g' /etc/systemd/system/openvpn-server@.service
fi
run_cmd "Reloading systemd" systemctl daemon-reload
run_cmd "Enabling OpenVPN service" systemctl enable openvpn-server@server
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
if [[ $DNS == 2 ]]; then if [[ $DNS == 2 ]]; then
installUnbound installUnbound
fi fi
@@ -1373,12 +1416,12 @@ WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service
# client-template.txt is created so we have a template to add further users later # client-template.txt is created so we have a template to add further users later
log_info "Creating client template..." log_info "Creating client template..."
echo "client" >/etc/openvpn/client-template.txt echo "client" >/etc/openvpn/server/client-template.txt
if [[ $PROTOCOL == 'udp' ]]; then if [[ $PROTOCOL == 'udp' ]]; then
echo "proto udp" >>/etc/openvpn/client-template.txt echo "proto udp" >>/etc/openvpn/server/client-template.txt
echo "explicit-exit-notify" >>/etc/openvpn/client-template.txt echo "explicit-exit-notify" >>/etc/openvpn/server/client-template.txt
elif [[ $PROTOCOL == 'tcp' ]]; then elif [[ $PROTOCOL == 'tcp' ]]; then
echo "proto tcp-client" >>/etc/openvpn/client-template.txt echo "proto tcp-client" >>/etc/openvpn/server/client-template.txt
fi fi
echo "remote $IP $PORT echo "remote $IP $PORT
dev tun dev tun
@@ -1399,10 +1442,10 @@ tls-version-min 1.2
tls-cipher $CC_CIPHER tls-cipher $CC_CIPHER
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/client-template.txt verb 3" >>/etc/openvpn/server/client-template.txt
if [[ $COMPRESSION_ENABLED == "y" ]]; then if [[ $COMPRESSION_ENABLED == "y" ]]; then
echo "compress $COMPRESSION_ALG" >>/etc/openvpn/client-template.txt echo "compress $COMPRESSION_ALG" >>/etc/openvpn/server/client-template.txt
fi fi
# Generate the custom client.ovpn # Generate the custom client.ovpn
@@ -1431,9 +1474,9 @@ function getHomeDir() {
function regenerateCRL() { function regenerateCRL() {
export EASYRSA_CRL_DAYS=$DEFAULT_CRL_VALIDITY_DURATION_DAYS export EASYRSA_CRL_DAYS=$DEFAULT_CRL_VALIDITY_DURATION_DAYS
run_cmd "Regenerating CRL" ./easyrsa gen-crl run_cmd "Regenerating CRL" ./easyrsa gen-crl
run_cmd "Removing old CRL" rm -f /etc/openvpn/crl.pem run_cmd "Removing old CRL" rm -f /etc/openvpn/server/crl.pem
run_cmd "Copying new CRL" cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem run_cmd "Copying new CRL" cp /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server/crl.pem
run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/crl.pem run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/server/crl.pem
} }
# Helper function to generate .ovpn client config file # Helper function to generate .ovpn client config file
@@ -1443,37 +1486,37 @@ function generateClientConfig() {
# Determine if we use tls-auth or tls-crypt # Determine if we use tls-auth or tls-crypt
local tls_sig="" local tls_sig=""
if grep -qs "^tls-crypt" /etc/openvpn/server.conf; then if grep -qs "^tls-crypt" /etc/openvpn/server/server.conf; then
tls_sig="1" tls_sig="1"
elif grep -qs "^tls-auth" /etc/openvpn/server.conf; then elif grep -qs "^tls-auth" /etc/openvpn/server/server.conf; then
tls_sig="2" tls_sig="2"
fi fi
# Generate the custom client.ovpn # Generate the custom client.ovpn
run_cmd "Creating client config" cp /etc/openvpn/client-template.txt "$home_dir/$client.ovpn" run_cmd "Creating client config" cp /etc/openvpn/server/client-template.txt "$home_dir/$client.ovpn"
{ {
echo "<ca>" echo "<ca>"
cat "/etc/openvpn/easy-rsa/pki/ca.crt" cat "/etc/openvpn/server/easy-rsa/pki/ca.crt"
echo "</ca>" echo "</ca>"
echo "<cert>" echo "<cert>"
awk '/BEGIN/,/END CERTIFICATE/' "/etc/openvpn/easy-rsa/pki/issued/$client.crt" awk '/BEGIN/,/END CERTIFICATE/' "/etc/openvpn/server/easy-rsa/pki/issued/$client.crt"
echo "</cert>" echo "</cert>"
echo "<key>" echo "<key>"
cat "/etc/openvpn/easy-rsa/pki/private/$client.key" cat "/etc/openvpn/server/easy-rsa/pki/private/$client.key"
echo "</key>" echo "</key>"
case $tls_sig in case $tls_sig in
1) 1)
echo "<tls-crypt>" echo "<tls-crypt>"
cat /etc/openvpn/tls-crypt.key cat /etc/openvpn/server/tls-crypt.key
echo "</tls-crypt>" echo "</tls-crypt>"
;; ;;
2) 2)
echo "key-direction 1" echo "key-direction 1"
echo "<tls-auth>" echo "<tls-auth>"
cat /etc/openvpn/tls-auth.key cat /etc/openvpn/server/tls-auth.key
echo "</tls-auth>" echo "</tls-auth>"
;; ;;
esac esac
@@ -1490,7 +1533,7 @@ function selectClient() {
local show_expiry="${1:-false}" local show_expiry="${1:-false}"
local client_number local client_number
NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c "^V") NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V")
if [[ $NUMBEROFCLIENTS == '0' ]]; then if [[ $NUMBEROFCLIENTS == '0' ]]; then
log_fatal "You have no existing clients!" log_fatal "You have no existing clients!"
fi fi
@@ -1498,16 +1541,16 @@ function selectClient() {
if [[ $show_expiry == "true" ]]; then if [[ $show_expiry == "true" ]]; then
local i=1 local i=1
while read -r client; do while read -r client; do
local client_cert="/etc/openvpn/easy-rsa/pki/issued/$client.crt" local client_cert="/etc/openvpn/server/easy-rsa/pki/issued/$client.crt"
local days local days
days=$(getDaysUntilExpiry "$client_cert") days=$(getDaysUntilExpiry "$client_cert")
local expiry local expiry
expiry=$(formatExpiry "$days") expiry=$(formatExpiry "$days")
echo " $i) $client $expiry" echo " $i) $client $expiry"
((i++)) ((i++))
done < <(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2) done < <(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2)
else else
tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') ' tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
fi fi
until [[ ${CLIENTNUMBER:-$client_number} -ge 1 && ${CLIENTNUMBER:-$client_number} -le $NUMBEROFCLIENTS ]]; do until [[ ${CLIENTNUMBER:-$client_number} -ge 1 && ${CLIENTNUMBER:-$client_number} -le $NUMBEROFCLIENTS ]]; do
@@ -1518,7 +1561,7 @@ function selectClient() {
fi fi
done done
CLIENTNUMBER="${CLIENTNUMBER:-$client_number}" CLIENTNUMBER="${CLIENTNUMBER:-$client_number}"
CLIENT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$CLIENTNUMBER"p) CLIENT=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$CLIENTNUMBER"p)
} }
function newClient() { function newClient() {
@@ -1548,12 +1591,12 @@ function newClient() {
read -rp "Select an option [1-2]: " -e -i 1 PASS read -rp "Select an option [1-2]: " -e -i 1 PASS
done done
CLIENTEXISTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -E "^V" | grep -c -E "/CN=$CLIENT\$") CLIENTEXISTS=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -E "^V" | grep -c -E "/CN=$CLIENT\$")
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 exit
else else
cd /etc/openvpn/easy-rsa/ || return cd /etc/openvpn/server/easy-rsa/ || return
log_info "Generating client certificate..." log_info "Generating client certificate..."
export EASYRSA_CERT_EXPIRE=$CLIENT_CERT_DURATION_DAYS export EASYRSA_CERT_EXPIRE=$CLIENT_CERT_DURATION_DAYS
case $PASS in case $PASS in
@@ -1584,14 +1627,14 @@ function revokeClient() {
log_prompt "Select the existing client certificate you want to revoke" log_prompt "Select the existing client certificate you want to revoke"
selectClient selectClient
cd /etc/openvpn/easy-rsa/ || return cd /etc/openvpn/server/easy-rsa/ || return
log_info "Revoking certificate for $CLIENT..." log_info "Revoking certificate for $CLIENT..."
run_cmd "Revoking certificate" ./easyrsa --batch revoke-issued "$CLIENT" run_cmd "Revoking certificate" ./easyrsa --batch revoke-issued "$CLIENT"
regenerateCRL regenerateCRL
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/ipp.txt run_cmd "Removing IP assignment" sed -i "/^$CLIENT,.*/d" /etc/openvpn/server/ipp.txt
run_cmd "Backing up index" cp /etc/openvpn/easy-rsa/pki/index.txt{,.bk} run_cmd "Backing up index" cp /etc/openvpn/server/easy-rsa/pki/index.txt{,.bk}
log_success "Certificate for client $CLIENT revoked." log_success "Certificate for client $CLIENT revoked."
} }
@@ -1614,11 +1657,11 @@ function renewClient() {
client_cert_duration_days=$CLIENT_CERT_DURATION_DAYS client_cert_duration_days=$CLIENT_CERT_DURATION_DAYS
fi fi
cd /etc/openvpn/easy-rsa/ || return cd /etc/openvpn/server/easy-rsa/ || return
log_info "Renewing certificate for $CLIENT..." log_info "Renewing certificate for $CLIENT..."
# Backup the old certificate before renewal # Backup the old certificate before renewal
run_cmd "Backing up old certificate" cp "/etc/openvpn/easy-rsa/pki/issued/$CLIENT.crt" "/etc/openvpn/easy-rsa/pki/issued/$CLIENT.crt.bak" run_cmd "Backing up old certificate" cp "/etc/openvpn/server/easy-rsa/pki/issued/$CLIENT.crt" "/etc/openvpn/server/easy-rsa/pki/issued/$CLIENT.crt.bak"
# Renew the certificate (keeps the same private key) # Renew the certificate (keeps the same private key)
export EASYRSA_CERT_EXPIRE=$client_cert_duration_days export EASYRSA_CERT_EXPIRE=$client_cert_duration_days
@@ -1645,10 +1688,10 @@ function renewServer() {
log_header "Renew Server Certificate" log_header "Renew Server Certificate"
# Get the server name from the config # Get the server name from the config (extract basename since path may be relative)
server_name=$(grep '^cert ' /etc/openvpn/server.conf | cut -d ' ' -f 2 | sed 's/\.crt$//') server_name=$(basename "$(grep '^cert ' /etc/openvpn/server/server.conf | cut -d ' ' -f 2)" .crt)
if [[ -z "$server_name" ]]; then if [[ -z "$server_name" ]]; then
log_fatal "Could not determine server certificate name from /etc/openvpn/server.conf" log_fatal "Could not determine server certificate name from /etc/openvpn/server/server.conf"
fi fi
log_prompt "This will renew the server certificate: $server_name" log_prompt "This will renew the server certificate: $server_name"
@@ -1672,11 +1715,11 @@ function renewServer() {
server_cert_duration_days=$SERVER_CERT_DURATION_DAYS server_cert_duration_days=$SERVER_CERT_DURATION_DAYS
fi fi
cd /etc/openvpn/easy-rsa/ || return cd /etc/openvpn/server/easy-rsa/ || return
log_info "Renewing server certificate..." log_info "Renewing server certificate..."
# Backup the old certificate before renewal # Backup the old certificate before renewal
run_cmd "Backing up old certificate" cp "/etc/openvpn/easy-rsa/pki/issued/$server_name.crt" "/etc/openvpn/easy-rsa/pki/issued/$server_name.crt.bak" run_cmd "Backing up old certificate" cp "/etc/openvpn/server/easy-rsa/pki/issued/$server_name.crt" "/etc/openvpn/server/easy-rsa/pki/issued/$server_name.crt.bak"
# Renew the certificate (keeps the same private key) # Renew the certificate (keeps the same private key)
export EASYRSA_CERT_EXPIRE=$server_cert_duration_days export EASYRSA_CERT_EXPIRE=$server_cert_duration_days
@@ -1688,18 +1731,12 @@ function renewServer() {
# Regenerate the CRL # Regenerate the CRL
regenerateCRL regenerateCRL
# Copy the new certificate to /etc/openvpn/ # Copy the new certificate to /etc/openvpn/server/
run_cmd "Copying new certificate" cp "/etc/openvpn/easy-rsa/pki/issued/$server_name.crt" /etc/openvpn/ run_cmd "Copying new certificate" cp "/etc/openvpn/server/easy-rsa/pki/issued/$server_name.crt" /etc/openvpn/server/
# Restart OpenVPN # Restart OpenVPN
log_info "Restarting OpenVPN service..." log_info "Restarting OpenVPN service..."
if [[ $OS =~ (fedora|arch|centos|oracle) ]]; then run_cmd "Restarting OpenVPN" systemctl restart openvpn-server@server
run_cmd "Restarting OpenVPN" systemctl restart openvpn-server@server
elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then
run_cmd "Restarting OpenVPN" systemctl restart openvpn
else
run_cmd "Restarting OpenVPN" systemctl restart openvpn@server
fi
log_success "Server certificate renewed successfully and is valid for $server_cert_duration_days days." log_success "Server certificate renewed successfully and is valid for $server_cert_duration_days days."
} }
@@ -1743,12 +1780,12 @@ function renewMenu() {
log_header "Certificate Renewal" log_header "Certificate Renewal"
# Get server certificate expiry for menu display # Get server certificate expiry for menu display (extract basename since path may be relative)
server_name=$(grep '^cert ' /etc/openvpn/server.conf | cut -d ' ' -f 2 | sed 's/\.crt$//') server_name=$(basename "$(grep '^cert ' /etc/openvpn/server/server.conf | cut -d ' ' -f 2)" .crt)
if [[ -z "$server_name" ]]; then if [[ -z "$server_name" ]]; then
server_expiry="(unknown expiry)" server_expiry="(unknown expiry)"
else else
server_cert="/etc/openvpn/easy-rsa/pki/issued/$server_name.crt" server_cert="/etc/openvpn/server/easy-rsa/pki/issued/$server_name.crt"
server_days=$(getDaysUntilExpiry "$server_cert") server_days=$(getDaysUntilExpiry "$server_cert")
server_expiry=$(formatExpiry "$server_days") server_expiry=$(formatExpiry "$server_days")
fi fi
@@ -1819,23 +1856,15 @@ function removeOpenVPN() {
read -rp "Do you really want to remove OpenVPN? [y/n]: " -e -i n REMOVE read -rp "Do you really want to remove OpenVPN? [y/n]: " -e -i n REMOVE
if [[ $REMOVE == 'y' ]]; then if [[ $REMOVE == 'y' ]]; then
# Get OpenVPN port from the configuration # Get OpenVPN port from the configuration
PORT=$(grep '^port ' /etc/openvpn/server.conf | cut -d " " -f 2) PORT=$(grep '^port ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
PROTOCOL=$(grep '^proto ' /etc/openvpn/server.conf | cut -d " " -f 2) PROTOCOL=$(grep '^proto ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
# Stop OpenVPN # Stop OpenVPN
log_info "Stopping OpenVPN service..." log_info "Stopping OpenVPN service..."
if [[ $OS =~ (fedora|arch|centos|oracle) ]]; then run_cmd "Disabling OpenVPN service" systemctl disable openvpn-server@server
run_cmd "Disabling OpenVPN service" systemctl disable openvpn-server@server run_cmd "Stopping OpenVPN service" systemctl stop openvpn-server@server
run_cmd "Stopping OpenVPN service" systemctl stop openvpn-server@server # Remove customised service
# Remove customised service run_cmd "Removing service file" rm -f /etc/systemd/system/openvpn-server@.service
run_cmd "Removing service file" rm /etc/systemd/system/openvpn-server@.service
else
# Debian, Ubuntu, openSUSE use openvpn@server
run_cmd "Disabling OpenVPN service" systemctl disable openvpn@server
run_cmd "Stopping OpenVPN service" systemctl stop openvpn@server
# Remove customised service
run_cmd "Removing service file" rm /etc/systemd/system/openvpn\@.service
fi
# Remove the iptables rules related to the script # Remove the iptables rules related to the script
log_info "Removing iptables rules..." log_info "Removing iptables rules..."
@@ -1946,7 +1975,7 @@ function manageMenu() {
initialCheck initialCheck
# Check if OpenVPN is already installed # Check if OpenVPN is already installed
if [[ -e /etc/openvpn/server.conf ]]; then if [[ -e /etc/openvpn/server/server.conf ]]; then
manageMenu manageMenu
else else
installOpenVPN installOpenVPN

View File

@@ -65,12 +65,12 @@ fi
echo "Verifying installation..." echo "Verifying installation..."
MISSING_FILES=0 MISSING_FILES=0
for f in \ for f in \
/etc/openvpn/server.conf \ /etc/openvpn/server/server.conf \
/etc/openvpn/ca.crt \ /etc/openvpn/server/ca.crt \
/etc/openvpn/ca.key \ /etc/openvpn/server/ca.key \
/etc/openvpn/tls-crypt.key \ /etc/openvpn/server/tls-crypt.key \
/etc/openvpn/crl.pem \ /etc/openvpn/server/crl.pem \
/etc/openvpn/easy-rsa/pki/ca.crt \ /etc/openvpn/server/easy-rsa/pki/ca.crt \
/etc/iptables/add-openvpn-rules.sh \ /etc/iptables/add-openvpn-rules.sh \
/root/testclient.ovpn; do /root/testclient.ovpn; do
if [ ! -f "$f" ]; then if [ ! -f "$f" ]; then
@@ -85,9 +85,66 @@ if [ $MISSING_FILES -gt 0 ]; then
fi fi
echo "All required files present" 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 ""
echo "Server config:" echo "Server config:"
cat /etc/openvpn/server.conf cat /etc/openvpn/server/server.conf
# Copy client config to shared volume # Copy client config to shared volume
cp /root/testclient.ovpn /shared/client.ovpn cp /root/testclient.ovpn /shared/client.ovpn
@@ -102,7 +159,7 @@ echo ""
echo "=== Testing Certificate Renewal ===" echo "=== Testing Certificate Renewal ==="
# Get the original certificate serial number for comparison # 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" echo "Original client certificate serial: $ORIG_CERT_SERIAL"
# Test client certificate renewal using the script # Test client certificate renewal using the script
@@ -120,7 +177,7 @@ else
fi fi
# Verify new certificate has different serial # 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" echo "New client certificate serial: $NEW_CERT_SERIAL"
if [ "$ORIG_CERT_SERIAL" != "$NEW_CERT_SERIAL" ]; then if [ "$ORIG_CERT_SERIAL" != "$NEW_CERT_SERIAL" ]; then
echo "PASS: Certificate serial changed (renewal created new cert)" echo "PASS: Certificate serial changed (renewal created new cert)"
@@ -131,8 +188,8 @@ fi
# Verify renewed certificate has correct validity period # Verify renewed certificate has correct validity period
# The default is 3650 days, so the cert should be valid for ~10 years from now # 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_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/easy-rsa/pki/issued/testclient.crt -noout -startdate | 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 from: $CLIENT_CERT_NOT_BEFORE"
echo "Client certificate valid until: $CLIENT_CERT_NOT_AFTER" echo "Client certificate valid until: $CLIENT_CERT_NOT_AFTER"
@@ -159,7 +216,7 @@ else
fi fi
# Verify CRL was updated # Verify CRL was updated
if [ -f /etc/openvpn/crl.pem ]; then if [ -f /etc/openvpn/server/crl.pem ]; then
echo "PASS: CRL file exists" echo "PASS: CRL file exists"
else else
echo "FAIL: CRL file missing after renewal" echo "FAIL: CRL file missing after renewal"
@@ -179,9 +236,9 @@ echo "=== Client Certificate Renewal Tests PASSED ==="
echo "" echo ""
echo "=== Testing Server Certificate Renewal ===" echo "=== Testing Server Certificate Renewal ==="
# Get server certificate name and original serial # Get server certificate name and original serial (extract basename since path may be relative)
SERVER_NAME=$(grep '^cert ' /etc/openvpn/server.conf | cut -d ' ' -f 2 | sed 's/\.crt$//') SERVER_NAME=$(basename "$(grep '^cert ' /etc/openvpn/server/server.conf | cut -d ' ' -f 2)" .crt)
ORIG_SERVER_SERIAL=$(openssl x509 -in "/etc/openvpn/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -serial | cut -d= -f2) 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 "Server certificate: $SERVER_NAME"
echo "Original server certificate serial: $ORIG_SERVER_SERIAL" echo "Original server certificate serial: $ORIG_SERVER_SERIAL"
@@ -200,7 +257,7 @@ else
fi fi
# Verify new certificate has different serial # 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" echo "New server certificate serial: $NEW_SERVER_SERIAL"
if [ "$ORIG_SERVER_SERIAL" != "$NEW_SERVER_SERIAL" ]; then if [ "$ORIG_SERVER_SERIAL" != "$NEW_SERVER_SERIAL" ]; then
echo "PASS: Server certificate serial changed (renewal created new cert)" echo "PASS: Server certificate serial changed (renewal created new cert)"
@@ -210,8 +267,8 @@ else
fi fi
# Verify renewed server certificate has correct validity period # 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_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/easy-rsa/pki/issued/$SERVER_NAME.crt" -noout -startdate | 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 from: $SERVER_CERT_NOT_BEFORE"
echo "Server certificate valid until: $SERVER_CERT_NOT_AFTER" echo "Server certificate valid until: $SERVER_CERT_NOT_AFTER"
@@ -227,17 +284,17 @@ else
exit 1 exit 1
fi fi
# Verify the new certificate was copied to /etc/openvpn/ # Verify the new certificate was copied to /etc/openvpn/server/
if [ -f "/etc/openvpn/$SERVER_NAME.crt" ]; then if [ -f "/etc/openvpn/server/$SERVER_NAME.crt" ]; then
DEPLOYED_SERIAL=$(openssl x509 -in "/etc/openvpn/$SERVER_NAME.crt" -noout -serial | cut -d= -f2) DEPLOYED_SERIAL=$(openssl x509 -in "/etc/openvpn/server/$SERVER_NAME.crt" -noout -serial | cut -d= -f2)
if [ "$NEW_SERVER_SERIAL" = "$DEPLOYED_SERIAL" ]; then 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 else
echo "FAIL: Deployed certificate doesn't match renewed certificate" echo "FAIL: Deployed certificate doesn't match renewed certificate"
exit 1 exit 1
fi fi
else else
echo "FAIL: Server certificate not found in /etc/openvpn/" echo "FAIL: Server certificate not found in /etc/openvpn/server/"
exit 1 exit 1
fi fi
@@ -309,11 +366,11 @@ else
fi fi
# Verify OpenVPN pushes correct DNS # 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" echo "PASS: OpenVPN configured to push Unbound DNS"
else else
echo "FAIL: OpenVPN not configured to push Unbound DNS" 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 exit 1
fi fi
@@ -346,9 +403,9 @@ if [ "$(cat /proc/sys/net/ipv4/ip_forward)" != "1" ]; then
} }
fi fi
# Start OpenVPN in background (run from /etc/openvpn so relative paths work) # Start OpenVPN in background (run from /etc/openvpn/server so relative paths work)
cd /etc/openvpn cd /etc/openvpn/server
openvpn --config /etc/openvpn/server.conf --log /var/log/openvpn-server.log & openvpn --config /etc/openvpn/server/server.conf --log /var/log/openvpn-server.log &
OPENVPN_PID=$! OPENVPN_PID=$!
# Wait for OpenVPN to start # Wait for OpenVPN to start
@@ -453,10 +510,10 @@ echo "Revoking certificate for '$REVOKE_CLIENT'..."
REVOKE_OUTPUT="/tmp/revoke-output.log" REVOKE_OUTPUT="/tmp/revoke-output.log"
# MENU_OPTION=2 is revoke, CLIENTNUMBER is dynamically determined from index.txt # MENU_OPTION=2 is revoke, CLIENTNUMBER is dynamically determined from index.txt
# We need to find the client number for revoketest # 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 if [ -z "$REVOKE_CLIENT_NUM" ]; then
echo "ERROR: Could not find client number for '$REVOKE_CLIENT'" 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 exit 1
fi fi
echo "Revoke client number: $REVOKE_CLIENT_NUM" echo "Revoke client number: $REVOKE_CLIENT_NUM"
@@ -471,11 +528,11 @@ else
fi fi
# Verify certificate is marked as revoked in index.txt # 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" echo "PASS: Certificate marked as revoked in index.txt"
else else
echo "FAIL: Certificate not marked as revoked" 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 exit 1
fi fi
@@ -520,23 +577,23 @@ else
fi fi
# Verify the new certificate is valid (V) in index.txt # 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" echo "PASS: New certificate is valid in index.txt"
else else
echo "FAIL: New certificate not marked as valid" 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 exit 1
fi 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/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/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 else
echo "FAIL: Unexpected certificate state" echo "FAIL: Unexpected certificate state"
cat /etc/openvpn/easy-rsa/pki/index.txt cat /etc/openvpn/server/easy-rsa/pki/index.txt
exit 1 exit 1
fi fi