diff options
author | Philipp Wollermann <philwo@google.com> | 2017-03-24 12:05:07 +0000 |
---|---|---|
committer | Yue Gan <yueg@google.com> | 2017-03-24 12:20:37 +0000 |
commit | d7b774bbef316a29c457771bd4309adc1a4bbdbd (patch) | |
tree | 1233c9c3339c9195bee1b966d9bb2ea85dad309b | |
parent | 7429648a3bf241becf38711f876201dd5c6c5cb5 (diff) |
sandbox: No longer change the user to 'nobody' by default.
This can be reactivated by passing the --sandbox_fake_username flag
to Bazel.
Reasoning: 'nobody' has a non-existent home directory on many Linux
distros, leading to issues when tools try to stat / read / write to the
home directory.
Related to #2688.
RELNOTES: The Linux sandbox no longer changes the user to 'nobody' by
default, instead the current user is used as is. The old behavior can be
restored via the --sandbox_fake_username flag.
--
PiperOrigin-RevId: 151115218
MOS_MIGRATED_REVID=151115218
11 files changed, 75 insertions, 19 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java index a297ebe4f9..471158a29e 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java @@ -108,7 +108,8 @@ final class DarwinSandboxRunner extends SandboxRunner { Map<String, String> environment, int timeout, boolean allowNetwork, - boolean useFakeHostname) + boolean useFakeHostname, + boolean useFakeUsername) throws IOException { writeConfig(allowNetwork); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java index 29d908415b..68d3f695bb 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java @@ -105,9 +105,10 @@ final class LinuxSandboxRunner extends SandboxRunner { Map<String, String> env, int timeout, boolean allowNetwork, - boolean useFakeHostname) + boolean useFakeHostname, + boolean useFakeUsername) throws IOException { - writeConfig(timeout, allowNetwork, useFakeHostname); + writeConfig(timeout, allowNetwork, useFakeHostname, useFakeUsername); List<String> commandLineArgs = new ArrayList<>(3 + spawnArguments.size()); commandLineArgs.add(execRoot.getRelative("_bin/linux-sandbox").getPathString()); @@ -117,7 +118,8 @@ final class LinuxSandboxRunner extends SandboxRunner { return new Command(commandLineArgs.toArray(new String[0]), env, sandboxExecRoot.getPathFile()); } - private void writeConfig(int timeout, boolean allowNetwork, boolean useFakeHostname) + private void writeConfig( + int timeout, boolean allowNetwork, boolean useFakeHostname, boolean useFakeUsername) throws IOException { List<String> fileArgs = new ArrayList<>(); @@ -163,10 +165,15 @@ final class LinuxSandboxRunner extends SandboxRunner { } if (useFakeHostname) { - // Use a fake hostname ("localhost") inside the sandbox when blocking network access. + // Use a fake hostname ("localhost") inside the sandbox. fileArgs.add("-H"); } + if (useFakeUsername) { + // Use a fake username ("nobody") inside the sandbox. + fileArgs.add("-U"); + } + FileSystemUtils.writeLinesAs(argumentsFilePath, StandardCharsets.ISO_8859_1, fileArgs); } } diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java index b4102a7e22..f3ee9d14d1 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java @@ -58,7 +58,8 @@ final class ProcessWrapperRunner extends SandboxRunner { Map<String, String> env, int timeout, boolean allowNetwork, - boolean useFakeHostname) { + boolean useFakeHostname, + boolean useFakeUsername) { List<String> commandLineArgs = new ArrayList<>(5 + spawnArguments.size()); commandLineArgs.add(execRoot.getRelative("_bin/process-wrapper").getPathString()); commandLineArgs.add(Integer.toString(timeout)); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java index 303ec0fc46..a7185f41fe 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java @@ -94,6 +94,14 @@ public class SandboxOptions extends OptionsBase { public boolean sandboxFakeHostname; @Option( + name = "sandbox_fake_username", + defaultValue = "false", + category = "strategy", + help = "Change the current username to 'nobody' for sandboxed actions." + ) + public boolean sandboxFakeUsername; + + @Option( name = "sandbox_tmpfs_path", allowMultiple = true, defaultValue = "", diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java index 49d661d743..efa423e320 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java @@ -50,6 +50,7 @@ abstract class SandboxRunner { * @param allowNetwork - whether networking should be allowed for the process. * @param sandboxDebug - whether debugging message should be printed. * @param useFakeHostname - whether the hostname should be set to 'localhost' inside the sandbox. + * @param useFakeUsername - whether the username should be set to 'nobody' inside the sandbox. */ void run( List<String> arguments, @@ -58,11 +59,14 @@ abstract class SandboxRunner { int timeout, boolean allowNetwork, boolean sandboxDebug, - boolean useFakeHostname) + boolean useFakeHostname, + boolean useFakeUsername) throws ExecException { Command cmd; try { - cmd = getCommand(arguments, environment, timeout, allowNetwork, useFakeHostname); + cmd = + getCommand( + arguments, environment, timeout, allowNetwork, useFakeHostname, useFakeUsername); } catch (IOException e) { throw new UserExecException("I/O error during sandboxed execution", e); } @@ -111,13 +115,15 @@ abstract class SandboxRunner { * @param timeout - after how many seconds should the process be killed. * @param allowNetwork - whether networking should be allowed for the process. * @param useFakeHostname - whether the hostname should be set to 'localhost' inside the sandbox. + * @param useFakeUsername - whether the username should be set to 'nobody' inside the sandbox. */ protected abstract Command getCommand( List<String> arguments, Map<String, String> environment, int timeout, boolean allowNetwork, - boolean useFakeHostname) + boolean useFakeHostname, + boolean useFakeUsername) throws IOException; /** diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java index ffa0ff4ee3..ba88c8fa54 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java @@ -84,7 +84,8 @@ abstract class SandboxStrategy implements SandboxedSpawnActionContext { Spawns.getTimeoutSeconds(spawn), SandboxHelpers.shouldAllowNetwork(buildRequest, spawn), sandboxOptions.sandboxDebug, - sandboxOptions.sandboxFakeHostname); + sandboxOptions.sandboxFakeHostname, + sandboxOptions.sandboxFakeUsername); } catch (ExecException e) { execException = e; } diff --git a/src/main/tools/linux-sandbox-options.cc b/src/main/tools/linux-sandbox-options.cc index 96354ec8ec..78831e6f03 100644 --- a/src/main/tools/linux-sandbox-options.cc +++ b/src/main/tools/linux-sandbox-options.cc @@ -73,7 +73,8 @@ static void Usage(char *program_name, const char *fmt, ...) { " The -M option specifies which directory to mount, the -m option " "specifies where to\n" " -N if set, a new network namespace will be created\n" - " -R if set, make the uid/gid be root, otherwise use nobody\n" + " -R if set, make the uid/gid be root\n" + " -U if set, make the uid/gid be nobody\n" " -D if set, debug info will be printed\n" " @FILE read newline-separated arguments from FILE\n" " -- command to run inside sandbox, followed by arguments\n"); @@ -123,8 +124,8 @@ static void ParseCommandLine(unique_ptr<vector<char *>> args) { int c; bool source_specified; - while ((c = getopt(args->size(), args->data(), ":CW:T:t:l:L:w:e:M:m:HNRD")) != - -1) { + while ((c = getopt(args->size(), args->data(), + ":CW:T:t:l:L:w:e:M:m:HNRUD")) != -1) { if (c != 'M' && c != 'm') source_specified = false; switch (c) { case 'C': @@ -200,8 +201,21 @@ static void ParseCommandLine(unique_ptr<vector<char *>> args) { opt.create_netns = true; break; case 'R': + if (opt.fake_username) { + Usage(args->front(), + "The -R option cannot be used at the same time us the -U " + "option."); + } opt.fake_root = true; break; + case 'U': + if (opt.fake_root) { + Usage(args->front(), + "The -U option cannot be used at the same time us the -R " + "option."); + } + opt.fake_username = true; + break; case 'D': opt.debug = true; break; diff --git a/src/main/tools/linux-sandbox-options.h b/src/main/tools/linux-sandbox-options.h index 5c78e46d31..daf1fd684e 100644 --- a/src/main/tools/linux-sandbox-options.h +++ b/src/main/tools/linux-sandbox-options.h @@ -46,6 +46,8 @@ struct Options { bool create_netns; // Pretend to be root inside the namespace (-R) bool fake_root; + // Set the username inside the sandbox to 'nobody' (-U) + bool fake_username; // Print debugging messages (-D) bool debug; // Command to run (--) diff --git a/src/main/tools/linux-sandbox-pid1.cc b/src/main/tools/linux-sandbox-pid1.cc index 76dccbad24..17a71432b9 100644 --- a/src/main/tools/linux-sandbox-pid1.cc +++ b/src/main/tools/linux-sandbox-pid1.cc @@ -120,8 +120,13 @@ static void SetupUserNamespace() { } } - int inner_uid = 0, inner_gid = 0; - if (!opt.fake_root) { + int inner_uid, inner_gid; + if (opt.fake_root) { + // Change our username to 'root'. + inner_uid = 0; + inner_gid = 0; + } else if (opt.fake_username) { + // Change our username to 'nobody'. struct passwd *pwd = getpwnam("nobody"); if (pwd == NULL) { DIE("unable to find passwd entry for user nobody") @@ -129,6 +134,10 @@ static void SetupUserNamespace() { inner_uid = pwd->pw_uid; inner_gid = pwd->pw_gid; + } else { + // Do not change the username inside the sandbox. + inner_uid = global_outer_uid; + inner_gid = global_outer_gid; } WriteFile("/proc/self/uid_map", "%d %d 1\n", inner_uid, global_outer_uid); diff --git a/src/main/tools/linux-sandbox.cc b/src/main/tools/linux-sandbox.cc index 0d73174727..9d2fd01767 100644 --- a/src/main/tools/linux-sandbox.cc +++ b/src/main/tools/linux-sandbox.cc @@ -25,12 +25,14 @@ * - If the process takes longer than the timeout (-T), it will be killed with * SIGTERM. If it does not exit within the grace period (-t), it all of its * children will be killed with SIGKILL. + * - If option -R is passed, the process will run as user 'root'. + * - If option -U is passed, the process will run as user 'nobody'. + * - Otherwise, the process runs using the current uid / gid. * - If linux-sandbox itself gets killed, the process and all of its children * will be killed. * - If linux-sandbox's parent dies, it will kill itself, the process and all * the children. * - Network access is allowed, but can be disabled via -N. - * - The process runs as user "nobody", unless fakeroot is enabled (-R). * - The hostname and domainname will be set to "sandbox". * - The process runs in its own PID namespace, so other processes on the * system are invisible. diff --git a/src/test/shell/bazel/linux-sandbox_test.sh b/src/test/shell/bazel/linux-sandbox_test.sh index 87310c3f85..6529ae2de4 100755 --- a/src/test/shell/bazel/linux-sandbox_test.sh +++ b/src/test/shell/bazel/linux-sandbox_test.sh @@ -47,14 +47,19 @@ function test_execvp_error_message_contains_path() { expect_log "\"execvp(/does/not/exist, 0x[[:alnum:]]*)\": No such file or directory" } -function test_default_user_is_nobody() { +function test_default_user_is_current_user() { $linux_sandbox $SANDBOX_DEFAULT_OPTS -- /usr/bin/id &> $TEST_log || fail - expect_log "uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)" + expect_log "$(id)" } function test_user_switched_to_root() { $linux_sandbox $SANDBOX_DEFAULT_OPTS -R -- /usr/bin/id &> $TEST_log || fail - expect_log "uid=0(root) gid=0(root)" + expect_log "uid=0(root) gid=0(root) groups=0(root)" +} + +function test_user_switched_to_nobody() { + $linux_sandbox $SANDBOX_DEFAULT_OPTS -U -- /usr/bin/id &> $TEST_log || fail + expect_log "uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)" } function test_network_namespace() { |