138 lines
5.7 KiB
Go
138 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const networkNATLine = "ipv4 2 tcp 6 431999 ESTABLISHED src=8.8.8.8 dst=192.168.0.12 sport=51000 dport=443 packets=12 bytes=1500 src=172.18.0.2 dst=8.8.8.8 sport=443 dport=51000 packets=16 bytes=2500 [ASSURED] mark=0 zone=2 use=1"
|
|
|
|
func TestNetworkConntrackTuples(t *testing.T) {
|
|
f, ok := parseNetworkFlow(networkNATLine)
|
|
if !ok || f.Source != "8.8.8.8" || f.Destination != "192.168.0.12" || f.ReplySource != "172.18.0.2" || f.SourcePort != 51000 || f.ReplySourcePort != 443 || f.State != "ESTABLISHED" || !f.Accounting || f.Bytes != 1500 || f.ReplyBytes != 2500 || f.Zone != "2" {
|
|
t.Fatalf("NAT tuple corrupted: %+v", f)
|
|
}
|
|
udp, ok := parseNetworkFlow("ipv6 10 udp 17 29 src=2001:4860:4860::8888 dst=fd00::2 sport=51000 dport=53 [UNREPLIED] src=fd00::2 dst=2001:4860:4860::8888 sport=53 dport=51000 mark=0 use=1")
|
|
if !ok || udp.State != "UNREPLIED" || udp.Accounting || udp.Protocol != "udp" {
|
|
t.Fatalf("UDP: %+v", udp)
|
|
}
|
|
icmp, ok := parseNetworkFlow("icmp 1 29 src=8.8.8.8 dst=192.168.0.12 type=8 code=0 id=3 src=192.168.0.12 dst=8.8.8.8 type=0 code=0 id=3 use=1")
|
|
if !ok || icmp.SourcePort != 0 || icmp.State != "TRACKED" {
|
|
t.Fatalf("ICMP: %+v", icmp)
|
|
}
|
|
if _, ok := parseNetworkFlow("tcp 6 20 SYN_SENT src=not-an-ip dst=192.168.0.12"); ok {
|
|
t.Fatal("invalid accepted")
|
|
}
|
|
}
|
|
func TestNetworkSnapshotLimit(t *testing.T) {
|
|
var s NetworkSnapshot
|
|
appendNetworkFlows(&s, []byte(strings.Repeat(networkNATLine+"\n", 5001)))
|
|
if len(s.Flows) != 5000 || !s.Limited {
|
|
t.Fatalf("limit not enforced %d", len(s.Flows))
|
|
}
|
|
b := networkBuffer{limit: 2}
|
|
if _, err := b.Write([]byte("123")); err == nil || b.Len() != 0 {
|
|
t.Fatal("output unbounded")
|
|
}
|
|
}
|
|
func TestFirewallOnlyConfirmedDropCounters(t *testing.T) {
|
|
raw := `{"nftables":[{"rule":{"family":"ip","table":"crowdsec","chain":"crowdsec-chain","handle":3,"expr":[{"match":{}},{"counter":{"packets":10,"bytes":1000}},{"drop":null}]}},{"rule":{"table":"crowdsec","expr":[{"counter":{"packets":99}},{"accept":null}]}},{"rule":{"table":"other","expr":[{"counter":{"packets":99}},{"drop":null}]}},{"rule":{"table":"crowdsec","expr":[{"counter":{"packets":99}},{"match":{}},{"drop":null}]}},{"rule":{"table":"crowdsec","expr":[{"counter":"named-counter"},{"drop":null}]}}]}`
|
|
counters, err := parseFirewallCounters([]byte(raw))
|
|
if err != nil || len(counters) != 1 || counters[0].Packets != 10 || counters[0].Bytes != 1000 {
|
|
t.Fatalf("invented blocks: %+v %v", counters, err)
|
|
}
|
|
if _, err := parseFirewallCounters([]byte("invalid")); err == nil {
|
|
t.Fatal("invalid nft JSON accepted")
|
|
}
|
|
}
|
|
func TestNetworkRouteAuthorizationAndSourceSelection(t *testing.T) {
|
|
s, a := trafficFixture(t)
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID})
|
|
mux := http.NewServeMux()
|
|
trafficRoutes(mux, s)
|
|
flow, _ := parseNetworkFlow(networkNATLine)
|
|
snapshot := NetworkSnapshot{At: time.Now().Unix(), Flows: []NetworkFlow{flow}}
|
|
raw, _ := json.Marshal(snapshot)
|
|
post := func(token string) int {
|
|
r := httptest.NewRequest("POST", "/api/agent/network", bytes.NewReader(raw))
|
|
r.Header.Set("Authorization", "Bearer "+token)
|
|
w := httptest.NewRecorder()
|
|
mux.ServeHTTP(w, r)
|
|
return w.Code
|
|
}
|
|
if got := post(a.ID + "." + a.Secret); got != 403 {
|
|
t.Fatalf("disabled accepts data: %d", got)
|
|
}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, NetworkEnabled: true, AgentID: a.ID})
|
|
if got := post("wrong.token"); got != 401 {
|
|
t.Fatalf("unauthenticated: %d", got)
|
|
}
|
|
if got := post(a.ID + "." + a.Secret); got != 200 {
|
|
t.Fatalf("valid: %d", got)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
mux.ServeHTTP(w, httptest.NewRequest("GET", "/api/network", nil))
|
|
var got NetworkSnapshot
|
|
json.Unmarshal(w.Body.Bytes(), &got)
|
|
if w.Code != 200 || len(got.Flows) != 1 || got.Flows[0].Country == "" || got.ReceivedAt == 0 {
|
|
t.Fatalf("bad snapshot: %s", w.Body.String())
|
|
}
|
|
token, _ := s.CreateAgentEnrollmentToken()
|
|
other, _ := s.EnrollAgent(agentEnrollmentRequest{Token: token, Name: "other"})
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, NetworkEnabled: true, AgentID: other.ID})
|
|
if code := post(a.ID + "." + a.Secret); code != 403 {
|
|
t.Fatalf("old source accepted: %d", code)
|
|
}
|
|
w = httptest.NewRecorder()
|
|
mux.ServeHTTP(w, httptest.NewRequest("GET", "/api/network", nil))
|
|
json.Unmarshal(w.Body.Bytes(), &got)
|
|
if len(got.Flows) != 0 {
|
|
t.Fatal("old source data leaked")
|
|
}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: false, NetworkEnabled: true, AgentID: a.ID})
|
|
for _, path := range []string{"/network", "/api/network"} {
|
|
w = httptest.NewRecorder()
|
|
mux.ServeHTTP(w, httptest.NewRequest("GET", path, nil))
|
|
if w.Code != 404 {
|
|
t.Fatalf("disabled page: %s", path)
|
|
}
|
|
}
|
|
}
|
|
func TestNetworkRejectBadSnapshots(t *testing.T) {
|
|
s, a := trafficFixture(t)
|
|
if err := s.SaveNetwork(a.ID, NetworkSnapshot{At: time.Now().Unix() - 300}); err == nil {
|
|
t.Fatal("stale timestamp")
|
|
}
|
|
f, _ := parseNetworkFlow(networkNATLine)
|
|
f.SourcePort = 99999
|
|
if err := s.SaveNetwork(a.ID, NetworkSnapshot{At: time.Now().Unix(), Flows: []NetworkFlow{f}}); err == nil {
|
|
t.Fatal("invalid port")
|
|
}
|
|
}
|
|
func TestNetworkCommandCancellation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
if _, err := networkCommand(ctx, "conntrack", "-L"); err == nil {
|
|
t.Fatal("cancelled command ran")
|
|
}
|
|
}
|
|
|
|
func TestNetworkExternalAddresses(t *testing.T) {
|
|
for _, ip := range []string{"192.168.1.1", "127.0.0.1", "100.64.0.1", "224.0.0.1", "ff02::1", "fe80::1", "fd00::1", "::"} {
|
|
if networkExternalIP(ip) {
|
|
t.Fatalf("not external: %s", ip)
|
|
}
|
|
}
|
|
for _, ip := range []string{"8.8.8.8", "2001:4860:4860::8888"} {
|
|
if !networkExternalIP(ip) {
|
|
t.Fatalf("external: %s", ip)
|
|
}
|
|
}
|
|
}
|