mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-16 08:57:03 +01:00
feat: add CLI interface with subcommands (#1398)
Replace environment variable-based configuration with a proper CLI interface using subcommands and flags. ### Commands ``` openvpn-install <command> [options] Commands: install Install and configure OpenVPN server uninstall Remove OpenVPN server client Manage client certificates (add/list/revoke/renew) server Server management (status/renew) interactive Launch interactive menu ``` ### Highlights - **No args → help**: Running without arguments shows help instead of interactive menu - **JSON output**: `client list` and `server status` support `--format json` - **25+ install flags**: Network, DNS, security, and client options - **Interactive mode preserved**: `install --interactive` or `interactive` command ### Breaking Changes Environment variables (`AUTO_INSTALL`, `MENU_OPTION`, `CLIENT`, etc.) are no longer supported. Use CLI flags instead. ```bash # Before MENU_OPTION=1 CLIENT=foo PASS=1 ./openvpn-install.sh # After ./openvpn-install.sh client add foo ``` Closes https://github.com/angristan/openvpn-install/issues/1202
This commit is contained in:
@@ -12,54 +12,40 @@ fi
|
||||
|
||||
echo "TUN device ready"
|
||||
|
||||
# Set up environment for auto-install
|
||||
export AUTO_INSTALL=y
|
||||
# Configuration for install
|
||||
export FORCE_COLOR=1
|
||||
export APPROVE_INSTALL=y
|
||||
export APPROVE_IP=y
|
||||
export IPV6_SUPPORT=n
|
||||
export VPN_SUBNET=10.9.0.0 # Custom subnet to test configurability
|
||||
export PORT_CHOICE=1
|
||||
export PROTOCOL_CHOICE=1
|
||||
export DNS=2 # Self-hosted Unbound DNS resolver
|
||||
export COMPRESSION_ENABLED=n
|
||||
export CLIENT=testclient
|
||||
export PASS=1
|
||||
export ENDPOINT=openvpn-server
|
||||
VPN_SUBNET=10.9.0.0 # Custom subnet to test configurability
|
||||
|
||||
# Calculate VPN gateway from subnet (first usable IP)
|
||||
VPN_GATEWAY="${VPN_SUBNET%.*}.1"
|
||||
export VPN_GATEWAY
|
||||
|
||||
# TLS key type configuration (default: tls-crypt-v2)
|
||||
# TLS_SIG: 1=tls-crypt-v2, 2=tls-crypt, 3=tls-auth
|
||||
# TLS_SIG: crypt-v2, crypt, auth
|
||||
# TLS_KEY_FILE: the expected key file name for verification
|
||||
TLS_SIG="${TLS_SIG:-1}"
|
||||
TLS_SIG="${TLS_SIG:-crypt-v2}"
|
||||
TLS_KEY_FILE="${TLS_KEY_FILE:-tls-crypt-v2.key}"
|
||||
export TLS_SIG
|
||||
|
||||
# If using non-default TLS settings, enable encryption customization
|
||||
if [ "$TLS_SIG" != "1" ]; then
|
||||
export CUSTOMIZE_ENC=y
|
||||
# Set other encryption defaults when customizing
|
||||
export CIPHER_CHOICE=1 # AES-128-GCM
|
||||
export CERT_TYPE=1 # ECDSA
|
||||
export CERT_CURVE_CHOICE=1 # prime256v1
|
||||
export CC_CIPHER_CHOICE=1 # ECDHE-ECDSA-AES-128-GCM-SHA256
|
||||
export DH_TYPE=1 # ECDH
|
||||
export DH_CURVE_CHOICE=1 # prime256v1
|
||||
export HMAC_ALG_CHOICE=1 # SHA-256
|
||||
# Build install command with CLI flags (using array for proper quoting)
|
||||
INSTALL_CMD=(/opt/openvpn-install.sh install)
|
||||
INSTALL_CMD+=(--endpoint openvpn-server)
|
||||
INSTALL_CMD+=(--dns unbound)
|
||||
INSTALL_CMD+=(--subnet "$VPN_SUBNET")
|
||||
INSTALL_CMD+=(--client testclient)
|
||||
|
||||
# Add TLS signature mode if non-default
|
||||
if [ "$TLS_SIG" != "crypt-v2" ]; then
|
||||
INSTALL_CMD+=(--tls-sig "$TLS_SIG")
|
||||
echo "Testing TLS key type: $TLS_SIG (key file: $TLS_KEY_FILE)"
|
||||
else
|
||||
export CUSTOMIZE_ENC=n
|
||||
fi
|
||||
|
||||
echo "Running OpenVPN install script..."
|
||||
echo "Command: ${INSTALL_CMD[*]}"
|
||||
# Run in subshell because the script calls 'exit 0' after generating client config
|
||||
# Capture output to validate logging format, while still displaying it
|
||||
# Use || true to prevent set -e from exiting on failure, then check exit code
|
||||
INSTALL_OUTPUT="/tmp/install-output.log"
|
||||
(bash /opt/openvpn-install.sh) 2>&1 | tee "$INSTALL_OUTPUT"
|
||||
("${INSTALL_CMD[@]}") 2>&1 | tee "$INSTALL_OUTPUT"
|
||||
INSTALL_EXIT_CODE=${PIPESTATUS[0]}
|
||||
|
||||
echo "=== Installation complete (exit code: $INSTALL_EXIT_CODE) ==="
|
||||
@@ -196,7 +182,7 @@ echo "Original client certificate serial: $ORIG_CERT_SERIAL"
|
||||
# Test client certificate renewal using the script
|
||||
echo "Testing client certificate renewal..."
|
||||
RENEW_OUTPUT="/tmp/renew-client-output.log"
|
||||
(MENU_OPTION=4 RENEW_OPTION=1 CLIENTNUMBER=1 CLIENT_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$RENEW_OUTPUT" || true
|
||||
(bash /opt/openvpn-install.sh client renew testclient --cert-days 3650) 2>&1 | tee "$RENEW_OUTPUT" || true
|
||||
|
||||
# Verify renewal succeeded
|
||||
if grep -q "Certificate for client testclient renewed" "$RENEW_OUTPUT"; then
|
||||
@@ -276,7 +262,7 @@ echo "Original server certificate serial: $ORIG_SERVER_SERIAL"
|
||||
# Test server certificate renewal
|
||||
echo "Testing server certificate renewal..."
|
||||
RENEW_SERVER_OUTPUT="/tmp/renew-server-output.log"
|
||||
(MENU_OPTION=4 RENEW_OPTION=2 CONTINUE=y SERVER_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$RENEW_SERVER_OUTPUT" || true
|
||||
(bash /opt/openvpn-install.sh server renew --cert-days 3650 --force) 2>&1 | tee "$RENEW_SERVER_OUTPUT" || true
|
||||
|
||||
# Verify renewal succeeded
|
||||
if grep -q "Server certificate renewed successfully" "$RENEW_SERVER_OUTPUT"; then
|
||||
@@ -545,7 +531,7 @@ echo "=== Testing Certificate Revocation ==="
|
||||
REVOKE_CLIENT="revoketest"
|
||||
echo "Creating client '$REVOKE_CLIENT' for revocation testing..."
|
||||
REVOKE_CREATE_OUTPUT="/tmp/revoke-create-output.log"
|
||||
(MENU_OPTION=1 CLIENT=$REVOKE_CLIENT PASS=1 CLIENT_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$REVOKE_CREATE_OUTPUT" || true
|
||||
(bash /opt/openvpn-install.sh client add "$REVOKE_CLIENT" --cert-days 3650) 2>&1 | tee "$REVOKE_CREATE_OUTPUT" || true
|
||||
|
||||
if [ -f "/root/$REVOKE_CLIENT.ovpn" ]; then
|
||||
echo "PASS: Client '$REVOKE_CLIENT' created successfully"
|
||||
@@ -579,6 +565,42 @@ if [ ! -f /shared/revoke-client-connected ]; then
|
||||
fi
|
||||
echo "PASS: Client connected with '$REVOKE_CLIENT' certificate"
|
||||
|
||||
# =====================================================
|
||||
# Test server status command
|
||||
# =====================================================
|
||||
echo ""
|
||||
echo "=== Testing Server Status ==="
|
||||
|
||||
# Note: OpenVPN status file updates periodically (default: 1 min)
|
||||
# so we just verify the command works, not that a specific client is visible
|
||||
|
||||
# Test table output
|
||||
STATUS_OUTPUT="/tmp/server-status-output.log"
|
||||
(bash /opt/openvpn-install.sh server status) 2>&1 | tee "$STATUS_OUTPUT" || true
|
||||
|
||||
if grep -q "Connected Clients" "$STATUS_OUTPUT"; then
|
||||
echo "PASS: Server status shows header"
|
||||
else
|
||||
echo "FAIL: Server status missing header"
|
||||
cat "$STATUS_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test JSON output
|
||||
STATUS_JSON_OUTPUT="/tmp/server-status-json-output.log"
|
||||
(bash /opt/openvpn-install.sh server status --format json) 2>&1 | tee "$STATUS_JSON_OUTPUT" || true
|
||||
|
||||
# Validate JSON structure (clients array exists, even if empty)
|
||||
if jq -e '.clients' "$STATUS_JSON_OUTPUT" >/dev/null 2>&1; then
|
||||
echo "PASS: Server status JSON is valid"
|
||||
else
|
||||
echo "FAIL: Server status JSON is invalid"
|
||||
cat "$STATUS_JSON_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Server Status Tests PASSED ==="
|
||||
|
||||
# Signal client to disconnect before revocation
|
||||
touch /shared/revoke-client-disconnect
|
||||
|
||||
@@ -597,11 +619,10 @@ if [ ! -f /shared/revoke-client-disconnected ]; then
|
||||
fi
|
||||
echo "Client disconnected"
|
||||
|
||||
# Now revoke the certificate using the new CLIENT name feature
|
||||
# Now revoke the certificate
|
||||
echo "Revoking certificate for '$REVOKE_CLIENT'..."
|
||||
REVOKE_OUTPUT="/tmp/revoke-output.log"
|
||||
# MENU_OPTION=3 is revoke, CLIENT specifies the client name directly
|
||||
(MENU_OPTION=3 CLIENT=$REVOKE_CLIENT bash /opt/openvpn-install.sh) 2>&1 | tee "$REVOKE_OUTPUT" || true
|
||||
(bash /opt/openvpn-install.sh client revoke "$REVOKE_CLIENT" --force) 2>&1 | tee "$REVOKE_OUTPUT" || true
|
||||
|
||||
if grep -q "Certificate for client $REVOKE_CLIENT revoked" "$REVOKE_OUTPUT"; then
|
||||
echo "PASS: Certificate for '$REVOKE_CLIENT' revoked successfully"
|
||||
@@ -652,7 +673,7 @@ echo "=== Testing List Client Certificates ==="
|
||||
# - testclient (Revoked) - the old certificate revoked during renewal
|
||||
# - revoketest (Revoked) - the revoked certificate
|
||||
LIST_OUTPUT="/tmp/list-clients-output.log"
|
||||
(MENU_OPTION=2 bash /opt/openvpn-install.sh) 2>&1 | tee "$LIST_OUTPUT" || true
|
||||
(bash /opt/openvpn-install.sh client list) 2>&1 | tee "$LIST_OUTPUT" || true
|
||||
|
||||
# Verify list output contains expected clients
|
||||
if grep -q "testclient" "$LIST_OUTPUT" && grep -q "Valid" "$LIST_OUTPUT"; then
|
||||
@@ -680,6 +701,48 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test JSON output
|
||||
echo "Testing client list JSON output..."
|
||||
LIST_JSON_OUTPUT="/tmp/list-clients-json-output.log"
|
||||
(bash /opt/openvpn-install.sh client list --format json) 2>&1 | tee "$LIST_JSON_OUTPUT" || true
|
||||
|
||||
# Validate JSON structure
|
||||
if jq -e '.clients' "$LIST_JSON_OUTPUT" >/dev/null 2>&1; then
|
||||
echo "PASS: Client list JSON is valid"
|
||||
else
|
||||
echo "FAIL: Client list JSON is invalid"
|
||||
cat "$LIST_JSON_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify client count in JSON
|
||||
JSON_CLIENT_COUNT=$(jq '.clients | length' "$LIST_JSON_OUTPUT")
|
||||
if [ "$JSON_CLIENT_COUNT" -eq 3 ]; then
|
||||
echo "PASS: Client list JSON has correct count ($JSON_CLIENT_COUNT)"
|
||||
else
|
||||
echo "FAIL: Client list JSON has wrong count: $JSON_CLIENT_COUNT (expected 3)"
|
||||
cat "$LIST_JSON_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify valid client in JSON
|
||||
if jq -e '.clients[] | select(.name == "testclient" and .status == "valid")' "$LIST_JSON_OUTPUT" >/dev/null 2>&1; then
|
||||
echo "PASS: Client list JSON shows testclient as valid"
|
||||
else
|
||||
echo "FAIL: Client list JSON does not show testclient correctly"
|
||||
cat "$LIST_JSON_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify revoked client in JSON
|
||||
if jq -e ".clients[] | select(.name == \"$REVOKE_CLIENT\" and .status == \"revoked\")" "$LIST_JSON_OUTPUT" >/dev/null 2>&1; then
|
||||
echo "PASS: Client list JSON shows $REVOKE_CLIENT as revoked"
|
||||
else
|
||||
echo "FAIL: Client list JSON does not show $REVOKE_CLIENT correctly"
|
||||
cat "$LIST_JSON_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== List Client Certificates Tests PASSED ==="
|
||||
|
||||
# =====================================================
|
||||
@@ -691,7 +754,7 @@ echo "=== Testing Reuse of Revoked Client Name ==="
|
||||
# Create a new certificate with the same name as the revoked one
|
||||
echo "Creating new client with same name '$REVOKE_CLIENT'..."
|
||||
RECREATE_OUTPUT="/tmp/recreate-output.log"
|
||||
(MENU_OPTION=1 CLIENT=$REVOKE_CLIENT PASS=1 CLIENT_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$RECREATE_OUTPUT" || true
|
||||
(bash /opt/openvpn-install.sh client add "$REVOKE_CLIENT" --cert-days 3650) 2>&1 | tee "$RECREATE_OUTPUT" || true
|
||||
|
||||
if [ -f "/root/$REVOKE_CLIENT.ovpn" ]; then
|
||||
echo "PASS: New client '$REVOKE_CLIENT' created successfully (reusing revoked name)"
|
||||
@@ -758,7 +821,7 @@ 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
|
||||
(bash /opt/openvpn-install.sh client add "$PASSPHRASE_CLIENT" --password "$TEST_PASSPHRASE" --cert-days 3650) 2>&1 | tee "$PASSPHRASE_OUTPUT" || true
|
||||
|
||||
# Verify client was created
|
||||
if [ -f "/root/$PASSPHRASE_CLIENT.ovpn" ]; then
|
||||
|
||||
Reference in New Issue
Block a user