Files
tomswall/internal/config/conntrack.go
T
unkin-agent 90d2875301
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Reject zone exclusions in conntrack entries
2026-10-03 23:53:09 +10:00

102 lines
2.9 KiB
Go

package config
import (
"fmt"
"strings"
)
type ConntrackAction string
const (
ConntrackNoTrack ConntrackAction = "notrack"
ConntrackHelper ConntrackAction = "helper"
ConntrackDrop ConntrackAction = "drop"
ConntrackLog ConntrackAction = "log"
)
type ConntrackChain string
const (
ConntrackPrerouting ConntrackChain = "prerouting"
ConntrackOutput ConntrackChain = "output"
ConntrackBoth ConntrackChain = "both"
)
type ConntrackRule struct {
// Action: notrack (bypass conntrack), helper (assign CT helper), drop (raw table drop), log.
Action ConntrackAction `yaml:"action"`
// Source zone spec. Supports zone, zone:interface, zone:interface:address.
Source string `yaml:"source,omitempty"`
// Dest zone spec. Same syntax as Source.
Dest string `yaml:"dest,omitempty"`
Proto string `yaml:"proto,omitempty"`
DPort PortSpec `yaml:"dport,omitempty"`
SPort PortSpec `yaml:"sport,omitempty"`
// Chain to install the rule in: prerouting, output, or both. Default: prerouting.
Chain ConntrackChain `yaml:"chain,omitempty"`
// CT helper name (for helper action): ftp, sip, tftp, irc, pptp, amanda, snmp, etc.
Helper string `yaml:"helper,omitempty"`
// User/group match (only valid for output chain).
User string `yaml:"user,omitempty"`
Comment string `yaml:"comment,omitempty"`
}
var validConntrackActions = map[ConntrackAction]bool{
ConntrackNoTrack: true, ConntrackHelper: true,
ConntrackDrop: true, ConntrackLog: true,
}
var validConntrackChains = map[ConntrackChain]bool{
ConntrackPrerouting: true, ConntrackOutput: true,
ConntrackBoth: true, "": true,
}
func (c *Config) validateConntrack() error {
for i, ct := range c.Conntrack {
if !validConntrackActions[ct.Action] {
return fmt.Errorf("conntrack[%d]: unknown action %q", i, ct.Action)
}
if !validConntrackChains[ct.Chain] {
return fmt.Errorf("conntrack[%d]: chain must be prerouting, output, or both", i)
}
if ct.Action == ConntrackHelper && ct.Helper == "" {
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)
}
if err := c.validateZoneRef(ct.Dest); err != nil {
return fmt.Errorf("conntrack[%d]: dest %w", i, err)
}
if ct.User != "" {
chain := ct.Chain
if chain == "" {
chain = ConntrackPrerouting
}
if chain == ConntrackPrerouting {
return fmt.Errorf("conntrack[%d]: user match only valid for output chain", i)
}
}
}
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:], "+!")
}