Selaa lähdekoodia

overlay2: support "userxattr" option (kernel 5.11)

The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11.

The "userxattr" option is NOT needed for the initial user namespace (aka "the host").

Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount the overlayfs in a user namespace without the "userxattr" option.

The corresponding kernel commit: 2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
> **ovl: user xattr**
>
> Optionally allow using "user.overlay." namespace instead of "trusted.overlay."
> ...
> Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the
> "user.overlay.redirect" or "user.overlay.metacopy" xattrs.

Fix issue 42055

Related to containerd/containerd PR 5076

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Akihiro Suda 4 vuotta sitten
vanhempi
commit
11ef8d3ba9

+ 17 - 6
daemon/graphdriver/overlay2/overlay.go

@@ -113,7 +113,8 @@ var (
 	useNaiveDiffLock sync.Once
 	useNaiveDiffOnly bool
 
-	indexOff string
+	indexOff  string
+	userxattr string
 )
 
 func init() {
@@ -204,7 +205,16 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 		logger.Warnf("Unable to detect whether overlay kernel module supports index parameter: %s", err)
 	}
 
-	logger.Debugf("backingFs=%s, projectQuotaSupported=%v, indexOff=%q", backingFs, projectQuotaSupported, indexOff)
+	needsUserXattr, err := overlayutils.NeedsUserXAttr(home)
+	if err != nil {
+		logger.Warnf("Unable to detect whether overlay kernel module needs \"userxattr\" parameter: %s", err)
+	}
+	if needsUserXattr {
+		userxattr = "userxattr,"
+	}
+
+	logger.Debugf("backingFs=%s, projectQuotaSupported=%v, indexOff=%q, userxattr=%q",
+		backingFs, projectQuotaSupported, indexOff, userxattr)
 
 	return d, nil
 }
@@ -257,6 +267,7 @@ func (d *Driver) Status() [][2]string {
 		{"Backing Filesystem", backingFs},
 		{"Supports d_type", strconv.FormatBool(d.supportsDType)},
 		{"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))},
+		{"userxattr", strconv.FormatBool(userxattr != "")},
 	}
 }
 
@@ -546,9 +557,9 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
 
 	var opts string
 	if readonly {
-		opts = indexOff + "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":")
+		opts = indexOff + userxattr + "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":")
 	} else {
-		opts = indexOff + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir
+		opts = indexOff + userxattr + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir
 	}
 
 	mountData := label.FormatMountLabel(opts, mountLabel)
@@ -571,9 +582,9 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
 	// smaller at the expense of requiring a fork exec to chroot.
 	if len(mountData) > pageSize-1 {
 		if readonly {
-			opts = indexOff + "lowerdir=" + path.Join(id, diffDirName) + ":" + string(lowers)
+			opts = indexOff + userxattr + "lowerdir=" + path.Join(id, diffDirName) + ":" + string(lowers)
 		} else {
-			opts = indexOff + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName)
+			opts = indexOff + userxattr + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName)
 		}
 		mountData = label.FormatMountLabel(opts, mountLabel)
 		if len(mountData) > pageSize-1 {

+ 112 - 0
daemon/graphdriver/overlayutils/userxattr.go

@@ -0,0 +1,112 @@
+// +build linux
+
+// Forked from https://github.com/containerd/containerd/blob/9ade247b38b5a685244e1391c86ff41ab109556e/snapshots/overlay/check.go
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package overlayutils
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	"github.com/containerd/containerd/mount"
+	"github.com/containerd/containerd/sys"
+	"github.com/sirupsen/logrus"
+)
+
+// NeedsUserXAttr returns whether overlayfs should be mounted with the "userxattr" mount option.
+//
+// The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11.
+//
+// The "userxattr" option is NOT needed for the initial user namespace (aka "the host").
+//
+// Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount
+// the overlayfs in a user namespace without the "userxattr" option.
+//
+// The corresponding kernel commit: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
+// > ovl: user xattr
+// >
+// > Optionally allow using "user.overlay." namespace instead of "trusted.overlay."
+// > ...
+// > Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the
+// > "user.overlay.redirect" or "user.overlay.metacopy" xattrs.
+// > ...
+//
+// The "userxattr" support is not exposed in "/sys/module/overlay/parameters".
+func NeedsUserXAttr(d string) (bool, error) {
+	if !sys.RunningInUserNS() {
+		// we are the real root (i.e., the root in the initial user NS),
+		// so we do never need "userxattr" opt.
+		return false, nil
+	}
+
+	// TODO: add fast path for kernel >= 5.11 .
+	//
+	// Keep in mind that distro vendors might be going to backport the patch to older kernels.
+	// So we can't completely remove the check.
+
+	tdRoot := filepath.Join(d, "userxattr-check")
+	if err := os.RemoveAll(tdRoot); err != nil {
+		logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
+	}
+
+	if err := os.MkdirAll(tdRoot, 0700); err != nil {
+		return false, err
+	}
+
+	defer func() {
+		if err := os.RemoveAll(tdRoot); err != nil {
+			logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
+		}
+	}()
+
+	td, err := ioutil.TempDir(tdRoot, "")
+	if err != nil {
+		return false, err
+	}
+
+	for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
+		if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
+			return false, err
+		}
+	}
+
+	opts := []string{
+		fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"), filepath.Join(td, "upper"), filepath.Join(td, "work")),
+		"userxattr",
+	}
+
+	m := mount.Mount{
+		Type:    "overlay",
+		Source:  "overlay",
+		Options: opts,
+	}
+
+	dest := filepath.Join(td, "merged")
+	if err := m.Mount(dest); err != nil {
+		// Probably the host is running Ubuntu/Debian kernel (< 5.11) with the userns patch but without the userxattr patch.
+		// Return false without error.
+		logrus.WithError(err).Debugf("cannot mount overlay with \"userxattr\", probably the kernel does not support userxattr")
+		return false, nil
+	}
+	if err := mount.UnmountAll(dest, 0); err != nil {
+		logrus.WithError(err).Warnf("Failed to unmount check directory %v", dest)
+	}
+	return true, nil
+}