Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rootlesskit for openSUSE:Factory 
checked in at 2026-08-11 17:09:33
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rootlesskit (Old)
 and      /work/SRC/openSUSE:Factory/.rootlesskit.new.17972 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rootlesskit"

Tue Aug 11 17:09:33 2026 rev:19 rq:1370455 version:3.1.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/rootlesskit/rootlesskit.changes  2026-07-20 
09:56:52.495152984 +0200
+++ /work/SRC/openSUSE:Factory/.rootlesskit.new.17972/rootlesskit.changes       
2026-08-11 17:09:49.751472622 +0200
@@ -1,0 +2,14 @@
+Mon Aug 10 05:05:25 UTC 2026 - Yash Maurya <[email protected]>
+
+- Update to version 3.1.0:
+  * v3.1.0
+  * feat: support --port-driver=pesto for IPv4
+  * docs: refresh network driver benchmark table
+  * fix: benchmark pasta, not slirp4netns
+  * fix: correct field names in ParsePortSpec error messages
+  * fix(pasta): block host loopback access from the namespace when 
`--disable-host-loopback` is set
+  * Build(deps): Bump actions/setup-go from 6 to 7
+  * chore: update error message when the pasta binary dosen't exist
+  * v3.0.2+dev
+
+-------------------------------------------------------------------

Old:
----
  rootlesskit-3.0.2.tar.gz

New:
----
  rootlesskit-3.1.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rootlesskit.spec ++++++
--- /var/tmp/diff_new_pack.w0Pmui/_old  2026-08-11 17:09:51.783558751 +0200
+++ /var/tmp/diff_new_pack.w0Pmui/_new  2026-08-11 17:09:51.787558921 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           rootlesskit
-Version:        3.0.2
+Version:        3.1.0
 Release:        0
 Summary:        Linux-native fakeroot using user namespaces
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.w0Pmui/_old  2026-08-11 17:09:51.819560277 +0200
+++ /var/tmp/diff_new_pack.w0Pmui/_new  2026-08-11 17:09:51.827560616 +0200
@@ -4,7 +4,7 @@
     <param 
name="url">https://github.com/rootless-containers/rootlesskit.git</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v3.0.2</param>
+    <param name="revision">v3.1.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.w0Pmui/_old  2026-08-11 17:09:51.855561803 +0200
+++ /var/tmp/diff_new_pack.w0Pmui/_new  2026-08-11 17:09:51.859561973 +0200
@@ -1,5 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/rootless-containers/rootlesskit.git</param>
-              <param 
name="changesrevision">6c019736083e46424b4b657d84ec0db510a8ef40</param></service></servicedata>
+              <param 
name="changesrevision">62d2101fbbe4f79bc845a337c4e868d27ff602c9</param></service></servicedata>
+(No newline at EOF)
 

++++++ rootlesskit-3.0.2.tar.gz -> rootlesskit-3.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/Dockerfile 
new/rootlesskit-3.1.0/Dockerfile
--- old/rootlesskit-3.0.2/Dockerfile    2026-07-10 17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/Dockerfile    2026-08-04 10:50:20.000000000 +0200
@@ -3,7 +3,7 @@
 ARG SHADOW_VERSION=4.17.4
 ARG SLIRP4NETNS_VERSION=v1.3.4
 ARG VPNKIT_VERSION=0.6.0
-ARG PASST_VERSION=2026_01_20.386b5f5
+ARG PASST_VERSION=2026_07_28.f8df3f1
 ARG DOCKER_VERSION=29.5.2
 ARG DOCKER_CHANNEL=stable
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/README.md 
new/rootlesskit-3.1.0/README.md
--- old/rootlesskit-3.0.2/README.md     2026-07-10 17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/README.md     2026-08-04 10:50:20.000000000 +0200
@@ -154,7 +154,7 @@
    rootlesskit [global options] [arguments...]
 
 VERSION:
-   3.0.0
+   3.1.0
 
 DESCRIPTION:
    RootlessKit is a Linux-native implementation of "fake root" using 
user_namespaces(7).
@@ -199,11 +199,12 @@
     --detach-netns                                           detach network 
namespaces  (default: false)
                                                              
   Network [lxc-user-nic]:                                    
-    --lxc-user-nic-binary value                              path of 
lxc-user-nic binary for --net=lxc-user-nic
+    --lxc-user-nic-binary value                              path of 
lxc-user-nic binary for --net=lxc-user-nic (default: 
"/usr/libexec/lxc/lxc-user-nic")
     --lxc-user-nic-bridge value                              lxc-user-nic 
bridge name (default: "lxcbr0")
                                                              
   Network [pasta]:                                           
     --pasta-binary value                                     path of pasta 
binary for --net=pasta (default: "pasta")
+    --pesto-binary value                                     path of pesto 
binary for --port-driver=pesto (default: "pesto")
                                                              
   Network [slirp4netns]:                                     
     --slirp4netns-binary value                               path of 
slirp4netns binary for --net=slirp4netns (default: "slirp4netns")
@@ -214,7 +215,7 @@
     --vpnkit-binary value                                    path of VPNKit 
binary for --net=vpnkit (default: "vpnkit")
                                                              
   Port:                                                      
