Browse Source

Merge pull request #47076 from AkihiroSuda/rootlesskit-v2

Update RootlessKit to v2.0.0 (support Pasta)
Sebastiaan van Stijn 1 year ago
parent
commit
b12cf05c82

+ 1 - 1
Dockerfile

@@ -352,7 +352,7 @@ FROM base AS rootlesskit-src
 WORKDIR /usr/src/rootlesskit
 RUN git init . && git remote add origin "https://github.com/rootless-containers/rootlesskit.git"
 # When updating, also update vendor.mod and hack/dockerfile/install/rootlesskit.installer accordingly.
-ARG ROOTLESSKIT_VERSION=v1.1.1
+ARG ROOTLESSKIT_VERSION=v2.0.0
 RUN git fetch -q --depth 1 origin "${ROOTLESSKIT_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
 
 FROM base AS rootlesskit-build

+ 21 - 2
contrib/dockerd-rootless.sh

@@ -9,12 +9,31 @@
 # * Either one of slirp4netns (>= v0.4.0), VPNKit, lxc-user-nic needs to be installed.
 #
 # Recognized environment variables:
-# * DOCKERD_ROOTLESS_ROOTLESSKIT_NET=(slirp4netns|vpnkit|lxc-user-nic): the rootlesskit network driver. Defaults to "slirp4netns" if slirp4netns (>= v0.4.0) is installed. Otherwise defaults to "vpnkit".
+# * DOCKERD_ROOTLESS_ROOTLESSKIT_NET=(slirp4netns|vpnkit|pasta|lxc-user-nic): the rootlesskit network driver. Defaults to "slirp4netns" if slirp4netns (>= v0.4.0) is installed. Otherwise defaults to "vpnkit".
 # * DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=NUM: the MTU value for the rootlesskit network driver. Defaults to 65520 for slirp4netns, 1500 for other drivers.
-# * DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=(builtin|slirp4netns): the rootlesskit port driver. Defaults to "builtin".
+# * DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=(builtin|slirp4netns|implicit): the rootlesskit port driver. Defaults to "builtin".
 # * DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX=(auto|true|false): whether to protect slirp4netns with a dedicated mount namespace. Defaults to "auto".
 # * DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP=(auto|true|false): whether to protect slirp4netns with seccomp. Defaults to "auto".
+
+# To apply an environment variable via systemd, create ~/.config/systemd/user/docker.service.d/override.conf as follows,
+# and run `systemctl --user daemon-reload && systemctl --user restart docker`:
+# --- BEGIN ---
+# [Service]
+# Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_NET=pasta"
+# Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=implicit"
+# ---  END  ---
+
+# Guide to choose the network driver and the port driver:
 #
+#  Network driver | Port driver    | Net throughput | Port throughput | Src IP | No SUID | Note
+#  ---------------|----------------|----------------|-----------------|--------|---------|---------------------------------------------------------
+#  slirp4netns    | builtin        | Slow           | Fast ✅         | ❌     | ✅      | Default in typical setup
+#  vpnkit         | builtin        | Slow           | Fast ✅         | ❌     | ✅      | Default when slirp4netns is not installed
+#  slirp4netns    | slirp4netns    | Slow           | Slow            | ✅     | ✅      |
+#  pasta          | implicit       | Slow           | Fast ✅         | ✅     | ✅      | Experimental; Needs recent version of pasta (2023_12_04)
+#  lxc-user-nic   | builtin        | Fast ✅        | Slow            | ❌     | ❌      | Experimental
+#  (bypass4netns) | (bypass4netns) | Fast ✅        | Fast ✅         | ✅     | ✅      | (Not integrated to RootlessKit)
+
 # See the documentation for the further information: https://docs.docker.com/go/rootless/
 
 set -e -x

+ 1 - 1
daemon/info_unix.go

@@ -21,7 +21,7 @@ import (
 	"github.com/docker/docker/pkg/rootless"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/pkg/errors"
-	rkclient "github.com/rootless-containers/rootlesskit/pkg/api/client"
+	rkclient "github.com/rootless-containers/rootlesskit/v2/pkg/api/client"
 )
 
 // fillPlatformInfo fills the platform related info.

+ 2 - 2
hack/dockerfile/install/rootlesskit.installer

