gistfile1.txt
· 4.7 KiB · Text
Raw
#!/usr/bin/env bash
# ============================================
# Config
# ============================================
MONITOR_DIR="$(ctx::root)/daemon"
WATCHLIST_FILE="${MONITOR_DIR}/watchlist.json"
EVENTS_LOG="${MONITOR_DIR}/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::wg "Watching: ${client} (${ip})"
}
function monitor::unwatch() {
local ip="$1"
json::delete "$WATCHLIST_FILE" "$ip"
}
function monitor::is_watched() {
local ip="$1"
python3 -c "
import json
with open('${WATCHLIST_FILE}') as f:
wl = json.load(f)
exit(0 if '${ip}' in wl else 1)
"
}
function monitor::unwatch_client() {
local name="$1"
python3 -c "
import json
with open('${WATCHLIST_FILE}') as f:
wl = json.load(f)
wl = {k: v for k, v in wl.items() if v != '${name}'}
with open('${WATCHLIST_FILE}', 'w') as f:
json.dump(wl, f, indent=2)
"
log::wg "Unwatched client: ${name}"
}
# ============================================
# Events
# ============================================
function monitor::last_attempt() {
local client="$1"
python3 -c "
import json
events = []
try:
with open('${EVENTS_LOG}') as f:
for line in f:
try:
e = json.loads(line.strip())
if e.get('client') == '${client}':
events.append(e)
except:
pass
except:
pass
if events:
print(events[-1].get('timestamp', ''))
"
}
function monitor::last_endpoint() {
local client="$1"
python3 -c "
import json
events = []
try:
with open('${EVENTS_LOG}') as f:
for line in f:
try:
e = json.loads(line.strip())
if e.get('client') == '${client}' and e.get('endpoint'):
events.append(e)
except:
pass
except:
pass
if events:
print(events[-1].get('endpoint', ''))
"
}
function monitor::events_for() {
local ip="$1"
local limit="${2:-50}"
python3 -c "
import json
from datetime import datetime, timezone
events = []
try:
with open('${EVENTS_LOG}') as f:
for line in f:
try:
e = json.loads(line.strip())
if e.get('ip') == '${ip}':
events.append(e)
except:
pass
except:
pass
for e in events[-${limit}:]:
ts = e.get('timestamp', '')
try:
dt = datetime.fromisoformat(ts)
ts = dt.strftime('%Y-%m-%d %H:%M:%S')
except:
pass
endpoint = e.get('endpoint', '—')
client = e.get('client', '—')
event = e.get('event', '—')
print(f' {ts} {client:<20} {endpoint:<20} {event}')
"
}
# ============================================
# 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::wg_start "Monitor daemon started"
}
function monitor::stop() {
systemctl stop "$MONITOR_SERVICE"
log::wg_stop "Monitor daemon stopped"
}
function monitor::restart() {
systemctl restart "$MONITOR_SERVICE"
log::wg_start "Monitor daemon restarted"
}
function monitor::is_running() {
systemctl is-active --quiet "$MONITOR_SERVICE"
}
| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | # ============================================ |
| 4 | # Config |
| 5 | # ============================================ |
| 6 | |
| 7 | MONITOR_DIR="$(ctx::root)/daemon" |
| 8 | WATCHLIST_FILE="${MONITOR_DIR}/watchlist.json" |
| 9 | EVENTS_LOG="${MONITOR_DIR}/events.log" |
| 10 | MONITOR_SERVICE="wgctl-monitor" |
| 11 | |
| 12 | # ============================================ |
| 13 | # Lifecycle |
| 14 | # ============================================ |
| 15 | |
| 16 | function monitor::on_load() { |
| 17 | if [[ ! -f "$WATCHLIST_FILE" ]]; then |
| 18 | echo '{}' > "$WATCHLIST_FILE" |
| 19 | fi |
| 20 | if [[ ! -f "$EVENTS_LOG" ]]; then |
| 21 | touch "$EVENTS_LOG" |
| 22 | fi |
| 23 | } |
| 24 | |
| 25 | # ============================================ |
| 26 | # Watchlist |
| 27 | # ============================================ |
| 28 | |
| 29 | function monitor::watch() { |
| 30 | local ip="$1" client="$2" |
| 31 | json::set "$WATCHLIST_FILE" "$ip" "$client" |
| 32 | log::wg "Watching: ${client} (${ip})" |
| 33 | } |
| 34 | |
| 35 | function monitor::unwatch() { |
| 36 | local ip="$1" |
| 37 | json::delete "$WATCHLIST_FILE" "$ip" |
| 38 | } |
| 39 | |
| 40 | function monitor::is_watched() { |
| 41 | local ip="$1" |
| 42 | python3 -c " |
| 43 | import json |
| 44 | with open('${WATCHLIST_FILE}') as f: |
| 45 | wl = json.load(f) |
| 46 | exit(0 if '${ip}' in wl else 1) |
| 47 | " |
| 48 | } |
| 49 | |
| 50 | function monitor::unwatch_client() { |
| 51 | local name="$1" |
| 52 | python3 -c " |
| 53 | import json |
| 54 | with open('${WATCHLIST_FILE}') as f: |
| 55 | wl = json.load(f) |
| 56 | wl = {k: v for k, v in wl.items() if v != '${name}'} |
| 57 | with open('${WATCHLIST_FILE}', 'w') as f: |
| 58 | json.dump(wl, f, indent=2) |
| 59 | " |
| 60 | log::wg "Unwatched client: ${name}" |
| 61 | } |
| 62 | |
| 63 | # ============================================ |
| 64 | # Events |
| 65 | # ============================================ |
| 66 | |
| 67 | function monitor::last_attempt() { |
| 68 | local client="$1" |
| 69 | python3 -c " |
| 70 | import json |
| 71 | |
| 72 | events = [] |
| 73 | try: |
| 74 | with open('${EVENTS_LOG}') as f: |
| 75 | for line in f: |
| 76 | try: |
| 77 | e = json.loads(line.strip()) |
| 78 | if e.get('client') == '${client}': |
| 79 | events.append(e) |
| 80 | except: |
| 81 | pass |
| 82 | except: |
| 83 | pass |
| 84 | |
| 85 | if events: |
| 86 | print(events[-1].get('timestamp', '')) |
| 87 | " |
| 88 | } |
| 89 | |
| 90 | function monitor::last_endpoint() { |
| 91 | local client="$1" |
| 92 | python3 -c " |
| 93 | import json |
| 94 | |
| 95 | events = [] |
| 96 | try: |
| 97 | with open('${EVENTS_LOG}') as f: |
| 98 | for line in f: |
| 99 | try: |
| 100 | e = json.loads(line.strip()) |
| 101 | if e.get('client') == '${client}' and e.get('endpoint'): |
| 102 | events.append(e) |
| 103 | except: |
| 104 | pass |
| 105 | except: |
| 106 | pass |
| 107 | |
| 108 | if events: |
| 109 | print(events[-1].get('endpoint', '')) |
| 110 | " |
| 111 | } |
| 112 | |
| 113 | function monitor::events_for() { |
| 114 | local ip="$1" |
| 115 | local limit="${2:-50}" |
| 116 | python3 -c " |
| 117 | import json |
| 118 | from datetime import datetime, timezone |
| 119 | |
| 120 | events = [] |
| 121 | try: |
| 122 | with open('${EVENTS_LOG}') as f: |
| 123 | for line in f: |
| 124 | try: |
| 125 | e = json.loads(line.strip()) |
| 126 | if e.get('ip') == '${ip}': |
| 127 | events.append(e) |
| 128 | except: |
| 129 | pass |
| 130 | except: |
| 131 | pass |
| 132 | |
| 133 | for e in events[-${limit}:]: |
| 134 | ts = e.get('timestamp', '') |
| 135 | try: |
| 136 | dt = datetime.fromisoformat(ts) |
| 137 | ts = dt.strftime('%Y-%m-%d %H:%M:%S') |
| 138 | except: |
| 139 | pass |
| 140 | endpoint = e.get('endpoint', '—') |
| 141 | client = e.get('client', '—') |
| 142 | event = e.get('event', '—') |
| 143 | print(f' {ts} {client:<20} {endpoint:<20} {event}') |
| 144 | " |
| 145 | } |
| 146 | |
| 147 | # ============================================ |
| 148 | # Endpoint Cache (for blocked clients) |
| 149 | # ============================================ |
| 150 | |
| 151 | ENDPOINT_CACHE="${WGCTL_DIR}/daemon/endpoint_cache.json" |
| 152 | |
| 153 | function monitor::cache_endpoint() { |
| 154 | local client="$1" ip="$2" |
| 155 | json::set "$ENDPOINT_CACHE" "$client" "$ip" |
| 156 | } |
| 157 | |
| 158 | function monitor::get_cached_endpoint() { |
| 159 | local client="$1" |
| 160 | json::get "$ENDPOINT_CACHE" "$client" |
| 161 | } |
| 162 | |
| 163 | function monitor::update_endpoint_cache() { |
| 164 | while IFS=$'\t' read -r key endpoint; do |
| 165 | [[ "$endpoint" == "(none)" ]] && continue |
| 166 | local ip |
| 167 | ip=$(echo "$endpoint" | cut -d':' -f1) |
| 168 | local client |
| 169 | client=$(keys::find_by_public "$key") || continue |
| 170 | monitor::cache_endpoint "$client" "$ip" |
| 171 | done < <(wg show "$(config::interface)" endpoints 2>/dev/null) |
| 172 | } |
| 173 | |
| 174 | # ============================================ |
| 175 | # Endpoint (from wg show, for active clients) |
| 176 | # ============================================ |
| 177 | |
| 178 | function monitor::endpoint_for_key() { |
| 179 | local public_key="$1" |
| 180 | wg show "$(config::interface)" endpoints 2>/dev/null \ |
| 181 | | grep "^${public_key}" \ |
| 182 | | awk '{print $2}' \ |
| 183 | | cut -d':' -f1 |
| 184 | } |
| 185 | |
| 186 | # ============================================ |
| 187 | # Service |
| 188 | # ============================================ |
| 189 | |
| 190 | function monitor::start() { |
| 191 | systemctl start "$MONITOR_SERVICE" |
| 192 | log::wg_start "Monitor daemon started" |
| 193 | } |
| 194 | |
| 195 | function monitor::stop() { |
| 196 | systemctl stop "$MONITOR_SERVICE" |
| 197 | log::wg_stop "Monitor daemon stopped" |
| 198 | } |
| 199 | |
| 200 | function monitor::restart() { |
| 201 | systemctl restart "$MONITOR_SERVICE" |
| 202 | log::wg_start "Monitor daemon restarted" |
| 203 | } |
| 204 | |
| 205 | function monitor::is_running() { |
| 206 | systemctl is-active --quiet "$MONITOR_SERVICE" |
| 207 | } |
| 208 |