-    --port-driver value                                      port driver for 
non-host network. [none, implicit (for pasta), builtin, slirp4netns, 
gvisor-tap-vsock(experimental)] (default: "none")
+    --port-driver value                                      port driver for 
non-host network. [none, implicit (for pasta), builtin, slirp4netns, 
gvisor-tap-vsock(experimental), pesto(experimental)] (default: "none")
     --publish value, -p value [ --publish value, -p value ]  publish ports. 
e.g. "127.0.0.1:8080:80/tcp"
     --source-ip-transparent                                  preserve real 
client source IP using IP_TRANSPARENT (builtin port driver, TCP only) (default: 
true)
                                                              
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/cmd/rootlesskit/main.go 
new/rootlesskit-3.1.0/cmd/rootlesskit/main.go
--- old/rootlesskit-3.0.2/cmd/rootlesskit/main.go       2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/cmd/rootlesskit/main.go       2026-08-04 
10:50:20.000000000 +0200
@@ -28,6 +28,7 @@
        "github.com/rootless-containers/rootlesskit/v3/pkg/parent"
        "github.com/rootless-containers/rootlesskit/v3/pkg/port/builtin"
        gvisortapvsock_port 
"github.com/rootless-containers/rootlesskit/v3/pkg/port/gvisortapvsock"
+       pasta_port 
"github.com/rootless-containers/rootlesskit/v3/pkg/port/pasta"
        "github.com/rootless-containers/rootlesskit/v3/pkg/port/portutil"
        slirp4netns_port 
"github.com/rootless-containers/rootlesskit/v3/pkg/port/slirp4netns"
        "github.com/rootless-containers/rootlesskit/v3/pkg/systemd/activation"
@@ -108,6 +109,9 @@
        if gvisortapvsock_port.Available {
                portDrivers = append(portDrivers, 
"gvisor-tap-vsock(experimental)")
        }
