github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/cmd/chroot.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  
    22  	"golang.org/x/sys/unix"
    23  	"github.com/SagerNet/gvisor/pkg/log"
    24  	"github.com/SagerNet/gvisor/runsc/specutils"
    25  )
    26  
    27  // mountInChroot creates the destination mount point in the given chroot and
    28  // mounts the source.
    29  func mountInChroot(chroot, src, dst, typ string, flags uint32) error {
    30  	chrootDst := filepath.Join(chroot, dst)
    31  	log.Infof("Mounting %q at %q", src, chrootDst)
    32  
    33  	if err := specutils.Mount(src, chrootDst, typ, flags, "/proc"); err != nil {
    34  		return fmt.Errorf("error mounting %q at %q: %v", src, chrootDst, err)
    35  	}
    36  	return nil
    37  }
    38  
    39  func pivotRoot(root string) error {
    40  	if err := os.Chdir(root); err != nil {
    41  		return fmt.Errorf("error changing working directory: %v", err)
    42  	}
    43  	// pivot_root(new_root, put_old) moves the root filesystem (old_root)
    44  	// of the calling process to the directory put_old and makes new_root
    45  	// the new root filesystem of the calling process.
    46  	//
    47  	// pivot_root(".", ".") makes a mount of the working directory the new
    48  	// root filesystem, so it will be moved in "/" and then the old_root
    49  	// will be moved to "/" too. The parent mount of the old_root will be
    50  	// new_root, so after umounting the old_root, we will see only
    51  	// the new_root in "/".
    52  	if err := unix.PivotRoot(".", "."); err != nil {
    53  		return fmt.Errorf("pivot_root failed, make sure that the root mount has a parent: %v", err)
    54  	}
    55  
    56  	if err := unix.Unmount(".", unix.MNT_DETACH); err != nil {
    57  		return fmt.Errorf("error umounting the old root file system: %v", err)
    58  	}
    59  	return nil
    60  }
    61  
    62  // setUpChroot creates an empty directory with runsc mounted at /runsc and proc
    63  // mounted at /proc.
    64  func setUpChroot(pidns bool) error {
    65  	// We are a new mount namespace, so we can use /tmp as a directory to
    66  	// construct a new root.
    67  	chroot := os.TempDir()
    68  
    69  	log.Infof("Setting up sandbox chroot in %q", chroot)
    70  
    71  	// Convert all shared mounts into slave to be sure that nothing will be
    72  	// propagated outside of our namespace.
    73  	if err := specutils.SafeMount("", "/", "", unix.MS_SLAVE|unix.MS_REC, "", "/proc"); err != nil {
    74  		return fmt.Errorf("error converting mounts: %v", err)
    75  	}
    76  
    77  	if err := specutils.SafeMount("runsc-root", chroot, "tmpfs", unix.MS_NOSUID|unix.MS_NODEV|unix.MS_NOEXEC, "", "/proc"); err != nil {
    78  		return fmt.Errorf("error mounting tmpfs in choot: %v", err)
    79  	}
    80  
    81  	if pidns {
    82  		flags := uint32(unix.MS_NOSUID | unix.MS_NODEV | unix.MS_NOEXEC | unix.MS_RDONLY)
    83  		if err := mountInChroot(chroot, "proc", "/proc", "proc", flags); err != nil {
    84  			return fmt.Errorf("error mounting proc in chroot: %v", err)
    85  		}
    86  	} else {
    87  		if err := mountInChroot(chroot, "/proc", "/proc", "bind", unix.MS_BIND|unix.MS_RDONLY|unix.MS_REC); err != nil {
    88  			return fmt.Errorf("error mounting proc in chroot: %v", err)
    89  		}
    90  	}
    91  
    92  	if err := specutils.SafeMount("", chroot, "", unix.MS_REMOUNT|unix.MS_RDONLY|unix.MS_BIND, "", "/proc"); err != nil {
    93  		return fmt.Errorf("error remounting chroot in read-only: %v", err)
    94  	}
    95  
    96  	return pivotRoot(chroot)
    97  }