Skip to content

Commit 5b22e89

Browse files
MichaelCuevasmeta-codesync[bot]
authored andcommitted
Refuse to start in isolated mount namespace
Summary: When `core:require-root-mount-namespace` is enabled (default false), EdenFS refuses to start if either the daemon or privhelper is not in the root mount namespace. Prevents the scenario from S626335 where agents restarted EdenFS inside private mount namespaces, making all mounts invisible to users. Also adds namespace detection logging at startup and renames the `DaemonStart` telemetry fields from `mount_namespace`/`pid_namespace`/ `is_root_mount_namespace` to `daemon_mount_namespace`/ `daemon_pid_namespace`/`is_daemon_in_root_mount_namespace`, and adds `is_privhelper_in_root_mount_namespace`. Reviewed By: muirdm, quark-zju Differential Revision: D94545183 fbshipit-source-id: 0b306af539a20ebfeb82f5f4d34303c4942e0666
1 parent 351e17e commit 5b22e89

4 files changed

Lines changed: 103 additions & 19 deletions

File tree

eden/fs/config/EdenConfig.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ class EdenConfig : private ConfigSettingManager {
288288
std::nullopt,
289289
this};
290290

291+
/**
292+
* If true, EdenFS refuses to start when running in a non-root mount
293+
* namespace.
294+
*/
295+
ConfigSetting<bool> requireRootMountNamespace{
296+
"core:require-root-mount-namespace",
297+
false,
298+
this};
299+
291300
/**
292301
* Timeout value for a clean shutdown on SIGTERM. If the timeout elapses, we
293302
* exit immediately. Set to zero to revert to the old behavior where we

eden/fs/service/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ cpp_library(
1414
"EdenMain.cpp",
1515
],
1616
deps = [
17+
"fbsource//third-party/fmt:fmt",
1718
":init",
1819
":startup_logger",
1920
":startup_status_subscriber",

eden/fs/service/EdenMain.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <fb303/FollyLoggingHandler.h>
1414
#include <fb303/TFunctionStatHandler.h>
15+
#include <fmt/format.h>
1516
#include <folly/Conv.h>
1617
#include <folly/MapUtil.h>
1718
#include <folly/executors/FunctionScheduler.h>
@@ -102,6 +103,10 @@ std::optional<bool> checkIsRootMountNamespace() {
102103
return std::nullopt;
103104
}
104105

106+
// Helper macro to convert optional to string for logging
107+
#define OPT_TO_STRING(opt) \
108+
((opt).has_value() ? folly::to<std::string>(*(opt)) : "nullopt")
109+
105110
std::optional<std::string> readCgroup() {
106111
std::string contents;
107112
if (folly::readFile("/proc/self/cgroup", contents)) {
@@ -398,17 +403,14 @@ int runEdenMain(EdenMain&& main, int argc, char** argv) {
398403
daemonPidNamespace = getNamespaceInode("/proc/self/ns/pid");
399404
isDaemonInRootMountNamespace = checkIsRootMountNamespace();
400405
}
401-
(void)isPrivhelperInRootMountNamespace;
402-
(void)privhelperPidNamespace;
403406
#else
404407
std::optional<uint64_t> daemonMountNamespace;
405408
std::optional<uint64_t> daemonPidNamespace;
406409
std::optional<std::string> cgroupInfo;
407410
std::optional<bool> isDaemonInRootMountNamespace;
408411
std::optional<bool> isPrivhelperInRootMountNamespace;
412+
std::optional<uint64_t> privhelperMountNamespace;
409413
std::optional<uint64_t> privhelperPidNamespace;
410-
(void)isPrivhelperInRootMountNamespace;
411-
(void)privhelperPidNamespace;
412414
#endif
413415

414416
std::vector<std::string> originalCommandLine{argv, argv + argc};
@@ -511,6 +513,39 @@ int runEdenMain(EdenMain&& main, int argc, char** argv) {
511513
daemonPid, daemonMemoryPriority.value());
512514
}
513515

516+
#ifdef __linux__
517+
XLOGF(
518+
DBG2,
519+
"Namespace detection: daemon(mnt={}, pid={}), privhelper(mnt={}, pid={}), "
520+
"rootMountNsInode={}, isDaemonInRootMountNamespace={}, "
521+
"isPrivhelperInRootMountNamespace={}",
522+
OPT_TO_STRING(daemonMountNamespace),
523+
OPT_TO_STRING(daemonPidNamespace),
524+
OPT_TO_STRING(privhelperMountNamespace),
525+
OPT_TO_STRING(privhelperPidNamespace),
526+
OPT_TO_STRING(rootMountNsInode),
527+
OPT_TO_STRING(isDaemonInRootMountNamespace),
528+
OPT_TO_STRING(isPrivhelperInRootMountNamespace));
529+
530+
if (edenConfig->requireRootMountNamespace.getValue()) {
531+
// To avoid disruption, we assume that the daemon and privhelper are in
532+
// the root mount namespace unless we can prove otherwise.
533+
bool daemonInRootNs = isDaemonInRootMountNamespace.value_or(true);
534+
bool privhelperInRootNs = isPrivhelperInRootMountNamespace.value_or(true);
535+
536+
if (!daemonInRootNs || !privhelperInRootNs) {
537+
auto errorMsg = fmt::format(
538+
"EdenFS cannot start: daemon in root mount namespace = {}, "
539+
"privhelper in root mount namespace = {}.\n To allow this "
540+
"behavior, set core.require-root-mount-namespace config to false",
541+
daemonInRootNs,
542+
privhelperInRootNs);
543+
XLOG(ERR, errorMsg);
544+
startupLogger->exitUnsuccessfully(kExitCodeError, errorMsg);
545+
}
546+
}
547+
#endif // __linux__
548+
514549
server.emplace(
515550
std::move(originalCommandLine),
516551
std::move(identity),
@@ -538,7 +573,10 @@ int runEdenMain(EdenMain&& main, int argc, char** argv) {
538573
false /*success*/,
539574
daemonMountNamespace,
540575
daemonPidNamespace,
576+
privhelperMountNamespace,
577+
privhelperPidNamespace,
541578
isDaemonInRootMountNamespace,
579+
isPrivhelperInRootMountNamespace,
542580
cgroupInfo});
543581
}
544582
startupLogger->exitUnsuccessfully(
@@ -573,7 +611,10 @@ int runEdenMain(EdenMain&& main, int argc, char** argv) {
573611
takeover = FLAGS_takeover,
574612
daemonMountNamespace,
575613
daemonPidNamespace,
614+
privhelperMountNamespace,
615+
privhelperPidNamespace,
576616
isDaemonInRootMountNamespace,
617+
isPrivhelperInRootMountNamespace,
577618
cgroupInfo,
578619
&server] {
579620
// This value is slightly different from `startTimeInSeconds`
@@ -592,7 +633,10 @@ int runEdenMain(EdenMain&& main, int argc, char** argv) {
592633
true /*success*/,
593634
daemonMountNamespace,
594635
daemonPidNamespace,
636+
privhelperMountNamespace,
637+
privhelperPidNamespace,
595638
isDaemonInRootMountNamespace,
639+
isPrivhelperInRootMountNamespace,
596640
cgroupInfo});
597641

598642
#ifndef _WIN32

eden/fs/telemetry/LogEvent.h

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -185,39 +185,69 @@ struct DaemonStart : public EdenFSEvent {
185185
double duration = 0.0;
186186
bool is_takeover = false;
187187
bool success = false;
188-
std::optional<uint64_t> mount_namespace;
189-
std::optional<uint64_t> pid_namespace;
190-
std::optional<bool> is_root_mount_namespace;
188+
std::optional<uint64_t> daemon_mount_namespace;
189+
std::optional<uint64_t> daemon_pid_namespace;
190+
std::optional<uint64_t> privhelper_mount_namespace;
191+
std::optional<uint64_t> privhelper_pid_namespace;
192+
std::optional<bool> is_daemon_in_root_mount_namespace;
193+
std::optional<bool> is_privhelper_in_root_mount_namespace;
191194
std::optional<std::string> cgroup;
192195

193196
DaemonStart(
194197
double duration,
195198
bool is_takeover,
196199
bool success,
197-
std::optional<uint64_t> mount_namespace = std::nullopt,
198-
std::optional<uint64_t> pid_namespace = std::nullopt,
199-
std::optional<bool> is_root_mount_namespace = std::nullopt,
200+
std::optional<uint64_t> daemon_mount_namespace = std::nullopt,
201+
std::optional<uint64_t> daemon_pid_namespace = std::nullopt,
202+
std::optional<uint64_t> privhelper_mount_namespace = std::nullopt,
203+
std::optional<uint64_t> privhelper_pid_namespace = std::nullopt,
204+
std::optional<bool> is_daemon_in_root_mount_namespace = std::nullopt,
205+
std::optional<bool> is_privhelper_in_root_mount_namespace = std::nullopt,
200206
std::optional<std::string> cgroup = std::nullopt)
201207
: duration(duration),
202208
is_takeover(is_takeover),
203209
success(success),
204-
mount_namespace(mount_namespace),
205-
pid_namespace(pid_namespace),
206-
is_root_mount_namespace(is_root_mount_namespace),
210+
daemon_mount_namespace(daemon_mount_namespace),
211+
daemon_pid_namespace(daemon_pid_namespace),
212+
privhelper_mount_namespace(privhelper_mount_namespace),
213+
privhelper_pid_namespace(privhelper_pid_namespace),
214+
is_daemon_in_root_mount_namespace(is_daemon_in_root_mount_namespace),
215+
is_privhelper_in_root_mount_namespace(
216+
is_privhelper_in_root_mount_namespace),
207217
cgroup(std::move(cgroup)) {}
208218

209219
void populate(DynamicEvent& event) const override {
210220
event.addDouble("duration", duration);
211221
event.addBool("is_takeover", is_takeover);
212222
event.addBool("success", success);
213-
if (mount_namespace.has_value()) {
214-
event.addInt("mount_namespace", static_cast<int64_t>(*mount_namespace));
223+
if (daemon_mount_namespace.has_value()) {
224+
event.addInt(
225+
"daemon_mount_namespace",
226+
static_cast<int64_t>(*daemon_mount_namespace));
227+
}
228+
if (daemon_pid_namespace.has_value()) {
229+
event.addInt(
230+
"daemon_pid_namespace", static_cast<int64_t>(*daemon_pid_namespace));
231+
}
232+
if (privhelper_mount_namespace.has_value()) {
233+
event.addInt(
234+
"privhelper_mount_namespace",
235+
static_cast<int64_t>(*privhelper_mount_namespace));
236+
}
237+
if (privhelper_pid_namespace.has_value()) {
238+
event.addInt(
239+
"privhelper_pid_namespace",
240+
static_cast<int64_t>(*privhelper_pid_namespace));
215241
}
216-
if (pid_namespace.has_value()) {
217-
event.addInt("pid_namespace", static_cast<int64_t>(*pid_namespace));
242+
if (is_daemon_in_root_mount_namespace.has_value()) {
243+
event.addBool(
244+
"is_daemon_in_root_mount_namespace",
245+
*is_daemon_in_root_mount_namespace);
218246
}
219-
if (is_root_mount_namespace.has_value()) {
220-
event.addBool("is_root_mount_namespace", *is_root_mount_namespace);
247+
if (is_privhelper_in_root_mount_namespace.has_value()) {
248+
event.addBool(
249+
"is_privhelper_in_root_mount_namespace",
250+
*is_privhelper_in_root_mount_namespace);
221251
}
222252
if (cgroup.has_value()) {
223253
event.addString("cgroup", *cgroup);

0 commit comments

Comments
 (0)