blob: 2f754b675a6e023958a121cb1efa3a8c9f6f3232 [file] [log] [blame]
Copyright 2020 The Monogon Project 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.
From 8335005ed1983ca5ac036af15dd04b8717898c35 Mon Sep 17 00:00:00 2001
From: Lorenz Brun <lorenz@brun.one>
Date: Mon, 16 Mar 2020 22:13:08 +0100
Subject: [PATCH 1/3] Provide native mounter implementation for Linux
---
BUILD.bazel | 2 +
mount/mount_linux.go | 141 ++++++++++++++++++++++-
2 files changed, 139 insertions(+), 4 deletions(-)
diff --git a/mount/BUILD b/mount/BUILD.bazel
index bef3ec2cf55..6f997103dac 100644
--- a/mount/BUILD.bazel
+++ b/mount/BUILD.bazel
@@ -21,6 +21,7 @@ go_library(
"//exec:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:android": [
+ "@org_golang_x_sys//unix:go_default_library",
"//io:go_default_library",
],
"@io_bazel_rules_go//go/platform:darwin": [
@@ -36,6 +37,7 @@ go_library(
"//io:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux": [
+ "@org_golang_x_sys//unix:go_default_library",
"//io:go_default_library",
],
"@io_bazel_rules_go//go/platform:nacl": [
diff --git a/mount/mount_linux.go b/mount/mount_linux.go
index 41f69efe3f0..01182684653 100644
--- a/mount/mount_linux.go
+++ b/mount/mount_linux.go
@@ -20,6 +20,7 @@ package mount
import (
"fmt"
+ "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -27,6 +28,7 @@ import (
"strings"
"syscall"
+ "golang.org/x/sys/unix"
"k8s.io/klog/v2"
utilexec "k8s.io/utils/exec"
utilio "k8s.io/utils/io"
@@ -49,8 +51,10 @@ const (
// for the linux platform. This implementation assumes that the
// kubelet is running in the host's root mount namespace.
type Mounter struct {
- mounterPath string
- withSystemd bool
+ mounterPath string
+ withSystemd bool
+ withLinuxUtils bool
+ nativeSupportedFstypes map[string]struct{}
}
// New returns a mount.Interface for the current system.
@@ -58,8 +62,10 @@ type Mounter struct {
// mounterPath allows using an alternative to `/bin/mount` for mounting.
func New(mounterPath string) Interface {
return &Mounter{
- mounterPath: mounterPath,
- withSystemd: detectSystemd(),
+ mounterPath: mounterPath,
+ withSystemd: detectSystemd(),
+ withLinuxUtils: detectLinuxUtils(),
+ nativeSupportedFstypes: detectNativeSupportedFstypes(),
}
}
@@ -78,6 +84,29 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
// method should be used by callers that pass sensitive material (like
// passwords) as mount options.
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
+ if !mounter.withLinuxUtils {
+ flags, pflags, fsoptions := parseMountOptions(options)
+ if len(pflags) > 0 {
+ return fmt.Errorf("the native mounter is active and does not support mount propagation at the moment")
+ }
+
+ if !mounter.nativeSupportsFstype(fstype) && flags&unix.MS_BIND == 0 {
+ return fmt.Errorf("the native mounter is active and cannot mount filesystems of type \"%v\"", fstype)
+ }
+
+ if flags&unix.MS_BIND != 0 && flags & ^uintptr(unix.MS_BIND) != 0 {
+ if err := unix.Mount(source, target, "", unix.MS_BIND, ""); err != nil {
+ return fmt.Errorf("bind pre-mount failed: %w", err)
+ }
+ flags |= unix.MS_REMOUNT
+ }
+
+ if err := unix.Mount(source, target, fstype, flags, fsoptions); err != nil {
+ return fmt.Errorf("failed to mount filesystem: %w", err)
+ }
+ return nil
+ }
+
// Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
// All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
mounterPath := ""
@@ -102,6 +131,80 @@ func (mounter *Mounter) MountSensitive(source string, target string, fstype stri
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions)
}
+// nativeSupportsFstype checks if the native mounter can mount the given fstype
+func (mounter *Mounter) nativeSupportsFstype(fstype string) bool {
+ _, ok := mounter.nativeSupportedFstypes[fstype]
+ return ok
+}
+
+// parseMountOptions parses the string and returns the flags, propagation
+// flags and any mount data that it contains.
+// Taken from libcontainer/specconv/spec_linux.go (Apache 2.0) and modified
+func parseMountOptions(options []string) (uintptr, []uintptr, string) {
+ var (
+ flag uintptr
+ pgflag []uintptr
+ data []string
+ )
+ flags := map[string]struct {
+ clear bool
+ flag uintptr
+ }{
+ "async": {true, syscall.MS_SYNCHRONOUS},
+ "atime": {true, syscall.MS_NOATIME},
+ "bind": {false, syscall.MS_BIND},
+ "defaults": {false, 0},
+ "dev": {true, syscall.MS_NODEV},
+ "diratime": {true, syscall.MS_NODIRATIME},
+ "dirsync": {false, syscall.MS_DIRSYNC},
+ "exec": {true, syscall.MS_NOEXEC},
+ "mand": {false, syscall.MS_MANDLOCK},
+ "noatime": {false, syscall.MS_NOATIME},
+ "nodev": {false, syscall.MS_NODEV},
+ "nodiratime": {false, syscall.MS_NODIRATIME},
+ "noexec": {false, syscall.MS_NOEXEC},
+ "nomand": {true, syscall.MS_MANDLOCK},
+ "norelatime": {true, syscall.MS_RELATIME},
+ "nostrictatime": {true, syscall.MS_STRICTATIME},
+ "nosuid": {false, syscall.MS_NOSUID},
+ "rbind": {false, syscall.MS_BIND | syscall.MS_REC},
+ "relatime": {false, syscall.MS_RELATIME},
+ "remount": {false, syscall.MS_REMOUNT},
+ "ro": {false, syscall.MS_RDONLY},
+ "rw": {true, syscall.MS_RDONLY},
+ "strictatime": {false, syscall.MS_STRICTATIME},
+ "suid": {true, syscall.MS_NOSUID},
+ "sync": {false, syscall.MS_SYNCHRONOUS},
+ }
+ propagationFlags := map[string]uintptr{
+ "private": syscall.MS_PRIVATE,
+ "shared": syscall.MS_SHARED,
+ "slave": syscall.MS_SLAVE,
+ "unbindable": syscall.MS_UNBINDABLE,
+ "rprivate": syscall.MS_PRIVATE | syscall.MS_REC,
+ "rshared": syscall.MS_SHARED | syscall.MS_REC,
+ "rslave": syscall.MS_SLAVE | syscall.MS_REC,
+ "runbindable": syscall.MS_UNBINDABLE | syscall.MS_REC,
+ }
+ for _, o := range options {
+ // If the option does not exist in the flags table or the flag
+ // is not supported on the platform,
+ // then it is a data value for a specific fs type
+ if f, exists := flags[o]; exists && f.flag != 0 {
+ if f.clear {
+ flag &= ^f.flag
+ } else {
+ flag |= f.flag
+ }
+ } else if f, exists := propagationFlags[o]; exists && f != 0 {
+ pgflag = append(pgflag, f)
+ } else {
+ data = append(data, o)
+ }
+ }
+ return flag, pgflag, strings.Join(data, ",")
+}
+
// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
// sensitiveOptions is an extention of options except they will not be logged (because they may contain sensitive material)
func (mounter *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string, sensitiveOptions []string) error {
@@ -179,6 +282,30 @@ func detectSystemd() bool {
return true
}
+// detectLinuxUtils detects if the host operating system has the mount and unmount commands present
+func detectLinuxUtils() bool {
+ _, err := exec.LookPath("mount")
+ return err == nil
+}
+
+func detectNativeSupportedFstypes() map[string]struct{} {
+ nativeSupportedFstypes := make(map[string]struct{})
+ filesystemsRaw, err := ioutil.ReadFile("/proc/filesystems")
+ if err != nil {
+ return nativeSupportedFstypes
+ }
+ filesystemLines := strings.Split(string(filesystemsRaw), "\n")
+ for _, line := range filesystemLines {
+ fields := strings.Fields(line)
+ if len(fields) != 2 {
+ continue
+ }
+ filesystem := fields[1]
+ nativeSupportedFstypes[filesystem] = struct{}{}
+ }
+ return nativeSupportedFstypes
+}
+
// MakeMountArgs makes the arguments to the mount(8) command.
// options MUST not contain sensitive material (like passwords).
func MakeMountArgs(source, target, fstype string, options []string) (mountArgs []string) {
@@ -236,6 +363,12 @@ func AddSystemdScopeSensitive(systemdRunPath, mountName, command string, args []
// Unmount unmounts the target.
func (mounter *Mounter) Unmount(target string) error {
klog.V(4).Infof("Unmounting %s", target)
+ if !mounter.withLinuxUtils {
+ if err := unix.Unmount(target, unix.UMOUNT_NOFOLLOW); err != nil {
+ return fmt.Errorf("unmount failed: %v", err)
+ }
+ return nil
+ }
command := exec.Command("umount", target)
output, err := command.CombinedOutput()
if err != nil {
--
2.25.1