#!/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" }