#!/usr/bin/env bash # peer.command.sh — peer management operations # ============================================ # Lifecycle # ============================================ function cmd::peer::on_load() { flag::register --name flag::register --type flag::register --all flag::register --mode flag::register --dns flag::register --fallback-dns flag::register --force } # ============================================ # Help # ============================================ function cmd::peer::help() { cat < [options] Manage peer configuration and settings. Subcommands: update-dns Update DNS settings in client config(s) update-tunnel Update tunnel mode (split/full) in client config(s) Options for update-dns: --name Target peer --all Apply to all peers --type Filter by device type --dns Primary DNS (default: from config) --fallback-dns Fallback DNS servers (comma-separated) Default: from WG_DNS_FALLBACK in wgctl.conf Options for update-tunnel: --name Target peer --all Apply to all peers --type Filter by device type --mode Tunnel mode: split | full --force Skip confirmation for --all Examples: wgctl peer update-dns --all wgctl peer update-dns --name phone-nuno wgctl peer update-dns --name phone-nuno --fallback-dns 9.9.9.9,1.1.1.1 wgctl peer update-tunnel --all --mode split wgctl peer update-tunnel --name phone-nuno --mode full EOF } # ============================================ # Run # ============================================ function cmd::peer::run() { local subcmd="${1:-help}" shift || true case "$subcmd" in update-dns) cmd::peer::update_dns "$@" ;; update-tunnel) cmd::peer::update_tunnel "$@" ;; help) cmd::peer::help ;; *) log::error "Unknown subcommand: '${subcmd}'" cmd::peer::help return 1 ;; esac } # ============================================ # Update DNS # ============================================ function cmd::peer::update_dns() { local name="" type="" all=false local dns="" fallback_dns="" while [[ $# -gt 0 ]]; do case "$1" in --name) name="$2"; shift 2 ;; --type) type="$2"; shift 2 ;; --all) all=true; shift ;; --dns) dns="$2"; shift 2 ;; --fallback-dns) fallback_dns="$2"; shift 2 ;; --help) cmd::peer::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" && "$all" == "false" ]] && \ log::error "Specify --name or --all" && return 1 # Resolve DNS string local primary="${dns:-$(config::dns)}" local fallback="${fallback_dns:-$(config::dns_fallback)}" local dns_string if [[ -n "$fallback" ]]; then dns_string="${primary}, ${fallback}" else dns_string="$primary" fi # Collect target peers local peers=() if $all; then while IFS= read -r conf; do peers+=("$(basename "$conf" .conf)") done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null) else name=$(peers::resolve_and_require "$name" "$type") || return 1 peers=("$name") fi local updated=0 for peer_name in "${peers[@]}"; do local conf conf="$(ctx::clients)/${peer_name}.conf" [[ ! -f "$conf" ]] && continue # Replace DNS line in-place if grep -q "^DNS" "$conf"; then sed -i "s|^DNS = .*|DNS = ${dns_string}|" "$conf" else # Add DNS line after Address line sed -i "/^Address/a DNS = ${dns_string}" "$conf" fi (( updated++ )) || true log::debug "Updated DNS for: ${peer_name}" done log::wg_success "Updated DNS to '${dns_string}' for ${updated} peer(s)" } # ============================================ # Update Tunnel # ============================================ function cmd::peer::update_tunnel() { local name="" type="" all=false mode="" force=false while [[ $# -gt 0 ]]; do case "$1" in --name) name="$2"; shift 2 ;; --type) type="$2"; shift 2 ;; --all) all=true; shift ;; --mode) mode="$2"; shift 2 ;; --force) force=true; shift ;; --help) cmd::peer::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" && "$all" == "false" ]] && \ log::error "Specify --name or --all" && return 1 [[ -z "$mode" ]] && \ log::error "Missing required flag: --mode (split|full)" && return 1 [[ "$mode" != "split" && "$mode" != "full" ]] && \ log::error "Invalid mode: ${mode} (must be split or full)" && return 1 local allowed_ips allowed_ips=$(config::allowed_ips_for "$mode") # Collect target peers local peers=() if $all; then if ! $force; then read -r -p "Update tunnel mode to '${mode}' for ALL peers? [y/N] " confirm case "$confirm" in [yY]*) ;; *) log::info "Aborted"; return 0 ;; esac fi while IFS= read -r conf; do peers+=("$(basename "$conf" .conf)") done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null) else name=$(peers::resolve_and_require "$name" "$type") || return 1 peers=("$name") fi local updated=0 for peer_name in "${peers[@]}"; do local conf conf="$(ctx::clients)/${peer_name}.conf" [[ ! -f "$conf" ]] && continue # Replace AllowedIPs line in-place sed -i "s|^AllowedIPs = .*|AllowedIPs = ${allowed_ips}|" "$conf" (( updated++ )) || true log::debug "Updated tunnel for: ${peer_name}" done log::wg_success "Updated tunnel to '${mode}' (${allowed_ips}) for ${updated} peer(s)" log::wg "Peers must reconnect to apply the new tunnel mode" }