mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-14 16:17:03 +01:00
Add PASSPHRASE support in headless mode (#1015)
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>
This commit is contained in:
@@ -95,7 +95,7 @@ If you want to customise your installation, you can export them or specify them
|
||||
- `COMPRESSION_ENABLED=n`
|
||||
- `CUSTOMIZE_ENC=n`
|
||||
- `CLIENT=clientname`
|
||||
- `PASS=1`
|
||||
- `PASS=1` (set to `2` for password-protected clients, requires `PASSPHRASE`)
|
||||
- `MULTI_CLIENT=n`
|
||||
- `CLIENT_CERT_DURATION_DAYS=3650`
|
||||
- `SERVER_CERT_DURATION_DAYS=3650`
|
||||
@@ -104,8 +104,6 @@ If the server is behind NAT, you can specify its endpoint with the `ENDPOINT` va
|
||||
|
||||
Other variables can be set depending on your choice (encryption, compression). You can search for them in the `installQuestions()` function of the script.
|
||||
|
||||
Password-protected clients are not supported by the headless installation method since user input is expected by Easy-RSA.
|
||||
|
||||
The headless install is more-or-less idempotent, in that it has been made safe to run multiple times with the same parameters, e.g. by a state provisioner like Ansible/Terraform/Salt/Chef/Puppet. It will only install and regenerate the Easy-RSA PKI if it doesn't already exist, and it will only install OpenVPN and other upstream dependencies if OpenVPN isn't already installed. It will recreate all local config and re-generate the client file on each headless run.
|
||||
|
||||
### Headless User Addition
|
||||
@@ -118,7 +116,7 @@ The following Bash script adds a new user `foo` to an existing OpenVPN configura
|
||||
#!/bin/bash
|
||||
export MENU_OPTION="1"
|
||||
export CLIENT="foo"
|
||||
export PASS="1"
|
||||
export PASS="1" # set to "2" for a password-protected client, and set PASSPHRASE
|
||||
./openvpn-install.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -1688,10 +1688,18 @@ function newClient() {
|
||||
run_cmd_fatal "Building client certificate" ./easyrsa --batch build-client-full "$CLIENT" nopass
|
||||
;;
|
||||
2)
|
||||
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"
|
||||
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
|
||||
|
||||
@@ -359,6 +359,86 @@ touch /shared/new-client-connected
|
||||
echo ""
|
||||
echo "=== Certificate Revocation E2E Tests PASSED ==="
|
||||
|
||||
# =====================================================
|
||||
# Test PASSPHRASE-protected client connection
|
||||
# =====================================================
|
||||
echo ""
|
||||
echo "=== Testing PASSPHRASE-protected Client Connection ==="
|
||||
|
||||
PASSPHRASE_CLIENT="passphrasetest"
|
||||
|
||||
# Wait for passphrase test client config
|
||||
echo "Waiting for passphrase test client config..."
|
||||
MAX_WAIT=120
|
||||
WAITED=0
|
||||
while [ ! -f /shared/passphrase-client-config-ready ] && [ $WAITED -lt $MAX_WAIT ]; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
echo "Waiting for passphrase test config... ($WAITED/$MAX_WAIT seconds)"
|
||||
done
|
||||
|
||||
if [ ! -f /shared/passphrase-client-config-ready ]; then
|
||||
echo "FAIL: Passphrase test client config not ready in time"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "/shared/$PASSPHRASE_CLIENT.ovpn" ]; then
|
||||
echo "FAIL: Passphrase test client config file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "/shared/$PASSPHRASE_CLIENT.pass" ]; then
|
||||
echo "FAIL: Passphrase file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Passphrase test client config found!"
|
||||
|
||||
# Disconnect current VPN before connecting with passphrase client
|
||||
echo "Disconnecting current VPN connection..."
|
||||
pkill openvpn || true
|
||||
sleep 2
|
||||
|
||||
# Connect with passphrase-protected client using --askpass
|
||||
echo "Connecting with '$PASSPHRASE_CLIENT' certificate (passphrase-protected)..."
|
||||
openvpn --config "/shared/$PASSPHRASE_CLIENT.ovpn" --askpass "/shared/$PASSPHRASE_CLIENT.pass" --daemon --log /var/log/openvpn-passphrase.log
|
||||
|
||||
# Wait for connection
|
||||
echo "Waiting for VPN connection with passphrase-protected client..."
|
||||
MAX_WAIT=60
|
||||
WAITED=0
|
||||
while ! ip addr show tun0 2>/dev/null | grep -q "inet " && [ $WAITED -lt $MAX_WAIT ]; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
echo "Waiting for tun0... ($WAITED/$MAX_WAIT seconds)"
|
||||
if [ -f /var/log/openvpn-passphrase.log ]; then
|
||||
tail -3 /var/log/openvpn-passphrase.log
|
||||
fi
|
||||
done
|
||||
|
||||
if ! ip addr show tun0 2>/dev/null | grep -q "inet "; then
|
||||
echo "FAIL: VPN connection with passphrase-protected client failed"
|
||||
cat /var/log/openvpn-passphrase.log || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PASS: Connected with passphrase-protected '$PASSPHRASE_CLIENT' certificate"
|
||||
ip addr show tun0
|
||||
|
||||
# Verify connectivity
|
||||
if ping -c 2 10.8.0.1 >/dev/null 2>&1; then
|
||||
echo "PASS: Can ping VPN gateway with passphrase-protected client"
|
||||
else
|
||||
echo "FAIL: Cannot ping VPN gateway with passphrase-protected client"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Signal server that we connected with passphrase client
|
||||
touch /shared/passphrase-client-connected
|
||||
|
||||
echo ""
|
||||
echo "=== PASSPHRASE-protected Client Tests PASSED ==="
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " ALL TESTS PASSED!"
|
||||
|
||||
@@ -618,6 +618,85 @@ fi
|
||||
echo "PASS: Client connected with new '$REVOKE_CLIENT' certificate"
|
||||
|
||||
echo "=== Reuse of Revoked Client Name Tests PASSED ==="
|
||||
|
||||
# =====================================================
|
||||
# Test PASSPHRASE support for headless client creation
|
||||
# =====================================================
|
||||
echo ""
|
||||
echo "=== Testing PASSPHRASE Support ==="
|
||||
|
||||
PASSPHRASE_CLIENT="passphrasetest"
|
||||
TEST_PASSPHRASE="TestP@ssw0rd#123"
|
||||
echo "Creating client '$PASSPHRASE_CLIENT' with passphrase in headless mode..."
|
||||
PASSPHRASE_OUTPUT="/tmp/passphrase-output.log"
|
||||
(MENU_OPTION=1 CLIENT=$PASSPHRASE_CLIENT PASS=2 PASSPHRASE="$TEST_PASSPHRASE" CLIENT_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$PASSPHRASE_OUTPUT" || true
|
||||
|
||||
# Verify client was created
|
||||
if [ -f "/root/$PASSPHRASE_CLIENT.ovpn" ]; then
|
||||
echo "PASS: Client '$PASSPHRASE_CLIENT' with passphrase created successfully"
|
||||
else
|
||||
echo "FAIL: Failed to create client '$PASSPHRASE_CLIENT' with passphrase"
|
||||
cat "$PASSPHRASE_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the passphrase is NOT leaked in the output
|
||||
if grep -q "$TEST_PASSPHRASE" "$PASSPHRASE_OUTPUT"; then
|
||||
echo "FAIL: Passphrase was leaked in command output!"
|
||||
exit 1
|
||||
else
|
||||
echo "PASS: Passphrase not leaked in command output"
|
||||
fi
|
||||
|
||||
# Verify the log file doesn't contain the passphrase
|
||||
if [ -f /opt/openvpn-install.log ] && grep -q "$TEST_PASSPHRASE" /opt/openvpn-install.log; then
|
||||
echo "FAIL: Passphrase was leaked in log file!"
|
||||
exit 1
|
||||
else
|
||||
echo "PASS: Passphrase not leaked in log file"
|
||||
fi
|
||||
|
||||
# Verify certificate was created with encryption (key should be encrypted)
|
||||
CLIENT_KEY="/etc/openvpn/server/easy-rsa/pki/private/$PASSPHRASE_CLIENT.key"
|
||||
if [ -f "$CLIENT_KEY" ]; then
|
||||
if grep -q "ENCRYPTED" "$CLIENT_KEY"; then
|
||||
echo "PASS: Client key is encrypted"
|
||||
else
|
||||
echo "FAIL: Client key is not encrypted"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "FAIL: Client key not found at $CLIENT_KEY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy config for passphrase client connectivity test
|
||||
cp "/root/$PASSPHRASE_CLIENT.ovpn" "/shared/$PASSPHRASE_CLIENT.ovpn"
|
||||
sed -i 's/^remote .*/remote openvpn-server 1194/' "/shared/$PASSPHRASE_CLIENT.ovpn"
|
||||
# Write passphrase to a file for client to use with --askpass
|
||||
echo "$TEST_PASSPHRASE" >"/shared/$PASSPHRASE_CLIENT.pass"
|
||||
echo "Copied $PASSPHRASE_CLIENT config and passphrase to /shared/"
|
||||
|
||||
# Signal client that passphrase test config is ready
|
||||
touch /shared/passphrase-client-config-ready
|
||||
|
||||
# Wait for client to confirm connection with passphrase client
|
||||
echo "Waiting for client to connect with '$PASSPHRASE_CLIENT' certificate..."
|
||||
MAX_WAIT=60
|
||||
WAITED=0
|
||||
while [ ! -f /shared/passphrase-client-connected ] && [ $WAITED -lt $MAX_WAIT ]; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
echo "Waiting for passphrase client connection... ($WAITED/$MAX_WAIT seconds)"
|
||||
done
|
||||
|
||||
if [ ! -f /shared/passphrase-client-connected ]; then
|
||||
echo "FAIL: Client did not connect with passphrase-protected certificate"
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: Client connected with passphrase-protected certificate"
|
||||
|
||||
echo "=== PASSPHRASE Support Tests PASSED ==="
|
||||
echo ""
|
||||
echo "=== All Revocation Tests PASSED ==="
|
||||
|
||||
|
||||
Reference in New Issue
Block a user