feat(iptables): make rule insertions idempotent and robust
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
Add lineExistsInContainer helper to check for existing rules before insertion, making InsertPreroutingRuleInContainer and InsertPostroutingMasqueradeInContainer idempotent. Change cleanup errors from fatal to warnings for better fault tolerance.
This commit is contained in:
@@ -397,13 +397,48 @@ func (m *Manager) DeleteForwardAccept(chain, comment string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// lineExistsInContainer checks if a line matching the patterns exists inside a container namespace
|
||||
func (m *Manager) lineExistsInContainer(pid int, table, chain string, patterns ...string) bool {
|
||||
iptPath := m.binary
|
||||
|
||||
nsenterArgs := []string{"-t", fmt.Sprintf("%d", pid), "-n", "--", iptPath, "-w", "-n", "-t", table, "-L", chain}
|
||||
cmd := exec.Command("nsenter", nsenterArgs...)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
logger.Debug("IPTABLES: lineExistsInContainer list failed for PID %d chain %s: %v", pid, chain, err)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(output), "\n") {
|
||||
matchesAll := true
|
||||
for _, pattern := range patterns {
|
||||
if !strings.Contains(line, pattern) {
|
||||
matchesAll = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if matchesAll {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InsertPreroutingRuleInContainer inserts a DNAT PREROUTING rule inside a container namespace
|
||||
func (m *Manager) InsertPreroutingRuleInContainer(pid int, sourceIP, proto, sourcePort, targetIP, targetPort, comment string) error {
|
||||
logger.Info("IPTABLES: inserting PREROUTING DNAT rule in container PID %d: src=%s proto=%s dport=%s -> %s:%s comment=%q",
|
||||
pid, sourceIP, proto, sourcePort, targetIP, targetPort, comment)
|
||||
|
||||
// Idempotent check: if the rule already exists, skip
|
||||
if m.lineExistsInContainer(pid, "nat", "PREROUTING", "DNAT", sourcePort, targetIP, comment) {
|
||||
logger.Debug("IPTABLES: PREROUTING DNAT rule already exists in container PID %d (pattern=%s -> %s), skipping", pid, sourcePort, targetIP)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clean up any stale/duplicate rules 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)
|
||||
logger.Warn("IPTABLES: failed to delete old container PREROUTING rules: %v (continuing)", err)
|
||||
}
|
||||
|
||||
args := []string{
|
||||
@@ -421,9 +456,17 @@ func (m *Manager) InsertPreroutingRuleInContainer(pid int, sourceIP, proto, sour
|
||||
func (m *Manager) InsertPostroutingMasqueradeInContainer(pid int, sourceCIDR, proto, sourcePort, comment string) error {
|
||||
logger.Info("IPTABLES: inserting POSTROUTING MASQUERADE rule in container PID %d: src=%s proto=%s sport=%s comment=%q",
|
||||
pid, sourceCIDR, proto, sourcePort, comment)
|
||||
|
||||
// Idempotent check: if the rule already exists, skip
|
||||
if m.lineExistsInContainer(pid, "nat", "POSTROUTING", "MASQUERADE", comment, sourceCIDR) {
|
||||
logger.Debug("IPTABLES: POSTROUTING MASQUERADE rule already exists in container PID %d (src=%s comment=%s), skipping", pid, sourceCIDR, comment)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clean up any stale/duplicate rules first
|
||||
patterns := []string{"MASQUERADE", comment, sourceCIDR, sourcePort}
|
||||
if err := m.deleteMatchingLinesInContainer(pid, "nat", "POSTROUTING", patterns...); err != nil {
|
||||
return fmt.Errorf("failed to delete old container POSTROUTING rules: %w", err)
|
||||
logger.Warn("IPTABLES: failed to delete old container POSTROUTING rules: %v (continuing)", err)
|
||||
}
|
||||
|
||||
args := []string{
|
||||
@@ -435,4 +478,4 @@ func (m *Manager) InsertPostroutingMasqueradeInContainer(pid int, sourceCIDR, pr
|
||||
"-j", "MASQUERADE",
|
||||
}
|
||||
return m.runInContainer(pid, "nat", args...)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user