mirror of
https://github.com/moby/buildkit.git
synced 2026-06-30 19:57:39 +00:00
rootless: fix up unprivileged mount opts
Port https://github.com/moby/moby/blob/v23.0.1/daemon/oci_linux.go#L430-L460 > // Get the set of mount flags that are set on the mount that contains the given > // path and are locked by CL_UNPRIVILEGED. This is necessary to ensure that > // bind-mounting "with options" will not fail with user namespaces, due to > // kernel restrictions that require user namespace mounts to preserve > // CL_UNPRIVILEGED locked flags. Fix issue 3098 Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
8
cache/refs.go
vendored
8
cache/refs.go
vendored
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/pkg/userns"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"github.com/moby/buildkit/util/flightcontrol"
|
||||
"github.com/moby/buildkit/util/leaseutil"
|
||||
"github.com/moby/buildkit/util/progress"
|
||||
rootlessmountopts "github.com/moby/buildkit/util/rootless/mountopts"
|
||||
"github.com/moby/buildkit/util/winlayers"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
@@ -1640,6 +1642,12 @@ func (sm *sharableMountable) Mount() (_ []mount.Mount, _ func() error, retErr er
|
||||
os.Remove(dir)
|
||||
}
|
||||
}()
|
||||
if userns.RunningInUserNS() {
|
||||
mounts, err = rootlessmountopts.FixUp(mounts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
if err := mount.All(mounts, dir); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -11,12 +11,14 @@ import (
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/pkg/userns"
|
||||
"github.com/containerd/continuity/fs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
"github.com/moby/buildkit/executor"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
rootlessmountopts "github.com/moby/buildkit/util/rootless/mountopts"
|
||||
traceexec "github.com/moby/buildkit/util/tracing/exec"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
@@ -193,6 +195,14 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
|
||||
}
|
||||
|
||||
s.Mounts = dedupMounts(s.Mounts)
|
||||
|
||||
if userns.RunningInUserNS() {
|
||||
s.Mounts, err = rootlessmountopts.FixUpOCI(s.Mounts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, releaseAll, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/pkg/userns"
|
||||
rootlessmountopts "github.com/moby/buildkit/util/rootless/mountopts"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -24,6 +26,14 @@ func (lm *localMounter) Mount() (string, error) {
|
||||
lm.release = release
|
||||
}
|
||||
|
||||
if userns.RunningInUserNS() {
|
||||
var err error
|
||||
lm.mounts, err = rootlessmountopts.FixUp(lm.mounts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if len(lm.mounts) == 1 && (lm.mounts[0].Type == "bind" || lm.mounts[0].Type == "rbind") {
|
||||
ro := false
|
||||
for _, opt := range lm.mounts[0].Options {
|
||||
|
||||
88
util/rootless/mountopts/mountopts_linux.go
Normal file
88
util/rootless/mountopts/mountopts_linux.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package mountopts
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/moby/buildkit/util/strutil"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// UnprivilegedMountFlags gets the set of mount flags that are set on the mount that contains the given
|
||||
// path and are locked by CL_UNPRIVILEGED. This is necessary to ensure that
|
||||
// bind-mounting "with options" will not fail with user namespaces, due to
|
||||
// kernel restrictions that require user namespace mounts to preserve
|
||||
// CL_UNPRIVILEGED locked flags.
|
||||
//
|
||||
// From https://github.com/moby/moby/blob/v23.0.1/daemon/oci_linux.go#L430-L460
|
||||
func UnprivilegedMountFlags(path string) ([]string, error) {
|
||||
var statfs unix.Statfs_t
|
||||
if err := unix.Statfs(path, &statfs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The set of keys come from https://github.com/torvalds/linux/blob/v4.13/fs/namespace.c#L1034-L1048.
|
||||
unprivilegedFlags := map[uint64]string{
|
||||
unix.MS_RDONLY: "ro",
|
||||
unix.MS_NODEV: "nodev",
|
||||
unix.MS_NOEXEC: "noexec",
|
||||
unix.MS_NOSUID: "nosuid",
|
||||
unix.MS_NOATIME: "noatime",
|
||||
unix.MS_RELATIME: "relatime",
|
||||
unix.MS_NODIRATIME: "nodiratime",
|
||||
}
|
||||
|
||||
var flags []string
|
||||
for mask, flag := range unprivilegedFlags {
|
||||
if uint64(statfs.Flags)&mask == mask {
|
||||
flags = append(flags, flag)
|
||||
}
|
||||
}
|
||||
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
// FixUp is for https://github.com/moby/buildkit/issues/3098
|
||||
func FixUp(mounts []mount.Mount) ([]mount.Mount, error) {
|
||||
for i, m := range mounts {
|
||||
var isBind bool
|
||||
for _, o := range m.Options {
|
||||
switch o {
|
||||
case "bind", "rbind":
|
||||
isBind = true
|
||||
}
|
||||
}
|
||||
if !isBind {
|
||||
continue
|
||||
}
|
||||
unpriv, err := UnprivilegedMountFlags(m.Source)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get unprivileged mount flags for %+v", m)
|
||||
}
|
||||
m.Options = strutil.DedupeSlice(append(m.Options, unpriv...))
|
||||
mounts[i] = m
|
||||
}
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
func FixUpOCI(mounts []specs.Mount) ([]specs.Mount, error) {
|
||||
for i, m := range mounts {
|
||||
var isBind bool
|
||||
for _, o := range m.Options {
|
||||
switch o {
|
||||
case "bind", "rbind":
|
||||
isBind = true
|
||||
}
|
||||
}
|
||||
if !isBind {
|
||||
continue
|
||||
}
|
||||
unpriv, err := UnprivilegedMountFlags(m.Source)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get unprivileged mount flags for %+v", m)
|
||||
}
|
||||
m.Options = strutil.DedupeSlice(append(m.Options, unpriv...))
|
||||
mounts[i] = m
|
||||
}
|
||||
return mounts, nil
|
||||
}
|
||||
21
util/rootless/mountopts/mountopts_others.go
Normal file
21
util/rootless/mountopts/mountopts_others.go
Normal file
@@ -0,0 +1,21 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package mountopts
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/mount"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func UnprivilegedMountFlags(path string) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func FixUp(mounts []mount.Mount) ([]mount.Mount, error) {
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
func FixUpOCI(mounts []specs.Mount) ([]specs.Mount, error) {
|
||||
return mounts, nil
|
||||
}
|
||||
30
util/strutil/strutil.go
Normal file
30
util/strutil/strutil.go
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 strutil
|
||||
|
||||
// DedupeSlice is from https://github.com/containerd/nerdctl/blob/v1.2.1/pkg/strutil/strutil.go#L72-L82
|
||||
func DedupeSlice(in []string) []string {
|
||||
m := make(map[string]struct{})
|
||||
var res []string
|
||||
for _, s := range in {
|
||||
if _, ok := m[s]; !ok {
|
||||
res = append(res, s)
|
||||
m[s] = struct{}{}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user