CrowdSec's blacklist chain runs at priority 0. Our loopback accept rule needs to run first so local traffic isn't blocked. Also cleans up .bak files after successful SafeApply.
159 lines
4.8 KiB
Go
159 lines
4.8 KiB
Go
package nftables
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestNewManager_DefaultPath(t *testing.T) {
|
|
m := NewManager("")
|
|
if m.GetRulesPath() != defaultRulesPath {
|
|
t.Errorf("got %q, want %q", m.GetRulesPath(), defaultRulesPath)
|
|
}
|
|
}
|
|
|
|
func TestNewManager_CustomPath(t *testing.T) {
|
|
m := NewManager("/tmp/wild-central.nft")
|
|
if m.GetRulesPath() != "/tmp/wild-central.nft" {
|
|
t.Errorf("got %q, want /tmp/wild-central.nft", m.GetRulesPath())
|
|
}
|
|
}
|
|
|
|
func TestPortsToStr(t *testing.T) {
|
|
cases := []struct {
|
|
ports []int
|
|
want string
|
|
}{
|
|
{[]int{80, 443, 8404}, "80, 443, 8404"},
|
|
{[]int{443}, "443"},
|
|
{[]int{}, ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := portsToStr(c.ports); got != c.want {
|
|
t.Errorf("portsToStr(%v) = %q, want %q", c.ports, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerate_AlwaysIncludesStructure(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{80, 443}, nil, nil, "")
|
|
|
|
for _, want := range []string{
|
|
"table inet wild-central",
|
|
"set allowed_tcp_ports",
|
|
"chain input",
|
|
"type filter hook input priority -1; policy accept;",
|
|
"iif \"lo\" accept",
|
|
"ct state { established, related } accept",
|
|
} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("expected %q in output, got:\n%s", want, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerate_WithPorts_IncludesElements(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{80, 443, 8404}, nil, nil, "")
|
|
|
|
if !strings.Contains(out, "elements = { 80, 443, 8404 }") {
|
|
t.Errorf("expected elements line with ports, got:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_NoPorts_TCPSetHasNoElements(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{}, nil, nil, "")
|
|
|
|
// TCP set should have no elements when no ports given
|
|
if strings.Contains(out, "allowed_tcp_ports") && strings.Contains(out, "elements") {
|
|
// Check that the elements line is not inside the TCP block
|
|
tcpBlock := out[strings.Index(out, "set allowed_tcp_ports"):]
|
|
tcpBlock = tcpBlock[:strings.Index(tcpBlock, "}")+1]
|
|
if strings.Contains(tcpBlock, "elements") {
|
|
t.Errorf("expected no elements in allowed_tcp_ports block when no ports, got:\n%s", tcpBlock)
|
|
}
|
|
}
|
|
// UDP set always has DNS/DHCP elements
|
|
if !strings.Contains(out, "53, 67, 68") {
|
|
t.Errorf("expected DNS/DHCP elements in allowed_udp_ports even with no TCP ports, got:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_ExtraPortsMerged(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{80, 443}, []int{22, 443}, nil, "") // 443 deduplicated
|
|
|
|
if !strings.Contains(out, "22") {
|
|
t.Errorf("expected extra port 22 in output, got:\n%s", out)
|
|
}
|
|
// 443 should appear only once
|
|
count := strings.Count(out, "443")
|
|
if count != 1 {
|
|
t.Errorf("expected 443 exactly once, got %d times in:\n%s", count, out)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_NoWANInterface_NoDropRules(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{80, 443}, nil, nil, "")
|
|
|
|
if strings.Contains(out, "drop") {
|
|
t.Errorf("expected no drop rules without WAN interface, got:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_WithWANInterface_IncludesDropRules(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{80, 443}, nil, nil, "eth0")
|
|
|
|
if !strings.Contains(out, `iif "eth0"`) {
|
|
t.Errorf("expected WAN interface in rules, got:\n%s", out)
|
|
}
|
|
if !strings.Contains(out, "drop") {
|
|
t.Errorf("expected drop rule with WAN interface, got:\n%s", out)
|
|
}
|
|
if !strings.Contains(out, "@allowed_tcp_ports") {
|
|
t.Errorf("expected reference to allowed_tcp_ports set, got:\n%s", out)
|
|
}
|
|
if !strings.Contains(out, "@allowed_udp_ports") {
|
|
t.Errorf("expected reference to allowed_udp_ports set, got:\n%s", out)
|
|
}
|
|
// DNS and DHCP UDP ports should be in the allowed_udp_ports set
|
|
if !strings.Contains(out, "53, 67, 68") {
|
|
t.Errorf("expected DNS/DHCP UDP ports in allowed_udp_ports set, got:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_AlwaysIncludesUDPSet(t *testing.T) {
|
|
m := NewManager("")
|
|
// No WAN interface — permissive mode, but UDP set should still be visible
|
|
out := m.Generate([]int{80, 443}, nil, []int{51820}, "")
|
|
|
|
if !strings.Contains(out, "set allowed_udp_ports") {
|
|
t.Errorf("expected allowed_udp_ports set even without WAN interface, got:\n%s", out)
|
|
}
|
|
if !strings.Contains(out, "51820") {
|
|
t.Errorf("expected extra UDP port 51820 in allowed_udp_ports set, got:\n%s", out)
|
|
}
|
|
// No drop rules without WAN interface
|
|
if strings.Contains(out, "drop") {
|
|
t.Errorf("expected no drop rules without WAN interface, got:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_ExtraUDPPorts(t *testing.T) {
|
|
m := NewManager("")
|
|
out := m.Generate([]int{80, 443}, nil, []int{5353}, "eth0")
|
|
|
|
// User UDP port should appear in the UDP exception list
|
|
if !strings.Contains(out, "5353") {
|
|
t.Errorf("expected extra UDP port 5353 in output, got:\n%s", out)
|
|
}
|
|
// Hardcoded DNS/DHCP should still be there
|
|
if !strings.Contains(out, "53, 67, 68") {
|
|
t.Errorf("expected DNS/DHCP UDP ports still present, got:\n%s", out)
|
|
}
|
|
}
|