Reject zone exclusions in conntrack entries
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

This commit is contained in:
2026-10-03 23:53:09 +10:00
parent bf235291d0
commit 90d2875301
4 changed files with 72 additions and 17 deletions
+13 -1
View File
@@ -1,6 +1,9 @@
package config
import "fmt"
import (
"fmt"
"strings"
)
type ConntrackAction string
@@ -68,6 +71,9 @@ func (c *Config) validateConntrack() error {
return fmt.Errorf("conntrack[%d]: helper name required for helper action", i)
}
if HasZoneExclusion(ct.Source) || HasZoneExclusion(ct.Dest) {
return fmt.Errorf("conntrack[%d]: zone exclusions are not supported in conntrack entries", i)
}
if err := c.validateZoneRef(ct.Source); err != nil {
return fmt.Errorf("conntrack[%d]: source %w", i, err)
}
@@ -87,3 +93,9 @@ func (c *Config) validateConntrack() error {
}
return nil
}
// HasZoneExclusion reports an all/any zone ref with a "+" or "!" modifier (all+, all!x, any+!x, ...).
func HasZoneExclusion(spec string) bool {
zones, _, _ := strings.Cut(spec, ":")
return (strings.HasPrefix(zones, "all") || strings.HasPrefix(zones, "any")) && strings.ContainsAny(zones[3:], "+!")
}
+30 -5
View File
@@ -67,13 +67,38 @@ func TestValidateConntrack(t *testing.T) {
wantErr: `dest zone "nte" not defined`,
},
{
name: "unknown excluded zone",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Source: "all!nte"}},
wantErr: `excluded zone "nte" not defined`,
name: "all and plain zone forms are valid",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Source: "net,fw", Dest: "all:192.0.2.1"}},
},
{
name: "exclusion and all forms are valid",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Source: "all!net", Dest: "all:192.0.2.1"}},
name: "Source all!net rejected",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Source: "all!net"}},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "Dest all!net:192.0.2.1 rejected",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Dest: "all!net:192.0.2.1"}},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "Source all+ rejected",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Source: "all+"}},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "Dest all+!net rejected",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Dest: "all+!net"}},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "Source any!net rejected",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Source: "any!net"}},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "Dest any+ rejected",
rules: []ConntrackRule{{Action: ConntrackNoTrack, Dest: "any+"}},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "helper without source/dest is valid",
+3 -5
View File
@@ -216,6 +216,9 @@ func (c *Compiler) compileConntrack(state *FirewallState) error {
fwZone := c.cfg.FirewallZone()
for i, ct := range c.cfg.Conntrack {
tag := fmt.Sprintf("conntrack:%d", i)
if config.HasZoneExclusion(ct.Source) || config.HasZoneExclusion(ct.Dest) {
return fmt.Errorf("conntrack[%d]: zone exclusions are not supported in conntrack entries", i)
}
srcs, dsts := c.zoneSpecs(ct.Source), c.zoneSpecs(ct.Dest)
if len(srcs) == 0 {
srcs = []config.ZoneSpec{{}}
@@ -225,11 +228,6 @@ func (c *Compiler) compileConntrack(state *FirewallState) error {
}
for _, src := range srcs {
// raw_output only sees locally generated traffic, so it applies to an fw (or omitted) source only.
if isZoneExclusion(ct.Source) && (ct.Chain == config.ConntrackPrerouting && src.Zone == fwZone ||
ct.Chain == config.ConntrackOutput && src.Zone != fwZone) {
continue
}
chains := []string{"raw_prerouting"}
switch {
case ct.Chain == config.ConntrackOutput && src.Zone != fwZone && src.Zone != "":
+26 -6
View File
@@ -2517,14 +2517,34 @@ func TestCompile_ConntrackZones(t *testing.T) {
want: map[string][]string{},
},
{
name: "all!net expands to every other zone",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "all!net", Dest: "fw:192.0.2.53"},
want: map[string][]string{"raw_output": {"daddr=192.0.2.53"}, "raw_prerouting": {"iif=eth1 daddr=192.0.2.53"}},
name: "all!net rejected",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "all!net"},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "all!net in prerouting skips fw",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "all!net", Dest: "fw", Chain: config.ConntrackPrerouting},
want: map[string][]string{"raw_prerouting": {"iif=eth1"}},
name: "all!net rejected",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Dest: "all!net"},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "all+ rejected",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "all+"},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "all+!net rejected",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Dest: "all+!net"},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "any!net rejected",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Source: "any!net"},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "any+ rejected",
ct: config.ConntrackRule{Action: config.ConntrackNoTrack, Dest: "any+"},
wantErr: "zone exclusions are not supported in conntrack entries",
},
{
name: "omitted source and dest is global",