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 }