234 lines
5 KiB
Bash
234 lines
5 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# ============================================
|
|
# Client Config
|
|
# ============================================
|
|
|
|
function peers::create_client_config() {
|
|
local name="$1"
|
|
local type="$2"
|
|
local ip="$3"
|
|
local allowed_ips="${4:-$(config::allowed_ips_for "$type" "$(config::default_tunnel_for "$type")")}"
|
|
|
|
local conf
|
|
conf="$(ctx::clients)/${name}.conf"
|
|
|
|
if [[ -f "$conf" ]]; then
|
|
log::wg_warning "Client config already exists: ${name}"
|
|
return 1
|
|
fi
|
|
|
|
local private_key
|
|
private_key=$(keys::private "$name")
|
|
|
|
local server_public_key
|
|
server_public_key=$(config::server_public_key)
|
|
|
|
cat > "$conf" <<EOF
|
|
[Interface]
|
|
PrivateKey = ${private_key}
|
|
Address = ${ip}/32
|
|
DNS = $(config::dns)
|
|
|
|
[Peer]
|
|
PublicKey = ${server_public_key}
|
|
Endpoint = $(config::endpoint)
|
|
AllowedIPs = ${allowed_ips}
|
|
PersistentKeepalive = 25
|
|
EOF
|
|
|
|
chmod 600 "$conf"
|
|
log::wg_add "Created client config: ${name} (${ip})"
|
|
}
|
|
|
|
function peers::remove_client_config() {
|
|
local name="$1"
|
|
local conf
|
|
conf="$(ctx::clients)/${name}.conf"
|
|
|
|
if [[ ! -f "$conf" ]]; then
|
|
log::wg_warning "Client config not found: ${name}"
|
|
return 1
|
|
fi
|
|
|
|
rm -f "$conf"
|
|
log::wg_remove "Removed client config: ${name}"
|
|
}
|
|
|
|
# ============================================
|
|
# Server Config (wg0.conf) Peer Management
|
|
# ============================================
|
|
|
|
function peers::cleanup_config() {
|
|
local config
|
|
config=$(config::config_file)
|
|
|
|
python3 -c "
|
|
import re
|
|
config = open('${config}').read()
|
|
|
|
# Normalize multiple blank lines to single blank line
|
|
config = re.sub(r'\n{3,}', '\n\n', config)
|
|
|
|
# Ensure file ends with single newline
|
|
config = config.rstrip('\n') + '\n'
|
|
|
|
open('${config}', 'w').write(config)
|
|
"
|
|
}
|
|
|
|
function peers::add_to_server() {
|
|
local name="$1"
|
|
local public_key="$2"
|
|
local ip="$3"
|
|
|
|
local config
|
|
config=$(config::config_file)
|
|
|
|
cat >> "$config" <<EOF
|
|
|
|
[Peer]
|
|
# ${name}
|
|
PublicKey = ${public_key}
|
|
AllowedIPs = ${ip}/32
|
|
EOF
|
|
|
|
log::wg_add "Added peer to server config: ${name}"
|
|
}
|
|
|
|
function peers::remove_block() {
|
|
local name="$1"
|
|
local config
|
|
config=$(config::config_file)
|
|
|
|
python3 -c "
|
|
import re
|
|
config = open('${config}').read()
|
|
pattern = r'\n\[Peer\]\n# ${name}\n[^\n]+\n[^\n]+\n'
|
|
result = re.sub(pattern, '\n', config)
|
|
open('${config}', 'w').write(result)
|
|
"
|
|
}
|
|
|
|
function peers::remove_from_server() {
|
|
local name="$1"
|
|
peers::remove_block "$name"
|
|
peers::cleanup_config
|
|
log::wg_remove "Removed peer from server config: ${name}"
|
|
}
|
|
|
|
# ============================================
|
|
# Listing
|
|
# ============================================
|
|
|
|
function peers::list() {
|
|
local dir
|
|
dir="$(ctx::clients)"
|
|
|
|
if [[ -z "$(ls -A "$dir"/*.conf 2>/dev/null)" ]]; then
|
|
log::wg_list "No clients configured"
|
|
return 0
|
|
fi
|
|
|
|
for conf in "${dir}"/*.conf; do
|
|
local client_name
|
|
client_name=$(basename "$conf" .conf)
|
|
|
|
local ip
|
|
ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
|
|
|
|
local public_key
|
|
public_key=$(keys::public "$client_name" 2>/dev/null || echo "unknown")
|
|
|
|
# Determine type from IP
|
|
local type="unknown"
|
|
for t in $(config::device_types); do
|
|
local subnet
|
|
subnet=$(config::subnet_for "$t")
|
|
if string::starts_with "$ip" "$subnet"; then
|
|
type="$t"
|
|
break
|
|
fi
|
|
done
|
|
|
|
printf " %-30s %-15s %-10s %s\n" \
|
|
"$client_name" "$ip" "$type" "$public_key"
|
|
done
|
|
}
|
|
|
|
function peers::list_by_type() {
|
|
local filter_type="$1"
|
|
local dir
|
|
dir="$(ctx::clients)"
|
|
|
|
for conf in "${dir}"/*.conf; do
|
|
local client_name
|
|
client_name=$(basename "$conf" .conf)
|
|
|
|
local ip
|
|
ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1)
|
|
|
|
local subnet
|
|
subnet=$(config::subnet_for "$filter_type")
|
|
|
|
if string::starts_with "$ip" "$subnet"; then
|
|
printf " %-30s %-15s\n" "$client_name" "$ip"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function peers::exists_in_server() {
|
|
local name="$1"
|
|
grep -q "^# ${name}$" "$(config::config_file)"
|
|
}
|
|
|
|
function peers::is_blocked() {
|
|
local name="$1"
|
|
! peers::exists_in_server "$name"
|
|
}
|
|
|
|
# ============================================
|
|
# Name + Type Parsing
|
|
# ============================================
|
|
|
|
function peers::resolve_name() {
|
|
local name="$1"
|
|
local type="${2:-}"
|
|
|
|
if [[ -n "$type" ]]; then
|
|
if ! config::is_valid_type "$type"; then
|
|
log::error "Invalid device type: ${type}"
|
|
return 1
|
|
fi
|
|
echo "${type}-${name}"
|
|
else
|
|
echo "$name"
|
|
fi
|
|
}
|
|
|
|
function peers::require_exists() {
|
|
local name="$1"
|
|
if [[ ! -f "$(ctx::clients)/${name}.conf" ]]; then
|
|
log::error "Client not found: ${name}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function peers::resolve_and_require() {
|
|
local name="$1"
|
|
local type="${2:-}"
|
|
|
|
local resolved
|
|
resolved=$(peers::resolve_name "$name" "$type") || return 1
|
|
peers::require_exists "$resolved" || return 1
|
|
echo "$resolved"
|
|
}
|
|
|
|
# ============================================
|
|
# Live Reload
|
|
# ============================================
|
|
|
|
function peers::reload() {
|
|
wg syncconf "$(config::interface)" <(wg-quick strip "$(config::interface)")
|
|
log::wg_success "WireGuard config reloaded"
|
|
}
|