In fingerprint mode (OpenVPN 2.6+ peer-fingerprint authentication),
easyrsa self-sign-* commands don't create/maintain index.txt, but
several client management functions depended on it.
This commit fixes:
- selectClient(): now uses getClientsFromFingerprints() helper
- listClients(): properly lists clients from server.conf fingerprints
- newClient(): duplicate check now works in fingerprint mode
- renewClient(): uses self-sign-client instead of easyrsa renew
- renewServer(): uses self-sign-server and regenerates all client
configs with the new server fingerprint
New helper functions:
- getAuthMode(): determines if PKI or fingerprint mode is active
- getClientsFromFingerprints(): parses <peer-fingerprint> block
- clientExistsInFingerprints(): checks if client exists
Fixes#1444
The fingerprint mode CI test was not actually testing fingerprint mode
because Docker environment variables (-e AUTH_MODE=fingerprint) were not
being inherited by the systemd service running the tests.
Add PassEnvironment directive to pass AUTH_MODE and other test config
env vars from Docker to the systemd service.
Fixes the CI gap where fingerprint mode issues went undetected.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
|
[docker/setup-buildx-action](https://redirect.github.com/docker/setup-buildx-action)
| action | minor | `v3.11.1` -> `v3.12.0` |
---
### Release Notes
<details>
<summary>docker/setup-buildx-action
(docker/setup-buildx-action)</summary>
###
[`v3.12.0`](https://redirect.github.com/docker/setup-buildx-action/compare/v3.11.1...v3.12.0)
[Compare
Source](https://redirect.github.com/docker/setup-buildx-action/compare/v3.11.1...v3.12.0)
</details>
---
### Configuration
📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).
🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/angristan/openvpn-install).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi41OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Adds FAQ entry for server-side split-tunnel configuration.
Closes#443. The script is focused on the road warrior use case
(full-tunnel for privacy on untrusted networks), so split-tunnel is
documented as a manual post-install configuration rather than a built-in
feature.
Closes#547.
## Summary
- Expands the FAQ entry about accessing computers on the server's LAN
- The previous answer only mentioned pushing a route, which is
insufficient for most setups
- Added explanation of the return routing requirement with two options:
- Static route on router (recommended)
- Masquerade rule (when router can't be modified)
Closes#1126
## Summary
Adds immediate client disconnect when a certificate is revoked, via
OpenVPN management interface.
Previously, revoked clients stayed connected until they voluntarily
disconnected or the server restarted.
Fixes#1199
## Changes
- Enable management interface (Unix socket at
`/var/run/openvpn/server.sock`)
- Add `disconnectClient()` function to send `kill` command on revoke
- Add `socat` dependency for socket communication
## Summary
- Eliminate all CHOICE intermediary variables (~17 variables removed)
- Replace numeric values with descriptive string values throughout
- Centralize configuration defaults in a single function
- Add comprehensive validation for all configuration values
- Add reusable helper functions for interactive prompts
- Fix non-interactive mode to skip interactive prompts entirely
## Changes
### Configuration Values Now Use Strings
| Variable | Before | After |
|----------|--------|-------|
| TLS_SIG | 1, 2, 3 | "crypt-v2", "crypt", "auth" |
| CERT_TYPE | 1, 2 | "ecdsa", "rsa" |
| DNS | 1-13 | "cloudflare", "quad9", "custom", etc. |
### Code Reduction
- Removed ~80 lines of duplicate defaults from `installOpenVPN()`
- Removed ~60 lines of CHOICE variable assignments from `cmd_install()`
- Net change: cleaner structure with better separation of concerns
### New Functions
- `set_installation_defaults()`: Single source of truth for all defaults
- `validate_configuration()`: Validates all config values after defaults
applied
- `select_from_array()`: Generic menu selection helper
- `select_with_labels()`: Menu with display labels different from values
- `prompt_yes_no()`: Yes/no prompts with validation
- `prompt_validated()`: Custom value prompts with validation function
- `detect_server_ips()`: Detects server IPv4/IPv6 addresses
- `prepare_network_config()`: Calculates derived network config
(gateways, etc.)
### Configuration Constants
Added readonly arrays for valid options:
- `PROTOCOLS`, `DNS_PROVIDERS`, `CIPHERS`, `CERT_TYPES`
- `CERT_CURVES`, `RSA_KEY_SIZES`, `TLS_VERSIONS`
- `TLS13_CIPHERSUITES_OPTIONS`, `TLS_GROUPS_OPTIONS`
- `HMAC_ALGORITHMS`, `TLS_SIG_MODES`
### Non-Interactive Mode Fix
- `installQuestions()` is now only called in interactive mode
- IP detection and gateway calculations extracted to separate functions
- Renamed `AUTO_INSTALL` to `NON_INTERACTIVE_INSTALL` for clarity
- Non-interactive installs no longer hang waiting for user input
## Test plan
- [ ] Run `make test` (default Ubuntu)
- [ ] Test interactive installation
- [ ] Test non-interactive installation with CLI flags
- [ ] Test non-interactive installation with environment variables
- [ ] Test invalid configuration values are rejected
## Summary
- Fixes firewall rules that hardcode `tun0` interface, which fails when
OpenVPN uses `tun1`, `tun2`, etc. because another service already
occupies `tun0`
- Uses a defense-in-depth approach combining interface wildcard matching
with source-based rules to prevent IP spoofing
Fixes#1298
## Changes
| Backend | Before | After |
|---------|--------|-------|
| **iptables** | `-i tun0` | `-i tun+ -s $VPN_SUBNET` |
| **nftables** | `iifname "tun0"` | `iifname "tun*" ip saddr
$VPN_SUBNET` |
| **firewalld** | rich rules (source-based) | no change needed |
## Implementation Details
- **iptables/nftables**: Combined interface wildcard (`tun+`/`tun*`)
with source matching provides defense in depth - traffic must come from
both a tun interface AND the VPN subnet
- **firewalld**: Already used source-based rich rules, so no changes
required (rich rules work reliably across both iptables and nftables
backends)
## Summary
- Replace all fixed-timeout wait loops with simple indefinite `while`
loops
- Add 5-second stabilization delay after VPN connection before running
ping tests
- Add server-side tun0 interface verification before signaling client
- Add wait for OpenVPN restart after server certificate renewal
## Problem
Tests fail randomly with errors like:
```
Test 2: Pinging VPN gateway (10.9.0.1)...
10 packets transmitted, 0 received, 100% packet loss
FAIL: Cannot ping VPN gateway
```
Example:
https://github.com/angristan/openvpn-install/actions/runs/20230801728/job/58072998112
## Solution
Instead of guessing timeout values that may be too short for slow CI
runners, all wait loops now run indefinitely and rely on the job-level
timeout to catch actual failures.
**Before:**
```bash
MAX_WAIT=60
WAITED=0
while [ condition ] && [ $WAITED -lt $MAX_WAIT ]; do
sleep 2
WAITED=$((WAITED + 2))
done
if [ condition ]; then exit 1; fi
```
**After:**
```bash
while [ condition ]; do
sleep 2
done
```
This removes 83 lines of boilerplate timeout logic.
## Summary
Fixed a bug where installation questions were asked twice in interactive
mode, and a related bug where the MTU choice prompt appeared in
non-interactive CLI mode.
**Root cause:** `installQuestions()` was being called both by the caller
(`cmd_install()` or `cmd_interactive()`) and again inside
`installOpenVPN()`.
**Changes:**
- Removed the redundant `installQuestions()` call from
`installOpenVPN()`
- Added `installQuestions()` call to the non-interactive branch of
`cmd_install()` (needed for IP detection and setup)
- Added `MTU_CHOICE` default to the non-interactive branch (was
previously set inside `installOpenVPN()` before calling
`installQuestions()`)
## Summary
- Changed `apt-get update` commands from `run_cmd` to `run_cmd_fatal`
- Package list updates are critical operations that should fail the
installation if they fail
- Affects 3 locations: initial update, post-repo-add update, and removal
cleanup
## Summary
- Add `is_valid_client_name()` helper and `validate_client_name()`
function to enforce client name constraints
- Reject client names longer than 64 characters (OpenSSL CN limit)
- Apply validation at all entry points: interactive prompt, `client add`
CLI, and `--client` install option
## Problem
Client names longer than 64 bytes cause Easy-RSA/OpenSSL to silently
truncate or reject Common Names, resulting in `.ovpn` files with empty
`<cert>/<key>` sections. Users (especially in headless/automated
deployments) would see the script complete successfully but get
non-functional output.
Fixes#1306
## Summary
- Add `--mtu <size>` CLI option to configure tunnel MTU (valid range:
576-65535)
- Add interactive prompt with user-friendly explanation for
non-technical users
- Write `tun-mtu` to server.conf and client template when custom value
is set
- OpenVPN auto-calculates MSSFIX based on the MTU value (no separate
option needed)
## Use cases
- PPPoE connections (typically need MTU ~1492)
- Mobile/cellular networks with variable MTU
- Networks with connectivity issues due to fragmentation
## Usage
```bash
# CLI mode
./openvpn-install.sh install --mtu 1400
# Interactive mode prompts with explanation:
# "MTU controls the maximum packet size. Lower values can help
# with connectivity issues on some networks (e.g., PPPoE, mobile)."
```
Close https://github.com/angristan/openvpn-install/pull/1300
Co-authored-by: Fabian Druschke <fdruschke@outlook.com>
## Summary
- Remove compression support from the script (CLI option, interactive
prompts, config generation)
- Compression is unsafe due to the VORACLE attack and OpenVPN is
deprecating it
- Simplify DCO compatibility check (no longer needs compression
condition)
Closes https://github.com/angristan/openvpn-install/issues/872
## Summary
- Add new menu option "3) List connected clients" to show currently
connected VPN clients
- Parses `/var/log/openvpn/status.log` and displays client name, real
IP, VPN IP, connection time, and transfer stats
- Human-readable byte formatting (K/M/G)
## Example output
```
Name Real Address VPN IP Connected Since Transfer
---- ------------ ------ --------------- --------
stan 123.45.211.11:28291 10.8.0.2 2025-12-14 10:13:22 ↓7.3M ↑123.5M
```
Closes https://github.com/angristan/openvpn-install/pull/863
## Summary
- Fix tls-crypt-v2 client key generation failing on Ubuntu 25.04+ with
"Permission denied"
- Add Ubuntu 25.10 to CI test matrix
## Root Cause
Ubuntu 25.04 introduced an AppArmor profile for openvpn
(`/etc/apparmor.d/openvpn`) that restricts where the binary can write.
The allowed paths are:
- `/etc/openvpn/{,**}`
- `@{HOME}/**` (owner only)
The script was using `mktemp` which creates files in `/tmp`, causing the
error:
```
Cannot open file '/tmp/tmp.XXX' for write: Permission denied (errno=13)
```
## Fix
Changed the temp file location from `/tmp` to `/etc/openvpn/server/`:
```bash
# Before
tls_crypt_v2_tmpfile=$(mktemp)
# After
tls_crypt_v2_tmpfile=$(mktemp /etc/openvpn/server/tls-crypt-v2-client.XXXXXX)
```
Fixes#1391
- Add nftables as a third firewall backend option alongside firewalld
and iptables
- Detection priority: firewalld → nftables → iptables (legacy fallback)
- Uses dedicated `openvpn` and `openvpn-nat` tables for clean isolation
- Integrates with native `nftables.service` via include in
`/etc/nftables.conf`
Closes https://github.com/angristan/openvpn-install/issues/530
## Summary
Follow-up improvements to #962:
- **Fix `getClientOwner()` edge case**: Now verifies the user actually
exists via `id` command before attempting to set ownership. Previously
only checked if `/home/$client` directory existed, which could fail if
the directory exists but the user doesn't.
- **Add directory creation for custom paths**: When `CLIENT_FILEPATH`
points to a non-existent directory, the script now creates it
automatically with `mkdir -p`.
- **Reduce code duplication**: Extract the repeated filepath/permission
logic from `newClient()` and `renewClient()` into a new
`writeClientConfig()` helper function, removing ~30 lines of duplicated
code.
Fix#961
- Adds CLIENT_FILEPATH env var to specify custom output path for .ovpn files
- Automatically sets correct ownership (chown) and permissions (chmod go-rw) when client name matches a system user
---------
Co-authored-by: Stanislas Lange <git@slange.me>
## Summary
- Add native firewalld support for RHEL/Fedora/CentOS systems
- When firewalld is active, use `firewall-cmd --permanent` instead of
raw iptables
- Rules persist across `firewall-cmd --reload`
- Fall back to iptables when firewalld is not active
- Add `After=firewalld.service` to iptables systemd unit for safety
## Changes
**Install:** Detect firewalld, use `firewall-cmd` to add port,
masquerade, and rich rules. Fall back to iptables if inactive.
**Uninstall:** Detect which method was used and clean up accordingly.
**Tests:** Add `fedora-42-firewalld` CI test with firewalld enabled.
---
Closes https://github.com/angristan/openvpn-install/issues/356
Closes https://github.com/angristan/openvpn-install/pull/1200
Add support for revoking clients by setting the CLIENT environment
variable directly with the client name, in addition to the existing
CLIENTNUMBER support (from
https://github.com/angristan/openvpn-install/pull/1328)
This makes headless revocation more user-friendly as users no longer
need to know the client's index number.
Add a flag `NEW_CLIENT` so that the user can choose whether or not he
wishes to create a new user after installation.
It is specially useful on headless installations, when upgrading to a
different server, but keeping old credentials.
It does not change any defaults, so if no flag is passed, it still
creates the new user.
---------
Co-authored-by: Stanislas Lange <git@slange.me>
## Summary
- Add new "List existing users" option to management menu (option 2)
- Displays all client certificates with status (Valid/Revoked),
expiration date, and days remaining
- Reads expiry directly from certificate files using openssl for
accurate 4-digit year dates
- Output sorted by expiration date (oldest first)
- Updates test MENU_OPTION values to match new menu numbering
Example output:
```
=== Existing Clients ===
Found 2 certificate(s)
Name Status Expiry Remaining
---- ------ ------ ---------
user1 Valid 2035-12-11 3649 days
user2 Revoked unknown unknown
```
Closes#567Closes#563Closes#587
## Summary
- Replace deprecated `--genkey --secret` syntax with `--genkey secret`
for tls-crypt and tls-auth key generation
The OpenVPN source explicitly warns about this:
```
WARNING: Using --genkey --secret filename is DEPRECATED. Use --genkey secret filename instead.
```
Closes#1256
Close https://github.com/angristan/openvpn-install/issues/1280
Add support for a password protected user in headless mode
Fixes#389
---------
Co-authored-by: Siebren Kraak <siebren.kraak@secura.com>
Co-authored-by: Stanislas Lange <git@slange.me>
- Only cancel in-progress CI jobs for pull requests, not for master
branch pushes
- Ensures all master branch jobs run to completion while still saving CI
resources on PRs
## Summary
- Add support for OpenVPN's `tls-crypt-v2` feature (per-client TLS keys)
- Set `tls-crypt-v2` as the new recommended default
- Add CI tests for all 3 TLS key types
Closes#983Closes#758
Closes https://github.com/angristan/openvpn-install/pull/1257
## What is tls-crypt-v2?
Unlike `tls-crypt` (shared key), `tls-crypt-v2` generates unique keys
per client:
- **Better security**: Compromised client keys don't affect other
clients
- **Easier management**: Individual client key revocation without
regenerating server key
- **Scalability**: Better suited for large deployments
Requires OpenVPN 2.5+ (released 2020).
## Menu options
```
1) tls-crypt-v2 (recommended): Encrypts control channel, unique key per client
2) tls-crypt: Encrypts control channel, shared key for all clients
3) tls-auth: Authenticates control channel, no encryption
```