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
This commit is contained in:
@@ -59,6 +59,7 @@ func runNinja(ctx Context, config Config) {
|
|||||||
"-w", "missingdepfile=err")
|
"-w", "missingdepfile=err")
|
||||||
|
|
||||||
cmd := Command(ctx, config, "ninja", executable, args...)
|
cmd := Command(ctx, config, "ninja", executable, args...)
|
||||||
|
cmd.Sandbox = ninjaSandbox
|
||||||
if config.HasKatiSuffix() {
|
if config.HasKatiSuffix() {
|
||||||
cmd.Environment.AppendFromKati(config.KatiEnvFile())
|
cmd.Environment.AppendFromKati(config.KatiEnvFile())
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ const (
|
|||||||
dumpvarsSandbox = globalSandbox
|
dumpvarsSandbox = globalSandbox
|
||||||
soongSandbox = globalSandbox
|
soongSandbox = globalSandbox
|
||||||
katiSandbox = globalSandbox
|
katiSandbox = globalSandbox
|
||||||
katiCleanSpecSandbox = globalSandbox
|
ninjaSandbox = noSandbox
|
||||||
)
|
)
|
||||||
|
|
||||||
var sandboxExecPath string
|
var sandboxExecPath string
|
||||||
|
@@ -14,20 +14,154 @@
|
|||||||
|
|
||||||
package build
|
package build
|
||||||
|
|
||||||
type Sandbox bool
|
import (
|
||||||
|
"bytes"
|
||||||
const (
|
"os"
|
||||||
noSandbox = false
|
"os/exec"
|
||||||
globalSandbox = false
|
"os/user"
|
||||||
dumpvarsSandbox = false
|
"strings"
|
||||||
soongSandbox = false
|
"sync"
|
||||||
katiSandbox = false
|
|
||||||
katiCleanSpecSandbox = false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 {
|
func (c *Cmd) sandboxSupported() bool {
|
||||||
|
if !c.Sandbox.Enabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cmd) wrapSandbox() {
|
// 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)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user