+       if pasta_port.Available {
+               portDrivers = append(portDrivers, "pesto(experimental)")
+       }
        portDriversHelp := strings.Join(portDrivers, ", ")
 
        app.Flags = []cli.Flag{
@@ -135,6 +139,11 @@
                        Value: "pasta",
                }, CategoryPasta),
                Categorize(&cli.StringFlag{
+                       Name:  "pesto-binary",
+                       Usage: "path of pesto binary for --port-driver=pesto",
+                       Value: "pesto",
+               }, CategoryPasta),
+               Categorize(&cli.StringFlag{
                        Name:  "slirp4netns-binary",
                        Usage: "path of slirp4netns binary for 
--net=slirp4netns",
                        Value: "slirp4netns",
@@ -431,6 +440,14 @@
        if clicontext.String("port-driver") == "slirp4netns" {
                slirp4netnsAPISocketPath = filepath.Join(opt.StateDir, 
".s4nn.sock")
        }
+       portDriver := clicontext.String("port-driver")
+       if portDriver == "pasta" {
+               return opt, errors.New(`unknown port driver "pasta": did you 
mean "pesto"?`)
+       }
+       pestoSocketPath := ""
+       if portDriver == "pesto" {
+               pestoSocketPath = filepath.Join(opt.StateDir, ".pasta.sock")
+       }
        switch s := clicontext.String("net"); s {
        case "host":
                // NOP
@@ -471,14 +488,14 @@
                }
                var implicitPortForward bool
                switch portDriver := clicontext.String("port-driver"); 
portDriver {
-               case "none":
+               case "none", "pesto":
                        implicitPortForward = false
                case "implicit":
                        implicitPortForward = true
                default:
-                       return opt, errors.New("network \"pasta\" requires port 
driver \"none\" or \"implicit\"")
+                       return opt, errors.New("network \"pasta\" requires port 
driver \"none\" or \"implicit\" or \"pesto\"")
                }
-               opt.NetworkDriver, err = 
pasta.NewParentDriver(&logrusDebugWriter{label: "network/pasta"}, binary, mtu, 
ipnet, ifname, disableHostLoopback, ipv6, implicitPortForward)
+               opt.NetworkDriver, err = 
pasta.NewParentDriver(&logrusDebugWriter{label: "network/pasta"}, binary, mtu, 
ipnet, ifname, pestoSocketPath, disableHostLoopback, ipv6, implicitPortForward)
                if err != nil {
                        return opt, err
                }
@@ -610,7 +627,7 @@
                }
        case "implicit":
                if clicontext.String("net") != "pasta" {
-                       return opt, errors.New("port driver requires pasta 
network")
+                       return opt, errors.New("port driver \"implicit\" 
requires --net=pasta")
                }
                // NOP
        case "slirp4netns":
@@ -638,6 +655,18 @@
                if err != nil {
                        return opt, err
                }
+       case "pesto":
+               if clicontext.String("net") != "pasta" {
+                       return opt, errors.New("port driver \"pesto\" requires 
--net=pasta")
+               }
+               binary := clicontext.String("pesto-binary")
+               if _, err := exec.LookPath(binary); err != nil {
+                       return opt, err
+               }
+               opt.PortDriver, err = 
pasta_port.NewParentDriver(&logrusDebugWriter{label: "port/pesto"}, binary, 
pestoSocketPath)
+               if err != nil {
+                       return opt, err
+               }
        default:
                return opt, fmt.Errorf("unknown port driver: %s", s)
        }
@@ -731,6 +760,8 @@
                opt.PortDriver = 
builtin.NewChildDriver(&logrusDebugWriter{label: "port/builtin"})
        case "gvisor-tap-vsock":
                opt.PortDriver = gvisortapvsock_port.NewChildDriver()
+       case "pesto":
+               opt.PortDriver = pasta_port.NewChildDriver()
        default:
                return opt, fmt.Errorf("unknown port driver: %s", s)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/docs/network.md 
new/rootlesskit-3.1.0/docs/network.md
--- old/rootlesskit-3.0.2/docs/network.md       2026-07-10 17:08:25.000000000 
+0200
+++ new/rootlesskit-3.1.0/docs/network.md       2026-08-04 10:50:20.000000000 
+0200
@@ -9,17 +9,17 @@
 * `--net=lxc-user-nic`: use `lxc-user-nic` (experimental)
 * `--net=gvisor-tap-vsock`: use 
[gvisor-tap-vsock](https://github.com/containers/gvisor-tap-vsock) 
(experimental)
 
-[Benchmark: iperf3 from the child to the parent (Apr 10, 
2026)](https://github.com/rootless-containers/rootlesskit/actions/runs/24200485791/job/70642399211):
+[Benchmark: iperf3 from the child to the parent (Jul 26, 
2026)](https://github.com/rootless-containers/rootlesskit/actions/runs/30191669102/job/89765864664):
 
 |                 Driver                |  MTU=1500  |  MTU=65520
 |---------------------------------------|------------|-------------
-|`slirp4netns`                          |  0.84 Gbps |  6.17 Gbps
-|`slirp4netns` (with sandbox + seccomp) |  0.80 Gbps |  6.19 Gbps
-|`vpnkit`                               |  0.10 Gbps |(Unsupported)
-|`pasta`                                |  0.87 Gbps |  6.48 Gbps
-|`gvisor-tap-vsock`                     |  1.55 Gbps |  5.40 Gbps
-|`lxc-user-nic`                         |  29.3 Gbps |  30.4 Gbps
-|(rootful veth)                         | (35.0 Gbps)| (36.2 Gbps)
+|`slirp4netns`                          |  1.69 Gbps |  8.11 Gbps
+|`slirp4netns` (with sandbox + seccomp) |  1.68 Gbps |  8.32 Gbps
+|`vpnkit`                               |  0.14 Gbps |(Unsupported)
+|`pasta`                                |  0.24 Gbps |  31.9 Gbps
+|`gvisor-tap-vsock`                     |  2.46 Gbps |  8.75 Gbps
+|`lxc-user-nic`                         |  49.1 Gbps |  50.7 Gbps
+|(rootful veth)                         | (49.3 Gbps)| (50.8 Gbps)
 
 * To be documented: 
[`bypass4netns`](https://github.com/rootless-containers/bypass4netns) for 
native performance.
 
@@ -147,7 +147,7 @@
 ### `--net=pasta` (experimental)
 
 `--net=pasta` (since RootlessKit v2.0, EXPERIMENTAL) uses [pasta 
(passt)](https://passt.top/passt/).
-`--net=pasta` is expected to be used in conjunction with 
`--port-driver=implicit`.
+`--net=pasta` is expected to be used in conjunction with `--port-driver=pesto` 
or `--port-driver=implicit`.
 
 > **Note**
 > `--net=pasta` needs [pasta (passt)](https://passt.top/passt/) 
 > `2023_06_25.32660ce` or later.
@@ -170,9 +170,7 @@
 * Supports ICMP Echo (`ping`) when `/proc/sys/net/ipv4/ping_group_range` is 
configured
 * TCP port forwarding (`--port-driver=implicit`) is very fast
 * TCP port forwarding (`--port-driver=implicit`) can retain source IP addresses
-
-Cons:
-* Lacks API for explicit port forwarding (`rootlessctl 
(list-ports|add-ports|remove-ports)`)
+* Explicit port forwarding via `rootlessctl 
(list-ports|add-ports|remove-ports)` is supported with `--port-driver=pesto` 
(experimental, IPv4-only, requires pasta `2026_05_07.1afd4ed` or later)
 
 The network configuration for pasta is similar to slirp4netns.
 As in `--net=slirp4netns`, specifying `--copy-up=/etc` and 
`--disable-host-loopback` is highly recommended.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/docs/port.md 
new/rootlesskit-3.1.0/docs/port.md
--- old/rootlesskit-3.0.2/docs/port.md  2026-07-10 17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/docs/port.md  2026-08-04 10:50:20.000000000 +0200
@@ -9,13 +9,20 @@
 | `slirp4netns`        | 8.03 Gbps   | Propagated |
 | `builtin`            | 29.9 Gbps   | Propagated for TCP (since v3.0) | 
Source IP propagation (`--source-ip-transparent`) applies to TCP only; UDP is 
not propagated. In the case of Rootless Docker, userland-proxy has to be 
disabled for propagating the source IP.
 | `implicit`           | 37.6 Gbps   | Propagated | Requires `pasta` network
+| `pesto` (Experimental) | TBD       | Propagated | Requires `pasta` network 
and passt `2026_05_07.1afd4ed` or later. Supports explicit port management 
through `rootlessctl`.
 | `gvisor-tap-vsock` (Experimental) | 3.83 Gbps | Not propagated | Throughput 
is currently limited; see issue link below for improvement ideas.
 
 Benchmark: iperf3 from the parent to the child is measured on GitHub Actions 
([Apr 10, 
2026](https://github.com/rootless-containers/rootlesskit/actions/runs/24200485791/job/70642399211))
 
 The `builtin` driver is fast and should be the best choice for most use cases.
 
-For [`pasta`](./network.md) networks, the `implicit` port driver is the best 
choice.
+For [`pasta`](./network.md) networks, choose the port driver based on how
+ports should be exposed:
+
+* Use `pesto` to expose only explicitly published ports using `rootlessctl 
list-ports`, `add-ports`, and `remove-ports`.
+* Use `implicit` to automatically expose ports listened on in the network 
namespace. This may expose ports that were not intended to be reachable from 
the host.
+
+The `pesto` port driver is experimental and currently supports IPv4 port 
forwarding only. Both the `pasta` and `pesto` executables must be installed.
 
 > [!NOTE]
 > The `gvisor-tap-vsock` port driver is experimental.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/hack/benchmark-iperf3-net.sh 
new/rootlesskit-3.1.0/hack/benchmark-iperf3-net.sh
--- old/rootlesskit-3.0.2/hack/benchmark-iperf3-net.sh  2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/hack/benchmark-iperf3-net.sh  2026-08-04 
10:50:20.000000000 +0200
@@ -1,13 +1,13 @@
 #!/bin/bash
 source $(realpath $(dirname $0))/common.inc.sh
 function benchmark::iperf3::pasta() {
-       INFO "[benchmark:iperf3] slirp4netns ($@)"
+       INFO "[benchmark:iperf3] pasta ($@)"
        statedir=$(mktemp -d)
        if echo "$@" | grep -q -- --detach-netns; then
                IPERF3C="nsenter -n${statedir}/netns $IPERF3C"
        fi
        set -x
-       $ROOTLESSKIT --state-dir=$statedir --net=slirp4netns $@ -- $IPERF3C 
10.0.2.2
+       $ROOTLESSKIT --state-dir=$statedir --net=pasta $@ -- $IPERF3C 10.0.2.2
        set +x
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/hack/benchmark-iperf3-port.sh 
new/rootlesskit-3.1.0/hack/benchmark-iperf3-port.sh
--- old/rootlesskit-3.0.2/hack/benchmark-iperf3-port.sh 2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/hack/benchmark-iperf3-port.sh 2026-08-04 
10:50:20.000000000 +0200
@@ -19,6 +19,8 @@
                portid=$($rootlessctl add-ports 127.0.0.1:5201:5201/tcp)
                $rootlessctl list-ports
        fi
+       # wait for the port to be bound
+       sleep 3
        $IPERF3C 127.0.0.1
        if [ $portdriver != "implicit" ]; then
                $rootlessctl remove-ports $portid
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/hack/integration-net.sh 
new/rootlesskit-3.1.0/hack/integration-net.sh
--- old/rootlesskit-3.0.2/hack/integration-net.sh       2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/hack/integration-net.sh       2026-08-04 
10:50:20.000000000 +0200
@@ -19,3 +19,30 @@
        sudo /usr/lib/$(uname -m)-linux-gnu/lxc/lxc-net start || sudo 
/etc/init.d/lxc-net start || true
 fi
 $ROOTLESSKIT --net=${net} --copy-up=/etc --copy-up=/run 
--disable-host-loopback ${flags} -- nslookup example.com
+
+# Test that a server process listening on the host loopback is not accessible 
from the isolated namespace
+tmp=$(mktemp -d)
+echo "hello host loopback" >${tmp}/index.html
+
+busybox httpd -f -p 127.0.0.1:8080 -h ${tmp} &
+pid=$!
+
+sleep 1
+
+statedir=$(mktemp -d)
+CURL="timeout 3 curl -fsSL http://127.0.0.1:8080";
+if echo "${flags}" | grep -q -- --detach-netns; then
+       # With --detach-netns, the child command runs in the host's network 
namespace,
+       # so it has to enter the detached network namespace explicitly.
+       CURL="nsenter -n${statedir}/netns ${CURL}"
+fi
+
+if $ROOTLESSKIT --state-dir=${statedir} --net=${net} --copy-up=/etc 
--copy-up=/run --disable-host-loopback ${flags} -- ${CURL}; then
+       ERROR "the host loopback should not be accessible from the isolated 
namespace"
+       exit 1
+fi
+
+INFO "the host loopback is not accessible from the isolated namespace, as 
expected"
+
+kill -9 $pid || true
+rm -rf ${tmp} ${statedir}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/hack/integration-port.sh 
new/rootlesskit-3.1.0/hack/integration-port.sh
--- old/rootlesskit-3.0.2/hack/integration-port.sh      2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/hack/integration-port.sh      2026-08-04 
10:50:20.000000000 +0200
@@ -1,6 +1,8 @@
 #!/bin/bash
 source $(realpath $(dirname $0))/common.inc.sh
 
+ROOTLESSCTL="rootlessctl"
+
 # test_port PORT_DRIVER CURL_URL EXPECTATION [ROOTLESSKIT ARGS...]
 function test_port() {
        args="$@"
@@ -12,19 +14,30 @@
        shift
        rootlesskit_args="$@"
        INFO "Testing port_driver=\"${port_driver}\" curl_url=\"${curl_url}\" 
expectation=\"${expectation}\" rootlesskit_args=\"${rootlesskit_args}\""
+       net=slirp4netns
+       if [ "${port_driver}" = "pesto" ]; then
+               # The pesto port driver is specific to the pasta network driver
+               net=pasta
+       fi
        tmp=$(mktemp -d)
        state_dir=${tmp}/state
        html_dir=${tmp}/html
        mkdir -p ${html_dir}
        echo "test_port ($args)" >${html_dir}/index.html
+       httpd="busybox httpd -f -v -p 80 -h ${html_dir}"
+       if echo "${rootlesskit_args}" | grep -q -- --detach-netns; then
+               # With --detach-netns, the child command runs in the host's 
network
+               # namespace, so the server has to enter the detached netns 
explicitly.
+               httpd="nsenter -n${state_dir}/netns ${httpd}"
+       fi
        $ROOTLESSKIT \
                --state-dir=${state_dir} \
-               --net=slirp4netns \
+               --net=${net} \
                --disable-host-loopback \
                --copy-up=/etc \
                --port-driver=${port_driver} \
                ${rootlesskit_args} \
-               busybox httpd -f -v -p 80 -h ${html_dir} \
+               ${httpd} \
                2>&1 &
        pid=$!
        sleep 1
@@ -49,8 +62,108 @@
        fi
 
        INFO "Test pasing, stopping httpd (\"exit status 255\" is negligible 
here)"
-       kill -SIGTERM $(cat ${state_dir}/child_pid)
+       # child_pid does not exist when rootlesskit itself was expected to fail 
on startup
+       if [ -f ${state_dir}/child_pid ]; then
+               kill -SIGTERM $(cat ${state_dir}/child_pid) || true
+       fi
        wait $pid >/dev/null 2>&1 || true
+       wait_for_pasta_exit
+       rm -rf $tmp
+}
+
+function wait_for_pasta_exit() {
+       for i in $(seq 50); do
+               pgrep -x pasta >/dev/null || break
+               sleep 0.1
+       done
+}
+
+# test_pesto_dynamic [ROOTLESSKIT ARGS...]
+function test_pesto_dynamic() {
+       rootlesskit_args="$@"
+       INFO "Testing dynamic port management 
(rootlesskit_args=\"${rootlesskit_args}\")"
+       tmp=$(mktemp -d)
+       state_dir=${tmp}/state
+       html_dir=${tmp}/html
+       mkdir -p ${html_dir}
+       echo "test_pesto_dynamic" >${html_dir}/index.html
+       httpd="busybox httpd -f -v -p 80 -h ${html_dir}"
+       if echo "${rootlesskit_args}" | grep -q -- --detach-netns; then
+               # With --detach-netns, the child command runs in the host's 
network
+               # namespace, so the server has to enter the detached netns 
explicitly.
+               httpd="nsenter -n${state_dir}/netns ${httpd}"
+       fi
+       $ROOTLESSKIT \
+               --state-dir=${state_dir} \
+               --net=pasta \
+               --disable-host-loopback \
+               --copy-up=/etc \
+               --port-driver=pesto \
+               ${rootlesskit_args} \
+               ${httpd} \
+               2>&1 &
+       pid=$!
+       sleep 1
+       api_sock=${state_dir}/api.sock
+
+       INFO "= the port must not be reachable before add-ports ="
+       if curl -fsSL http://127.0.0.1:8080; then
+               ERROR "curl should not success before add-ports"
+               exit 1
+       fi
+
+       INFO "= add-ports, then the port must be reachable ="
+       id=$($ROOTLESSCTL --socket=${api_sock} add-ports 127.0.0.1:8080:80/tcp)
+       curl -fsSL http://127.0.0.1:8080
+       $ROOTLESSCTL --socket=${api_sock} list-ports --json | grep -q 
'"parentPort":8080'
+
+       INFO "= adding a conflicting port must fail ="
+       if $ROOTLESSCTL --socket=${api_sock} add-ports 127.0.0.1:8080:80/tcp; 
then
+               ERROR "add-ports should fail for a conflicting port"
+               exit 1
+       fi
+
+       INFO "= ChildIP matching the namespace address (10.0.2.100) is accepted 
="
+       # 10.0.2.100 is the first address of the default CIDR (10.0.2.0/24) + 
100
+       $ROOTLESSCTL --socket=${api_sock} add-ports 
127.0.0.1:8081:10.0.2.100:80/tcp
+       curl -fsSL http://127.0.0.1:8081
+
+       INFO "= any other ChildIP must be rejected (pasta cannot honor it) ="
+       if $ROOTLESSCTL --socket=${api_sock} add-ports 
127.0.0.1:8083:10.9.9.9:80/tcp; then
+               ERROR "add-ports should fail for an unsupported ChildIP"
+               exit 1
+       fi
+
+       INFO "= unsupported proto (tcp6) must be rejected ="
+       if $ROOTLESSCTL --socket=${api_sock} add-ports :8082:80/tcp6; then
+               ERROR "add-ports should fail for tcp6"
+               exit 1
+       fi
+
+       INFO "= udp rules can be added and removed ="
+       udp_id=$($ROOTLESSCTL --socket=${api_sock} add-ports 
127.0.0.1:5353:5353/udp)
+       $ROOTLESSCTL --socket=${api_sock} list-ports --json | grep -q 
'"proto":"udp"'
+       $ROOTLESSCTL --socket=${api_sock} remove-ports ${udp_id}
+
+       INFO "= remove-ports, then new connections must be refused ="
+       $ROOTLESSCTL --socket=${api_sock} remove-ports ${id}
+       if curl -fsSL http://127.0.0.1:8080; then
+               ERROR "curl should not success after remove-ports"
+               exit 1
+       fi
+
+       INFO "= removing an unknown port ID must fail ="
+       if $ROOTLESSCTL --socket=${api_sock} remove-ports ${id}; then
+               ERROR "remove-ports should fail for an unknown ID"
+               exit 1
+       fi
+
+       INFO "Test passing, stopping httpd (\"exit status 255\" is negligible 
here)"
+       if [ -f ${state_dir}/child_pid ]; then
+               kill -SIGTERM $(cat ${state_dir}/child_pid) || true
+       fi
+       wait $pid >/dev/null 2>&1 || true
+       wait_for_pasta_exit
        rm -rf $tmp
 }
 
@@ -89,4 +202,26 @@
 INFO "=== protocol \"tcp4\" is strictly v4-only ==="
 test_port slirp4netns http://[::1]:8080 "should fail" -p 0.0.0.0:8080:80/tcp4
 
+INFO "===== Port driver: pesto (--net=pasta) ====="
+INFO "=== static publishing via -p ==="
+test_port pesto http://127.0.0.1:8080 "should success" -p 0.0.0.0:8080:80/tcp
+test_port pesto http://127.0.0.1:8080 "should success" -p 127.0.0.1:8080:80/tcp
+test_port pesto http://127.0.0.1:8080 "should success" -p 0.0.0.0:8080:80/tcp4
+
+INFO "=== port translation (8080 -> 80) ==="
+test_port pesto http://127.0.0.1:8080 "should success" -p :8080:80/tcp
+
+INFO "=== a port that is not published must not be reachable ==="
+test_port pesto http://127.0.0.1:9090 "should fail" -p 0.0.0.0:8080:80/tcp
+
+INFO "=== unsupported proto (tcp6) makes the startup fail ==="
+test_port pesto http://[::1]:8080 "should fail" -p [::]:8080:80/tcp6
+
+INFO "=== dynamic port management via rootlessctl ==="
+test_pesto_dynamic
+
+INFO "=== with --detach-netns ==="
+test_port pesto http://127.0.0.1:8080 "should success" -p 0.0.0.0:8080:80/tcp 
--detach-netns
+test_pesto_dynamic --detach-netns
+
 INFO "===== PASSING ====="
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/pkg/network/pasta/pasta.go 
new/rootlesskit-3.1.0/pkg/network/pasta/pasta.go
--- old/rootlesskit-3.0.2/pkg/network/pasta/pasta.go    2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/pkg/network/pasta/pasta.go    2026-08-04 
10:50:20.000000000 +0200
@@ -23,6 +23,9 @@
        // Has `--host-lo-to-ns-lo` (introduced in passt 2024_10_30.ee7d0b6)
        // 
https://passt.top/passt/commit/?id=b4dace8f462b346ae2135af1f8d681a99a849a5f
        HasHostLoToNsLo bool
+       // Has `--conf-path` (introduced in passt 2026_05_07.1afd4ed)
+       // 
https://passt.top/passt/commit/?id=f1d893ca1926e58ae5a2bf5602b515a883f3f994
+       HasConfPath bool
 }
 
 func DetectFeatures(binary string) (*Features, error) {
@@ -46,14 +49,18 @@
        if cmd.Run() == nil {
                f.HasHostLoToNsLo = true
        }
+       cmd = exec.Command(realBinary, "--conf-path=/dev/null", "--version")
+       if cmd.Run() == nil {
+               f.HasConfPath = true
+       }
        return &f, nil
 }
 
 // NewParentDriver instantiates new parent driver.
-func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet 
*net.IPNet, ifname string,
+func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet 
*net.IPNet, ifname, apiSocketPath string,
        disableHostLoopback, enableIPv6, implicitPortForwarding bool) 
(network.ParentDriver, error) {
        if binary == "" {
-               return nil, errors.New("got empty slirp4netns binary")
+               return nil, errors.New("got empty pasta binary")
        }
        if mtu < 0 {
                return nil, errors.New("got negative mtu")
@@ -79,6 +86,10 @@
                return nil, err
        }
 
+       if !feat.HasConfPath && apiSocketPath != "" {
+               return nil, errors.New("port driver \"pesto\" requires pasta 
with --conf-path support (passt >= 2026_05_07.1afd4ed)")
+       }
+
        return &parentDriver{
                logWriter:              logWriter,
                binary:                 binary,
@@ -87,6 +98,7 @@
                disableHostLoopback:    disableHostLoopback,
                enableIPv6:             enableIPv6,
                ifname:                 ifname,
+               apiSocketPath:          apiSocketPath,
                implicitPortForwarding: implicitPortForwarding,
                feat:                   feat,
        }, nil
@@ -100,6 +112,7 @@
        disableHostLoopback    bool
        enableIPv6             bool
        ifname                 string
+       apiSocketPath          string
        infoMu                 sync.RWMutex
        implicitPortForwarding bool
        info                   func() *api.NetworkDriverInfo
@@ -155,7 +168,7 @@
                "--dns-forward=" + dns.String(),
        }
        if d.disableHostLoopback {
-               opts = append(opts, "--no-map-gw")
+               opts = append(opts, "--no-map-gw", "--tcp-ns=none", 
"--udp-ns=none")
        }
        if !d.enableIPv6 {
                opts = append(opts, "--ipv4-only")
@@ -168,7 +181,10 @@
                        "--udp-ports=none")
        }
        if d.feat != nil {
-               if d.feat.HasHostLoToNsLo {
+               // Skipped for the "pesto" port driver: inbound connections 
must be
+               // forwarded to the child IP, because that is where the 
listener in the
+               // namespace (e.g. docker-proxy) is bound.
+               if d.feat.HasHostLoToNsLo && d.apiSocketPath == "" {
                        // Needed to keep `docker run -p 127.0.0.1:8080:80` 
functional with
                        // passt >= 2024_10_30.ee7d0b6
                        //
@@ -176,6 +192,10 @@
                        opts = append(opts, "--host-lo-to-ns-lo")
                }
        }
+       if d.apiSocketPath != "" {
+               // passt >= 2026_05_07.1afd4ed
+               opts = append(opts, "--conf-path="+d.apiSocketPath)
+       }
        if detachedNetNSPath == "" {
                opts = append(opts, strconv.Itoa(childPID))
        } else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/pkg/port/pasta/pesto.go 
new/rootlesskit-3.1.0/pkg/port/pasta/pesto.go
--- old/rootlesskit-3.0.2/pkg/port/pasta/pesto.go       1970-01-01 
01:00:00.000000000 +0100
+++ new/rootlesskit-3.1.0/pkg/port/pasta/pesto.go       2026-08-04 
10:50:20.000000000 +0200
@@ -0,0 +1,187 @@
+package pasta
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "io"
+       "net"
+       "os/exec"
+       "sync"
+
+       "github.com/rootless-containers/rootlesskit/v3/pkg/api"
+       "github.com/rootless-containers/rootlesskit/v3/pkg/port"
+       "github.com/rootless-containers/rootlesskit/v3/pkg/port/portutil"
+       "github.com/sirupsen/logrus"
+)
+
+func NewParentDriver(logWriter io.Writer, binary, socketPath string) 
(port.ParentDriver, error) {
+       if socketPath == "" {
+               return nil, errors.New("configuration socket path is not set")
+       }
+
+       cmd := exec.Command(binary, "--version")
+       b, err := cmd.CombinedOutput()
+       if err != nil {
+               return nil, fmt.Errorf(`command "%s --version" failed, make 
sure pesto is installed: %q: %w`,
+                       binary, string(b), err)
+       }
+
+       d := driver{
+               logWriter:     logWriter,
+               ports:         make(map[int]*port.Status),
+               apiSocketPath: socketPath,
+               binary:        binary,
+               nextID:        1,
+       }
+
+       return &d, nil
+}
+
+type driver struct {
+       logWriter     io.Writer
+       apiSocketPath string
+       mu            sync.Mutex
+       childIP       string // can be empty
+       ports         map[int]*port.Status
+       binary        string
+       nextID        int
+}
+
+func (d *driver) Info(ctx context.Context) (*api.PortDriverInfo, error) {
+       info := &api.PortDriverInfo{
+               Driver: "pesto",
+               // No IPv6 support yet
+               Protos:                  []string{"tcp", "tcp4", "udp", "udp4"},
+               DisallowLoopbackChildIP: true,
+       }
+       return info, nil
+}
+
+func (d *driver) OpaqueForChild() map[string]string {
+       // NOP, as this driver does not have child-side logic.
+       return nil
+}
+
+func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan 
struct{}, cctx *port.ChildContext) error {
+       if cctx != nil && cctx.IP != nil {
+               if cctx.IP.To4() != nil {
+                       d.childIP = cctx.IP.To4().String()
+               }
+       }
+       initComplete <- struct{}{}
+       <-quit
+       return nil
+}
+
+func (d *driver) createArgs(method string, spec port.Spec) ([]string, error) {
+       opts := []string{method}
+       o := ""
+
+       switch spec.Proto {
+       case "tcp", "tcp4":
+               o += "--tcp-ports="
+       case "udp", "udp4":
+               o += "--udp-ports="
+       default:
+               return nil, fmt.Errorf("unsupported protocol: %s", spec.Proto)
+       }
+       parentIP := spec.ParentIP
+       if parentIP == "" {
+               parentIP = "0.0.0.0"
+       }
+       p := net.ParseIP(parentIP)
+       if p == nil {
+               return nil, fmt.Errorf("invalid IP: %q", parentIP)
+       }
+       p = p.To4()
+       if p == nil {
+               return nil, fmt.Errorf("unsupported IP: %s", parentIP)
+       }
+       o += fmt.Sprintf("%s/%d:%d", p.String(), spec.ParentPort, 
spec.ChildPort)
+       opts = append(opts, o)
+
+       return append(opts, d.apiSocketPath), nil
+}
+
+func (d *driver) AddPort(ctx context.Context, spec port.Spec) (*port.Status, 
error) {
+       d.mu.Lock()
+       defer d.mu.Unlock()
+
+       if err := portutil.ValidatePortSpec(spec, d.ports); err != nil {
+               return nil, err
+       }
+       if spec.ChildIP != "" && spec.ChildIP != d.childIP {
+               return nil, fmt.Errorf("unsupported ChildIP %q: the pesto port 
driver only supports the namespace address %q", spec.ChildIP, d.childIP)
+       }
+
+       opts, err := d.createArgs("-A", spec)
+       if err != nil {
+               return nil, err
+       }
+       cmd := exec.Command(d.binary, opts...)
+       logrus.Debugf("Executing %v", cmd.Args)
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               return nil, fmt.Errorf("pesto failed: %w\noutput: %s", err, out)
+       }
+
+       id := d.nextID
+       st := port.Status{
+               ID:   id,
+               Spec: spec,
+       }
+       d.ports[id] = &st
+       d.nextID++
+
+       return &st, nil
+}
+
+func (d *driver) ListPorts(ctx context.Context) ([]port.Status, error) {
+       var ports []port.Status
+       d.mu.Lock()
+       for _, p := range d.ports {
+               ports = append(ports, *p)
+       }
+       d.mu.Unlock()
+       return ports, nil
+}
+
+func (d *driver) RemovePort(ctx context.Context, id int) error {
+       d.mu.Lock()
+       defer d.mu.Unlock()
+
+       st, ok := d.ports[id]
+       if !ok {
+               return fmt.Errorf("invalid ID: %d", id)
+       }
+       opts, err := d.createArgs("-D", st.Spec)
+       if err != nil {
+               return err
+       }
+       cmd := exec.Command(d.binary, opts...)
+       logrus.Debugf("Executing %v", cmd.Args)
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               return fmt.Errorf("pesto failed: %w\noutput: %s", err, out)
+       }
+       delete(d.ports, id)
+
+       return nil
+}
+
+func NewChildDriver() port.ChildDriver {
+       return &childDriver{}
+}
+
+type childDriver struct {
+}
+
+func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan 
struct{}, detachedNetNSPath string) error {
+       // NOP
+       <-quit
+       return nil
+}
+
+// Available indicates whether this port driver is compiled in (used for 
generating help text)
+const Available = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/pkg/port/port.go 
new/rootlesskit-3.1.0/pkg/port/port.go
--- old/rootlesskit-3.0.2/pkg/port/port.go      2026-07-10 17:08:25.000000000 
+0200
+++ new/rootlesskit-3.1.0/pkg/port/port.go      2026-08-04 10:50:20.000000000 
+0200
@@ -20,6 +20,7 @@
        // - builtin     driver: 127.0.0.1
        // - slirp4netns driver: slirp4netns's child IP, e.g., 10.0.2.100
        // - gvisor-tap-vsock driver: gvisor-tap-vsock's child IP, e.g., 
10.0.2.100
+       // - pesto driver: pasta's child IP, e.g., 10.0.2.100
        ChildIP string `json:"childIP,omitempty"`
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/pkg/port/portutil/portutil.go 
new/rootlesskit-3.1.0/pkg/port/portutil/portutil.go
--- old/rootlesskit-3.0.2/pkg/port/portutil/portutil.go 2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/pkg/port/portutil/portutil.go 2026-08-04 
10:50:20.000000000 +0200
@@ -94,7 +94,7 @@
                return nil, fmt.Errorf("unexpected ParentIP in PortSpec string: 
%q", portSpec)
        }
        if parts[childIP] != "" && net.ParseIP(parts[childIP]) == nil {
-               return nil, fmt.Errorf("unexpected ParentIP in PortSpec string: 
%q", portSpec)
+               return nil, fmt.Errorf("unexpected ChildIP in PortSpec string: 
%q", portSpec)
        }
 
        ps := &port.Spec{
@@ -105,12 +105,12 @@
 
        ps.ParentPort, err = strconv.Atoi(parts[parentPort])
        if err != nil {
-               return nil, fmt.Errorf("unexpected ChildPort in PortSpec 
string: %q: %w", portSpec, err)
+               return nil, fmt.Errorf("unexpected ParentPort in PortSpec 
string: %q: %w", portSpec, err)
        }
 
        ps.ChildPort, err = strconv.Atoi(parts[childPort])
        if err != nil {
-               return nil, fmt.Errorf("unexpected ParentPort in PortSpec 
string: %q: %w", portSpec, err)
+               return nil, fmt.Errorf("unexpected ChildPort in PortSpec 
string: %q: %w", portSpec, err)
        }
 
        return ps, nil
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rootlesskit-3.0.2/pkg/version/version.go 
new/rootlesskit-3.1.0/pkg/version/version.go
--- old/rootlesskit-3.0.2/pkg/version/version.go        2026-07-10 
17:08:25.000000000 +0200
+++ new/rootlesskit-3.1.0/pkg/version/version.go        2026-08-04 
10:50:20.000000000 +0200
@@ -1,3 +1,3 @@
 package version
 
-const Version = "3.0.2"
+const Version = "3.1.0"

++++++ vendor.tar.gz ++++++

Reply via email to