diff --git a/cmd/lima-guestagent/daemon_linux.go b/cmd/lima-guestagent/daemon_linux.go index 87e4a4feaa8..8ac0a304f0e 100644 --- a/cmd/lima-guestagent/daemon_linux.go +++ b/cmd/lima-guestagent/daemon_linux.go @@ -66,7 +66,7 @@ func daemonAction(cmd *cobra.Command, _ []string) error { tickerInst = ticker.NewCompoundTicker(simpleTicker, ebpfTicker) } - agent, err := guestagent.New(ctx, tickerInst, tick*20) + agent, err := guestagent.New(ctx, tickerInst) if err != nil { return err } diff --git a/go.mod b/go.mod index ebbb1772812..bd1b8d7a330 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/digitalocean/go-qemu v0.0.0-20221209210016-f035778c97f7 github.com/diskfs/go-diskfs v1.7.0 // gomodjail:unconfined github.com/docker/go-units v0.5.0 - github.com/elastic/go-libaudit/v2 v2.6.2 github.com/foxcpp/go-mockdns v1.1.0 github.com/goccy/go-yaml v1.18.0 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index 7673449fcc3..58e3c9ad45e 100644 --- a/go.sum +++ b/go.sum @@ -67,10 +67,6 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/elastic/go-libaudit/v2 v2.6.2 h1:1PM6wVBTJHJQYsKl8jfA9/Aw9pFty5uUezPiUfKtOI4= -github.com/elastic/go-libaudit/v2 v2.6.2/go.mod h1:8205nkf2oSrXFlO4H5j8/cyVMoSF3Y7jt+FjgS4ubQU= -github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4= -github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU= github.com/elliotchance/orderedmap v1.8.0 h1:TrOREecvh3JbS+NCgwposXG5ZTFHtEsQiCGOhPElnMw= github.com/elliotchance/orderedmap v1.8.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY= diff --git a/pkg/guestagent/guestagent_linux.go b/pkg/guestagent/guestagent_linux.go index d9181def2a5..1ad20152967 100644 --- a/pkg/guestagent/guestagent_linux.go +++ b/pkg/guestagent/guestagent_linux.go @@ -5,91 +5,30 @@ package guestagent import ( "context" - "errors" "os" "reflect" - "sync" - "syscall" "time" - "github.com/elastic/go-libaudit/v2" - "github.com/elastic/go-libaudit/v2/auparse" "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/timestamppb" "github.com/lima-vm/lima/v2/pkg/guestagent/api" - "github.com/lima-vm/lima/v2/pkg/guestagent/iptables" "github.com/lima-vm/lima/v2/pkg/guestagent/kubernetesservice" "github.com/lima-vm/lima/v2/pkg/guestagent/sockets" "github.com/lima-vm/lima/v2/pkg/guestagent/ticker" "github.com/lima-vm/lima/v2/pkg/guestagent/timesync" ) -func New(ctx context.Context, ticker ticker.Ticker, iptablesIdle time.Duration) (Agent, error) { +func New(ctx context.Context, ticker ticker.Ticker) (Agent, error) { a := &agent{ ticker: ticker, kubernetesServiceWatcher: kubernetesservice.NewServiceWatcher(), } - auditClient, err := libaudit.NewMulticastAuditClient(nil) - if err != nil { - // syscall.EPROTONOSUPPORT or syscall.EAFNOSUPPORT is returned when calling attempting to connect to NETLINK_AUDIT - // on a kernel built without auditing support. - // https://github.com/elastic/go-libaudit/blob/ec298e53a6841a1f7715abbc7122635622f349bd/audit.go#L112-L115 - if !errors.Is(err, syscall.EPROTONOSUPPORT) && !errors.Is(err, syscall.EAFNOSUPPORT) { - return nil, err - } - logrus.Infof("Auditing is not available: %s", err) - return startGuestAgentRoutines(ctx, a, false), nil - } - - auditStatus, err := auditClient.GetStatus() - if err != nil { - // syscall.EPERM is returned when using audit from a non-initial namespace - // https://github.com/torvalds/linux/blob/633b47cb009d09dc8f4ba9cdb3a0ca138809c7c7/kernel/audit.c#L1054-L1057 - if !errors.Is(err, syscall.EPERM) { - return nil, err - } - logrus.Infof("Auditing is not permitted: %s", err) - return startGuestAgentRoutines(ctx, a, false), nil - } - - if auditStatus.Enabled == 0 { - logrus.Info("Enabling auditing") - if err = auditClient.SetEnabled(true, libaudit.WaitForReply); err != nil { - return nil, err - } - auditStatus, err := auditClient.GetStatus() - if err != nil { - return nil, err - } - if auditStatus.Enabled == 0 { - if err = auditClient.SetEnabled(true, libaudit.WaitForReply); err != nil { - return nil, err - } - } - } - - a.worthCheckingIPTables = true // allow initial iptables scan - go a.setWorthCheckingIPTablesRoutine(auditClient, iptablesIdle) - - logrus.Infof("Auditing enabled (%d)", auditStatus.Enabled) - return startGuestAgentRoutines(ctx, a, true), nil -} - -// startGuestAgentRoutines sets worthCheckingIPTables to true if auditing is not supported, -// instead of using setWorthCheckingIPTablesRoutine to dynamically set the value. -// -// Auditing is not supported in a kernels and is not currently supported outside of the initial namespace, so does not work -// from inside a container or WSL2 instance, for example. -func startGuestAgentRoutines(ctx context.Context, a *agent, supportsAuditing bool) *agent { - if !supportsAuditing { - a.worthCheckingIPTables = true - } go a.kubernetesServiceWatcher.Start(ctx) go a.fixSystemTimeSkew() - return a + return a, nil } type agent struct { @@ -98,51 +37,9 @@ type agent struct { // reload /proc/net/tcp. ticker ticker.Ticker - worthCheckingIPTables bool - worthCheckingIPTablesMu sync.RWMutex - latestIPTables []iptables.Entry - latestIPTablesMu sync.RWMutex kubernetesServiceWatcher *kubernetesservice.ServiceWatcher } -// setWorthCheckingIPTablesRoutine sets worthCheckingIPTables to be true -// when received NETFILTER_CFG audit message. -// -// setWorthCheckingIPTablesRoutine sets worthCheckingIPTables to be false -// when no NETFILTER_CFG audit message was received for the iptablesIdle time. -func (a *agent) setWorthCheckingIPTablesRoutine(auditClient *libaudit.AuditClient, iptablesIdle time.Duration) { - logrus.Info("setWorthCheckingIPTablesRoutine(): monitoring netfilter audit events") - // Initialize to now so the first sleeper loop does not immediately mark it false. - latestTrue := time.Now() - go func() { - for { - time.Sleep(iptablesIdle) - a.worthCheckingIPTablesMu.Lock() - // time is monotonic, see https://pkg.go.dev/time#hdr-Monotonic_Clocks - elapsedSinceLastTrue := time.Since(latestTrue) - if elapsedSinceLastTrue >= iptablesIdle { - logrus.Debug("setWorthCheckingIPTablesRoutine(): setting to false") - a.worthCheckingIPTables = false - } - a.worthCheckingIPTablesMu.Unlock() - } - }() - for { - msg, err := auditClient.Receive(false) - if err != nil { - logrus.Error(err) - continue - } - if msg.Type == auparse.AUDIT_NETFILTER_CFG { - a.worthCheckingIPTablesMu.Lock() - logrus.Debug("setWorthCheckingIPTablesRoutine(): setting to true") - a.worthCheckingIPTables = true - latestTrue = time.Now() - a.worthCheckingIPTablesMu.Unlock() - } - } -} - type eventState struct { ports []*api.IPPort } @@ -220,7 +117,7 @@ func (a *agent) Events(ctx context.Context, ch chan *api.Event) { } } -func (a *agent) LocalPorts(ctx context.Context) ([]*api.IPPort, error) { +func (a *agent) LocalPorts(_ context.Context) ([]*api.IPPort, error) { var res []*api.IPPort socketsList, err := sockets.List() if err != nil { @@ -252,47 +149,6 @@ func (a *agent) LocalPorts(ctx context.Context) ([]*api.IPPort, error) { } } - a.worthCheckingIPTablesMu.RLock() - worthCheckingIPTables := a.worthCheckingIPTables - a.worthCheckingIPTablesMu.RUnlock() - logrus.Debugf("LocalPorts(): worthCheckingIPTables=%v", worthCheckingIPTables) - - var ipts []iptables.Entry - if a.worthCheckingIPTables { - ipts, err = iptables.GetPorts(ctx) - if err != nil { - return res, err - } - a.latestIPTablesMu.Lock() - a.latestIPTables = ipts - a.latestIPTablesMu.Unlock() - } else { - a.latestIPTablesMu.RLock() - ipts = a.latestIPTables - a.latestIPTablesMu.RUnlock() - } - - for _, ipt := range ipts { - port := int32(ipt.AddrPort.Port()) - // Make sure the port isn't already listed from sockets - found := false - for _, re := range res { - if re.Port == port { - found = true - } - } - if !found { - if ipt.TCP { - res = append(res, - &api.IPPort{ - Ip: ipt.AddrPort.Addr().String(), - Port: port, - Protocol: "tcp", - }) - } - } - } - kubernetesEntries := a.kubernetesServiceWatcher.GetPorts() for _, entry := range kubernetesEntries { found := false diff --git a/pkg/guestagent/iptables/iptables.go b/pkg/guestagent/iptables/iptables.go deleted file mode 100644 index fd2a8143c9f..00000000000 --- a/pkg/guestagent/iptables/iptables.go +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Lima Authors -// SPDX-License-Identifier: Apache-2.0 - -package iptables - -import ( - "bytes" - "context" - "errors" - "net" - "net/netip" - "os/exec" - "regexp" - "strconv" - "strings" - "time" -) - -type Entry struct { - TCP bool - AddrPort netip.AddrPort -} - -// This regex can detect a line in the iptables added by portmap to do the -// forwarding. The following two are examples of lines (notice that one has the -// destination IP and the other does not): -// -// -A CNI-DN-2e2f8d5b91929ef9fc152 -d 127.0.0.1/32 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 10.4.0.7:80 -// -A CNI-DN-04579c7bb67f4c3f6cca0 -p tcp -m tcp --dport 8082 -j DNAT --to-destination 10.4.0.10:80 -// -// The -A on the front is to amend the rule that was already created. portmap -// ensures the rule is created before creating this line so it is always -A. -// CNI-DN- is the prefix used for rule for an individual container. -// -d is followed by the IP address. The regular expression looks for a valid -// ipv4 IP address. We need to detect this IP. -// --dport is the destination port. We need to detect this port -// -j DNAT this tells us it's the line doing the port forwarding. -var findPortRegex = regexp.MustCompile(`-A\s+CNI-DN-\w*\s+(?:-d ((?:\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))?(?:/32\s+)?-p (tcp)?.*--dport (\d+) -j DNAT`) - -func GetPorts(ctx context.Context) ([]Entry, error) { - // TODO: add support for ipv6 - - // Detect the location of iptables. If it is not installed skip the lookup - // and return no results. The lookup is performed on each run so that the - // agent does not need to be started to detect if iptables was installed - // after the agent is already running. - pth, err := exec.LookPath("iptables") - if err != nil { - if errors.Is(err, exec.ErrNotFound) { - return nil, nil - } - - return nil, err - } - - res, err := listNATRules(pth) - if err != nil { - return nil, err - } - - pts, err := parsePortsFromRules(res) - if err != nil { - return nil, err - } - - return checkPortsOpen(ctx, pts) -} - -func parsePortsFromRules(rules []string) ([]Entry, error) { - var entries []Entry - for _, rule := range rules { - found := findPortRegex.FindStringSubmatch(rule) - if len(found) != 4 { - continue - } - port16, err := strconv.ParseUint(found[3], 10, 16) - if err != nil { - return nil, err - } - port := uint16(port16) - - isTCP := found[2] == "tcp" - - // When no IP is present the rule applies to all interfaces. - addr := netip.IPv4Unspecified() - if s := found[1]; s != "" { - addr, err = netip.ParseAddr(s) - if err != nil { - return nil, err - } - } - ent := Entry{ - AddrPort: netip.AddrPortFrom(addr, port), - TCP: isTCP, - } - entries = append(entries, ent) - } - - return entries, nil -} - -// listNATRules performs the lookup with iptables and returns the raw rules -// Note, this does not use github.com/coreos/go-iptables (a transitive dependency -// of lima) because that package would require multiple calls to iptables. This -// function does everything in a single call. -func listNATRules(pth string) ([]string, error) { - args := []string{pth, "-t", "nat", "-S"} - - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd := exec.Cmd{ - Path: pth, - Args: args, - Stdout: &stdout, - Stderr: &stderr, - } - if err := cmd.Run(); err != nil { - return nil, err - } - - // turn the output into a rule per line. - rules := strings.Split(stdout.String(), "\n") - if len(rules) > 0 && rules[len(rules)-1] == "" { - rules = rules[:len(rules)-1] - } - - return rules, nil -} - -func checkPortsOpen(ctx context.Context, pts []Entry) ([]Entry, error) { - var entries []Entry - for _, pt := range pts { - if pt.TCP { - var dialer net.Dialer - dialer.Timeout = time.Second - conn, err := dialer.DialContext(ctx, "tcp", pt.AddrPort.String()) - if err == nil && conn != nil { - conn.Close() - entries = append(entries, pt) - } - } else { - entries = append(entries, pt) - } - } - - return entries, nil -} diff --git a/pkg/guestagent/iptables/iptables_test.go b/pkg/guestagent/iptables/iptables_test.go deleted file mode 100644 index a5e89680d85..00000000000 --- a/pkg/guestagent/iptables/iptables_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Lima Authors -// SPDX-License-Identifier: Apache-2.0 - -package iptables - -import ( - "net/netip" - "strings" - "testing" - - "gotest.tools/v3/assert" -) - -// data is from a run of `iptables -t nat -S` with two containers running (started -// with sudo nerdctl) and have exposed ports 8081 and 8082. -const data = `# Warning: iptables-legacy tables present, use iptables-legacy to see them --P PREROUTING ACCEPT --P INPUT ACCEPT --P OUTPUT ACCEPT --P POSTROUTING ACCEPT --N CNI-04579c7bb67f4c3f6cca0185 --N CNI-28e04aad9bf52e38b43f8700 --N CNI-2d72aeb202429907277c53c5 --N CNI-2e2f8d5b91929ef9fc152e75 --N CNI-3cbb832b23c724bdddedd7e4 --N CNI-5033e3bad9f1265a2b04037f --N CNI-DN-04579c7bb67f4c3f6cca0 --N CNI-DN-2d72aeb202429907277c5 --N CNI-DN-2e2f8d5b91929ef9fc152 --N CNI-HOSTPORT-DNAT --N CNI-HOSTPORT-MASQ --N CNI-HOSTPORT-SETMARK --N CNI-cb0db077a14ecd8d4a843636 --N CNI-f1ca917e7b9939c7d8457d68 --A PREROUTING -m addrtype --dst-type LOCAL -j CNI-HOSTPORT-DNAT --A OUTPUT -m addrtype --dst-type LOCAL -j CNI-HOSTPORT-DNAT --A POSTROUTING -m comment --comment "CNI portfwd requiring masquerade" -j CNI-HOSTPORT-MASQ --A POSTROUTING -s 10.4.0.3/32 -m comment --comment "name: \"bridge\" id: \"default-44540a2b2cc6c1154d2a21aec473d6987ec4d6bc339e89ee295a6db433ad623e\"" -j CNI-5033e3bad9f1265a2b04037f --A POSTROUTING -s 10.4.0.4/32 -m comment --comment "name: \"bridge\" id: \"default-cf12b94944785a4c8937e237a0a277d893cbadebd50409ed5d4b8ca3f90fedf3\"" -j CNI-28e04aad9bf52e38b43f8700 --A POSTROUTING -s 10.4.0.5/32 -m comment --comment "name: \"bridge\" id: \"default-e9d499901490e6a66277688ba8d71cca35a6d1ca6261bc5a7e11e45e80aa3ea3\"" -j CNI-3cbb832b23c724bdddedd7e4 --A POSTROUTING -s 10.4.0.6/32 -m comment --comment "name: \"bridge\" id: \"default-a65e32cc21f9da99b4aa826914873e343f8f09f910657450be551aa24d676e51\"" -j CNI-f1ca917e7b9939c7d8457d68 --A POSTROUTING -s 10.4.0.7/32 -m comment --comment "name: \"bridge\" id: \"default-c93e2a3a2264f98647f0d33dc80d88de81c0710bf30ea822e2ed19213f9c53b5\"" -j CNI-2e2f8d5b91929ef9fc152e75 --A POSTROUTING -s 10.4.0.8/32 -m comment --comment "name: \"bridge\" id: \"default-a8df9868a5f7ee2468118331dd6185e5655f7ff8e77f067408b7ff40e9457860\"" -j CNI-cb0db077a14ecd8d4a843636 --A POSTROUTING -s 10.4.0.9/32 -m comment --comment "name: \"bridge\" id: \"default-393bd750d06186633a02b44487765ce038b7df434bfb16027ca1903bf5f3dc31\"" -j CNI-2d72aeb202429907277c53c5 --A POSTROUTING -s 10.4.0.10/32 -m comment --comment "name: \"bridge\" id: \"default-3d263c6a1c710edc1362764464c073ca834ec9adc0766411772f2b7a3dd1de0f\"" -j CNI-04579c7bb67f4c3f6cca0185 --A CNI-04579c7bb67f4c3f6cca0185 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-3d263c6a1c710edc1362764464c073ca834ec9adc0766411772f2b7a3dd1de0f\"" -j ACCEPT --A CNI-04579c7bb67f4c3f6cca0185 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-3d263c6a1c710edc1362764464c073ca834ec9adc0766411772f2b7a3dd1de0f\"" -j MASQUERADE --A CNI-28e04aad9bf52e38b43f8700 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-cf12b94944785a4c8937e237a0a277d893cbadebd50409ed5d4b8ca3f90fedf3\"" -j ACCEPT --A CNI-28e04aad9bf52e38b43f8700 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-cf12b94944785a4c8937e237a0a277d893cbadebd50409ed5d4b8ca3f90fedf3\"" -j MASQUERADE --A CNI-2d72aeb202429907277c53c5 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-393bd750d06186633a02b44487765ce038b7df434bfb16027ca1903bf5f3dc31\"" -j ACCEPT --A CNI-2d72aeb202429907277c53c5 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-393bd750d06186633a02b44487765ce038b7df434bfb16027ca1903bf5f3dc31\"" -j MASQUERADE --A CNI-2e2f8d5b91929ef9fc152e75 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-c93e2a3a2264f98647f0d33dc80d88de81c0710bf30ea822e2ed19213f9c53b5\"" -j ACCEPT --A CNI-2e2f8d5b91929ef9fc152e75 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-c93e2a3a2264f98647f0d33dc80d88de81c0710bf30ea822e2ed19213f9c53b5\"" -j MASQUERADE --A CNI-3cbb832b23c724bdddedd7e4 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-e9d499901490e6a66277688ba8d71cca35a6d1ca6261bc5a7e11e45e80aa3ea3\"" -j ACCEPT --A CNI-3cbb832b23c724bdddedd7e4 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-e9d499901490e6a66277688ba8d71cca35a6d1ca6261bc5a7e11e45e80aa3ea3\"" -j MASQUERADE --A CNI-5033e3bad9f1265a2b04037f -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-44540a2b2cc6c1154d2a21aec473d6987ec4d6bc339e89ee295a6db433ad623e\"" -j ACCEPT --A CNI-5033e3bad9f1265a2b04037f ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-44540a2b2cc6c1154d2a21aec473d6987ec4d6bc339e89ee295a6db433ad623e\"" -j MASQUERADE --A CNI-DN-04579c7bb67f4c3f6cca0 -s 10.4.0.0/24 -p tcp -m tcp --dport 8082 -j CNI-HOSTPORT-SETMARK --A CNI-DN-04579c7bb67f4c3f6cca0 -s 127.0.0.1/32 -p tcp -m tcp --dport 8082 -j CNI-HOSTPORT-SETMARK --A CNI-DN-04579c7bb67f4c3f6cca0 -p tcp -m tcp --dport 8082 -j DNAT --to-destination 10.4.0.10:80 --A CNI-DN-2e2f8d5b91929ef9fc152 -s 10.4.0.0/24 -d 127.0.0.1/32 -p tcp -m tcp --dport 8081 -j CNI-HOSTPORT-SETMARK --A CNI-DN-2e2f8d5b91929ef9fc152 -s 127.0.0.1/32 -d 127.0.0.1/32 -p tcp -m tcp --dport 8081 -j CNI-HOSTPORT-SETMARK --A CNI-DN-2e2f8d5b91929ef9fc152 -d 127.0.0.1/32 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 10.4.0.7:80 --A CNI-HOSTPORT-DNAT -p tcp -m comment --comment "dnat name: \"bridge\" id: \"default-c93e2a3a2264f98647f0d33dc80d88de81c0710bf30ea822e2ed19213f9c53b5\"" -m multiport --dports 8081 -j CNI-DN-2e2f8d5b91929ef9fc152 --A CNI-HOSTPORT-DNAT -p tcp -m comment --comment "dnat name: \"bridge\" id: \"default-393bd750d06186633a02b44487765ce038b7df434bfb16027ca1903bf5f3dc31\"" -m multiport --dports 8082 -j CNI-DN-2d72aeb202429907277c5 --A CNI-HOSTPORT-DNAT -p tcp -m comment --comment "dnat name: \"bridge\" id: \"default-3d263c6a1c710edc1362764464c073ca834ec9adc0766411772f2b7a3dd1de0f\"" -m multiport --dports 8082 -j CNI-DN-04579c7bb67f4c3f6cca0 --A CNI-HOSTPORT-MASQ -m mark --mark 0x2000/0x2000 -j MASQUERADE --A CNI-HOSTPORT-SETMARK -m comment --comment "CNI portfwd masquerade mark" -j MARK --set-xmark 0x2000/0x2000 --A CNI-cb0db077a14ecd8d4a843636 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-a8df9868a5f7ee2468118331dd6185e5655f7ff8e77f067408b7ff40e9457860\"" -j ACCEPT --A CNI-cb0db077a14ecd8d4a843636 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-a8df9868a5f7ee2468118331dd6185e5655f7ff8e77f067408b7ff40e9457860\"" -j MASQUERADE --A CNI-f1ca917e7b9939c7d8457d68 -d 10.4.0.0/24 -m comment --comment "name: \"bridge\" id: \"default-a65e32cc21f9da99b4aa826914873e343f8f09f910657450be551aa24d676e51\"" -j ACCEPT --A CNI-f1ca917e7b9939c7d8457d68 ! -d 224.0.0.0/4 -m comment --comment "name: \"bridge\" id: \"default-a65e32cc21f9da99b4aa826914873e343f8f09f910657450be551aa24d676e51\"" -j MASQUERADE -` - -func TestParsePortsFromRules(t *testing.T) { - // Turn the string into individual lines - rules := strings.Split(data, "\n") - if len(rules) > 0 && rules[len(rules)-1] == "" { - rules = rules[:len(rules)-1] - } - - res, err := parsePortsFromRules(rules) - assert.NilError(t, err, "parsing iptables ports failed") - - l := len(res) - assert.Equal(t, l, 2, "unexpected number of ports parsed from iptables") - - assert.Equal(t, res[0], Entry{AddrPort: netip.MustParseAddrPort("0.0.0.0:8082"), TCP: true}) - assert.Equal(t, res[1], Entry{AddrPort: netip.MustParseAddrPort("127.0.0.1:8081"), TCP: true}) -} diff --git a/pkg/limayaml/containerd.yaml b/pkg/limayaml/containerd.yaml index 782a97ad3e3..a7e9462c730 100644 --- a/pkg/limayaml/containerd.yaml +++ b/pkg/limayaml/containerd.yaml @@ -1,9 +1,11 @@ +# nerdctl version must be >= 2.1.6 since Lima v2.0.0. +# Port forwarding in rootful mode no longer works with older versions of nerdctl. archives: -- location: https://github.com/containerd/nerdctl/releases/download/v2.1.5/nerdctl-full-2.1.5-linux-amd64.tar.gz +- location: https://github.com/containerd/nerdctl/releases/download/v2.1.6/nerdctl-full-2.1.6-linux-amd64.tar.gz arch: x86_64 - digest: sha256:f2a96cf2a9612cc0945da04beda29df7b1d75173cb7ca96275b3352f04a2e416 -- location: https://github.com/containerd/nerdctl/releases/download/v2.1.5/nerdctl-full-2.1.5-linux-arm64.tar.gz + digest: sha256:c30545867913696210991fe055761ae2e2d36344da2a351bcd1073fb5c71e232 +- location: https://github.com/containerd/nerdctl/releases/download/v2.1.6/nerdctl-full-2.1.6-linux-arm64.tar.gz arch: aarch64 - digest: sha256:f1c9bbc4c2116c997030f6b003c73cf505bc1156b6e668227079d190b7256fe3 + digest: sha256:2f98346ed3dcaf47cfd6461a5685e582284329a1ece1d952ade881b9fe7491e8 # No arm-v7 # No riscv64 diff --git a/templates/alpine-iso.yaml b/templates/alpine-iso.yaml index 0e86c1f1996..eb2a1ecaf11 100644 --- a/templates/alpine-iso.yaml +++ b/templates/alpine-iso.yaml @@ -6,6 +6,13 @@ base: # The built-in containerd installer does not support Alpine currently. # Use a provisioning script to install containerd, buildkit, and nerdctl. +# +# NOTE: Starting with Lima v2.0, nerdctl version must be v2.1.6 or later to +# enable port forwarding in rootful mode. +# As of the time of releasing Lima v2.0, nerdctl v2.1.6 or later is not yet +# available in the official Alpine package repository (`apk add`). +# See for installing +# the latest version of nerdctl from the binary or the source. containerd: system: false user: false diff --git a/templates/alpine.yaml b/templates/alpine.yaml index c11a7c6149a..ecddf9da99a 100644 --- a/templates/alpine.yaml +++ b/templates/alpine.yaml @@ -6,6 +6,13 @@ base: # The built-in containerd installer does not support Alpine currently. # Use a provisioning script to install containerd, buildkit, and nerdctl. +# +# NOTE: Starting with Lima v2.0, nerdctl version must be v2.1.6 or later to +# enable port forwarding in rootful mode. +# As of the time of releasing Lima v2.0, nerdctl v2.1.6 or later is not yet +# available in the official Alpine package repository (`apk add`). +# See for installing +# the latest version of nerdctl from the binary or the source. containerd: system: false user: false