diff options
| author | Dan Willemsen <dwillemsen@google.com> | 2019-01-02 12:24:44 -0800 |
|---|---|---|
| committer | Dan Willemsen <dwillemsen@google.com> | 2019-01-15 13:47:31 -0800 |
| commit | 63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b (patch) | |
| tree | 43841e3620caf538eb63f1061fd58e5741c920ea /ui | |
| parent | 073941d780016e0770f083e1edd425739d15b80a (diff) | |
| download | build_soong-63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b.tar.gz build_soong-63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b.tar.bz2 build_soong-63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b.zip | |
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
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/build/ninja.go | 1 | ||||
| -rw-r--r-- | ui/build/sandbox_darwin.go | 12 | ||||
| -rw-r--r-- | ui/build/sandbox_linux.go | 154 |
3 files changed, 151 insertions, 16 deletions
diff --git a/ui/build/ninja.go b/ui/build/ninja.go index 835f8203..cb41579c 100644 --- a/ui/build/ninja.go +++ b/ui/build/ninja.go @@ -59,6 +59,7 @@ func runNinja(ctx Context, config Config) { "-w", "missingdepfile=err") cmd := Command(ctx, config, "ninja", executable, args...) + cmd.Sandbox = ninjaSandbox if config.HasKatiSuffix() { cmd.Environment.AppendFromKati(config.KatiEnvFile()) } diff --git a/ui/build/sandbox_darwin.go b/ui/build/sandbox_darwin.go index 7e75167a..43c5480c 100644 --- a/ui/build/sandbox_darwin.go +++ b/ui/build/sandbox_darwin.go @@ -21,12 +21,12 @@ import ( type Sandbox string const ( - noSandbox = "" - globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb" - dumpvarsSandbox = globalSandbox - soongSandbox = globalSandbox - katiSandbox = globalSandbox - katiCleanSpecSandbox = globalSandbox + noSandbox = "" + globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb" + dumpvarsSandbox = globalSandbox + soongSandbox = globalSandbox + katiSandbox = globalSandbox + ninjaSandbox = noSandbox ) var sandboxExecPath string diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go index f2bfac29..b87637f7 100644 --- a/ui/build/sandbox_linux.go +++ b/ui/build/sandbox_linux.go @@ -14,20 +14,154 @@ package build -type Sandbox bool - -const ( - noSandbox = false - globalSandbox = false - dumpvarsSandbox = false - soongSandbox = false - katiSandbox = false - katiCleanSpecSandbox = false +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 { - return false + 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) } |
