platform_build_soong/ui/build/sandbox_linux.go
Dan Willemsen 63663c6bc9 Implement linux sandboxing with nsjail
This really only initializes the sandbox, it does not attempt to change
the view of the filesystem, nor does it turn off networking.

Bug: 122270019
Test: m
Test: trigger nsjail check failure; lunch; m; cat out/soong.log
Test: USE_GOMA=true m libc
Change-Id: Ib291072dcee8247c7a15f5b6831295ead6e4fc22
2019-01-15 13:47:31 -08:00

167 lines
4.2 KiB
Go

// Copyright 2017 Google Inc. All rights reserved.
//
// 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.
package build
import (
"bytes"
"os"
"os/exec"
"os/user"
"strings"
"sync"
)
type Sandbox struct {
Enabled bool
DisableWhenUsingGoma bool
}
var (
noSandbox = Sandbox{}
basicSandbox = Sandbox{
Enabled: true,
}
dumpvarsSandbox = basicSandbox
katiSandbox = basicSandbox
soongSandbox = basicSandbox
ninjaSandbox = Sandbox{
Enabled: true,
DisableWhenUsingGoma: true,
}
)
const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
var sandboxConfig struct {
once sync.Once
working bool
group string
}
func (c *Cmd) sandboxSupported() bool {
if !c.Sandbox.Enabled {
return false
}
// Goma is incompatible with PID namespaces and Mount namespaces. b/122767582
if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() {
return false
}
sandboxConfig.once.Do(func() {
sandboxConfig.group = "nogroup"
if _, err := user.LookupGroup(sandboxConfig.group); err != nil {
sandboxConfig.group = "nobody"
}
cmd := exec.CommandContext(c.ctx.Context, nsjailPath,
"-H", "android-build",
"-e",
"-u", "nobody",
"-g", sandboxConfig.group,
"-B", "/",
"--disable_clone_newcgroup",
"--",
"/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
cmd.Env = c.config.Environment().Environ()
c.ctx.Verboseln(cmd.Args)
data, err := cmd.CombinedOutput()
if err == nil && bytes.Contains(data, []byte("Android Success")) {
sandboxConfig.working = true
return
}
c.ctx.Println("Build sandboxing disabled due to nsjail error. This may become fatal in the future.")
c.ctx.Println("Please let us know why nsjail doesn't work in your environment at:")
c.ctx.Println(" https://groups.google.com/forum/#!forum/android-building")
c.ctx.Println(" https://issuetracker.google.com/issues/new?component=381517")
for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
c.ctx.Verboseln(line)
}
if err == nil {
c.ctx.Verboseln("nsjail exited successfully, but without the correct output")
} else if e, ok := err.(*exec.ExitError); ok {
c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String())
} else {
c.ctx.Verbosef("nsjail failed with %v", err)
}
})
return sandboxConfig.working
}
func (c *Cmd) wrapSandbox() {
wd, _ := os.Getwd()
sandboxArgs := []string{
// The executable to run
"-x", c.Path,
// Set the hostname to something consistent
"-H", "android-build",
// Use the current working dir
"--cwd", wd,
// No time limit
"-t", "0",
// Keep all environment variables, we already filter them out
// in soong_ui
"-e",
// Use a consistent user & group.
// Note that these are mapped back to the real UID/GID when
// doing filesystem operations, so they're rather arbitrary.
"-u", "nobody",
"-g", sandboxConfig.group,
// Set high values, as nsjail uses low defaults.
"--rlimit_as", "soft",
"--rlimit_core", "soft",
"--rlimit_cpu", "soft",
"--rlimit_fsize", "soft",
"--rlimit_nofile", "soft",
// For now, just map everything. Eventually we should limit this, especially to make most things readonly.
"-B", "/",
// Enable networking for now. TODO: remove
"-N",
// Disable newcgroup for now, since it may require newer kernels
// TODO: try out cgroups
"--disable_clone_newcgroup",
// Only log important warnings / errors
"-q",
// Stop parsing arguments
"--",
}
c.Args = append(sandboxArgs, c.Args[1:]...)
c.Path = nsjailPath
env := Environment(c.Env)
if _, hasUser := env.Get("USER"); hasUser {
env.Set("USER", "nobody")
}
c.Env = []string(env)
}