#!/usr/bin/env bash # ============================================ # Config # ============================================ MONITOR_DIR="$(ctx::daemon)" WATCHLIST_FILE="${MONITOR_DIR}/watchlist.json" EVENTS_LOG="${MONITOR_DIR}/events.log" ENDPOINT_CACHE="${MONITOR_DIR}/endpoint_cache.json" FW_EVENTS_LOG="${MONITOR_DIR}/fw_events.log" MONITOR_SERVICE="wgctl-monitor" # ============================================ # Lifecycle # ============================================ function monitor::on_load() { if [[ ! -f "$WATCHLIST_FILE" ]]; then echo '{}' > "$WATCHLIST_FILE" fi if [[ ! -f "$EVENTS_LOG" ]]; then touch "$EVENTS_LOG" fi } # ============================================ # Watchlist # ============================================ function monitor::watch() { local ip="$1" client="$2" json::set "$WATCHLIST_FILE" "$ip" "$client" log::debug "Watching: ${client} (${ip})" } function monitor::unwatch() { local ip="$1" json::delete "$WATCHLIST_FILE" "$ip" } function monitor::is_watched() { local ip="$1" json::has_key "$WATCHLIST_FILE" "$ip" } function monitor::unwatch_client() { local name="$1" json::filter_values "$WATCHLIST_FILE" "value" "$name" log::debug "Unwatched client: ${name}" } # ============================================ # Events # ============================================ function monitor::last_attempt() { local client="$1" json::last_event "$EVENTS_LOG" "client" "timestamp" "$client" } function monitor::last_endpoint() { local client="$1" json::last_event "$EVENTS_LOG" "client" "endpoint" "$client" } function monitor::events_for() { local ip="$1" local limit="${2:-50}" json::events_for "$EVENTS_LOG" "$ip" "$limit" } function monitor::get_handshake_ts() { local public_key="${1:-}" [[ -z "$public_key" ]] && echo "0" && return wg show "$(config::interface)" latest-handshakes 2>/dev/null \ | grep "^${public_key}" | awk '{print $2}' || echo "0" } # ============================================ # Endpoint Cache (for blocked clients) # ============================================ ENDPOINT_CACHE="${WGCTL_DIR}/daemon/endpoint_cache.json" function monitor::cache_endpoint() { local client="$1" ip="$2" json::set "$ENDPOINT_CACHE" "$client" "$ip" } function monitor::get_cached_endpoint() { local client="${1:-}" json::get "$ENDPOINT_CACHE" "$client" } function monitor::update_endpoint_cache() { while IFS=$'\t' read -r key endpoint; do [[ "$endpoint" == "(none)" ]] && continue local ip ip=$(echo "$endpoint" | cut -d':' -f1) local client client=$(keys::find_by_public "$key") || continue monitor::cache_endpoint "$client" "$ip" done < <(wg show "$(config::interface)" endpoints 2>/dev/null) } # ============================================ # Endpoint (from wg show, for active clients) # ============================================ function monitor::endpoint_for_key() { local public_key="$1" wg show "$(config::interface)" endpoints 2>/dev/null \ | grep "^${public_key}" \ | awk '{print $2}' \ | cut -d':' -f1 } # ============================================ # Service # ============================================ function monitor::start() { systemctl start "$MONITOR_SERVICE" log::debug "Monitor daemon started" } function monitor::stop() { systemctl stop "$MONITOR_SERVICE" log::debug "Monitor daemon stopped" } function monitor::restart() { systemctl restart "$MONITOR_SERVICE" log::debug "Monitor daemon restarted" } function monitor::is_running() { systemctl is-active --quiet "$MONITOR_SERVICE" } function monitor::live() { local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}" local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}" local raw="${7:-false}" [[ "$raw" == "true" ]] && _WGCTL_RAW=true rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_* 2>/dev/null || true local w_client=20 w_dest=18 if ! $blocked_only && ! $restricted_only; then ( while true; do cmd::watch::_poll_handshakes \ "$filter_name" "$filter_type" "$filter_peers" "$w_client" "$w_dest" sleep 5 done ) & local poller_pid=$! fi cmd::watch::_tail_events \ "$filter_name" "$filter_type" "$filter_peers" \ "$blocked_only" "$restricted_only" "$allowed_only" \ "$w_client" "$w_dest" & local tailer_pid=$! trap "kill $tailer_pid ${poller_pid:-} 2>/dev/null; \ rm -f /tmp/wgctl_hs_* /tmp/wgctl_attempt_*; printf '\n'; exit 0" INT TERM wait }