fix(network-go): handle reconnection gracefully and fix DNAT rule issues
continuous-integration/drone/push Build is passing

- Ignore "endpoint already exists" error in ConnectContainer on re-reconciliation
- Improve iptables comment generation to avoid trailing dashes
- Enhance DNAT rule logic: try multiple selectors and fall back to host rules
- Add missing "-t nat" flag in InsertPreroutingRuleOnInterface
This commit is contained in:
gyurix
2026-06-15 16:12:08 +02:00
parent 6c19e22deb
commit 2d6e22b9e6
4 changed files with 655 additions and 18 deletions
+4
View File
@@ -123,6 +123,10 @@ func (c *Client) ConnectContainer(ctx context.Context, containerName, networkNam
err := c.cli.NetworkConnect(ctx, networkName, containerName, endpointSettings)
if err != nil {
// "endpoint with name ... already exists" is expected on re-reconciliation
if strings.Contains(err.Error(), "already exists") {
return nil
}
return fmt.Errorf("failed to connect container %s to network %s: %w", containerName, networkName, err)
}
return nil
+37 -17
View File
@@ -110,9 +110,17 @@ func (o *Orchestrator) reconcilePolicies(ctx context.Context, cfg *config.Networ
port := strconv.Itoa(policy.Port)
// Build comment for iptables (matches shell script's NAME-COMMENT pattern)
comment := policy.ServiceName
// Use Name if present, otherwise ServiceName, to avoid trailing dashes
comment := ""
if policy.Name != "" {
comment = policy.Name + "-" + policy.ServiceName
comment = policy.Name
}
if policy.ServiceName != "" {
if comment != "" {
comment += "-" + policy.ServiceName
} else {
comment = policy.ServiceName
}
}
// CASE 1: Rule with "from" field — this is a FORWARD ACCEPT rule
@@ -158,7 +166,6 @@ func (o *Orchestrator) applyForwardRule(ctx context.Context, cfg *config.Network
}
func (o *Orchestrator) applyNATRule(ctx context.Context, cfg *config.NetworksConfig, policy config.PolicyConfig, proto, port, comment string) {
selector := policy.Selector
to := policy.To
// Resolve "to" as target IP
@@ -170,22 +177,35 @@ func (o *Orchestrator) applyNATRule(ctx context.Context, cfg *config.NetworksCon
}
if policy.Nat == "dnat" {
// Get the container PID for nsenter
pid, err := o.dockerClient.GetContainerPID(ctx, selector)
if err != nil {
log.Printf("FIREWALL: WARNING cannot get PID for container %s: %v, trying host rules", selector, err)
// Fall back to host-level PREROUTING
if policy.Iface != "" {
if err := o.iptablesMgr.InsertPreroutingRuleOnInterface(policy.Iface, proto, port, targetIP, port, comment); err != nil {
log.Printf("FIREWALL: ERROR inserting interface PREROUTING rule: %v", err)
}
}
return
// Determine the best container selector from the policy: try Selector, then ContainerName, then Name
selector := policy.Selector
if selector == "" {
selector = policy.ContainerName
}
if selector == "" {
selector = policy.Name
}
// Insert DNAT PREROUTING inside container namespace
if err := o.iptablesMgr.InsertPreroutingRuleInContainer(pid, "0.0.0.0/0", proto, port, targetIP, port, comment); err != nil {
log.Printf("FIREWALL: ERROR inserting container PREROUTING rule: %v", err)
// Try to insert rules inside the container namespace via nsenter
usedContainer := false
if selector != "" {
pid, err := o.dockerClient.GetContainerPID(ctx, selector)
if err == nil {
if err := o.iptablesMgr.InsertPreroutingRuleInContainer(pid, "0.0.0.0/0", proto, port, targetIP, port, comment); err != nil {
log.Printf("FIREWALL: ERROR inserting container PREROUTING rule: %v", err)
} else {
usedContainer = true
}
} else {
log.Printf("FIREWALL: WARNING cannot get PID for container %s: %v, trying host rules", selector, err)
}
}
// Fall back to host-level PREROUTING if container not used
if !usedContainer && policy.Iface != "" {
if err := o.iptablesMgr.InsertPreroutingRuleOnInterface(policy.Iface, proto, port, targetIP, port, comment); err != nil {
log.Printf("FIREWALL: ERROR inserting interface PREROUTING rule: %v", err)
}
}
}
}
+1 -1
View File
@@ -236,7 +236,7 @@ func (m *Manager) InsertPreroutingRule(sourceIP, proto, sourcePort, targetIP, ta
// InsertPreroutingRuleOnInterface inserts a DNAT PREROUTING rule on a specific interface
func (m *Manager) InsertPreroutingRuleOnInterface(iface, proto, sourcePort, targetIP, targetPort, comment string) error {
args := []string{
"-w", "-I", "PREROUTING",
"-w", "-t", "nat", "-I", "PREROUTING",
"-i", iface,
"-p", proto,
"--dport", sourcePort,