github.com/qichengzx/mattermost-server@v4.5.1-0.20180604164826-2c75247c97d0+incompatible/plugin/rpcplugin/sandbox/sandbox_linux.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package sandbox
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"syscall"
    17  	"unsafe"
    18  
    19  	"github.com/pkg/errors"
    20  	"golang.org/x/sys/unix"
    21  
    22  	"github.com/mattermost/mattermost-server/plugin/rpcplugin"
    23  )
    24  
    25  func init() {
    26  	if len(os.Args) < 4 || os.Args[0] != "sandbox.runProcess" {
    27  		return
    28  	}
    29  
    30  	var config Configuration
    31  	if err := json.Unmarshal([]byte(os.Args[1]), &config); err != nil {
    32  		fmt.Println(err.Error())
    33  		os.Exit(1)
    34  	}
    35  	if err := runProcess(&config, os.Args[2], os.Args[3]); err != nil {
    36  		if eerr, ok := err.(*exec.ExitError); ok {
    37  			if status, ok := eerr.Sys().(syscall.WaitStatus); ok {
    38  				os.Exit(status.ExitStatus())
    39  			}
    40  		}
    41  		fmt.Println(err.Error())
    42  		os.Exit(1)
    43  	}
    44  	os.Exit(0)
    45  }
    46  
    47  func systemMountPoints() (points []*MountPoint) {
    48  	points = append(points, &MountPoint{
    49  		Source:      "proc",
    50  		Destination: "/proc",
    51  		Type:        "proc",
    52  	}, &MountPoint{
    53  		Source:      "/dev/null",
    54  		Destination: "/dev/null",
    55  	}, &MountPoint{
    56  		Source:      "/dev/zero",
    57  		Destination: "/dev/zero",
    58  	}, &MountPoint{
    59  		Source:      "/dev/full",
    60  		Destination: "/dev/full",
    61  	})
    62  
    63  	readOnly := []string{
    64  		"/dev/random",
    65  		"/dev/urandom",
    66  		"/etc/resolv.conf",
    67  		"/lib",
    68  		"/lib32",
    69  		"/lib64",
    70  		"/usr/lib",
    71  		"/usr/lib32",
    72  		"/usr/lib64",
    73  		"/etc/ca-certificates",
    74  		"/etc/ssl/certs",
    75  		"/system/etc/security/cacerts",
    76  		"/usr/local/share/certs",
    77  		"/etc/pki/tls/certs",
    78  		"/etc/openssl/certs",
    79  		"/etc/ssl/ca-bundle.pem",
    80  		"/etc/pki/tls/cacert.pem",
    81  		"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
    82  	}
    83  
    84  	for _, v := range []string{"SSL_CERT_FILE", "SSL_CERT_DIR"} {
    85  		if path := os.Getenv(v); path != "" {
    86  			readOnly = append(readOnly, path)
    87  		}
    88  	}
    89  
    90  	for _, point := range readOnly {
    91  		points = append(points, &MountPoint{
    92  			Source:      point,
    93  			Destination: point,
    94  			ReadOnly:    true,
    95  		})
    96  	}
    97  
    98  	return
    99  }
   100  
   101  func runProcess(config *Configuration, path, root string) error {
   102  	if err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
   103  		return errors.Wrapf(err, "unable to make root private")
   104  	}
   105  
   106  	if err := mountMountPoints(root, systemMountPoints()); err != nil {
   107  		return errors.Wrapf(err, "unable to mount sandbox system mount points")
   108  	}
   109  
   110  	if err := mountMountPoints(root, config.MountPoints); err != nil {
   111  		return errors.Wrapf(err, "unable to mount sandbox config mount points")
   112  	}
   113  
   114  	if err := pivotRoot(root); err != nil {
   115  		return errors.Wrapf(err, "unable to pivot sandbox root")
   116  	}
   117  
   118  	if err := os.Mkdir("/tmp", 0755); err != nil {
   119  		return errors.Wrapf(err, "unable to create /tmp")
   120  	}
   121  
   122  	if config.WorkingDirectory != "" {
   123  		if err := os.Chdir(config.WorkingDirectory); err != nil {
   124  			return errors.Wrapf(err, "unable to set working directory")
   125  		}
   126  	}
   127  
   128  	if err := dropInheritableCapabilities(); err != nil {
   129  		return errors.Wrapf(err, "unable to drop inheritable capabilities")
   130  	}
   131  
   132  	if err := enableSeccompFilter(); err != nil {
   133  		return errors.Wrapf(err, "unable to enable seccomp filter")
   134  	}
   135  
   136  	return runExecutable(path)
   137  }
   138  
   139  func mountMountPoint(root string, mountPoint *MountPoint) error {
   140  	isDir := true
   141  	if mountPoint.Type == "" {
   142  		stat, err := os.Lstat(mountPoint.Source)
   143  		if err != nil {
   144  			return nil
   145  		}
   146  		if (stat.Mode() & os.ModeSymlink) != 0 {
   147  			if path, err := filepath.EvalSymlinks(mountPoint.Source); err == nil {
   148  				newMountPoint := *mountPoint
   149  				newMountPoint.Source = path
   150  				if err := mountMountPoint(root, &newMountPoint); err != nil {
   151  					return errors.Wrapf(err, "unable to mount symbolic link target: "+mountPoint.Source)
   152  				}
   153  				return nil
   154  			}
   155  		}
   156  		isDir = stat.IsDir()
   157  	}
   158  
   159  	target := filepath.Join(root, mountPoint.Destination)
   160  
   161  	if isDir {
   162  		if err := os.MkdirAll(target, 0755); err != nil {
   163  			return errors.Wrapf(err, "unable to create directory: "+target)
   164  		}
   165  	} else {
   166  		if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
   167  			return errors.Wrapf(err, "unable to create directory: "+target)
   168  		}
   169  		f, err := os.Create(target)
   170  		if err != nil {
   171  			return errors.Wrapf(err, "unable to create file: "+target)
   172  		}
   173  		f.Close()
   174  	}
   175  
   176  	flags := uintptr(syscall.MS_NOSUID | syscall.MS_NODEV)
   177  	if mountPoint.Type == "" {
   178  		flags |= syscall.MS_BIND
   179  	}
   180  	if mountPoint.ReadOnly {
   181  		flags |= syscall.MS_RDONLY
   182  	}
   183  
   184  	if err := syscall.Mount(mountPoint.Source, target, mountPoint.Type, flags, ""); err != nil {
   185  		return errors.Wrapf(err, "unable to mount "+mountPoint.Source)
   186  	}
   187  
   188  	if (flags & syscall.MS_BIND) != 0 {
   189  		// If this was a bind mount, our other flags actually got silently ignored during the above syscall:
   190  		//
   191  		//     If mountflags includes MS_BIND [...] The remaining bits in the mountflags argument are
   192  		//     also ignored, with the exception of MS_REC.
   193  		//
   194  		// Furthermore, remounting will fail if we attempt to unset a bit that was inherited from
   195  		// the mount's parent:
   196  		//
   197  		//     The mount(2) flags MS_RDONLY, MS_NOSUID, MS_NOEXEC, and the "atime" flags
   198  		//     (MS_NOATIME, MS_NODIRATIME, MS_RELATIME) settings become locked when propagated from
   199  		//     a more privileged to a less privileged mount namespace, and may not be changed in the
   200  		//     less privileged mount namespace.
   201  		//
   202  		// So we need to get the actual flags, add our new ones, then do a remount if needed.
   203  		var stats syscall.Statfs_t
   204  		if err := syscall.Statfs(target, &stats); err != nil {
   205  			return errors.Wrap(err, "unable to get mount flags for target: "+target)
   206  		}
   207  		const lockedFlagsMask = unix.MS_RDONLY | unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NOATIME | unix.MS_NODIRATIME | unix.MS_RELATIME
   208  		lockedFlags := uintptr(stats.Flags & lockedFlagsMask)
   209  		if lockedFlags != ((flags | lockedFlags) & lockedFlagsMask) {
   210  			if err := syscall.Mount("", target, "", flags|lockedFlags|syscall.MS_REMOUNT, ""); err != nil {
   211  				return errors.Wrapf(err, "unable to remount "+mountPoint.Source)
   212  			}
   213  		}
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  func mountMountPoints(root string, mountPoints []*MountPoint) error {
   220  	for _, mountPoint := range mountPoints {
   221  		if err := mountMountPoint(root, mountPoint); err != nil {
   222  			return err
   223  		}
   224  	}
   225  
   226  	return nil
   227  }
   228  
   229  func pivotRoot(newRoot string) error {
   230  	if err := syscall.Mount(newRoot, newRoot, "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
   231  		return errors.Wrapf(err, "unable to mount new root")
   232  	}
   233  
   234  	prevRoot := filepath.Join(newRoot, ".prev_root")
   235  
   236  	if err := os.MkdirAll(prevRoot, 0700); err != nil {
   237  		return errors.Wrapf(err, "unable to create directory for previous root")
   238  	}
   239  
   240  	if err := syscall.PivotRoot(newRoot, prevRoot); err != nil {
   241  		return errors.Wrapf(err, "syscall error")
   242  	}
   243  
   244  	if err := os.Chdir("/"); err != nil {
   245  		return errors.Wrapf(err, "unable to change directory")
   246  	}
   247  
   248  	prevRoot = "/.prev_root"
   249  
   250  	if err := syscall.Unmount(prevRoot, syscall.MNT_DETACH); err != nil {
   251  		return errors.Wrapf(err, "unable to unmount previous root")
   252  	}
   253  
   254  	if err := os.RemoveAll(prevRoot); err != nil {
   255  		return errors.Wrapf(err, "unable to remove previous root directory")
   256  	}
   257  
   258  	return nil
   259  }
   260  
   261  func dropInheritableCapabilities() error {
   262  	type capHeader struct {
   263  		version uint32
   264  		pid     int32
   265  	}
   266  
   267  	type capData struct {
   268  		effective   uint32
   269  		permitted   uint32
   270  		inheritable uint32
   271  	}
   272  
   273  	var hdr capHeader
   274  	var data [2]capData
   275  
   276  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&hdr)), 0, 0); errno != 0 {
   277  		return errors.Wrapf(syscall.Errno(errno), "unable to get capabilities version")
   278  	}
   279  
   280  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&hdr)), uintptr(unsafe.Pointer(&data[0])), 0); errno != 0 {
   281  		return errors.Wrapf(syscall.Errno(errno), "unable to get capabilities")
   282  	}
   283  
   284  	data[0].inheritable = 0
   285  	data[1].inheritable = 0
   286  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&hdr)), uintptr(unsafe.Pointer(&data[0])), 0); errno != 0 {
   287  		return errors.Wrapf(syscall.Errno(errno), "unable to set inheritable capabilities")
   288  	}
   289  
   290  	for i := 0; i < 64; i++ {
   291  		if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_CAPBSET_DROP, uintptr(i), 0); errno != 0 && errno != syscall.EINVAL {
   292  			return errors.Wrapf(syscall.Errno(errno), "unable to drop bounding set capability")
   293  		}
   294  	}
   295  
   296  	return nil
   297  }
   298  
   299  func enableSeccompFilter() error {
   300  	return EnableSeccompFilter(SeccompFilter(NATIVE_AUDIT_ARCH, AllowedSyscalls))
   301  }
   302  
   303  func runExecutable(path string) error {
   304  	childFiles := []*os.File{
   305  		os.NewFile(3, ""), os.NewFile(4, ""),
   306  	}
   307  	defer childFiles[0].Close()
   308  	defer childFiles[1].Close()
   309  
   310  	cmd := exec.Command(path)
   311  	cmd.Stdout = os.Stdout
   312  	cmd.Stderr = os.Stderr
   313  	cmd.ExtraFiles = childFiles
   314  	cmd.SysProcAttr = &syscall.SysProcAttr{
   315  		Pdeathsig: syscall.SIGTERM,
   316  	}
   317  
   318  	if err := cmd.Run(); err != nil {
   319  		return err
   320  	}
   321  
   322  	return nil
   323  }
   324  
   325  type process struct {
   326  	command *exec.Cmd
   327  	root    string
   328  }
   329  
   330  func newProcess(ctx context.Context, config *Configuration, path string) (pOut rpcplugin.Process, rwcOut io.ReadWriteCloser, errOut error) {
   331  	configJSON, err := json.Marshal(config)
   332  	if err != nil {
   333  		return nil, nil, err
   334  	}
   335  
   336  	ipc, childFiles, err := rpcplugin.NewIPC()
   337  	if err != nil {
   338  		return nil, nil, err
   339  	}
   340  	defer childFiles[0].Close()
   341  	defer childFiles[1].Close()
   342  
   343  	root, err := ioutil.TempDir("", "")
   344  	if err != nil {
   345  		return nil, nil, err
   346  	}
   347  	defer func() {
   348  		if errOut != nil {
   349  			os.RemoveAll(root)
   350  		}
   351  	}()
   352  
   353  	cmd := exec.CommandContext(ctx, "/proc/self/exe")
   354  	cmd.Args = []string{"sandbox.runProcess", string(configJSON), path, root}
   355  	cmd.Stdout = os.Stdout
   356  	cmd.Stderr = os.Stderr
   357  	cmd.ExtraFiles = childFiles
   358  
   359  	cmd.SysProcAttr = &syscall.SysProcAttr{
   360  		Cloneflags: syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWUSER,
   361  		Pdeathsig:  syscall.SIGTERM,
   362  		GidMappings: []syscall.SysProcIDMap{
   363  			{
   364  				ContainerID: 0,
   365  				HostID:      os.Getgid(),
   366  				Size:        1,
   367  			},
   368  		},
   369  		UidMappings: []syscall.SysProcIDMap{
   370  			{
   371  				ContainerID: 0,
   372  				HostID:      os.Getuid(),
   373  				Size:        1,
   374  			},
   375  		},
   376  	}
   377  
   378  	err = cmd.Start()
   379  	if err != nil {
   380  		ipc.Close()
   381  		return nil, nil, err
   382  	}
   383  
   384  	return &process{
   385  		command: cmd,
   386  		root:    root,
   387  	}, ipc, nil
   388  }
   389  
   390  func (p *process) Wait() error {
   391  	defer os.RemoveAll(p.root)
   392  	return p.command.Wait()
   393  }
   394  
   395  func init() {
   396  	if len(os.Args) < 2 || os.Args[0] != "sandbox.checkSupportInNamespace" {
   397  		return
   398  	}
   399  
   400  	if err := checkSupportInNamespace(os.Args[1]); err != nil {
   401  		fmt.Fprintf(os.Stderr, "%v", err.Error())
   402  		os.Exit(1)
   403  	}
   404  
   405  	os.Exit(0)
   406  }
   407  
   408  func checkSupportInNamespace(root string) error {
   409  	if err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
   410  		return errors.Wrapf(err, "unable to make root private")
   411  	}
   412  
   413  	if err := mountMountPoints(root, systemMountPoints()); err != nil {
   414  		return errors.Wrapf(err, "unable to mount sandbox system mount points")
   415  	}
   416  
   417  	if err := pivotRoot(root); err != nil {
   418  		return errors.Wrapf(err, "unable to pivot sandbox root")
   419  	}
   420  
   421  	if err := dropInheritableCapabilities(); err != nil {
   422  		return errors.Wrapf(err, "unable to drop inheritable capabilities")
   423  	}
   424  
   425  	if err := enableSeccompFilter(); err != nil {
   426  		return errors.Wrapf(err, "unable to enable seccomp filter")
   427  	}
   428  
   429  	if f, err := os.Create(os.DevNull); err != nil {
   430  		return errors.Wrapf(err, "unable to open os.DevNull")
   431  	} else {
   432  		defer f.Close()
   433  		if _, err = f.Write([]byte("foo")); err != nil {
   434  			return errors.Wrapf(err, "unable to write to os.DevNull")
   435  		}
   436  	}
   437  
   438  	return nil
   439  }
   440  
   441  func checkSupport() error {
   442  	if AllowedSyscalls == nil {
   443  		return fmt.Errorf("unsupported architecture")
   444  	}
   445  
   446  	stderr := &bytes.Buffer{}
   447  
   448  	root, err := ioutil.TempDir("", "")
   449  	if err != nil {
   450  		return err
   451  	}
   452  	defer os.RemoveAll(root)
   453  
   454  	cmd := exec.Command("/proc/self/exe")
   455  	cmd.Args = []string{"sandbox.checkSupportInNamespace", root}
   456  	cmd.Stderr = stderr
   457  	cmd.SysProcAttr = &syscall.SysProcAttr{
   458  		Cloneflags: syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWUSER,
   459  		Pdeathsig:  syscall.SIGTERM,
   460  		GidMappings: []syscall.SysProcIDMap{
   461  			{
   462  				ContainerID: 0,
   463  				HostID:      os.Getgid(),
   464  				Size:        1,
   465  			},
   466  		},
   467  		UidMappings: []syscall.SysProcIDMap{
   468  			{
   469  				ContainerID: 0,
   470  				HostID:      os.Getuid(),
   471  				Size:        1,
   472  			},
   473  		},
   474  	}
   475  
   476  	if err := cmd.Start(); err != nil {
   477  		return errors.Wrapf(err, "unable to create user namespace")
   478  	}
   479  
   480  	if err := cmd.Wait(); err != nil {
   481  		if _, ok := err.(*exec.ExitError); ok {
   482  			return errors.Wrapf(fmt.Errorf("%v", stderr.String()), "unable to prepare namespace")
   483  		}
   484  		return errors.Wrapf(err, "unable to prepare namespace")
   485  	}
   486  
   487  	return nil
   488  }