From a4be5bb9f1959863142134258908438351f6a144 Mon Sep 17 00:00:00 2001 From: akshitguptaa Date: Sun, 12 Jul 2026 23:24:25 +0530 Subject: [PATCH 1/2] rootless: enable IPv6 in RootlessKit and update detection API Signed-off-by: akshitguptaa --- docs/rootless.md | 6 ++++++ extras/rootless/containerd-rootless.sh | 19 +++++++++++++++++ pkg/testutil/testutil_linux.go | 29 ++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/docs/rootless.md b/docs/rootless.md index 011fa3ff7b2..4c796dc3fef 100644 --- a/docs/rootless.md +++ b/docs/rootless.md @@ -165,6 +165,12 @@ Rootless containerd recognizes the following environment variables to configure the host loopback IP address (127.0.0.1) and abstract sockets are exposed to Dockerfile's "RUN" instructions during `nerdctl build` (not `nerdctl run`). The drawback is fixed in BuildKit v0.13. Upgrading from a prior version of BuildKit needs removing the old systemd unit: `containerd-rootless-setuptool.sh uninstall-buildkit && rm -f ~/.config/buildkit/buildkitd.toml` +* `CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6=(true|false)`: whether to enable IPv6 inside the RootlessKit network namespace. + Defaults to "false". After enabling this, create IPv6-capable CNI networks with + `nerdctl network create --ipv6 --subnet ` as usual. Note that this mainly + affects outgoing connections with `slirp4netns` and `pasta` network drivers. + It does not affect port forwarding in the built-in port driver. + The `gvisor-tap-vsock` network driver does not currently support IPv6. To set these variables, create `~/.config/systemd/user/containerd.service.d/override.conf` as follows: ```ini diff --git a/extras/rootless/containerd-rootless.sh b/extras/rootless/containerd-rootless.sh index bfa7acc4578..631ba91174f 100755 --- a/extras/rootless/containerd-rootless.sh +++ b/extras/rootless/containerd-rootless.sh @@ -44,6 +44,11 @@ # the host loopback IP address (127.0.0.1) and abstract sockets are exposed to Dockerfile's "RUN" instructions during `nerdctl build` (not `nerdctl run`). # The drawback is fixed in BuildKit v0.13. Upgrading from a prior version of BuildKit needs removing the old systemd unit: # `containerd-rootless-setuptool.sh uninstall-buildkit && rm -f ~/.config/buildkit/buildkitd.toml` +# * CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6=(true|false): whether to enable IPv6 inside the RootlessKit network namespace. +# Defaults to "false". +# This mainly affects outgoing connections with slirp4netns and pasta network drivers. +# It does not affect port forwarding in the built-in port driver. +# Note: The gvisor-tap-vsock network driver does not currently support IPv6. # See also: https://github.com/containerd/nerdctl/blob/main/docs/rootless.md#configuring-rootlesskit @@ -78,6 +83,7 @@ if [ -z "$_CONTAINERD_ROOTLESS_CHILD" ]; then : "${CONTAINERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX:=auto}" : "${CONTAINERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP:=auto}" : "${CONTAINERD_ROOTLESS_ROOTLESSKIT_DETACH_NETNS:=auto}" + : "${CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6:=false}" net=$CONTAINERD_ROOTLESS_ROOTLESSKIT_NET mtu=$CONTAINERD_ROOTLESS_ROOTLESSKIT_MTU if [ -z "$net" ]; then @@ -137,6 +143,19 @@ if [ -z "$_CONTAINERD_ROOTLESS_CHILD" ]; then ;; esac + case "$CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6" in + 1 | true) + CONTAINERD_ROOTLESS_ROOTLESSKIT_FLAGS="--ipv6 $CONTAINERD_ROOTLESS_ROOTLESSKIT_FLAGS" + ;; + 0 | false) + # NOP + ;; + *) + echo "Unknown CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6 value: $CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6" + exit 1 + ;; + esac + # Re-exec the script via RootlessKit, so as to create unprivileged {user,mount,network} namespaces. # # --copy-up allows removing/creating files in the directories by creating tmpfs and symlinks diff --git a/pkg/testutil/testutil_linux.go b/pkg/testutil/testutil_linux.go index 3f7f2c85337..305d3496d6d 100644 --- a/pkg/testutil/testutil_linux.go +++ b/pkg/testutil/testutil_linux.go @@ -16,6 +16,14 @@ package testutil +import ( + "context" + + "github.com/containerd/log" + + "github.com/containerd/nerdctl/v2/pkg/rootlessutil" +) + var ( AlpineImage = GetTestImage("alpine") BusyboxImage = GetTestImage("busybox") @@ -53,3 +61,24 @@ const ( // Foreign layer digest NonDistBlobDigest = "sha256:be691b1535726014cdf3b715ff39361b19e121ca34498a9ceea61ad776b9c215" ) + +// RootlessKitIPv6Enabled reports whether the running RootlessKit parent process supports IPv6. +func RootlessKitIPv6Enabled(ctx context.Context) bool { + rlkClient, err := rootlessutil.NewRootlessKitClient() + if err != nil { + log.G(ctx).WithError(err).Warn("failed to create RootlessKit client") + return false + } + + info, err := rlkClient.Info(ctx) + if err != nil { + log.G(ctx).WithError(err).Warn("failed to get RootlessKit info") + return false + } + + if info != nil && info.NetworkDriver != nil { + return info.NetworkDriver.IPv6 + } + + return false +} From 624b7697246a3aee661fbc19c68bcbfc33f28475 Mon Sep 17 00:00:00 2001 From: akshitguptaa Date: Sun, 12 Jul 2026 23:25:04 +0530 Subject: [PATCH 2/2] ci: add IPv6 target to rootless test matrix Signed-off-by: akshitguptaa --- .github/workflows/job-test-in-host.yml | 2 ++ .github/workflows/workflow-test.yml | 5 +++++ .../container/container_run_network_linux_test.go | 4 ++-- hack/test-integration-rootless.sh | 9 +++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/job-test-in-host.yml b/.github/workflows/job-test-in-host.yml index bd4e29e352b..50e5a0c6f00 100644 --- a/.github/workflows/job-test-in-host.yml +++ b/.github/workflows/job-test-in-host.yml @@ -312,6 +312,7 @@ jobs: INPUTS_TARGET: ${{ inputs.target }} INPUTS_IPV6: ${{ inputs.ipv6 }} CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER: ${{ inputs.target == 'rootless-port-slirp4netns' && 'slirp4netns' || '' }} + CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6: ${{ inputs.ipv6 && 'true' || '' }} # FIXME: this must go - if: ${{ env.SHOULD_RUN == 'yes' && !fromJSON(inputs.skip-flaky) }} @@ -332,3 +333,4 @@ jobs: INPUTS_TARGET: ${{ inputs.target }} INPUTS_IPV6: ${{ inputs.ipv6 }} CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER: ${{ inputs.target == 'rootless-port-slirp4netns' && 'slirp4netns' || '' }} + CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6: ${{ inputs.ipv6 && 'true' || '' }} diff --git a/.github/workflows/workflow-test.yml b/.github/workflows/workflow-test.yml index 1febbd999e1..6a294cdded6 100644 --- a/.github/workflows/workflow-test.yml +++ b/.github/workflows/workflow-test.yml @@ -87,6 +87,11 @@ jobs: - runner: ubuntu-26.04 target: rootless binary: "nerdctl.gomodjail" + # ipv6 + - runner: ubuntu-24.04 + target: rootless + ipv6: true + skip-flaky: true ###### Rootful # amd64 - runner: ubuntu-26.04 diff --git a/cmd/nerdctl/container/container_run_network_linux_test.go b/cmd/nerdctl/container/container_run_network_linux_test.go index 25c62db3733..300014f2f04 100644 --- a/cmd/nerdctl/container/container_run_network_linux_test.go +++ b/cmd/nerdctl/container/container_run_network_linux_test.go @@ -838,8 +838,8 @@ func TestHostsFileMounts(t *testing.T) { } func TestRunContainerWithStaticIP6(t *testing.T) { - if rootlessutil.IsRootless() { - t.Skip("Static IP6 assignment is not supported rootless mode yet.") + if rootlessutil.IsRootless() && !testutil.RootlessKitIPv6Enabled(t.Context()) { + t.Skip("Rootless IPv6 requires CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6=true; see docs/rootless.md") } networkName := "test-network" networkSubnet := "2001:db8:5::/64" diff --git a/hack/test-integration-rootless.sh b/hack/test-integration-rootless.sh index 3bf6bd4d981..1d7763731c8 100755 --- a/hack/test-integration-rootless.sh +++ b/hack/test-integration-rootless.sh @@ -82,6 +82,15 @@ if [ ! -e "$HOME/.config/nerdctl-test-setup-done" ]; then systemctl --user daemon-reload fi + if [ "${CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6:-false}" = "true" ]; then + mkdir -p "$HOME/.config/systemd/user/containerd.service.d" + cat <<-EOF >"$HOME/.config/systemd/user/containerd.service.d/ipv6.conf" + [Service] + Environment="CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6=true" + EOF + systemctl --user daemon-reload + fi + containerd-rootless-setuptool.sh install if grep -q "options use-vc" /etc/resolv.conf; then containerd-rootless-setuptool.sh nsenter -- sh -euc 'echo "options use-vc" >>/etc/resolv.conf'