From 51bade459d3795757a241075dcb9b8ea91ebe244 Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Tue, 5 Nov 2024 12:25:14 -0800 Subject: [PATCH 1/4] Bring back iptable scanning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the guestAgent in Rancher Desktop was picking up port mappings from iptables using the GetPorts function, as seen [here](https://github.com/lima-vm/lima/blob/3e01f2597cba83914e0dcd8ff3b87a409ca8e0ae/pkg/guestagent/iptables/iptables.go#L36). However, since the iptables scanning package was removed in commit [ca0009c1eb3e4f7f577b35ce001e52f4eb2e2940](https://github.com/rancher-sandbox/rancher-desktop/commit/ca0009c1eb3e4f7f577b35ce001e52f4eb2e2940) from the guestAgent, it no longer retrieves these port mappings. An example of such a rule in iptables is as follows: ``` CNI-HOSTPORT-SETMARK tcp -- 10.42.0.0/24 anywhere tcp dpt:12345 CNI-HOSTPORT-SETMARK tcp -- localhost anywhere tcp dpt:12345 DNAT tcp -- anywhere anywhere tcp dpt:12345 to:10.42.0.100:80 CNI-DN-6ef6bede9244615191a81 tcp -- anywhere anywhere /* dnat name: "cbr0" id: "0344293f74a8790f54a73bdd10f97bfaf98b3fb80b8e88c610f2da8ddf1c241a" */ multiport dports 12345 ``` The rules above are custom rules generated by the CNI plugin to manage traffic to the cbr0 network bridge, the default bridge network interface Kubernetes uses for pod-to-pod communication. These rules seem to be a part of Kubernetes’ networking setup. Given that these rules are essential for Kubernetes networking—ensuring traffic on specific hostports (e.g., port 12345) is correctly routed to a pod and handled by the CNI plugin—it seems important that they are not ignored. Signed-off-by: Nino Kodabande --- src/go/guestagent/go.mod | 5 +- src/go/guestagent/go.sum | 10 +- src/go/guestagent/main.go | 20 ++- src/go/guestagent/pkg/iptables/iptables.go | 144 +++++++++++++++++++++ 4 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 src/go/guestagent/pkg/iptables/iptables.go diff --git a/src/go/guestagent/go.mod b/src/go/guestagent/go.mod index 012ae889c70..eedc6ca344f 100644 --- a/src/go/guestagent/go.mod +++ b/src/go/guestagent/go.mod @@ -11,6 +11,7 @@ require ( github.com/containers/gvisor-tap-vsock v0.8.0 github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 + github.com/lima-vm/lima v1.0.0-alpha.0 github.com/stretchr/testify v1.9.0 golang.org/x/sync v0.8.0 golang.org/x/sys v0.26.0 @@ -56,7 +57,7 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.2 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect @@ -85,6 +86,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/sdk v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect @@ -97,7 +99,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.5.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect diff --git a/src/go/guestagent/go.sum b/src/go/guestagent/go.sum index 3a998c22331..961e7981ecd 100644 --- a/src/go/guestagent/go.sum +++ b/src/go/guestagent/go.sum @@ -127,8 +127,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= -github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -136,6 +136,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lima-vm/lima v1.0.0-alpha.0 h1:ytvyw0N3X7TLKH//D2rbM3IEnjYVcm0yH2cxlSIjE6M= +github.com/lima-vm/lima v1.0.0-alpha.0/go.mod h1:qonzb8JiUsTeuypJVmfzluU/wnN/tg+o2GeSubwzIiY= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -223,8 +225,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMey go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= diff --git a/src/go/guestagent/main.go b/src/go/guestagent/main.go index 0d3bf8b5cbe..425fa651e88 100644 --- a/src/go/guestagent/main.go +++ b/src/go/guestagent/main.go @@ -34,6 +34,7 @@ import ( "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/containerd" "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/docker" "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/forwarder" + "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/iptables" "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/kube" "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/tracker" "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/types" @@ -60,10 +61,11 @@ var ( ) const ( - socketInterval = 5 * time.Second - socketRetryTimeout = 2 * time.Minute - dockerSocketFile = "/var/run/docker.sock" - containerdSocketFile = "/run/k3s/containerd/containerd.sock" + iptablesUpdateInterval = 3 * time.Second + socketInterval = 5 * time.Second + socketRetryTimeout = 2 * time.Minute + dockerSocketFile = "/var/run/docker.sock" + containerdSocketFile = "/run/k3s/containerd/containerd.sock" ) func main() { @@ -184,13 +186,21 @@ func main() { k8sServiceListenerIP, portTracker) if err != nil { - return fmt.Errorf("error watching services: %w", err) + return fmt.Errorf("kubernetes service watcher failed: %w", err) } return nil }) } + group.Go(func() error { + err := iptables.ForwardPorts(ctx, portTracker, iptablesUpdateInterval) + if err != nil { + return fmt.Errorf("iptables port forwarding failed: %w", err) + } + return nil + }) + if err := group.Wait(); err != nil { log.Fatal(err) } diff --git a/src/go/guestagent/pkg/iptables/iptables.go b/src/go/guestagent/pkg/iptables/iptables.go new file mode 100644 index 00000000000..2e6de327388 --- /dev/null +++ b/src/go/guestagent/pkg/iptables/iptables.go @@ -0,0 +1,144 @@ +/* +Copyright © 2024 SUSE LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package iptables handles forwarding ports found in iptables DNAT +package iptables + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "net" + "strconv" + "strings" + "time" + + "github.com/Masterminds/log-go" + "github.com/docker/go-connections/nat" + "github.com/lima-vm/lima/pkg/guestagent/iptables" + "github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/tracker" +) + +// ForwardPorts forwards ports found in iptables DNAT. In some environments, +// like WSL, ports defined using the CNI portmap plugin happen through iptables. +// These ports are not sent to places like /proc/net/tcp and are not picked up +// as part of the normal forwarding system. This function detects those ports +// and binds them so that they are picked up. +// The argument is a time, in seconds, to wait between updating. +func ForwardPorts(ctx context.Context, tracker tracker.Tracker, updateInterval time.Duration) error { + var ports []iptables.Entry + for { + // Detect ports for forward + newPorts, err := iptables.GetPorts() + if err != nil { + // iptables exiting with an exit status of 4 means there + // is a resource problem. For example, something else is + // running iptables. In that case, we can skip trying it for + // this loop. You can find the exit code in the iptables + // source at https://git.netfilter.org/iptables/tree/include/xtables.h + if strings.Contains(err.Error(), "exit status 4") { + log.Debug("iptables exited with status 4 (resource error). Retrying...") + time.Sleep(updateInterval) + continue + } + return err + } + log.Debugf("iptable found ports %+v", newPorts) + // Diff from existing forwarded ports + added, removed := comparePorts(ports, newPorts) + ports = newPorts + // Remove old forwards + for _, p := range removed { + name := entryToString(p) + if err := tracker.Remove(generateID(name)); err != nil { + log.Warnf("failed to close listener %q: %w", name, err) + } + } + portMap := make(nat.PortMap) + // Add new forwards + for _, p := range added { + if p.TCP { + port := strconv.Itoa(p.Port) + portMapKey, err := nat.NewPort("tcp", port) + if err != nil { + log.Errorf("failed to create a corresponding key for the portMap: %s", err) + continue + } + portBinding := nat.PortBinding{ + // We can set the hostIP to INADDR_ANY the API Tracker will determine + // the admin installation and can adjust this to localhost accordingly + HostIP: "0.0.0.0", + HostPort: port, + } + if pb, ok := portMap[portMapKey]; ok { + portMap[portMapKey] = append(pb, portBinding) + } else { + portMap[portMapKey] = []nat.PortBinding{portBinding} + } + name := entryToString(p) + if err := tracker.Add(generateID(name), portMap); err != nil { + log.Errorf("failed to listen %q: %w", name, err) + } else { + log.Infof("opened listener for %q", name) + } + } + } + select { + case <-ctx.Done(): + return nil + default: // continue the loop + } + // Wait for next loop + time.Sleep(updateInterval) + } +} + +// comparePorts compares the old and new ports to find those added or removed. +// This function is mostly lifted from lima (github.com/lima-vm/lima) which is +// licensed under the Apache 2. +// +//nolint:nonamedreturns +func comparePorts(oldPorts, newPorts []iptables.Entry) (added, removed []iptables.Entry) { + mRaw := make(map[string]iptables.Entry, len(oldPorts)) + mStillExist := make(map[string]bool, len(oldPorts)) + for _, f := range oldPorts { + k := entryToString(f) + mRaw[k] = f + mStillExist[k] = false + } + for _, f := range newPorts { + k := entryToString(f) + mStillExist[k] = true + if _, ok := mRaw[k]; !ok { + added = append(added, f) + } + } + for k, stillExist := range mStillExist { + if !stillExist { + if x, ok := mRaw[k]; ok { + removed = append(removed, x) + } + } + } + return +} + +func entryToString(ip iptables.Entry) string { + return net.JoinHostPort(ip.IP.String(), strconv.Itoa(ip.Port)) +} + +func generateID(entry string) string { + hasher := sha256.New() + hasher.Write([]byte(entry)) + return hex.EncodeToString(hasher.Sum(nil)) +} From 8b1bab008f0d6297f93c38580ae17949f1d1c83a Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Thu, 7 Nov 2024 10:45:07 -0800 Subject: [PATCH 2/4] Replace Time.Sleep for a ticker in ForwardPorts Signed-off-by: Nino Kodabande --- src/go/guestagent/pkg/iptables/iptables.go | 115 +++++++++++---------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/src/go/guestagent/pkg/iptables/iptables.go b/src/go/guestagent/pkg/iptables/iptables.go index 2e6de327388..8188c6b235b 100644 --- a/src/go/guestagent/pkg/iptables/iptables.go +++ b/src/go/guestagent/pkg/iptables/iptables.go @@ -37,69 +37,74 @@ import ( // The argument is a time, in seconds, to wait between updating. func ForwardPorts(ctx context.Context, tracker tracker.Tracker, updateInterval time.Duration) error { var ports []iptables.Entry + + ticker := time.NewTicker(updateInterval) + defer ticker.Stop() + for { - // Detect ports for forward - newPorts, err := iptables.GetPorts() - if err != nil { - // iptables exiting with an exit status of 4 means there - // is a resource problem. For example, something else is - // running iptables. In that case, we can skip trying it for - // this loop. You can find the exit code in the iptables - // source at https://git.netfilter.org/iptables/tree/include/xtables.h - if strings.Contains(err.Error(), "exit status 4") { - log.Debug("iptables exited with status 4 (resource error). Retrying...") - time.Sleep(updateInterval) - continue - } - return err - } - log.Debugf("iptable found ports %+v", newPorts) - // Diff from existing forwarded ports - added, removed := comparePorts(ports, newPorts) - ports = newPorts - // Remove old forwards - for _, p := range removed { - name := entryToString(p) - if err := tracker.Remove(generateID(name)); err != nil { - log.Warnf("failed to close listener %q: %w", name, err) - } - } - portMap := make(nat.PortMap) - // Add new forwards - for _, p := range added { - if p.TCP { - port := strconv.Itoa(p.Port) - portMapKey, err := nat.NewPort("tcp", port) - if err != nil { - log.Errorf("failed to create a corresponding key for the portMap: %s", err) - continue - } - portBinding := nat.PortBinding{ - // We can set the hostIP to INADDR_ANY the API Tracker will determine - // the admin installation and can adjust this to localhost accordingly - HostIP: "0.0.0.0", - HostPort: port, - } - if pb, ok := portMap[portMapKey]; ok { - portMap[portMapKey] = append(pb, portBinding) - } else { - portMap[portMapKey] = []nat.PortBinding{portBinding} + select { + case <-ticker.C: + // Detect ports for forward + newPorts, err := iptables.GetPorts() + if err != nil { + // iptables exiting with an exit status of 4 means there + // is a resource problem. For example, something else is + // running iptables. In that case, we can skip trying it for + // this loop. You can find the exit code in the iptables + // source at https://git.netfilter.org/iptables/tree/include/xtables.h + if strings.Contains(err.Error(), "exit status 4") { + log.Debug("iptables exited with status 4 (resource error). Retrying...") + continue // Retry in the next iteration } + return err + } + log.Debugf("iptable found ports %+v", newPorts) + + // Diff from existing forwarded ports + added, removed := comparePorts(ports, newPorts) + ports = newPorts + + // Remove old forwards + for _, p := range removed { name := entryToString(p) - if err := tracker.Add(generateID(name), portMap); err != nil { - log.Errorf("failed to listen %q: %w", name, err) - } else { - log.Infof("opened listener for %q", name) + if err := tracker.Remove(generateID(name)); err != nil { + log.Warnf("failed to close listener %q: %w", name, err) + } + } + + portMap := make(nat.PortMap) + + // Add new forwards + for _, p := range added { + if p.TCP { + port := strconv.Itoa(p.Port) + portMapKey, err := nat.NewPort("tcp", port) + if err != nil { + log.Errorf("failed to create a corresponding key for the portMap: %s", err) + continue + } + portBinding := nat.PortBinding{ + // We can set the hostIP to INADDR_ANY the API Tracker will determine + // the admin installation and can adjust this to localhost accordingly + HostIP: "0.0.0.0", + HostPort: port, + } + if pb, ok := portMap[portMapKey]; ok { + portMap[portMapKey] = append(pb, portBinding) + } else { + portMap[portMapKey] = []nat.PortBinding{portBinding} + } + name := entryToString(p) + if err := tracker.Add(generateID(name), portMap); err != nil { + log.Errorf("failed to listen %q: %w", name, err) + } else { + log.Infof("opened listener for %q", name) + } } } - } - select { case <-ctx.Done(): return nil - default: // continue the loop } - // Wait for next loop - time.Sleep(updateInterval) } } From da1ac1c7a6f5cc984ba32941930b2a89b6a4814d Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Thu, 7 Nov 2024 10:46:02 -0800 Subject: [PATCH 3/4] Cleanup comparePorts Function Signed-off-by: Nino Kodabande --- src/go/guestagent/pkg/iptables/iptables.go | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/go/guestagent/pkg/iptables/iptables.go b/src/go/guestagent/pkg/iptables/iptables.go index 8188c6b235b..06755c21efe 100644 --- a/src/go/guestagent/pkg/iptables/iptables.go +++ b/src/go/guestagent/pkg/iptables/iptables.go @@ -114,24 +114,24 @@ func ForwardPorts(ctx context.Context, tracker tracker.Tracker, updateInterval t // //nolint:nonamedreturns func comparePorts(oldPorts, newPorts []iptables.Entry) (added, removed []iptables.Entry) { - mRaw := make(map[string]iptables.Entry, len(oldPorts)) - mStillExist := make(map[string]bool, len(oldPorts)) - for _, f := range oldPorts { - k := entryToString(f) - mRaw[k] = f - mStillExist[k] = false + oldPortMap := make(map[string]iptables.Entry, len(oldPorts)) + portExistMap := make(map[string]bool, len(oldPorts)) + for _, oldPort := range oldPorts { + key := entryToString(oldPort) + oldPortMap[key] = oldPort + portExistMap[key] = false } - for _, f := range newPorts { - k := entryToString(f) - mStillExist[k] = true - if _, ok := mRaw[k]; !ok { - added = append(added, f) + for _, newPort := range newPorts { + key := entryToString(newPort) + portExistMap[key] = true + if _, ok := oldPortMap[key]; !ok { + added = append(added, newPort) } } - for k, stillExist := range mStillExist { + for k, stillExist := range portExistMap { if !stillExist { - if x, ok := mRaw[k]; ok { - removed = append(removed, x) + if entry, ok := oldPortMap[k]; ok { + removed = append(removed, entry) } } } From 153f9f9cae45270e14948f4d241c25abee355370 Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Tue, 12 Nov 2024 10:16:11 -0800 Subject: [PATCH 4/4] Move the logic out of ticker case statement Signed-off-by: Nino Kodabande --- src/go/guestagent/pkg/iptables/iptables.go | 104 ++++++++++----------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/go/guestagent/pkg/iptables/iptables.go b/src/go/guestagent/pkg/iptables/iptables.go index 06755c21efe..f2a5f90e791 100644 --- a/src/go/guestagent/pkg/iptables/iptables.go +++ b/src/go/guestagent/pkg/iptables/iptables.go @@ -43,67 +43,67 @@ func ForwardPorts(ctx context.Context, tracker tracker.Tracker, updateInterval t for { select { + case <-ctx.Done(): + return nil case <-ticker.C: - // Detect ports for forward - newPorts, err := iptables.GetPorts() - if err != nil { - // iptables exiting with an exit status of 4 means there - // is a resource problem. For example, something else is - // running iptables. In that case, we can skip trying it for - // this loop. You can find the exit code in the iptables - // source at https://git.netfilter.org/iptables/tree/include/xtables.h - if strings.Contains(err.Error(), "exit status 4") { - log.Debug("iptables exited with status 4 (resource error). Retrying...") - continue // Retry in the next iteration - } - return err + } + // Detect ports for forward + newPorts, err := iptables.GetPorts() + if err != nil { + // iptables exiting with an exit status of 4 means there + // is a resource problem. For example, something else is + // running iptables. In that case, we can skip trying it for + // this loop. You can find the exit code in the iptables + // source at https://git.netfilter.org/iptables/tree/include/xtables.h + if strings.Contains(err.Error(), "exit status 4") { + log.Debug("iptables exited with status 4 (resource error). Retrying...") + continue // Retry in the next iteration } - log.Debugf("iptable found ports %+v", newPorts) + return err + } + log.Debugf("iptable found ports %+v", newPorts) - // Diff from existing forwarded ports - added, removed := comparePorts(ports, newPorts) - ports = newPorts + // Diff from existing forwarded ports + added, removed := comparePorts(ports, newPorts) + ports = newPorts - // Remove old forwards - for _, p := range removed { - name := entryToString(p) - if err := tracker.Remove(generateID(name)); err != nil { - log.Warnf("failed to close listener %q: %w", name, err) - } + // Remove old forwards + for _, p := range removed { + name := entryToString(p) + if err := tracker.Remove(generateID(name)); err != nil { + log.Warnf("failed to close listener %q: %w", name, err) } + } - portMap := make(nat.PortMap) + portMap := make(nat.PortMap) - // Add new forwards - for _, p := range added { - if p.TCP { - port := strconv.Itoa(p.Port) - portMapKey, err := nat.NewPort("tcp", port) - if err != nil { - log.Errorf("failed to create a corresponding key for the portMap: %s", err) - continue - } - portBinding := nat.PortBinding{ - // We can set the hostIP to INADDR_ANY the API Tracker will determine - // the admin installation and can adjust this to localhost accordingly - HostIP: "0.0.0.0", - HostPort: port, - } - if pb, ok := portMap[portMapKey]; ok { - portMap[portMapKey] = append(pb, portBinding) - } else { - portMap[portMapKey] = []nat.PortBinding{portBinding} - } - name := entryToString(p) - if err := tracker.Add(generateID(name), portMap); err != nil { - log.Errorf("failed to listen %q: %w", name, err) - } else { - log.Infof("opened listener for %q", name) - } + // Add new forwards + for _, p := range added { + if p.TCP { + port := strconv.Itoa(p.Port) + portMapKey, err := nat.NewPort("tcp", port) + if err != nil { + log.Errorf("failed to create a corresponding key for the portMap: %s", err) + continue + } + portBinding := nat.PortBinding{ + // We can set the hostIP to INADDR_ANY the API Tracker will determine + // the admin installation and can adjust this to localhost accordingly + HostIP: "0.0.0.0", + HostPort: port, + } + if pb, ok := portMap[portMapKey]; ok { + portMap[portMapKey] = append(pb, portBinding) + } else { + portMap[portMapKey] = []nat.PortBinding{portBinding} + } + name := entryToString(p) + if err := tracker.Add(generateID(name), portMap); err != nil { + log.Errorf("failed to listen %q: %w", name, err) + } else { + log.Infof("opened listener for %q", name) } } - case <-ctx.Done(): - return nil } } }