Files
ProxmoxDash/traffic_monitoring_test.go

141 lines
5.4 KiB
Go

package main
import (
"encoding/json"
"testing"
"time"
)
func TestMonitoringRulesValidation(t *testing.T) {
rules, err := validateMonitoringRules([]MonitoringRule{{IP: "2001:db8:0:0::1", Domain: "EXAMPLE.org", PathPrefix: "/health"}})
if err != nil || rules[0].IP != "2001:db8::1" || rules[0].Domain != "example.org" {
t.Fatalf("normalization: %+v %v", rules, err)
}
for _, r := range []MonitoringRule{{IP: "192.168.0.0/24"}, {IP: "8.8.8.8", Domain: "https://example.org"}, {IP: "8.8.8.8", PathPrefix: "health"}} {
if _, err := validateMonitoringRules([]MonitoringRule{r}); err == nil {
t.Fatalf("bad rule accepted: %+v", r)
}
}
if _, err := validateMonitoringRules(make([]MonitoringRule, 33)); err == nil {
t.Fatal("unbounded rules")
}
}
func TestMonitoringFiltersKeepHistoryAndSecurityEvents(t *testing.T) {
s, a := trafficFixture(t)
rules := []MonitoringRule{{IP: "192.168.0.10"}, {IP: "8.8.8.8", Domain: "example.org", PathPrefix: "/health"}}
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID, MonitoringRules: rules})
now := time.Now().Unix()
batch := TrafficBatch{Events: []TrafficEvent{
{ID: "probe", Time: now, Kind: "request", IP: "192.168.0.10", Domain: "other.org", Path: "/"},
{ID: "health", Time: now, Kind: "request", IP: "8.8.8.8", Domain: "EXAMPLE.org", Path: "/health/live"},
{ID: "visitor", Time: now, Kind: "request", IP: "8.8.8.8", Domain: "example.org", Path: "/login"},
{ID: "other-domain", Time: now, Kind: "request", IP: "8.8.8.8", Domain: "other.org", Path: "/health"},
{ID: "alert", Time: now, Kind: "alert", IP: "192.168.0.10"},
{ID: "ban", Time: now, Kind: "decision", IP: "192.168.0.10"},
}}
if err := s.SaveTraffic(a.ID, batch); err != nil {
t.Fatal(err)
}
all, err := s.TrafficView("", "", false)
if err != nil || all.Requests != 4 {
t.Fatalf("all: %+v %v", all, err)
}
tagged := 0
for _, e := range all.Events {
if e.Monitoring {
tagged++
}
}
if tagged != 2 {
t.Fatalf("tags %d", tagged)
}
filtered, err := s.TrafficView("", "", false, true)
if err != nil || filtered.Requests != 2 || filtered.Alerts != 1 || filtered.Decisions != 1 || len(filtered.Events) != 4 {
t.Fatalf("filter: %+v %v", filtered, err)
}
if len(filtered.Series) != 1 || filtered.Series[0].Count != 2 {
t.Fatalf("graph leaks probes: %+v", filtered.Series)
}
var count int
s.db.QueryRow("SELECT COUNT(*) FROM traffic_events").Scan(&count)
if count != 6 {
t.Fatal("history deleted")
}
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID, MonitoringRules: []MonitoringRule{}})
filtered, err = s.TrafficView("", "", false, true)
if err != nil || filtered.Requests != 4 {
t.Fatal("cleared rules not applied retroactively")
}
}
func TestMonitoringPrefixIsLiteralAndDefaultCanBeCleared(t *testing.T) {
s, a := trafficFixture(t)
if len(s.TrafficSettings().MonitoringRules) != 1 {
t.Fatal("missing suggested rule")
}
rules := []MonitoringRule{{IP: "8.8.8.8", PathPrefix: "/health_%"}}
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID, MonitoringRules: rules})
s.SaveTraffic(a.ID, TrafficBatch{Events: []TrafficEvent{{ID: "x", Time: time.Now().Unix(), Kind: "request", IP: "8.8.8.8", Path: "/health_ok"}}})
v, err := s.TrafficView("", "", false, true)
if err != nil || v.Requests != 1 {
t.Fatal("prefix used as wildcard")
}
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID})
if len(s.TrafficSettings().MonitoringRules) != 0 {
t.Fatal("cannot clear default")
}
}
func TestNetworkMapAndMonitoringSourceOnly(t *testing.T) {
s, a := trafficFixture(t)
rules := []MonitoringRule{{IP: "192.168.0.10"}, {IP: "8.8.8.8", Domain: "example.org"}}
s.SaveTrafficSettings(TrafficSettings{Enabled: true, NetworkEnabled: true, AgentID: a.ID, MonitoringRules: rules})
incoming, _ := parseNetworkFlow(networkNATLine)
probe := incoming
probe.Source = "192.168.0.10"
outgoing := incoming
outgoing.Source = "192.168.0.12"
outgoing.Destination = "1.1.1.1"
outgoing.Protocol = "udp"
local := incoming
local.Source = "192.168.0.15"
local.Destination = "192.168.0.10"
local.Protocol = "icmpv6"
s.SaveNetwork(a.ID, NetworkSnapshot{At: time.Now().Unix(), Flows: []NetworkFlow{incoming, probe, outgoing, local}})
snapshot, err := s.ReadNetworkSnapshot(a.ID, rules, true)
if err != nil || len(snapshot.Flows) != 3 || snapshot.MonitoringHidden != 1 {
t.Fatalf("source-only: %+v %v", snapshot, err)
}
mapped := makeNetworkMap(snapshot, false)
if mapped.TCP != 1 || mapped.UDP != 1 || mapped.ICMP != 1 || mapped.Unmapped != 1 || len(mapped.Points) != 2 {
t.Fatalf("map: %+v", mapped)
}
for _, p := range mapped.Points {
if p.Protocol == "udp" && p.Direction != "out" {
t.Fatal("outbound direction lost")
}
if p.Protocol == "tcp" && p.Direction != "in" {
t.Fatal("inbound direction lost")
}
}
mapped = makeNetworkMap(snapshot, true)
if mapped.ICMP != 0 {
t.Fatal("LAN flow leaked")
}
view, err := s.TrafficView("example.org", "alert", false, true)
if err != nil || view.Network == nil || view.Network.TCP != 1 {
t.Fatal("HTTP filters incorrectly hide network layer")
}
encoded, _ := json.Marshal(view)
if len(encoded) == 0 {
t.Fatal("map not serializable")
}
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID})
view, _ = s.TrafficView("", "", false)
if view.Network != nil {
t.Fatal("disabled network leaked")
}
v6 := NetworkFlow{Source: "2001:db8:0:0::1"}
if !isMonitoringFlow(v6, []MonitoringRule{{IP: "2001:db8::1"}}) {
t.Fatal("IPv6 representation mismatch")
}
}