Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated cherry pick of #9976 #13739

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/kubelet/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (s *KubeletServer) Run(_ []string) error {
mounter := mount.New()
if s.Containerized {
glog.V(2).Info("Running kubelet in containerized mode (experimental)")
mounter = &mount.NsenterMounter{}
mounter = mount.NewNsenterMounter()
}

var dockerExecHandler dockertools.ExecHandler
Expand Down
75 changes: 60 additions & 15 deletions pkg/util/mount/nsenter_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ limitations under the License.
package mount

import (
"os"
"path/filepath"
"strings"

Expand All @@ -39,45 +40,79 @@ import (
// performed in the host's mount namespace do not propagate out to the
// bind-mount in this docker version.
// 2. The host's root filesystem must be available at /rootfs
// 3. The nsenter binary must be at /nsenter in the container's filesystem.
// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
// filesystem.
// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
// the present, this effectively means that the kubelet is running in a
// privileged container.
// 5. The volume path used by the Kubelet must be the same inside and outside
// the container and be writable by the container (to initialize volume)
// contents. TODO: remove this requirement.
// 6. The host image must have mount, findmnt, and umount binaries in /bin,
// /usr/sbin, or /usr/bin
//
// For more information about mount propagation modes, see:
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
type NsenterMounter struct{}
type NsenterMounter struct {
// a map of commands to their paths on the host filesystem
paths map[string]string
}

func NewNsenterMounter() *NsenterMounter {
m := &NsenterMounter{
paths: map[string]string{
"mount": "",
"findmnt": "",
"umount": "",
},
}
// search for the mount command in other locations besides /usr/bin
for binary := range m.paths {
// default to root
m.paths[binary] = filepath.Join("/", binary)
for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
binPath := filepath.Join(hostRootFsPath, path, binary)
if _, err := os.Stat(binPath); err != nil {
continue
}
m.paths[binary] = binPath
break
}
// TODO: error, so that the kubelet can stop if the mounts don't exist
}
return m
}

// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})

const (
hostRootFsPath = "/rootfs"
hostProcMountsPath = "/rootfs/proc/mounts"
nsenterPath = "/nsenter"
nsenterPath = "nsenter"
)

// Mount runs mount(8) in the host's root mount namespace. Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
bind, bindRemountOpts := isBind(options)

if bind {
err := doNsenterMount(source, target, fstype, []string{"bind"})
err := n.doNsenterMount(source, target, fstype, []string{"bind"})
if err != nil {
return err
}
return doNsenterMount(source, target, fstype, bindRemountOpts)
return n.doNsenterMount(source, target, fstype, bindRemountOpts)
}

return doNsenterMount(source, target, fstype, options)
return n.doNsenterMount(source, target, fstype, options)
}

// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func doNsenterMount(source, target, fstype string, options []string) error {
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options)
args := makeNsenterArgs(source, target, fstype, options)
args := n.makeNsenterArgs(source, target, fstype, options)

glog.V(5).Infof("Mount command: %v %v", nsenterPath, args)
exec := exec.New()
Expand All @@ -91,10 +126,11 @@ func doNsenterMount(source, target, fstype string, options []string) error {

// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func makeNsenterArgs(source, target, fstype string, options []string) []string {
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string {
nsenterArgs := []string{
"--mount=/rootfs/proc/1/ns/mnt",
"/usr/bin/mount",
"--",
n.absHostPath("mount"),
}

args := makeMountArgs(source, target, fstype, options)
Expand All @@ -103,10 +139,11 @@ func makeNsenterArgs(source, target, fstype string, options []string) []string {
}

// Unmount runs umount(8) in the host's mount namespace.
func (*NsenterMounter) Unmount(target string) error {
func (n *NsenterMounter) Unmount(target string) error {
args := []string{
"--mount=/rootfs/proc/1/ns/mnt",
"/usr/bin/umount",
"--",
n.absHostPath("umount"),
target,
}

Expand All @@ -127,13 +164,13 @@ func (*NsenterMounter) List() ([]MountPoint, error) {

// IsMountPoint determines whether a path is a mountpoint by calling findmnt
// in the host's root mount namespace.
func (*NsenterMounter) IsMountPoint(file string) (bool, error) {
func (n *NsenterMounter) IsMountPoint(file string) (bool, error) {
file, err := filepath.Abs(file)
if err != nil {
return false, err
}

args := []string{"--mount=/rootfs/proc/1/ns/mnt", "/usr/bin/findmnt", "-o", "target", "--noheadings", "--target", file}
args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", n.absHostPath("findmnt"), "-o", "target", "--noheadings", "--target", file}
glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args)

exec := exec.New()
Expand All @@ -151,3 +188,11 @@ func (*NsenterMounter) IsMountPoint(file string) (bool, error) {

return false, nil
}

func (n *NsenterMounter) absHostPath(command string) string {
path, ok := n.paths[command]
if !ok {
return command
}
return path
}
4 changes: 4 additions & 0 deletions pkg/util/mount/nsenter_mount_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ package mount

type NsenterMounter struct{}

func NewNsenterMounter() *NsenterMounter {
return &NsenterMounter{}
}

var _ = Interface(&NsenterMounter{})

func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
Expand Down