aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/namespaces.cc
blob: cde44267177dac1b2f5d14d1f972755f7afcacd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#if __linux__

#include "file-system.hh"
#include "logging.hh"
#include "util.hh"
#include "namespaces.hh"
#include "processes.hh"

#include <sys/mount.h>

namespace nix {

static void diagnoseUserNamespaces()
{
    if (!pathExists("/proc/self/ns/user")) {
        warn("'/proc/self/ns/user' does not exist; your kernel was likely built without CONFIG_USER_NS=y");
    }

    Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
    if (!pathExists(maxUserNamespaces) ||
        trim(readFile(maxUserNamespaces)) == "0")
    {
        warn("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'");
    }

    Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
    if (pathExists(procSysKernelUnprivilegedUsernsClone)
        && trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0")
    {
        warn("user namespaces appear to be disabled for unprivileged users; check '/proc/sys/kernel/unprivileged_userns_clone'");
    }
}

bool userNamespacesSupported()
{
    static auto res = [&]() -> bool
    {
        try {
            Pid pid = startProcess([&]()
            {
                _exit(0);
            }, {
                .cloneFlags = CLONE_NEWUSER
            });

            auto r = pid.wait();
            assert(!r);
        } catch (SysError & e) {
            warn("user namespaces do not work on this system: %s", e.msg());
            diagnoseUserNamespaces();
            return false;
        }

        return true;
    }();
    return res;
}

bool mountAndPidNamespacesSupported()
{
    static auto res = [&]() -> bool
    {
        try {

            Pid pid = startProcess([&]()
            {
                /* Make sure we don't remount the parent's /proc. */
                if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
                    _exit(1);

                /* Test whether we can remount /proc. The kernel disallows
                   this if /proc is not fully visible, i.e. if there are
                   filesystems mounted on top of files inside /proc.  See
                   https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
                if (mount("none", "/proc", "proc", 0, 0) == -1)
                    _exit(2);

                _exit(0);
            }, {
                .cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)
            });

            if (pid.wait()) {
                debug("PID namespaces do not work on this system: cannot remount /proc");
                return false;
            }

        } catch (SysError & e) {
            debug("mount namespaces do not work on this system: %s", e.msg());
            return false;
        }

        return true;
    }();
    return res;
}

}

#endif