diff options
author | Dan Willemsen | 2019-01-02 14:24:44 -0600 |
---|---|---|
committer | Dan Willemsen | 2019-01-15 15:47:31 -0600 |
commit | 63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b (patch) | |
tree | 43841e3620caf538eb63f1061fd58e5741c920ea /ui | |
parent | 073941d780016e0770f083e1edd425739d15b80a (diff) | |
download | platform-build-soong-63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b.tar.gz platform-build-soong-63663c6bc9ee270e02b06ff3d4f8812f1ef4d49b.tar.xz platform-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) { | |||
59 | "-w", "missingdepfile=err") | 59 | "-w", "missingdepfile=err") |
60 | 60 | ||
61 | cmd := Command(ctx, config, "ninja", executable, args...) | 61 | cmd := Command(ctx, config, "ninja", executable, args...) |
62 | cmd.Sandbox = ninjaSandbox | ||
62 | if config.HasKatiSuffix() { | 63 | if config.HasKatiSuffix() { |
63 | cmd.Environment.AppendFromKati(config.KatiEnvFile()) | 64 | cmd.Environment.AppendFromKati(config.KatiEnvFile()) |
64 | } | 65 | } |
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 ( | |||
21 | type Sandbox string | 21 | type Sandbox string |
22 | 22 | ||
23 | const ( | 23 | const ( |
24 | noSandbox = "" | 24 | noSandbox = "" |
25 | globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb" | 25 | globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb" |
26 | dumpvarsSandbox = globalSandbox | 26 | dumpvarsSandbox = globalSandbox |
27 | soongSandbox = globalSandbox | 27 | soongSandbox = globalSandbox |
28 | katiSandbox = globalSandbox | 28 | katiSandbox = globalSandbox |
29 | katiCleanSpecSandbox = globalSandbox | 29 | ninjaSandbox = noSandbox |
30 | ) | 30 | ) |
31 | 31 | ||
32 | var sandboxExecPath string | 32 | 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 @@ | |||
14 | 14 | ||
15 | package build | 15 | package build |
16 | 16 | ||
17 | type Sandbox bool | 17 | import ( |
18 | 18 | "bytes" | |
19 | const ( | 19 | "os" |
20 | noSandbox = false | 20 | "os/exec" |
21 | globalSandbox = false | 21 | "os/user" |
22 | dumpvarsSandbox = false | 22 | "strings" |
23 | soongSandbox = false | 23 | "sync" |
24 | katiSandbox = false | ||
25 | katiCleanSpecSandbox = false | ||
26 | ) | 24 | ) |
27 | 25 | ||
26 | type Sandbox struct { | ||
27 | Enabled bool | ||
28 | DisableWhenUsingGoma bool | ||
29 | } | ||
30 | |||
31 | var ( | ||
32 | noSandbox = Sandbox{} | ||
33 | basicSandbox = Sandbox{ | ||
34 | Enabled: true, | ||
35 | } | ||
36 | |||
37 | dumpvarsSandbox = basicSandbox | ||
38 | katiSandbox = basicSandbox | ||
39 | soongSandbox = basicSandbox | ||
40 | ninjaSandbox = Sandbox{ | ||
41 | Enabled: true, | ||
42 | DisableWhenUsingGoma: true, | ||
43 | } | ||
44 | ) | ||
45 | |||
46 | const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail" | ||
47 | |||
48 | var sandboxConfig struct { | ||
49 | once sync.Once | ||
50 | |||
51 | working bool | ||
52 | group string | ||
53 | } | ||
54 | |||
28 | func (c *Cmd) sandboxSupported() bool { | 55 | func (c *Cmd) sandboxSupported() bool { |
29 | return false | 56 | if !c.Sandbox.Enabled { |
57 | return false | ||
58 | } | ||
59 | |||
60 | // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582 | ||
61 | if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() { | ||
62 | return false | ||
63 | } | ||
64 | |||
65 | sandboxConfig.once.Do(func() { | ||
66 | sandboxConfig.group = "nogroup" | ||
67 | if _, err := user.LookupGroup(sandboxConfig.group); err != nil { | ||
68 | sandboxConfig.group = "nobody" | ||
69 | } | ||
70 | |||
71 | cmd := exec.CommandContext(c.ctx.Context, nsjailPath, | ||
72 | "-H", "android-build", | ||
73 | "-e", | ||
74 | "-u", "nobody", | ||
75 | "-g", sandboxConfig.group, | ||
76 | "-B", "/", | ||
77 | "--disable_clone_newcgroup", | ||
78 | "--", | ||
79 | "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`) | ||
80 | cmd.Env = c.config.Environment().Environ() | ||
81 | |||
82 | c.ctx.Verboseln(cmd.Args) | ||
83 | data, err := cmd.CombinedOutput() | ||
84 | if err == nil && bytes.Contains(data, []byte("Android Success")) { | ||
85 | sandboxConfig.working = true | ||
86 | return | ||
87 | } | ||
88 | |||
89 | c.ctx.Println("Build sandboxing disabled due to nsjail error. This may become fatal in the future.") | ||
90 | c.ctx.Println("Please let us know why nsjail doesn't work in your environment at:") | ||
91 | c.ctx.Println(" https://groups.google.com/forum/#!forum/android-building") | ||
92 | c.ctx.Println(" https://issuetracker.google.com/issues/new?component=381517") | ||
93 | |||
94 | for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") { | ||
95 | c.ctx.Verboseln(line) | ||
96 | } | ||
97 | |||
98 | if err == nil { | ||
99 | c.ctx.Verboseln("nsjail exited successfully, but without the correct output") | ||
100 | } else if e, ok := err.(*exec.ExitError); ok { | ||
101 | c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String()) | ||
102 | } else { | ||
103 | c.ctx.Verbosef("nsjail failed with %v", err) | ||
104 | } | ||
105 | }) | ||
106 | |||
107 | return sandboxConfig.working | ||
30 | } | 108 | } |
31 | 109 | ||
32 | func (c *Cmd) wrapSandbox() { | 110 | func (c *Cmd) wrapSandbox() { |
111 | wd, _ := os.Getwd() | ||
112 | |||
113 | sandboxArgs := []string{ | ||
114 | // The executable to run | ||
115 | "-x", c.Path, | ||
116 | |||
117 | // Set the hostname to something consistent | ||
118 | "-H", "android-build", | ||
119 | |||
120 | // Use the current working dir | ||
121 | "--cwd", wd, | ||
122 | |||
123 | // No time limit | ||
124 | "-t", "0", | ||
125 | |||
126 | // Keep all environment variables, we already filter them out | ||
127 | // in soong_ui | ||
128 | "-e", | ||
129 | |||
130 | // Use a consistent user & group. | ||
131 | // Note that these are mapped back to the real UID/GID when | ||
132 | // doing filesystem operations, so they're rather arbitrary. | ||
133 | "-u", "nobody", | ||
134 | "-g", sandboxConfig.group, | ||
135 | |||
136 | // Set high values, as nsjail uses low defaults. | ||
137 | "--rlimit_as", "soft", | ||
138 | "--rlimit_core", "soft", | ||
139 | "--rlimit_cpu", "soft", | ||
140 | "--rlimit_fsize", "soft", | ||
141 | "--rlimit_nofile", "soft", | ||
142 | |||
143 | // For now, just map everything. Eventually we should limit this, especially to make most things readonly. | ||
144 | "-B", "/", | ||
145 | |||
146 | // Enable networking for now. TODO: remove | ||
147 | "-N", | ||
148 | |||
149 | // Disable newcgroup for now, since it may require newer kernels | ||
150 | // TODO: try out cgroups | ||
151 | "--disable_clone_newcgroup", | ||
152 | |||
153 | // Only log important warnings / errors | ||
154 | "-q", | ||
155 | |||
156 | // Stop parsing arguments | ||
157 | "--", | ||
158 | } | ||
159 | c.Args = append(sandboxArgs, c.Args[1:]...) | ||
160 | c.Path = nsjailPath | ||
161 | |||
162 | env := Environment(c.Env) | ||
163 | if _, hasUser := env.Get("USER"); hasUser { | ||
164 | env.Set("USER", "nobody") | ||
165 | } | ||
166 | c.Env = []string(env) | ||
33 | } | 167 | } |