| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 1 | // Copyright 2017 Google Inc. All rights reserved. | 
|  | 2 | // | 
|  | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 4 | // you may not use this file except in compliance with the License. | 
|  | 5 | // You may obtain a copy of the License at | 
|  | 6 | // | 
|  | 7 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 8 | // | 
|  | 9 | // Unless required by applicable law or agreed to in writing, software | 
|  | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 12 | // See the License for the specific language governing permissions and | 
|  | 13 | // limitations under the License. | 
|  | 14 |  | 
|  | 15 | package build | 
|  | 16 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 17 | import ( | 
|  | 18 | "bytes" | 
|  | 19 | "os" | 
|  | 20 | "os/exec" | 
|  | 21 | "os/user" | 
|  | 22 | "strings" | 
|  | 23 | "sync" | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 24 | ) | 
|  | 25 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 26 | type Sandbox struct { | 
|  | 27 | Enabled              bool | 
|  | 28 | DisableWhenUsingGoma bool | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 29 |  | 
|  | 30 | AllowBuildBrokenUsesNetwork bool | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 31 | } | 
|  | 32 |  | 
|  | 33 | var ( | 
|  | 34 | noSandbox    = Sandbox{} | 
|  | 35 | basicSandbox = Sandbox{ | 
|  | 36 | Enabled: true, | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | dumpvarsSandbox = basicSandbox | 
|  | 40 | katiSandbox     = basicSandbox | 
|  | 41 | soongSandbox    = basicSandbox | 
|  | 42 | ninjaSandbox    = Sandbox{ | 
|  | 43 | Enabled:              true, | 
|  | 44 | DisableWhenUsingGoma: true, | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 45 |  | 
|  | 46 | AllowBuildBrokenUsesNetwork: true, | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 47 | } | 
|  | 48 | ) | 
|  | 49 |  | 
|  | 50 | const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail" | 
|  | 51 |  | 
|  | 52 | var sandboxConfig struct { | 
|  | 53 | once sync.Once | 
|  | 54 |  | 
|  | 55 | working bool | 
|  | 56 | group   string | 
|  | 57 | } | 
|  | 58 |  | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 59 | func (c *Cmd) sandboxSupported() bool { | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 60 | if !c.Sandbox.Enabled { | 
|  | 61 | return false | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582 | 
|  | 65 | if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() { | 
|  | 66 | return false | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | sandboxConfig.once.Do(func() { | 
|  | 70 | sandboxConfig.group = "nogroup" | 
|  | 71 | if _, err := user.LookupGroup(sandboxConfig.group); err != nil { | 
|  | 72 | sandboxConfig.group = "nobody" | 
|  | 73 | } | 
|  | 74 |  | 
|  | 75 | cmd := exec.CommandContext(c.ctx.Context, nsjailPath, | 
|  | 76 | "-H", "android-build", | 
|  | 77 | "-e", | 
|  | 78 | "-u", "nobody", | 
|  | 79 | "-g", sandboxConfig.group, | 
|  | 80 | "-B", "/", | 
|  | 81 | "--disable_clone_newcgroup", | 
|  | 82 | "--", | 
|  | 83 | "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`) | 
|  | 84 | cmd.Env = c.config.Environment().Environ() | 
|  | 85 |  | 
|  | 86 | c.ctx.Verboseln(cmd.Args) | 
|  | 87 | data, err := cmd.CombinedOutput() | 
|  | 88 | if err == nil && bytes.Contains(data, []byte("Android Success")) { | 
|  | 89 | sandboxConfig.working = true | 
|  | 90 | return | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | c.ctx.Println("Build sandboxing disabled due to nsjail error. This may become fatal in the future.") | 
|  | 94 | c.ctx.Println("Please let us know why nsjail doesn't work in your environment at:") | 
|  | 95 | c.ctx.Println("  https://groups.google.com/forum/#!forum/android-building") | 
|  | 96 | c.ctx.Println("  https://issuetracker.google.com/issues/new?component=381517") | 
|  | 97 |  | 
|  | 98 | for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") { | 
|  | 99 | c.ctx.Verboseln(line) | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | if err == nil { | 
|  | 103 | c.ctx.Verboseln("nsjail exited successfully, but without the correct output") | 
|  | 104 | } else if e, ok := err.(*exec.ExitError); ok { | 
|  | 105 | c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String()) | 
|  | 106 | } else { | 
|  | 107 | c.ctx.Verbosef("nsjail failed with %v", err) | 
|  | 108 | } | 
|  | 109 | }) | 
|  | 110 |  | 
|  | 111 | return sandboxConfig.working | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 112 | } | 
|  | 113 |  | 
|  | 114 | func (c *Cmd) wrapSandbox() { | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 115 | wd, _ := os.Getwd() | 
|  | 116 |  | 
|  | 117 | sandboxArgs := []string{ | 
|  | 118 | // The executable to run | 
|  | 119 | "-x", c.Path, | 
|  | 120 |  | 
|  | 121 | // Set the hostname to something consistent | 
|  | 122 | "-H", "android-build", | 
|  | 123 |  | 
|  | 124 | // Use the current working dir | 
|  | 125 | "--cwd", wd, | 
|  | 126 |  | 
|  | 127 | // No time limit | 
|  | 128 | "-t", "0", | 
|  | 129 |  | 
|  | 130 | // Keep all environment variables, we already filter them out | 
|  | 131 | // in soong_ui | 
|  | 132 | "-e", | 
|  | 133 |  | 
| Dan Willemsen | 3a4dbd6 | 2019-01-16 23:02:24 -0800 | [diff] [blame] | 134 | // Mount /proc read-write, necessary to run a nested nsjail or minijail0 | 
|  | 135 | "--proc_rw", | 
|  | 136 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 137 | // Use a consistent user & group. | 
|  | 138 | // Note that these are mapped back to the real UID/GID when | 
|  | 139 | // doing filesystem operations, so they're rather arbitrary. | 
|  | 140 | "-u", "nobody", | 
|  | 141 | "-g", sandboxConfig.group, | 
|  | 142 |  | 
|  | 143 | // Set high values, as nsjail uses low defaults. | 
|  | 144 | "--rlimit_as", "soft", | 
|  | 145 | "--rlimit_core", "soft", | 
|  | 146 | "--rlimit_cpu", "soft", | 
|  | 147 | "--rlimit_fsize", "soft", | 
|  | 148 | "--rlimit_nofile", "soft", | 
|  | 149 |  | 
|  | 150 | // For now, just map everything. Eventually we should limit this, especially to make most things readonly. | 
|  | 151 | "-B", "/", | 
|  | 152 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 153 | // Disable newcgroup for now, since it may require newer kernels | 
|  | 154 | // TODO: try out cgroups | 
|  | 155 | "--disable_clone_newcgroup", | 
|  | 156 |  | 
|  | 157 | // Only log important warnings / errors | 
|  | 158 | "-q", | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 159 | } | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 160 |  | 
|  | 161 | if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() { | 
|  | 162 | c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork) | 
|  | 163 | c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork()) | 
|  | 164 | sandboxArgs = append(sandboxArgs, "-N") | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | // Stop nsjail from parsing arguments | 
|  | 168 | sandboxArgs = append(sandboxArgs, "--") | 
|  | 169 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 170 | c.Args = append(sandboxArgs, c.Args[1:]...) | 
|  | 171 | c.Path = nsjailPath | 
|  | 172 |  | 
|  | 173 | env := Environment(c.Env) | 
|  | 174 | if _, hasUser := env.Get("USER"); hasUser { | 
|  | 175 | env.Set("USER", "nobody") | 
|  | 176 | } | 
|  | 177 | c.Env = []string(env) | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 178 | } |