about summary refs log tree commit diff
diff options
context:
space:
mode:
authoraszlig <aszlig@nix.build>2020-09-11 18:34:13 +0200
committeraszlig <aszlig@nix.build>2020-09-11 18:47:53 +0200
commit877fb29635370f3ebbdfd0211460bc66347269ea (patch)
treeaed068f654a39ab43306920ee510e28c4a870e80
parentd7f2e2f0b32717d75e7968ab638aec8136f7803e (diff)
sandbox: Allow to enable/disable namespaces
While the sandbox was initially written for games, I now use the
implementation for other things, such as sandboxing database management
systems inside "nix develop".

However, both MariaDB and PostgreSQL do not like it very much if for
example IPC is too restricted and if the PID file contains the PID of
the process inside the namespace.

Additionally I always wanted to have a way to enable network namespaces
for games as well, so this is a good occasion to make them configurable.

Of course, since we need the mount and user namespaces to implement our
sandbox in the first place, we can't allow users to disable these
namespaces, but for everything else, we now have a new "namespaces"
attribute.

Signed-off-by: aszlig <aszlig@nix.build>
-rw-r--r--pkgs/build-support/build-sandbox/default.nix23
-rw-r--r--pkgs/build-support/build-sandbox/src/Makefile2
-rw-r--r--pkgs/build-support/build-sandbox/src/setup.c3
-rw-r--r--tests/sandbox.nix25
4 files changed, 49 insertions, 4 deletions
diff --git a/pkgs/build-support/build-sandbox/default.nix b/pkgs/build-support/build-sandbox/default.nix
index 0e1d4a4d..50b8f78f 100644
--- a/pkgs/build-support/build-sandbox/default.nix
+++ b/pkgs/build-support/build-sandbox/default.nix
@@ -16,6 +16,25 @@ let
   # TODO: get rid of nix & pkg-config if this is enabled (in the Makefile)
   fullNixStore = attrs.fullNixStore or false;
 