@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # When updating, also update vendor.mod and Dockerfile accordingly.
-: "${ROOTLESSKIT_VERSION:=v1.1.1}"
+: "${ROOTLESSKIT_VERSION:=v2.0.0}"
 
 install_rootlesskit() {
 	case "$1" in
@@ -28,6 +28,6 @@ install_rootlesskit_dynamic() {
 _install_rootlesskit() (
 	echo "Install rootlesskit version ${ROOTLESSKIT_VERSION}"
 	for f in rootlesskit rootlesskit-docker-proxy; do
-		GOBIN="${PREFIX}" GO111MODULE=on go install ${BUILD_MODE} -ldflags="$ROOTLESSKIT_LDFLAGS" "github.com/rootless-containers/rootlesskit/cmd/${f}@${ROOTLESSKIT_VERSION}"
+		GOBIN="${PREFIX}" GO111MODULE=on go install ${BUILD_MODE} -ldflags="$ROOTLESSKIT_LDFLAGS" "github.com/rootless-containers/rootlesskit/v2/cmd/${f}@${ROOTLESSKIT_VERSION}"
 	done
 )

+ 1 - 1
vendor.mod

@@ -82,7 +82,7 @@ require (
 	github.com/pelletier/go-toml v1.9.5
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.14.0
-	github.com/rootless-containers/rootlesskit v1.1.1
+	github.com/rootless-containers/rootlesskit/v2 v2.0.0
 	github.com/sirupsen/logrus v1.9.3
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5

+ 3 - 3
vendor.sum

@@ -117,8 +117,8 @@ github.com/Graylog2/go-gelf v0.0.0-20191017102106-1550ee647df0/go.mod h1:fBaQWrf
 github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
 github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
 github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
 github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
 github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
@@ -1102,8 +1102,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rootless-containers/rootlesskit v1.1.1 h1:F5psKWoWY9/VjZ3ifVcaosjvFZJOagX85U22M0/EQZE=
-github.com/rootless-containers/rootlesskit v1.1.1/go.mod h1:UD5GoA3dqKCJrnvnhVgQQnweMF2qZnf9KLw8EewcMZI=
+github.com/rootless-containers/rootlesskit/v2 v2.0.0 h1:oAtnD6sgsNmdxXCm5zont5nEYTAkSqExmQOalZYiYJM=
+github.com/rootless-containers/rootlesskit/v2 v2.0.0/go.mod h1:G7x0sK6onoLhFYZahpSsM/HaWEAh664SK5nCeQs4MQE=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=

+ 0 - 0
vendor/github.com/rootless-containers/rootlesskit/LICENSE → vendor/github.com/rootless-containers/rootlesskit/v2/LICENSE


+ 0 - 5
vendor/github.com/rootless-containers/rootlesskit/pkg/api/api.go → vendor/github.com/rootless-containers/rootlesskit/v2/pkg/api/api.go

@@ -8,11 +8,6 @@ const (
 	Version = "1.1.1"
 )
 
-// ErrorJSON is returned with "application/json" content type and non-2XX status code
-type ErrorJSON struct {
-	Message string `json:"message"`
-}
-
 // Info is the structure returned by `GET /info`
 type Info struct {
 	APIVersion    string             `json:"apiVersion"` // REST API version

+ 9 - 72
vendor/github.com/rootless-containers/rootlesskit/pkg/api/client/client.go → vendor/github.com/rootless-containers/rootlesskit/v2/pkg/api/client/client.go

@@ -4,15 +4,12 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
-	"io"
-	"net"
 	"net/http"
-	"os"
 
-	"github.com/rootless-containers/rootlesskit/pkg/api"
-	"github.com/rootless-containers/rootlesskit/pkg/port"
+	"github.com/rootless-containers/rootlesskit/v2/pkg/api"
+	"github.com/rootless-containers/rootlesskit/v2/pkg/httputil"
+	"github.com/rootless-containers/rootlesskit/v2/pkg/port"
 )
 
 type Client interface {
@@ -24,17 +21,10 @@ type Client interface {
 // New creates a client.
 // socketPath is a path to the UNIX socket, without unix:// prefix.
 func New(socketPath string) (Client, error) {
-	if _, err := os.Stat(socketPath); err != nil {
+	hc, err := httputil.NewHTTPClient(socketPath)
+	if err != nil {
 		return nil, err
 	}
-	hc := &http.Client{
-		Transport: &http.Transport{
-			DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
-				var d net.Dialer
-				return d.DialContext(ctx, "unix", socketPath)
-			},
-		},
-	}
 	return NewWithHTTPClient(hc), nil
 }
 
@@ -76,7 +66,7 @@ func (c *client) Info(ctx context.Context) (*api.Info, error) {
 		return nil, err
 	}
 	defer resp.Body.Close()
-	if err := successful(resp); err != nil {
+	if err := httputil.Successful(resp); err != nil {
 		return nil, err
 	}
 	var info api.Info
@@ -87,59 +77,6 @@ func (c *client) Info(ctx context.Context) (*api.Info, error) {
 	return &info, nil
 }
 
-func readAtMost(r io.Reader, maxBytes int) ([]byte, error) {
-	lr := &io.LimitedReader{
-		R: r,
-		N: int64(maxBytes),
-	}
-	b, err := io.ReadAll(lr)
-	if err != nil {
-		return b, err
-	}
-	if lr.N == 0 {
-		return b, fmt.Errorf("expected at most %d bytes, got more", maxBytes)
-	}
-	return b, nil
-}
-
-// HTTPStatusErrorBodyMaxLength specifies the maximum length of HTTPStatusError.Body
-const HTTPStatusErrorBodyMaxLength = 64 * 1024
-
-// HTTPStatusError is created from non-2XX HTTP response
-type HTTPStatusError struct {
-	// StatusCode is non-2XX status code
-	StatusCode int
-	// Body is at most HTTPStatusErrorBodyMaxLength
-	Body string
-}
-
-// Error implements error.
-// If e.Body is a marshalled string of api.ErrorJSON, Error returns ErrorJSON.Message .
-// Otherwise Error returns a human-readable string that contains e.StatusCode and e.Body.
-func (e *HTTPStatusError) Error() string {
-	if e.Body != "" && len(e.Body) < HTTPStatusErrorBodyMaxLength {
-		var ej api.ErrorJSON
-		if json.Unmarshal([]byte(e.Body), &ej) == nil {
-			return ej.Message
-		}
-	}
-	return fmt.Sprintf("unexpected HTTP status %s, body=%q", http.StatusText(e.StatusCode), e.Body)
-}
-
-func successful(resp *http.Response) error {
-	if resp == nil {
-		return errors.New("nil response")
-	}
-	if resp.StatusCode/100 != 2 {
-		b, _ := readAtMost(resp.Body, HTTPStatusErrorBodyMaxLength)
-		return &HTTPStatusError{
-			StatusCode: resp.StatusCode,
-			Body:       string(b),
-		}
-	}
-	return nil
-}
-
 type portManager struct {
 	*client
 }
@@ -161,7 +98,7 @@ func (pm *portManager) AddPort(ctx context.Context, spec port.Spec) (*port.Statu
 		return nil, err
 	}
 	defer resp.Body.Close()
-	if err := successful(resp); err != nil {
+	if err := httputil.Successful(resp); err != nil {
 		return nil, err
 	}
 	dec := json.NewDecoder(resp.Body)
@@ -183,7 +120,7 @@ func (pm *portManager) ListPorts(ctx context.Context) ([]port.Status, error) {
 		return nil, err
 	}
 	defer resp.Body.Close()
-	if err := successful(resp); err != nil {
+	if err := httputil.Successful(resp); err != nil {
 		return nil, err
 	}
 	var statuses []port.Status
@@ -205,7 +142,7 @@ func (pm *portManager) RemovePort(ctx context.Context, id int) error {
 		return err
 	}
 	defer resp.Body.Close()
-	if err := successful(resp); err != nil {
+	if err := httputil.Successful(resp); err != nil {
 		return err
 	}
 	return nil

+ 0 - 0
vendor/github.com/rootless-containers/rootlesskit/pkg/api/openapi.yaml → vendor/github.com/rootless-containers/rootlesskit/v2/pkg/api/openapi.yaml


+ 96 - 0
vendor/github.com/rootless-containers/rootlesskit/v2/pkg/httputil/httputil.go

@@ -0,0 +1,96 @@
+package httputil
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"os"
+)
+
+// ErrorJSON is returned with "application/json" content type and non-2XX status code
+type ErrorJSON struct {
+	Message string `json:"message"`
+}
+
+func readAtMost(r io.Reader, maxBytes int) ([]byte, error) {
+	lr := &io.LimitedReader{
+		R: r,
+		N: int64(maxBytes),
+	}
+	b, err := io.ReadAll(lr)
+	if err != nil {
+		return b, err
+	}
+	if lr.N == 0 {
+		return b, fmt.Errorf("expected at most %d bytes, got more", maxBytes)
+	}
+	return b, nil
+}
+
+// HTTPStatusErrorBodyMaxLength specifies the maximum length of HTTPStatusError.Body
+const HTTPStatusErrorBodyMaxLength = 64 * 1024
+
+// HTTPStatusError is created from non-2XX HTTP response
+type HTTPStatusError struct {
+	// StatusCode is non-2XX status code
+	StatusCode int
+	// Body is at most HTTPStatusErrorBodyMaxLength
+	Body string
+}
+
+// Error implements error.
+// If e.Body is a marshalled string of api.ErrorJSON, Error returns ErrorJSON.Message .
+// Otherwise Error returns a human-readable string that contains e.StatusCode and e.Body.
+func (e *HTTPStatusError) Error() string {
+	if e.Body != "" && len(e.Body) < HTTPStatusErrorBodyMaxLength {
+		var ej ErrorJSON
+		if json.Unmarshal([]byte(e.Body), &ej) == nil {
+			return ej.Message
+		}
+	}
+	return fmt.Sprintf("unexpected HTTP status %s, body=%q", http.StatusText(e.StatusCode), e.Body)
+}
+
+// Successful returns an error if the status code is not 2xx.
+func Successful(resp *http.Response) error {
+	if resp == nil {
+		return errors.New("nil response")
+	}
+	if resp.StatusCode/100 != 2 {
+		b, _ := readAtMost(resp.Body, HTTPStatusErrorBodyMaxLength)
+		return &HTTPStatusError{
+			StatusCode: resp.StatusCode,
+			Body:       string(b),
+		}
+	}
+	return nil
+}
+
+func NewHTTPClient(socketPath string) (*http.Client, error) {
+	if _, err := os.Stat(socketPath); err != nil {
+		return nil, err
+	}
+	return &http.Client{
+		Transport: &http.Transport{
+			DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
+				var d net.Dialer
+				return d.DialContext(ctx, "unix", socketPath)
+			},
+		},
+	}, nil
+}
+
+// WriteError writes an error.
+// WriteError sould not be used if an error may contain sensitive information and the client is not reliable.
+func WriteError(w http.ResponseWriter, r *http.Request, err error, ec int) {
+	w.WriteHeader(ec)
+	w.Header().Set("Content-Type", "application/json")
+	e := ErrorJSON{
+		Message: err.Error(),
+	}
+	_ = json.NewEncoder(w).Encode(e)
+}

+ 3 - 4
vendor/github.com/rootless-containers/rootlesskit/pkg/port/port.go → vendor/github.com/rootless-containers/rootlesskit/v2/pkg/port/port.go

@@ -4,7 +4,7 @@ import (
 	"context"
 	"net"
 
-	"github.com/rootless-containers/rootlesskit/pkg/api"
+	"github.com/rootless-containers/rootlesskit/v2/pkg/api"
 )
 
 type Spec struct {
@@ -35,8 +35,6 @@ type Manager interface {
 
 // ChildContext is used for RunParentDriver
 type ChildContext struct {
-	// PID of the child, can be used for ns-entering to the child namespaces.
-	PID int
 	// IP of the tap device
 	IP net.IP
 }
@@ -57,5 +55,6 @@ type ParentDriver interface {
 }
 
 type ChildDriver interface {
-	RunChildDriver(opaque map[string]string, quit <-chan struct{}) error
+	// RunChildDriver is executed in the child's namespaces, excluding detached-netns.
+	RunChildDriver(opaque map[string]string, quit <-chan struct{}, detachedNetNSPath string) error
 }

+ 5 - 4
vendor/modules.txt

@@ -1002,11 +1002,12 @@ github.com/prometheus/common/model
 github.com/prometheus/procfs
 github.com/prometheus/procfs/internal/fs
 github.com/prometheus/procfs/internal/util
-# github.com/rootless-containers/rootlesskit v1.1.1
+# github.com/rootless-containers/rootlesskit/v2 v2.0.0
 ## explicit; go 1.19
-github.com/rootless-containers/rootlesskit/pkg/api
-github.com/rootless-containers/rootlesskit/pkg/api/client
-github.com/rootless-containers/rootlesskit/pkg/port
+github.com/rootless-containers/rootlesskit/v2/pkg/api
+github.com/rootless-containers/rootlesskit/v2/pkg/api/client
+github.com/rootless-containers/rootlesskit/v2/pkg/httputil
+github.com/rootless-containers/rootlesskit/v2/pkg/port
 # github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529
 ## explicit
 github.com/sean-/seed