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.
138 lines
4.4 KiB
Bash
138 lines
4.4 KiB
Bash
#!/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
|
|
}
|