Skip to content

Commit

Permalink
[WIP] [Carry 2535] rootless: support detach-netns mode
Browse files Browse the repository at this point in the history
When RootlessKit v2.0 (rootless-containers/rootlesskit PR 379) is
installed, `containerd-rootless.sh` launches it with `--detach-netns`
so as to run the daemon in the host network namespace.

This will enable:
- Accelerated (and deflaked) `nerdctl pull`, `nerdctl push`, `nerdctl build`, etc
- Proper support for `nerdctl pull 127.0.0.1:.../...`
- Proper support for `nerdctl run --net=host`

Replaces Fahed Dorgaa's PR 2535

Co-authored-by: fahed dorgaa <[email protected]>
Signed-off-by: Akihiro Suda <[email protected]>
  • Loading branch information
AkihiroSuda and fahedouch committed Jan 2, 2024
1 parent 17c93dc commit 18acfa9
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ARG STARGZ_SNAPSHOTTER_VERSION=v0.15.1
# Extra deps: Encryption
ARG IMGCRYPT_VERSION=v1.1.9
# Extra deps: Rootless
ARG ROOTLESSKIT_VERSION=v1.1.1
ARG ROOTLESSKIT_VERSION=v2.0.0-alpha.2
ARG SLIRP4NETNS_VERSION=v1.2.2
# Extra deps: bypass4netns
ARG BYPASS4NETNS_VERSION=v0.3.0
Expand Down
6 changes: 6 additions & 0 deletions Dockerfile.d/SHA256SUMS.d/rootlesskit-v2.0.0-alpha.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
7c8c07c159aef32b5e68f5b8dc508dc422499744f61fa916c246bfae16a0d85c rootlesskit-aarch64.tar.gz
683ba2c34bfa4a3477f9c50508a233c94b71a6eeaa0ee080abd1ebc2c09a8b9c rootlesskit-armv7l.tar.gz
844b6297021d914be9f266e341ff77da4b98f43074504fe9cc020ae5c61a791d rootlesskit-ppc64le.tar.gz
d317e9c519d862508d6659083f9e1773573e899aa6e48e89d121211e5e823b6a rootlesskit-riscv64.tar.gz
720b425c608f8ab4326354582bc92825031d9d8c40865df155c2c7cb8368f115 rootlesskit-s390x.tar.gz
d29edd2e3d903974754edb14b251ef19bfa9317e6626436fac760d1213879e8d rootlesskit-x86_64.tar.gz
7 changes: 6 additions & 1 deletion extras/rootless/containerd-rootless-setuptool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ cmd_entrypoint_check() {
cmd_entrypoint_nsenter() {
# No need to call init()
pid=$(cat "$XDG_RUNTIME_DIR/containerd-rootless/child_pid")
exec nsenter --no-fork --wd="$(pwd)" --preserve-credentials -m -n -U -t "$pid" -- "$@"
n=""
# If RootlessKit is running with `--detach-netns` mode, we do NOT enter the detached netns here
if [ ! -e "$XDG_RUNTIME_DIR/containerd-rootless/netns" ]; then
n="-n"
fi
exec nsenter --no-fork --wd="$(pwd)" --preserve-credentials -m $n -U -t "$pid" -- "$@"
}

show_systemd_error() {
Expand Down
3 changes: 3 additions & 0 deletions extras/rootless/containerd-rootless.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ if [ -z $_CONTAINERD_ROOTLESS_CHILD ]; then
export _CONTAINERD_ROOTLESS_SELINUX
fi
fi
if rootlesskit --help | grep -qw -- "--detach-netns"; then
CONTAINERD_ROOTLESS_ROOTLESSKIT_FLAGS=--detach-netns $CONTAINERD_ROOTLESS_ROOTLESSKIT_FLAGS
fi
# 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
24 changes: 24 additions & 0 deletions pkg/cmd/container/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"github.com/containerd/nerdctl/pkg/namestore"
"github.com/containerd/nerdctl/pkg/platformutil"
"github.com/containerd/nerdctl/pkg/referenceutil"
"github.com/containerd/nerdctl/pkg/rootlessutil"
"github.com/containerd/nerdctl/pkg/strutil"
dockercliopts "github.com/docker/cli/opts"
dockeropts "github.com/docker/docker/opts"
Expand Down Expand Up @@ -418,6 +419,29 @@ func GenerateLogURI(dataStore string) (*url.URL, error) {
}

func withNerdctlOCIHook(cmd string, args []string) (oci.SpecOpts, error) {
if rootlessutil.IsRootless() {
detachedNetNS, err := rootlessutil.DetachedNetNS()
if err != nil {
return nil, fmt.Errorf("failed to check whether RootlessKit is running with --detach-netns: %w", err)
}
if detachedNetNS != "" {
// Rewrite {cmd, args} if RootlessKit with running with --detach-netns, so that the hook can gain
// CAP_NET_ADMIN in the namespaces.
// - Old:
// - cmd: "/usr/local/bin/nerdctl"
// - args: {"--data-root=/foo"}
// - New:
// - cmd: "/usr/bin/nsenter"
// - args: {"-n/run/user/1000/containerd-rootless/netns", "--", "/usr/local/bin/nerdctl", "--data-root=/foo"}
oldCmd, oldArgs := cmd, args
cmd, err = exec.LookPath("nsenter")
if err != nil {
return nil, err
}
args = append([]string{"-n" + detachedNetNS, "--", oldCmd}, oldArgs...)
}
}

args = append([]string{cmd}, append(args, "internal", "oci-hook")...)
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
if s.Hooks == nil {
Expand Down
29 changes: 29 additions & 0 deletions pkg/containerutil/container_network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/containerd/nerdctl/pkg/mountutil"
"github.com/containerd/nerdctl/pkg/netutil"
"github.com/containerd/nerdctl/pkg/netutil/nettype"
"github.com/containerd/nerdctl/pkg/rootlessutil"
"github.com/containerd/nerdctl/pkg/strutil"
"github.com/opencontainers/runtime-spec/specs-go"
)
Expand Down Expand Up @@ -461,9 +462,37 @@ func (m *hostNetworkManager) ContainerNetworkingOpts(_ context.Context, containe
}
}

if rootlessutil.IsRootless() {
detachedNetNS, err := rootlessutil.DetachedNetNS()
if err != nil {
return nil, nil, fmt.Errorf("failed to check whether RootlessKit is running with --detach-netns: %w", err)
}
if detachedNetNS != "" {
// For rootless + host netns, we can't mount /sys.
//
// TODO: consider to just bind-mount /sys from the host with rro,
// when rro is available (kernel >= 5.12, runc >= 1.1)
//
// Relevant: https://github.com/moby/buildkit/blob/v0.12.4/util/rootless/specconv/specconv_linux.go#L15-L34
specs = append(specs, withRemoveSysfs)
}
}

return specs, cOpts, nil
}

func withRemoveSysfs(_ context.Context, _ oci.Client, c *containers.Container, s *oci.Spec) error {
var mounts []specs.Mount // nolint: prealloc
for _, mount := range s.Mounts {
if strings.HasPrefix(mount.Destination, "/sys") {
continue
}
mounts = append(mounts, mount)
}
s.Mounts = mounts
return nil
}

// types.NetworkOptionsManager implementation for CNI networking settings.
// This is a more specialized and OS-dependendant networking model so this
// struct provides different implementations on different platforms.
Expand Down
15 changes: 14 additions & 1 deletion pkg/rootlessutil/parent_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ func ParentMain(hostGatewayIP string) error {
return err
}

detachedNetNSPath, err := detachedNetNS(stateDir)
if err != nil {
return err
}
detachNetNSMode := detachedNetNSPath != ""
log.L.Debugf("RootlessKit detach-netns mode: %v", detachNetNSMode)
if err != nil {
return err
}

wd, err := os.Getwd()
if err != nil {
return err
Expand All @@ -92,10 +102,13 @@ func ParentMain(hostGatewayIP string) error {
"-r/", // root dir (busybox nsenter wants this to be explicitly specified),
"-w" + wd, // work dir
"--preserve-credentials",
"-m", "-n", "-U",
"-m", "-U",
"-t", strconv.Itoa(childPid),
"-F", // no fork
}
if !detachNetNSMode {
args = append(args, "-n")
}
args = append(args, os.Args...)
log.L.Debugf("rootless parent main: executing %q with %v", arg0, args)

Expand Down
25 changes: 25 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 (
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -80,3 +81,27 @@ func RootlessContainredSockAddress() (string, error) {
}
return filepath.Join(fmt.Sprintf("/proc/%d/root/run/containerd/containerd.sock", childPid)), nil
}

// DetachedNetNS returns non-empty netns path if RootlessKit is running with --detach-netns mode.
// Otherwise returns "" without an error.
func DetachedNetNS() (string, error) {
if !IsRootless() {
return "", nil
}
stateDir, err := RootlessKitStateDir()
if err != nil {
return "", err
}
return detachedNetNS(stateDir)
}

func detachedNetNS(stateDir string) (string, error) {
p := filepath.Join(stateDir, "netns")
if _, err := os.Stat(p); err != nil {
if errors.Is(err, os.ErrNotExist) {
return "", nil
}
return "", err
}
return p, nil
}
4 changes: 4 additions & 0 deletions pkg/rootlessutil/rootlessutil_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ func ParentMain(hostGatewayIP string) error {
func RootlessContainredSockAddress() (string, error) {
return "", fmt.Errorf("cannot inspect RootlessKit state on non-Linux hosts")
}

func DetachedNetNS() (string, error) {
return "", nil
}

0 comments on commit 18acfa9

Please sign in to comment.