Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/job-test-in-host.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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) }}
Expand All @@ -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' || '' }}
5 changes: 5 additions & 0 deletions .github/workflows/workflow-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions cmd/nerdctl/container/container_run_network_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() && !rootlessutil.RootlessKitIPv6Enabled() {
t.Skip("Rootless IPv6 requires CONTAINERD_ROOTLESS_ROOTLESSKIT_IPV6=true; see docs/rootless.md")
}
networkName := "test-network"
networkSubnet := "2001:db8:5::/64"
Expand Down
5 changes: 5 additions & 0 deletions docs/rootless.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ 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 <v6-subnet>` as usual. Published ports over IPv6 are
supported with `--port-driver=builtin` (the default). Other port drivers do not yet
support IPv6 port forwarding upstream.

To set these variables, create `~/.config/systemd/user/containerd.service.d/override.conf` as follows:
```ini
Expand Down
18 changes: 18 additions & 0 deletions extras/rootless/containerd-rootless.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
# 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". Requires a RootlessKit network driver that supports IPv6.
# Published ports over IPv6 are currently only supported when using '--port-driver=builtin'.
# Other port drivers (such as slirp4netns or implicit/pasta) do not currently support IPv6 port forwarding.

# See also: https://github.com/containerd/nerdctl/blob/main/docs/rootless.md#configuring-rootlesskit

Expand Down Expand Up @@ -78,6 +82,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
Expand Down Expand Up @@ -137,6 +142,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
Expand Down
9 changes: 9 additions & 0 deletions hack/test-integration-rootless.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
Akshitguptaa marked this conversation as resolved.
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'
Expand Down
23 changes: 23 additions & 0 deletions pkg/rootlessutil/rootlessutil_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package rootlessutil

import (
"bytes"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -119,3 +120,25 @@ func WithDetachedNetNSIfAny(fn func() error) error {
}
return ns.WithNetNSPath(netns, func(_ ns.NetNS) error { return fn() })
}

// RootlessKitIPv6Enabled reports whether the running RootlessKit parent process was launched with --ipv6.
func RootlessKitIPv6Enabled() bool {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks that is a better approach

I've opened rootless-containers/rootlesskit#607 to add the IPv6 boolean to NetworkDriverInfo

could you take a look at that pr when you have a chance?
once that is merged, I can update this logic as well and we can get this PR merged as well.

@AkihiroSuda AkihiroSuda Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider moving this to testutil pkg

stateDir, err := RootlessKitStateDir()
if err != nil {
return false
}
pid, err := RootlessKitChildPid(stateDir)
if err != nil {
return false
}
cmdline, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "cmdline"))
if err != nil {
return false
}
for _, arg := range bytes.Split(cmdline, []byte{0}) {
if string(arg) == "--ipv6" {
return true
}
}
return false
}
Loading