mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-16 00:47:02 +01:00
feat: add list clients menu option (#1382)
## 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 #567 Closes #563 Closes #587
This commit is contained in:
@@ -59,7 +59,8 @@ The first time you run it, you'll have to follow the assistant and answer a few
|
|||||||
When OpenVPN is installed, you can run the script again, and you will get the choice to:
|
When OpenVPN is installed, you can run the script again, and you will get the choice to:
|
||||||
|
|
||||||
- Add a client
|
- Add a client
|
||||||
- Remove a client
|
- List client certificates
|
||||||
|
- Revoke a client
|
||||||
- Renew certificates (client or server)
|
- Renew certificates (client or server)
|
||||||
- Uninstall OpenVPN
|
- Uninstall OpenVPN
|
||||||
|
|
||||||
|
|||||||
@@ -1648,6 +1648,88 @@ function selectClient() {
|
|||||||
CLIENT=$(tail -n +2 /etc/openvpn/server/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 listClients() {
|
||||||
|
log_header "Client Certificates"
|
||||||
|
|
||||||
|
local index_file="/etc/openvpn/server/easy-rsa/pki/index.txt"
|
||||||
|
local number_of_clients
|
||||||
|
# Exclude server certificates (CN starting with server_)
|
||||||
|
number_of_clients=$(tail -n +2 "$index_file" | grep "^[VR]" | grep -cv "/CN=server_")
|
||||||
|
|
||||||
|
if [[ $number_of_clients == '0' ]]; then
|
||||||
|
log_warn "You have no existing client certificates!"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Found $number_of_clients client certificate(s)"
|
||||||
|
log_menu ""
|
||||||
|
printf " %-25s %-10s %-12s %s\n" "Name" "Status" "Expiry" "Remaining"
|
||||||
|
printf " %-25s %-10s %-12s %s\n" "----" "------" "------" "---------"
|
||||||
|
|
||||||
|
local cert_dir="/etc/openvpn/server/easy-rsa/pki/issued"
|
||||||
|
|
||||||
|
# Parse index.txt and sort by expiry date (oldest first)
|
||||||
|
# Exclude server certificates (CN starting with server_)
|
||||||
|
{
|
||||||
|
while read -r line; do
|
||||||
|
local status="${line:0:1}"
|
||||||
|
local client_name
|
||||||
|
client_name=$(echo "$line" | sed 's/.*\/CN=//')
|
||||||
|
|
||||||
|
# Format status
|
||||||
|
local status_text
|
||||||
|
if [[ "$status" == "V" ]]; then
|
||||||
|
status_text="Valid"
|
||||||
|
elif [[ "$status" == "R" ]]; then
|
||||||
|
status_text="Revoked"
|
||||||
|
else
|
||||||
|
status_text="Unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get expiry date from certificate file
|
||||||
|
local cert_file="$cert_dir/$client_name.crt"
|
||||||
|
local expiry_date="unknown"
|
||||||
|
local relative="unknown"
|
||||||
|
|
||||||
|
if [[ -f "$cert_file" ]]; then
|
||||||
|
# Get expiry from certificate (format: notAfter=Mon DD HH:MM:SS YYYY GMT)
|
||||||
|
local enddate
|
||||||
|
enddate=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
|
||||||
|
|
||||||
|
if [[ -n "$enddate" ]]; then
|
||||||
|
# Parse date and convert to epoch
|
||||||
|
local expiry_epoch
|
||||||
|
expiry_epoch=$(date -d "$enddate" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$enddate" +%s 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ -n "$expiry_epoch" ]]; then
|
||||||
|
# Format as YYYY-MM-DD
|
||||||
|
expiry_date=$(date -d "@$expiry_epoch" +%Y-%m-%d 2>/dev/null || date -r "$expiry_epoch" +%Y-%m-%d 2>/dev/null)
|
||||||
|
|
||||||
|
# Calculate days remaining
|
||||||
|
local now_epoch days_remaining
|
||||||
|
now_epoch=$(date +%s)
|
||||||
|
days_remaining=$(((expiry_epoch - now_epoch) / 86400))
|
||||||
|
|
||||||
|
if [[ $days_remaining -lt 0 ]]; then
|
||||||
|
relative="$((-days_remaining)) days ago"
|
||||||
|
elif [[ $days_remaining -eq 0 ]]; then
|
||||||
|
relative="today"
|
||||||
|
elif [[ $days_remaining -eq 1 ]]; then
|
||||||
|
relative="1 day"
|
||||||
|
else
|
||||||
|
relative="$days_remaining days"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf " %-25s %-10s %-12s %s\n" "$client_name" "$status_text" "$expiry_date" "$relative"
|
||||||
|
done < <(tail -n +2 "$index_file" | grep "^[VR]" | grep -v "/CN=server_" | sort -t$'\t' -k2)
|
||||||
|
}
|
||||||
|
|
||||||
|
log_menu ""
|
||||||
|
}
|
||||||
|
|
||||||
function newClient() {
|
function newClient() {
|
||||||
log_header "New Client Setup"
|
log_header "New Client Setup"
|
||||||
log_prompt "Tell me a name for the client."
|
log_prompt "Tell me a name for the client."
|
||||||
@@ -2036,12 +2118,13 @@ function manageMenu() {
|
|||||||
log_menu ""
|
log_menu ""
|
||||||
log_prompt "What do you want to do?"
|
log_prompt "What do you want to do?"
|
||||||
log_menu " 1) Add a new user"
|
log_menu " 1) Add a new user"
|
||||||
log_menu " 2) Revoke existing user"
|
log_menu " 2) List client certificates"
|
||||||
log_menu " 3) Renew certificate"
|
log_menu " 3) Revoke existing user"
|
||||||
log_menu " 4) Remove OpenVPN"
|
log_menu " 4) Renew certificate"
|
||||||
log_menu " 5) Exit"
|
log_menu " 5) Remove OpenVPN"
|
||||||
until [[ ${MENU_OPTION:-$menu_option} =~ ^[1-5]$ ]]; do
|
log_menu " 6) Exit"
|
||||||
read -rp "Select an option [1-5]: " menu_option
|
until [[ ${MENU_OPTION:-$menu_option} =~ ^[1-6]$ ]]; do
|
||||||
|
read -rp "Select an option [1-6]: " menu_option
|
||||||
done
|
done
|
||||||
menu_option="${MENU_OPTION:-$menu_option}"
|
menu_option="${MENU_OPTION:-$menu_option}"
|
||||||
|
|
||||||
@@ -2050,15 +2133,18 @@ function manageMenu() {
|
|||||||
newClient
|
newClient
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
revokeClient
|
listClients
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
renewMenu
|
revokeClient
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
removeOpenVPN
|
renewMenu
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
|
removeOpenVPN
|
||||||
|
;;
|
||||||
|
6)
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ echo "Original client certificate serial: $ORIG_CERT_SERIAL"
|
|||||||
# Test client certificate renewal using the script
|
# Test client certificate renewal using the script
|
||||||
echo "Testing client certificate renewal..."
|
echo "Testing client certificate renewal..."
|
||||||
RENEW_OUTPUT="/tmp/renew-client-output.log"
|
RENEW_OUTPUT="/tmp/renew-client-output.log"
|
||||||
(MENU_OPTION=3 RENEW_OPTION=1 CLIENTNUMBER=1 CLIENT_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$RENEW_OUTPUT" || true
|
(MENU_OPTION=4 RENEW_OPTION=1 CLIENTNUMBER=1 CLIENT_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$RENEW_OUTPUT" || true
|
||||||
|
|
||||||
# Verify renewal succeeded
|
# Verify renewal succeeded
|
||||||
if grep -q "Certificate for client testclient renewed" "$RENEW_OUTPUT"; then
|
if grep -q "Certificate for client testclient renewed" "$RENEW_OUTPUT"; then
|
||||||
@@ -257,7 +257,7 @@ echo "Original server certificate serial: $ORIG_SERVER_SERIAL"
|
|||||||
# Test server certificate renewal
|
# Test server certificate renewal
|
||||||
echo "Testing server certificate renewal..."
|
echo "Testing server certificate renewal..."
|
||||||
RENEW_SERVER_OUTPUT="/tmp/renew-server-output.log"
|
RENEW_SERVER_OUTPUT="/tmp/renew-server-output.log"
|
||||||
(MENU_OPTION=3 RENEW_OPTION=2 CONTINUE=y SERVER_CERT_DURATION_DAYS=3650 bash /opt/openvpn-install.sh) 2>&1 | tee "$RENEW_SERVER_OUTPUT" || true
|
(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
|
||||||
|
|
||||||
# Verify renewal succeeded
|
# Verify renewal succeeded
|
||||||
if grep -q "Server certificate renewed successfully" "$RENEW_SERVER_OUTPUT"; then
|
if grep -q "Server certificate renewed successfully" "$RENEW_SERVER_OUTPUT"; then
|
||||||
@@ -504,7 +504,7 @@ echo "Client disconnected"
|
|||||||
# Now revoke the certificate
|
# Now revoke the certificate
|
||||||
echo "Revoking certificate for '$REVOKE_CLIENT'..."
|
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=3 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/server/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
|
||||||
@@ -513,7 +513,7 @@ if [ -z "$REVOKE_CLIENT_NUM" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Revoke client number: $REVOKE_CLIENT_NUM"
|
echo "Revoke client number: $REVOKE_CLIENT_NUM"
|
||||||
(MENU_OPTION=2 CLIENTNUMBER=$REVOKE_CLIENT_NUM bash /opt/openvpn-install.sh) 2>&1 | tee "$REVOKE_OUTPUT" || true
|
(MENU_OPTION=3 CLIENTNUMBER=$REVOKE_CLIENT_NUM bash /opt/openvpn-install.sh) 2>&1 | tee "$REVOKE_OUTPUT" || true
|
||||||
|
|
||||||
if grep -q "Certificate for client $REVOKE_CLIENT revoked" "$REVOKE_OUTPUT"; then
|
if grep -q "Certificate for client $REVOKE_CLIENT revoked" "$REVOKE_OUTPUT"; then
|
||||||
echo "PASS: Certificate for '$REVOKE_CLIENT' revoked successfully"
|
echo "PASS: Certificate for '$REVOKE_CLIENT' revoked successfully"
|
||||||
@@ -553,6 +553,47 @@ echo "PASS: Connection with revoked certificate correctly rejected"
|
|||||||
|
|
||||||
echo "=== Certificate Revocation Tests PASSED ==="
|
echo "=== Certificate Revocation Tests PASSED ==="
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Test listing client certificates
|
||||||
|
# =====================================================
|
||||||
|
echo ""
|
||||||
|
echo "=== Testing List Client Certificates ==="
|
||||||
|
|
||||||
|
# At this point we have 3 client certificates:
|
||||||
|
# - testclient (Valid) - the renewed certificate
|
||||||
|
# - 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
|
||||||
|
|
||||||
|
# Verify list output contains expected clients
|
||||||
|
if grep -q "testclient" "$LIST_OUTPUT" && grep -q "Valid" "$LIST_OUTPUT"; then
|
||||||
|
echo "PASS: List shows testclient as Valid"
|
||||||
|
else
|
||||||
|
echo "FAIL: List does not show testclient correctly"
|
||||||
|
cat "$LIST_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "$REVOKE_CLIENT" "$LIST_OUTPUT" && grep -q "Revoked" "$LIST_OUTPUT"; then
|
||||||
|
echo "PASS: List shows $REVOKE_CLIENT as Revoked"
|
||||||
|
else
|
||||||
|
echo "FAIL: List does not show $REVOKE_CLIENT correctly"
|
||||||
|
cat "$LIST_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify certificate count (3 certs: testclient valid, testclient revoked from renewal, revoketest revoked)
|
||||||
|
if grep -q "Found 3 client certificate(s)" "$LIST_OUTPUT"; then
|
||||||
|
echo "PASS: List shows correct certificate count"
|
||||||
|
else
|
||||||
|
echo "FAIL: List does not show correct certificate count"
|
||||||
|
cat "$LIST_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== List Client Certificates Tests PASSED ==="
|
||||||
|
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# Test reusing revoked client name
|
# Test reusing revoked client name
|
||||||
# =====================================================
|
# =====================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user