added test go implementation
continuous-integration/drone/push Build encountered an error

This commit is contained in:
gyurix
2026-06-08 17:02:13 +02:00
parent a555cce680
commit fcda599ec7
9 changed files with 1112 additions and 44 deletions
+20 -15
View File
@@ -6,12 +6,31 @@ import (
"strings"
)
// IPTablesAPI defines the interface for iptables operations, enabling mock implementations for testing
type IPTablesAPI interface {
Binary() string
EnsureIPForward() error
EnsureEstablishedRelated(chain string) error
DeleteLine(chain string, lineNum string) error
InsertPreroutingRule(sourceIP, proto, sourcePort, targetIP, targetPort, comment string) error
InsertPreroutingRuleOnInterface(iface, proto, sourcePort, targetIP, targetPort, comment string) error
InsertPostroutingMasquerade(sourceCIDR, proto, sourcePort, comment string) error
InsertPostroutingMasqueradeForTarget(targetCIDR, proto, targetPort, comment string) error
InsertForwardAccept(chain, sourceIP, targetIP, proto, sourcePort, targetPort, comment string) error
DeleteForwardAccept(chain, comment string) error
InsertPreroutingRuleInContainer(pid int, sourceIP, proto, sourcePort, targetIP, targetPort, comment string) error
InsertPostroutingMasqueradeInContainer(pid int, sourceCIDR, proto, sourcePort, comment string) error
}
// Manager manages iptables rules via the iptables/iptables-legacy CLI
type Manager struct {
binary string // /usr/sbin/iptables or /usr/sbin/iptables-legacy
binary string
debug bool
}
// Ensure Manager implements IPTablesAPI
var _ IPTablesAPI = (*Manager)(nil)
// NewManager creates a new iptables manager, auto-detecting the binary
func NewManager(debug bool) *Manager {
m := &Manager{debug: debug}
@@ -83,17 +102,14 @@ func (m *Manager) EnsureIPForward() error {
}
// EnsureEstablishedRelated inserts an ESTABLISHED,RELATED accept rule at the top of a chain
// if it doesn't already exist
func (m *Manager) EnsureEstablishedRelated(chain string) error {
checkArgs := []string{"-w", "-n", "-L", chain}
cmd := exec.Command(m.binary, checkArgs...)
output, err := cmd.Output()
if err != nil {
// Chain may not exist, create it
return nil
}
// Only insert if ESTABLISHED,RELATED rule is not present
if !strings.Contains(string(output), "ESTABLISHED") || !strings.Contains(string(output), "RELATED") {
args := []string{"-w", "-I", chain, "-m", "state", "--state", "established,related", "-j", "ACCEPT"}
return m.run(args...)
@@ -114,7 +130,6 @@ func (m *Manager) DeleteLineInContainer(pid int, table, chain, lineNum string) e
}
// getLineNumbers returns line numbers matching certain criteria in a chain/table
// This implements the grep logic from the shell script: iptables -w --line-number -n -L $CHAIN | grep ...
func (m *Manager) getLineNumbers(chain, table string, grepPatterns ...string) []string {
args := []string{"-w", "--line-number", "-n", "-L", chain}
if table != "" {
@@ -150,7 +165,6 @@ func (m *Manager) getLineNumbers(chain, table string, grepPatterns ...string) []
// deleteMatchingLines deletes all lines in a chain matching the given patterns
func (m *Manager) deleteMatchingLines(chain, table string, grepPatterns ...string) error {
lines := m.getLineNumbers(chain, table, grepPatterns...)
// Reverse order (highest line first) so deletions don't shift line numbers
for i := len(lines) - 1; i >= 0; i-- {
if err := m.DeleteLine(chain, lines[i]); err != nil {
return err
@@ -161,7 +175,6 @@ func (m *Manager) deleteMatchingLines(chain, table string, grepPatterns ...strin
// deleteMatchingLinesInContainer deletes matching lines inside a container namespace
func (m *Manager) deleteMatchingLinesInContainer(pid int, table, chain string, grepPatterns ...string) error {
// For container namespaces, we use a different approach: list via nsenter + grep
iptPath := "/sbin/iptables-legacy"
if !strings.Contains(m.binary, "legacy") {
iptPath = "/sbin/iptables"
@@ -192,7 +205,6 @@ func (m *Manager) deleteMatchingLinesInContainer(pid int, table, chain string, g
}
}
// Delete in reverse order
for i := len(matchingLines) - 1; i >= 0; i-- {
if err := m.DeleteLineInContainer(pid, table, chain, matchingLines[i]); err != nil {
return err
@@ -203,13 +215,11 @@ func (m *Manager) deleteMatchingLinesInContainer(pid int, table, chain string, g
// InsertPreroutingRule inserts a DNAT PREROUTING rule on the host
func (m *Manager) InsertPreroutingRule(sourceIP, proto, sourcePort, targetIP, targetPort, comment string) error {
// First, delete existing matching rules
patterns := []string{"DNAT", sourcePort, targetIP, targetPort, comment}
if err := m.deleteMatchingLines("PREROUTING", "nat", patterns...); err != nil {
return fmt.Errorf("failed to delete old PREROUTING rules: %w", err)
}
// Insert the new rule
args := []string{
"-w", "-t", "nat", "-I", "PREROUTING",
"-d", sourceIP,
@@ -236,7 +246,6 @@ func (m *Manager) InsertPreroutingRuleOnInterface(iface, proto, sourcePort, targ
// InsertPostroutingMasquerade inserts a MASQUERADE POSTROUTING rule on the host
func (m *Manager) InsertPostroutingMasquerade(sourceCIDR, proto, sourcePort, comment string) error {
// Delete existing matching rules first
patterns := []string{"MASQUERADE", comment, sourceCIDR, sourcePort}
if err := m.deleteMatchingLines("POSTROUTING", "nat", patterns...); err != nil {
return fmt.Errorf("failed to delete old POSTROUTING rules: %w", err)
@@ -273,7 +282,6 @@ func (m *Manager) InsertPostroutingMasqueradeForTarget(targetCIDR, proto, target
// InsertForwardAccept inserts a FORWARD ACCEPT rule on the host
func (m *Manager) InsertForwardAccept(chain, sourceIP, targetIP, proto, sourcePort, targetPort, comment string) error {
// Build grep patterns to match existing rules
var grepPatterns []string
grepPatterns = append(grepPatterns, proto)
if sourceIP != "" {
@@ -289,12 +297,10 @@ func (m *Manager) InsertForwardAccept(chain, sourceIP, targetIP, proto, sourcePo
grepPatterns = append(grepPatterns, targetPort)
}
// Delete old matching rules
if err := m.deleteMatchingLines(chain, "", grepPatterns...); err != nil {
return fmt.Errorf("failed to delete old FORWARD rules: %w", err)
}
// Build iptables args
args := []string{"-w", "-I", chain, "-p", proto}
if sourceIP != "" {
args = append(args, "-s", sourceIP)
@@ -326,7 +332,6 @@ func (m *Manager) DeleteForwardAccept(chain, comment string) error {
// InsertPreroutingRuleInContainer inserts a DNAT PREROUTING rule inside a container namespace
func (m *Manager) InsertPreroutingRuleInContainer(pid int, sourceIP, proto, sourcePort, targetIP, targetPort, comment string) error {
// Delete existing first
patterns := []string{"DNAT", sourcePort, targetIP, targetPort, comment}
if err := m.deleteMatchingLinesInContainer(pid, "nat", "PREROUTING", patterns...); err != nil {
return fmt.Errorf("failed to delete old container PREROUTING rules: %w", err)