templates-zabbix-itguys/templates_gold/pfsense_hybrid_snmp_agent/files/openvpn-access-tracker.sh

69 lines
2.6 KiB
Bash

#!/bin/sh
# OpenVPN Access Tracker - Sampled Replay (Arthur's Gold Standard v1)
# Correlaciona usuários VPN com a tabela de estados do Packet Filter
# Compatível com: pfSense 2.x / FreeBSD
#
# Instalação:
# 1. Copie para /opt/zabbix/openvpn-access-tracker.sh
# 2. chmod +x /opt/zabbix/openvpn-access-tracker.sh
# 3. Adicione ao cron: */1 * * * * /opt/zabbix/openvpn-access-tracker.sh
#
# Output: /var/log/openvpn_user_activity.log
# Format: TIMESTAMP|USER|REAL_IP|DST_IP:PORT|PROTOCOL
METRICS_FILE="/tmp/openvpn_metrics.json"
LOG_FILE="/var/log/openvpn_user_activity.log"
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
# Verifica se temos dados de VPN
if [ ! -f "$METRICS_FILE" ]; then
exit 0
fi
# Extrai lista de usuários e seus IPs virtuais
# Formato esperado: "user": { ... "virtual_ip": "10.0.8.5" ... }
user_ips=$(cat "$METRICS_FILE" | sed 's/},{/}\n{/g' | grep -oE '"[^"]+":{"bytes_recv"[^}]+' | \
sed 's/"//g' | awk -F: '{user=$1; for(i=2;i<=NF;i++) if($i ~ /virtual_ip/) {getline; print user":"$2}}' 2>/dev/null)
# Alternativa mais robusta: usar jq se disponível, senão parse manual
if command -v jq >/dev/null 2>&1; then
# jq disponível - parse limpo
user_list=$(jq -r 'to_entries[] | "\(.key):\(.value.virtual_ip)"' "$METRICS_FILE" 2>/dev/null)
else
# Parse manual simplificado
user_list=$(cat "$METRICS_FILE" | tr ',' '\n' | tr '{' '\n' | tr '}' '\n' | \
grep -E '^"[a-zA-Z0-9._-]+"|virtual_ip' | paste - - 2>/dev/null | \
sed 's/"//g;s/virtual_ip://g' | awk '{print $1":"$2}')
fi
# Se não temos usuários, sai
[ -z "$user_list" ] && exit 0
# Captura tabela de estados
state_table=$(pfctl -ss 2>/dev/null)
[ -z "$state_table" ] && exit 0
# Para cada usuário VPN, busca conexões na state table
echo "$user_list" | while IFS=: read -r user vip; do
[ -z "$user" ] || [ -z "$vip" ] && continue
# Busca estados onde o IP virtual é a origem
# Formato pfctl -ss: all tcp 10.0.8.5:54321 -> 192.168.1.100:443 ESTABLISHED:ESTABLISHED
echo "$state_table" | grep " $vip:" | while read -r state_line; do
# Extrai protocolo, destino
proto=$(echo "$state_line" | awk '{print $2}')
dst=$(echo "$state_line" | awk '{print $4}' | sed 's/->//g')
state=$(echo "$state_line" | awk '{print $NF}')
[ -z "$dst" ] && continue
# Loga a atividade
printf "%s|%s|%s|%s|%s|%s\n" "$TIMESTAMP" "$user" "$vip" "$dst" "$proto" "$state" >> "$LOG_FILE"
done
done
# Rotação simples: mantém só últimas 50000 linhas (~5MB)
if [ -f "$LOG_FILE" ] && [ $(wc -l < "$LOG_FILE") -gt 50000 ]; then
tail -40000 "$LOG_FILE" > "$LOG_FILE.tmp" && mv "$LOG_FILE.tmp" "$LOG_FILE"
fi