feat: add wireguard secure installer with modular architecture
This commit introduces a new WireGuard VPN installer with enterprise-grade security features. The installer includes: - Zero-touch installation with automatic configuration - Modular architecture for maintainability (separate lib files) - Client management interface with bandwidth monitoring - Support for multiple Linux distributions - Secure defaults and hardened configurations The implementation provides a complete solution for deploying WireGuard VPN servers with minimal user interaction while maintaining security best practices.
This commit is contained in:
137
lib/client_mgmt.sh
Normal file
137
lib/client_mgmt.sh
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
# WireGuard Secure Installer
|
||||
# Copyright (c) 2025 Muhammad Fadhila Abiyyu Faris
|
||||
# GitHub: [github.com/fadhila36/wireguard-secure-installer](https://github.com/fadhila36/wireguard-secure-installer)
|
||||
|
||||
new_client() {
|
||||
echo -e "${BLUE}=== Add New Client ===${NC}"
|
||||
echo -n "Enter Client Name (no spaces): "
|
||||
read -r CLIENT_NAME
|
||||
|
||||
if [[ -z "$CLIENT_NAME" ]]; then
|
||||
log_error "Client name cannot be empty."
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if client already exists
|
||||
if grep -q "### Client: $CLIENT_NAME" "$WG_CONFIG"; then
|
||||
log_error "Client '$CLIENT_NAME' already exists."
|
||||
return
|
||||
fi
|
||||
|
||||
# Find next available IP octet
|
||||
# We scan for 10.66.66.X and find the first missing number starting from 2
|
||||
for i in {2..254}; do
|
||||
if ! grep -q "10.66.66.$i" "$WG_CONFIG"; then
|
||||
NEXT_IP="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$NEXT_IP" ]]; then
|
||||
fatal_error "No available IPs in the subnet."
|
||||
fi
|
||||
|
||||
# Re-detect public IP in case it changed
|
||||
detect_public_ip
|
||||
|
||||
# Use existing function from wg_core.sh
|
||||
create_client_config "$CLIENT_NAME" "$NEXT_IP"
|
||||
|
||||
echo -e "${GREEN}Client $CLIENT_NAME added with IP 10.66.66.$NEXT_IP${NC}"
|
||||
}
|
||||
|
||||
remove_client() {
|
||||
echo -e "${BLUE}=== Remove Client ===${NC}"
|
||||
|
||||
# List clients
|
||||
echo "Existing Clients:"
|
||||
grep "### Client:" "$WG_CONFIG" | cut -d ' ' -f 3 | nl -s ') '
|
||||
|
||||
echo -n "Enter Client Name to remove: "
|
||||
read -r CLIENT_TO_REMOVE
|
||||
|
||||
if [[ -z "$CLIENT_TO_REMOVE" ]]; then
|
||||
log_error "Client name cannot be empty."
|
||||
return
|
||||
fi
|
||||
|
||||
if ! grep -q "### Client: $CLIENT_TO_REMOVE" "$WG_CONFIG"; then
|
||||
log_error "Client '$CLIENT_TO_REMOVE' not found."
|
||||
return
|
||||
fi
|
||||
|
||||
# Backup config
|
||||
cp "$WG_CONFIG" "${WG_CONFIG}.bak"
|
||||
|
||||
# Remove the block from config
|
||||
# We use a more robust sed command that handles the block structure
|
||||
# It deletes from the line containing "### Client: NAME" up to the next blank line
|
||||
sed -i "/### Client: $CLIENT_TO_REMOVE/,/^$/d" "$WG_CONFIG"
|
||||
|
||||
# Also clean up any potential double empty lines left behind
|
||||
sed -i '/^$/N;/^\n$/D' "$WG_CONFIG"
|
||||
|
||||
# Reload WireGuard
|
||||
wg syncconf "$SERVER_WG_NIC" <(wg-quick strip "$SERVER_WG_NIC")
|
||||
|
||||
# Remove config file
|
||||
rm -f "$INSTALL_DIR/clients/$CLIENT_TO_REMOVE.conf"
|
||||
|
||||
echo -e "${GREEN}Client $CLIENT_TO_REMOVE removed.${NC}"
|
||||
}
|
||||
|
||||
view_usage_logs() {
|
||||
echo -e "${BLUE}=== Bandwidth Usage ===${NC}"
|
||||
|
||||
# Header
|
||||
printf "%-20s | %-15s | %-15s | %-20s\n" "Client Name" "Data Received" "Data Sent" "Last Handshake"
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Get dump
|
||||
DUMP=$(wg show "$SERVER_WG_NIC" dump)
|
||||
|
||||
# Iterate over clients in config to map names to keys
|
||||
# Grep Client Names
|
||||
CLIENT_NAMES=$(grep "### Client:" "$WG_CONFIG" | cut -d ' ' -f 3)
|
||||
|
||||
for NAME in $CLIENT_NAMES; do
|
||||
# Get Public Key for this client from config
|
||||
# We need to read the config file more smartly.
|
||||
# Let's assume the structure:
|
||||
# ### Client: NAME
|
||||
# [Peer]
|
||||
# PublicKey = KEY
|
||||
|
||||
# Extract Public Key for the specific client
|
||||
PUB_KEY=$(sed -n "/### Client: $NAME/,/PublicKey/p" "$WG_CONFIG" | grep "PublicKey" | cut -d ' ' -f 3)
|
||||
|
||||
if [[ -n "$PUB_KEY" ]]; then
|
||||
# Find stats in dump
|
||||
# Dump format: public-key preshared-key endpoint allowed-ips latest-handshake transfer-rx transfer-tx persistent-keepalive
|
||||
STATS=$(echo "$DUMP" | grep "$PUB_KEY")
|
||||
|
||||
if [[ -n "$STATS" ]]; then
|
||||
RX_BYTES=$(echo "$STATS" | awk '{print $6}')
|
||||
TX_BYTES=$(echo "$STATS" | awk '{print $7}')
|
||||
LAST_HANDSHAKE=$(echo "$STATS" | awk '{print $5}')
|
||||
|
||||
# Convert Bytes
|
||||
RX_HUMAN=$(numfmt --to=iec-i --suffix=B "$RX_BYTES")
|
||||
TX_HUMAN=$(numfmt --to=iec-i --suffix=B "$TX_BYTES")
|
||||
|
||||
# Convert Handshake Time
|
||||
if [[ "$LAST_HANDSHAKE" -eq 0 ]]; then
|
||||
TIME_HUMAN="Never"
|
||||
else
|
||||
TIME_HUMAN=$(date -d @"$LAST_HANDSHAKE" '+%Y-%m-%d %H:%M')
|
||||
fi
|
||||
|
||||
printf "%-20s | %-15s | %-15s | %-20s\n" "$NAME" "$RX_HUMAN" "$TX_HUMAN" "$TIME_HUMAN"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo -n "Press Enter to continue..."
|
||||
read -r
|
||||
}
|
||||
Reference in New Issue
Block a user