Add network activity map overlay and configurable monitoring filters
This commit is contained in:
140
traffic_monitoring_test.go
Normal file
140
traffic_monitoring_test.go
Normal file
@@ -0,0 +1,140 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user