Skip to content

Commit

Permalink
Merge pull request #7723 from rancher-sandbox/bring-back-iptable-scan…
Browse files Browse the repository at this point in the history
…ning

Bring back iptable scanning
  • Loading branch information
jandubois authored Nov 12, 2024
2 parents 12b3b87 + 153f9f9 commit eca49fb
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 11 deletions.
5 changes: 3 additions & 2 deletions src/go/guestagent/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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.9.0
golang.org/x/sys v0.27.0
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.31.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
Expand All @@ -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
Expand Down
10 changes: 6 additions & 4 deletions src/go/guestagent/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,17 @@ 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=
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=
Expand Down Expand Up @@ -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=
Expand Down
20 changes: 15 additions & 5 deletions src/go/guestagent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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() {
Expand Down Expand Up @@ -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)
}
Expand Down
149 changes: 149 additions & 0 deletions src/go/guestagent/pkg/iptables/iptables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
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

ticker := time.NewTicker(updateInterval)
defer ticker.Stop()

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
}
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)
}
}
}
}
}

// 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) {
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 _, newPort := range newPorts {
key := entryToString(newPort)
portExistMap[key] = true
if _, ok := oldPortMap[key]; !ok {
added = append(added, newPort)
}
}
for k, stillExist := range portExistMap {
if !stillExist {
if entry, ok := oldPortMap[k]; ok {
removed = append(removed, entry)
}
}
}
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))
}

0 comments on commit eca49fb

Please sign in to comment.