Browse code

plugin: fix mounting /etc/hosts when running in UserNS

Fix `error mounting "/etc/hosts" to rootfs at "/etc/hosts": mount
/etc/hosts:/etc/hosts (via /proc/self/fd/6), flags: 0x5021: operation
not permitted`.

This error was introduced in 7d08d84b039d2f4661a2242e765a141e65943920
(`dockerd-rootless.sh: set rootlesskit --state-dir=DIR`) that changed
the filesystem of the state dir from /tmp to /run (in a typical setup).

Fix issue 47248

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>

Akihiro Suda authored on 2024/03/14 13:49:35
Showing 1 changed files
... ...
@@ -1,3 +1,6 @@
1
+// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2
+//go:build go1.19
3
+
1 4
 package v2 // import "github.com/docker/docker/plugin/v2"
2 5
 
3 6
 import (
... ...
@@ -6,7 +9,10 @@ import (
6 6
 	"runtime"
7 7
 	"strings"
8 8
 
9
+	"github.com/containerd/containerd/pkg/userns"
9 10
 	"github.com/docker/docker/api/types"
11
+	"github.com/docker/docker/internal/rootless/mountopts"
12
+	"github.com/docker/docker/internal/sliceutil"
10 13
 	"github.com/docker/docker/oci"
11 14
 	specs "github.com/opencontainers/runtime-spec/specs-go"
12 15
 	"github.com/pkg/errors"
... ...
@@ -136,5 +142,35 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
136 136
 		p.modifyRuntimeSpec(&s)
137 137
 	}
138 138
 
139
+	// Rootless mode requires modifying the mount flags
140
+	// https://github.com/moby/moby/issues/47248#issuecomment-1927776700
141
+	// https://github.com/moby/moby/pull/47558
142
+	if userns.RunningInUserNS() {
143
+		for i := range s.Mounts {
144
+			m := &s.Mounts[i]
145
+			for _, o := range m.Options {
146
+				switch o {
147
+				case "bind", "rbind":
148
+					if _, err := os.Lstat(m.Source); err != nil {
149
+						if errors.Is(err, os.ErrNotExist) {
150
+							continue
151
+						}
152
+						return nil, err
153
+					}
154
+					// UnprivilegedMountFlags gets the set of mount flags that are set on the mount that contains the given
155
+					// path and are locked by CL_UNPRIVILEGED. This is necessary to ensure that
156
+					// bind-mounting "with options" will not fail with user namespaces, due to
157
+					// kernel restrictions that require user namespace mounts to preserve
158
+					// CL_UNPRIVILEGED locked flags.
159
+					unpriv, err := mountopts.UnprivilegedMountFlags(m.Source)
160
+					if err != nil {
161
+						return nil, errors.Wrapf(err, "failed to get unprivileged mount flags for %+v", m)
162
+					}
163
+					m.Options = sliceutil.Dedup(append(m.Options, unpriv...))
164
+				}
165
+			}
166
+		}
167
+	}
168
+
139 169
 	return &s, nil
140 170
 }