Match source/dest zones and addresses on conntrack rules
This commit is contained in:
@@ -213,35 +213,65 @@ func (c *Compiler) compileBlrules(state *FirewallState) error {
|
||||
}
|
||||
|
||||
func (c *Compiler) compileConntrack(state *FirewallState) error {
|
||||
fwZone := c.cfg.FirewallZone()
|
||||
for i, ct := range c.cfg.Conntrack {
|
||||
tag := fmt.Sprintf("conntrack:%d", i)
|
||||
|
||||
chains := []string{"raw_prerouting"}
|
||||
switch ct.Chain {
|
||||
case config.ConntrackOutput:
|
||||
chains = []string{"raw_output"}
|
||||
case config.ConntrackBoth:
|
||||
chains = []string{"raw_prerouting", "raw_output"}
|
||||
srcs, dsts := zoneSpecs(ct.Source), zoneSpecs(ct.Dest)
|
||||
if len(srcs) == 0 {
|
||||
srcs = []config.ZoneSpec{{}}
|
||||
}
|
||||
if len(dsts) == 0 {
|
||||
dsts = []config.ZoneSpec{{}}
|
||||
}
|
||||
|
||||
matches, err := l4Matches(ct.Proto, ct.DPort, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conntrack[%d]: %w", i, err)
|
||||
for _, src := range srcs {
|
||||
chains := []string{"raw_prerouting"}
|
||||
switch {
|
||||
case ct.Chain == config.ConntrackOutput, ct.Chain == "" && src.Zone == fwZone:
|
||||
chains = []string{"raw_output"}
|
||||
case ct.Chain == config.ConntrackBoth:
|
||||
chains = []string{"raw_prerouting", "raw_output"}
|
||||
}
|
||||
for _, srcAddr := range splitAddrs(src.Addr) {
|
||||
for _, dst := range dsts {
|
||||
for _, dstAddr := range splitAddrs(dst.Addr) {
|
||||
for _, chain := range chains {
|
||||
if err := c.compileConntrackPair(state, tag, chain, ct, src.Zone, srcAddr, dst.Zone, dstAddr); err != nil {
|
||||
return fmt.Errorf("conntrack[%d]: %w", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, chain := range chains {
|
||||
// compileConntrackPair matches iif of the source zone in raw_prerouting and oif of the dest zone in raw_output.
|
||||
func (c *Compiler) compileConntrackPair(state *FirewallState, tag, chain string, ct config.ConntrackRule,
|
||||
srcZone, srcAddr, dstZone, dstAddr string) error {
|
||||
if ct.Action == config.ConntrackHelper {
|
||||
return nil
|
||||
}
|
||||
srcIfaces, dstIfaces := c.resolveZoneInterfaces(srcZone, srcAddr), []string{""}
|
||||
if chain == "raw_output" {
|
||||
srcIfaces, dstIfaces = []string{""}, c.resolveZoneInterfaces(dstZone, dstAddr)
|
||||
}
|
||||
for _, srcIface := range srcIfaces {
|
||||
for _, dstIface := range dstIfaces {
|
||||
matches, err := c.buildMatchExprs(srcIface, dstIface, chain, ct.Proto, ct.DPort, ct.SPort, srcAddr, dstAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, m := range matches {
|
||||
exprs := append([]expr.Any{}, m.exprs...)
|
||||
|
||||
exprs := m.exprs
|
||||
switch ct.Action {
|
||||
case config.ConntrackNoTrack:
|
||||
exprs = append(exprs, &expr.Notrack{})
|
||||
case config.ConntrackHelper:
|
||||
continue
|
||||
case config.ConntrackDrop:
|
||||
exprs = append(exprs, &expr.Verdict{Kind: expr.VerdictDrop})
|
||||
}
|
||||
|
||||
state.Rules[chain] = append(state.Rules[chain], ManagedRule{
|
||||
Chain: chain,
|
||||
Exprs: exprs,
|
||||
|
||||
@@ -2416,3 +2416,56 @@ func TestCompile_OrigDestForwardRejected(t *testing.T) {
|
||||
t.Fatalf("Compile() error = %v, want forwarded ORIGDEST rejection", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompile_ConntrackZones(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ct config.ConntrackRule
|
||||
want map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "source zone matches iif",
|
||||
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "net", Dest: "fw", Proto: "udp", DPort: config.PortSpec{"53"}},
|
||||
want: map[string][]string{"raw_prerouting": {"iif=eth0"}},
|
||||
},
|
||||
{
|
||||
name: "source and dest addresses",
|
||||
ct: config.ConntrackRule{Action: config.ConntrackDrop, Source: "net:192.0.2.1,198.51.100.1", Dest: "fw:203.0.113.1"},
|
||||
want: map[string][]string{"raw_prerouting": {"iif=eth0 saddr=192.0.2.1 daddr=203.0.113.1", "iif=eth0 saddr=198.51.100.1 daddr=203.0.113.1"}},
|
||||
},
|
||||
{
|
||||
name: "fw source goes to raw_output with dest oif",
|
||||
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "fw", Dest: "net,lan"},
|
||||
want: map[string][]string{"raw_output": {"oif=eth0", "oif=eth1"}},
|
||||
},
|
||||
{
|
||||
name: "all matches no interface",
|
||||
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "all", Dest: "fw:192.0.2.53"},
|
||||
want: map[string][]string{"raw_prerouting": {"daddr=192.0.2.53"}},
|
||||
},
|
||||
{
|
||||
name: "interface-less zone fails closed",
|
||||
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "dmz", Dest: "fw"},
|
||||
want: map[string][]string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
state := mustCompile(t, listCfg(func(cfg *config.Config) {
|
||||
cfg.Zones["lan"] = config.Zone{Type: config.ZoneIP}
|
||||
cfg.Zones["dmz"] = config.Zone{Type: config.ZoneIP}
|
||||
cfg.Interfaces = append(cfg.Interfaces, config.Interface{Zone: "lan", Interface: "eth1"})
|
||||
cfg.Conntrack = []config.ConntrackRule{tt.ct}
|
||||
}))
|
||||
got := map[string][]string{}
|
||||
for _, chain := range []string{"raw_prerouting", "raw_output"} {
|
||||
for _, r := range taggedRules(state, chain, "conntrack:0:"+chain) {
|
||||
got[chain] = append(got[chain], describeRule(r))
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("rules = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user