From 371d148b66741c72a887b397eff3183d947d9036 Mon Sep 17 00:00:00 2001 From: Hayato Kiwata Date: Mon, 20 Jul 2026 17:39:18 +0900 Subject: [PATCH] fix(pasta): block host loopback access from the namespace when `--disable-host-loopback` is set Currently, even when rootlesskit is run with `--net=pasta --disable-host-loopback`, a process in the namespace created by pasta can reach a server listening on the loopback in the host namespace. This goes against what `--disable-host-loopback` is meant to do. The following demonstrates this behavior: ```bash (host) $ python3 -m http.server 8080 Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ... ``` ``` (host) $ rootlesskit --net=pasta --copy-up=/etc --disable-host-loopback --port-driver=implicit --debug bash -c "curl localhost:8080 2>/dev/null | head -3" ... DEBU[0000] [rootlesskit:parent] Executing [pasta --stderr --ns-ifname=tap0 --mtu=65520 --config-net --address=10.0.2.100 --netmask=24 --gateway=10.0.2.2 --dns-forward=10.0.2.3 --no-map-gw --ipv4-only --tcp-ports=auto --udp-ports=auto --host-lo-to-ns-lo 2777069] ... ... ``` However, `--disable-host-loopback` is meant to prevent a process in the isolated namespace created by pasta from reaching the server listening on the loopback in the host namespace. So, the above connection is expected to fail. Currently, rootlesskit translates `--disable-host-loopback` to pasta's `--no-map-gw`. `--no-map-gw` blocks host access via the gateway, but it does not block the splice path (`-T auto -U auto`). ```bash > pasta --help | grep "forwarding\sto\sinit" -A 2 -T, --tcp-ns SPEC TCP port forwarding to init namespace SPEC is as described above default: auto -U, --udp-ns SPEC UDP port forwarding to init namespace SPEC is as described above default: auto ``` As a result, a process in the isolated namespace can still reach the host loopback. When rootlesskit is invoked with `--disable-host-loopback`, this commit passes `-T none -U none` to pasta as well. This prevents a process in the isolated namespace from reaching a process running on the host loopback. After applying this fix: ```bash (host) $ rootlesskit --net=pasta --copy-up=/etc --disable-host-loopback --port-driver=implicit bash -c "curl localhost:8080" WARN[0000] [rootlesskit:parent] "pasta" network driver is experimental. Needs very recent version of pasta (see docs/network.md). curl: (7) Failed to connect to localhost port 8080 after 0 ms: Could not connect to server ... ``` Signed-off-by: Hayato Kiwata --- hack/integration-net.sh | 27 +++++++++++++++++++++++++++ pkg/network/pasta/pasta.go | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/hack/integration-net.sh b/hack/integration-net.sh index 14a3b901..85a8f08f 100755 --- a/hack/integration-net.sh +++ b/hack/integration-net.sh @@ -19,3 +19,30 @@ if [ "${net}" = "lxc-user-nic" ]; then 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 --git a/pkg/network/pasta/pasta.go b/pkg/network/pasta/pasta.go index e4411c2f..62f27935 100644 --- a/pkg/network/pasta/pasta.go +++ b/pkg/network/pasta/pasta.go @@ -155,7 +155,7 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPat "--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")