+  # The mount and user namespaces are needed for this functionality, so these
+  # namespaces are always enabled.
+  #
+  # However, the following namespaces can be enabled/disabled:
+  # +----------------+---------+--------------------------------------+
+  # | Attribute path | Default | Isolates                             |
+  # +----------------+---------+--------------------------------------+
+  # | namespaces.pid |  true   | Process IDs                          |
+  # | namespaces.uts |  true   | Hostname and NIS domain name         |
+  # | namespaces.ipc |  true   | System V IPC, POSIX message queues   |
+  # | namespaces.net |  false  | Network devices, stacks, ports, etc. |
+  # +----------------+---------+--------------------------------------+
+  extraNamespaceFlags = let
+    flags = lib.optional (attrs.namespaces.pid or true) "CLONE_NEWPID"
+         ++ lib.optional (attrs.namespaces.uts or true) "CLONE_NEWUTS"
+         ++ lib.optional (attrs.namespaces.ipc or true) "CLONE_NEWIPC"
+         ++ lib.optional (attrs.namespaces.net or false) "CLONE_NEWNET";
+  in if flags == [] then "0" else lib.concatStringsSep "|" flags;
+
   # Create code snippets for params.c to add extra_mount() calls.
   mkExtraMountParams = isRequired: lib.concatMapStringsSep "\n" (extra: let
     escaped = lib.escape ["\\" "\""] extra;
@@ -80,8 +99,8 @@ in stdenv.mkDerivation ({
 
   nativeBuildInputs = [ pkgconfig ];
   buildInputs = [ boost nix ];
-  makeFlags = [ "BINDIR=${drv}/bin" ]
+  makeFlags = [ "BINDIR=${drv}/bin" "EXTRA_NS_FLAGS=${extraNamespaceFlags}" ]
            ++ lib.optional allowBinSh "BINSH_EXECUTABLE=${dash}/bin/dash"
            ++ lib.optional fullNixStore "FULL_NIX_STORE=1";
 
-} // removeAttrs attrs [ "paths" "allowBinSh" ])
+} // removeAttrs attrs [ "namespaces" "paths" "allowBinSh" ])
diff --git a/pkgs/build-support/build-sandbox/src/Makefile b/pkgs/build-support/build-sandbox/src/Makefile
index 8e1218f6..ebe66c0e 100644
--- a/pkgs/build-support/build-sandbox/src/Makefile
+++ b/pkgs/build-support/build-sandbox/src/Makefile
@@ -6,6 +6,8 @@ CFLAGS = -g -Wall -std=gnu11 -DFS_ROOT_DIR=\"$(out)\"
 CXXFLAGS = -g -Wall -std=c++14 `pkg-config --cflags nix-main`
 LDFLAGS = -Wl,--copy-dt-needed-entries `pkg-config --libs nix-main`
 
+CFLAGS += -DEXTRA_NS_FLAGS="$(EXTRA_NS_FLAGS)"
+
 ifdef FULL_NIX_STORE
 CFLAGS += -DFULL_NIX_STORE
 else
diff --git a/pkgs/build-support/build-sandbox/src/setup.c b/pkgs/build-support/build-sandbox/src/setup.c
index 98205710..dc8bbf14 100644
--- a/pkgs/build-support/build-sandbox/src/setup.c
+++ b/pkgs/build-support/build-sandbox/src/setup.c
@@ -850,8 +850,7 @@ bool setup_sandbox(void)
             close(sync_pipe[0]);
             _exit(write_maps(parent_pid) ? 0 : 1);
         default:
-            if (unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWPID |
-                        CLONE_NEWUTS | CLONE_NEWIPC) == -1) {
+            if (unshare(CLONE_NEWNS | CLONE_NEWUSER | EXTRA_NS_FLAGS) == -1) {
                 perror("unshare");
                 if (write(sync_pipe[1], "X", 1) == -1)
                     perror("signal child exit");
diff --git a/tests/sandbox.nix b/tests/sandbox.nix
index 63d8af6e..b9f087a0 100644
--- a/tests/sandbox.nix
+++ b/tests/sandbox.nix
@@ -17,6 +17,20 @@
     services.xserver.enable = true;
     systemd.services.display-manager.enable = false;
 
+    systemd.sockets.netnstest = {
+      description = "Host Socket for Testing Network Namespaces";
+      requiredBy = [ "sockets.target" ];
+
+      socketConfig.ListenStream = "3000";
+      socketConfig.Accept = true;
+    };
+
+    systemd.services."netnstest@" = {
+      description = "Host Service for Testing Network Namespaces";
+      serviceConfig.StandardInput = "socket";
+      serviceConfig.ExecStart = "${pkgs.coreutils}/bin/tee /tmp/netns.log";
+    };
+
     environment.systemPackages = let
       mkNestedLinksTo = drv: let
         mkLink = name: to: pkgs.runCommandLocal name { inherit to; } ''
@@ -115,6 +129,12 @@
         # Another /bin/sh just to be sure :-)
         /bin/sh -c 'echo /bin/sh works'
       '') { allowBinSh = true; })
+
+      (pkgs.vuizvui.buildSandbox (pkgs.writeScriptBin "test-sandbox3" ''
+        #!${pkgs.stdenv.shell}
+        echo hello network | ${pkgs.netcat-openbsd}/bin/nc -N 127.0.0.1 3000 \
+          || echo netcat has failed
+      '') { namespaces.net = true; })
     ];
     users.users.foo.isNormalUser = true;
   };
@@ -137,5 +157,10 @@
     machine.succeed('test "$(< /home/foo/.cache/xdg/ownpid)" = 1')
 
     machine.succeed('test "$(su -c test-sandbox2 foo)" = "/bin/sh works"')
+
+    machine.succeed('su -c "echo root netns | nc -N 127.0.0.1 3000" foo')
+    machine.succeed('test "$(su -c test-sandbox3 foo)" = "netcat has failed"')
+    machine.fail('grep -F "hello network" /tmp/netns.log')
+    machine.succeed('grep -F "root netns" /tmp/netns.log')
   '';
 }