diff --git a/src/sandstorm/config.c++ b/src/sandstorm/config.c++ index d7d037809..cfbc9b047 100644 --- a/src/sandstorm/config.c++ +++ b/src/sandstorm/config.c++ @@ -241,6 +241,8 @@ Config readConfig(const char *path, bool parseUids) { config.allowDemoAccounts = value == "true" || value == "yes"; } else if (key == "ALLOW_DEV_ACCOUNTS") { config.allowDevAccounts = value == "true" || value == "yes"; + } else if (key == "ALLOW_UNINVITED") { + config.allowUninvited = value == "true" || value == "yes"; } else if (key == "IS_TESTING") { config.isTesting = value == "true" || value == "yes"; } else if (key == "HIDE_TROUBLESHOOTING") { diff --git a/src/sandstorm/config.h b/src/sandstorm/config.h index 38e64d169..c70b0bd74 100644 --- a/src/sandstorm/config.h +++ b/src/sandstorm/config.h @@ -25,6 +25,7 @@ struct Config { kj::String sandcatsHostname = nullptr; bool allowDemoAccounts = false; bool isTesting = false; + bool allowUninvited = false; bool allowDevAccounts = false; bool hideTroubleshooting = false; uint smtpListenPort = 30025; diff --git a/src/sandstorm/run-bundle.c++ b/src/sandstorm/run-bundle.c++ index d7ec4d330..10b4853ed 100644 --- a/src/sandstorm/run-bundle.c++ +++ b/src/sandstorm/run-bundle.c++ @@ -1396,6 +1396,13 @@ private: } } + void idMapUidNamespace(uid_t uid, gid_t gid) { + // Identity-map the given uid & gid in the current uid namespace. + writeSetgroupsIfPresent("deny\n"); + writeUserNSMap("uid", kj::str(uid, " ", uid, " 1\n")); + writeUserNSMap("gid", kj::str(gid, " ", gid, " 1\n")); + } + void unshareUidNamespaceOnce() { if (!unsharedUidNamespace) { uid_t uid = getuid(); @@ -1403,16 +1410,7 @@ private: KJ_SYSCALL(unshare(CLONE_NEWUSER)); - // Set up the UID namespace. We map ourselves as UID zero because this allows capabilities - // to be inherited through exec(), which we need to support update and restart. With any - // other UID, capabilities can only be inherited through exec() if the target exec'd file - // has its inheritable capabilities set filled. By default, the inheritable capability set - // for all files is empty, and only the filesystem's superuser (i.e. not us) can change them. - // But if our UID is zero, then the file's attributes are ignored and all capabilities are - // inherited. - writeSetgroupsIfPresent("deny\n"); - writeUserNSMap("uid", kj::str("0 ", uid, " 1\n")); - writeUserNSMap("gid", kj::str("0 ", gid, " 1\n")); + idMapUidNamespace(uid, gid); unsharedUidNamespace = true; } @@ -1951,10 +1949,23 @@ private: pid_t updaterPid = startUpdater(config, fdBundle, false); - pid_t sandstormPid = fork(); - if (sandstormPid == 0) { - runServerMonitor(config, fdBundle); - KJ_UNREACHABLE; + // Start the server monitor. + pid_t sandstormPid; + { + int cloneFlags = CLONE_PARENT_SETTID | SIGCHLD; + if(!runningAsRoot) { + cloneFlags |= CLONE_NEWUSER|CLONE_NEWPID; + unsharedUidNamespace = true; + } + + uid_t uid = getuid(); + gid_t gid = getgid(); + + KJ_SYSCALL(syscall(SYS_clone, cloneFlags, nullptr, &sandstormPid, 0)); + if(sandstormPid == 0) { + runServerMonitor(config, fdBundle, uid, gid); + KJ_UNREACHABLE; + } } for (;;) { @@ -2019,11 +2030,19 @@ private: } } - [[noreturn]] void runServerMonitor(const Config& config, FdBundle& fdBundle) { + [[noreturn]] void runServerMonitor(const Config& config, FdBundle& fdBundle, + uid_t uid, gid_t gid) { // Run the server monitor, which runs node and mongo and deals with them dying. + // The uid and gid should be the uid and gid for the parent process in its own + // user namespace (if user namespaces are in use). setProcessName("montr", "(server monitor)"); + if(!runningAsRoot) { + // We were cloned into a new user namespace, so we need to set up a mapping: + idMapUidNamespace(uid, gid); + } + enterChroot(config.uids, true); // Make sure socket directory exists (since the installer doesn't create it). @@ -2640,6 +2659,7 @@ private: "{ \"build\":", buildstamp, ", \"allowDemoAccounts\":", config.allowDemoAccounts ? "true" : "false", ", \"allowDevAccounts\":", config.allowDevAccounts ? "true" : "false", + ", \"allowUninvited\":", config.allowUninvited ? "true" : "false", ", \"isTesting\":", config.isTesting ? "true" : "false", ", \"hideTroubleshooting\":", config.hideTroubleshooting ? "true" : "false", ", \"wildcardHost\":\"", config.wildcardHost, "\"", diff --git a/tests/run-local.sh b/tests/run-local.sh index 7bd574e11..9661873f4 100755 --- a/tests/run-local.sh +++ b/tests/run-local.sh @@ -138,6 +138,7 @@ rm -rf "$SANDSTORM_DIR" echo "IS_TESTING=true ALLOW_DEMO_ACCOUNTS=true +ALLOW_UNINVITED=true BASE_URL=http://local.sandstorm.io:$PORT WILDCARD_HOST=*.local.sandstorm.io:$PORT PORT=$PORT