diff --git a/bubblewrap.c b/bubblewrap.c
index 3b9719c4..06be1a39 100644
--- a/bubblewrap.c
+++ b/bubblewrap.c
@@ -90,6 +90,7 @@ static char *opt_args_data = NULL; /* owned */
static int opt_userns_fd = -1;
static int opt_userns2_fd = -1;
static int opt_pidns_fd = -1;
+static int opt_netns_fd = -1;
static int opt_tmp_overlay_count = 0;
static int next_perms = -1;
static size_t next_size_arg = 0;
@@ -303,6 +304,7 @@ usage (int ecode, FILE *out)
" --disable-userns Disable further use of user namespaces inside sandbox\n"
" --assert-userns-disabled Fail unless further use of user namespace inside sandbox is disabled\n"
" --pidns FD Use this pid namespace (as parent namespace if using --unshare-pid)\n"
+ " --netns FD Use this net namespace (cannot combine with --unshare-net)\n"
" --uid UID Custom uid in the sandbox (requires --unshare-user or --userns)\n"
" --gid GID Custom gid in the sandbox (requires --unshare-user or --userns)\n"
" --hostname NAME Custom hostname in the sandbox (requires --unshare-uts)\n"
@@ -2210,6 +2212,26 @@ parse_args_recurse (int *argcp,
opt_pidns_fd = the_fd;
+ argv += 1;
+ argc -= 1;
+ }
+ else if (strcmp (arg, "--netns") == 0)
+ {
+ int the_fd;
+ char *endptr;
+
+ if (argc < 2)
+ die ("--netns takes an argument");
+
+ if (opt_netns_fd != -1)
+ warn_only_last_option ("--netns");
+
+ the_fd = strtol (argv[1], &endptr, 10);
+ if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0)
+ die ("Invalid fd: %s", argv[1]);
+
+ opt_netns_fd = the_fd;
+
argv += 1;
argc -= 1;
}
@@ -2656,6 +2678,9 @@ main (int argc,
if (opt_userns_fd != -1 && opt_unshare_user_try)
die ("--userns is not compatible with --unshare-user-try");
+ if (opt_netns_fd != -1 && opt_unshare_net)
+ die ("--netns is not compatible with --unshare-net");
+
if (opt_disable_userns && !opt_unshare_user)
die ("--disable-userns requires --unshare-user");
@@ -2798,6 +2823,9 @@ main (int argc,
die_with_error ("Joining specified user namespace failed");
}
+ if (opt_netns_fd != -1 && setns (opt_netns_fd, CLONE_NEWNET) != 0)
+ die_with_error ("Joining specified net namespace failed");
+
/* Sometimes we have uninteresting intermediate pids during the setup, set up code to pass the real pid down */
if (opt_pidns_fd != -1)
{
@@ -2938,6 +2966,10 @@ main (int argc,
*/
switch_to_user_with_privs ();
+ /* If a pre-existing network namespace is specified via --netns, we assume
+ * that it already contains a loopback interface, so we do not call
+ * loopback_setup().
+ */
if (opt_unshare_net)
loopback_setup (); /* Will exit if unsuccessful */
diff --git a/bwrap.xml b/bwrap.xml
index ca717abd..13ee82f9 100644
--- a/bwrap.xml
+++ b/bwrap.xml
@@ -197,6 +197,10 @@
Use an existing pid namespace instead of creating one. This is often used with --userns, because the pid namespace must be owned by the same user namespace that bwrap uses. Note that this can be combined with --unshare-pid, and in that case it means that the sandbox will be in its own pid namespace, which is a child of the passed in one.
+
+
+ Use an existing net namespace instead of creating one. This cannot be combined with .
+ Use a custom user id in the sandbox (requires )
diff --git a/completions/bash/bwrap b/completions/bash/bwrap
index e7a523c2..232d4711 100644
--- a/completions/bash/bwrap
+++ b/completions/bash/bwrap
@@ -51,6 +51,7 @@ _bwrap() {
--hostname
--info-fd
--lock-file
+ --netns
--overlay
--overlay-src
--perms
diff --git a/completions/zsh/_bwrap b/completions/zsh/_bwrap
index fbddda43..20b71287 100644
--- a/completions/zsh/_bwrap
+++ b/completions/zsh/_bwrap
@@ -54,6 +54,7 @@ _bwrap_args=(
'--lock-file[Take a lock on DEST while sandbox is running]:lock file:_files'
'--mqueue[Mount new mqueue on DEST]:mount point for mqueue:_files -/'
'--new-session[Create a new terminal session]'
+ '--netns[Use this net namespace (cannot combine with --unshare-net)]: :'
'--perms[Set permissions for next action argument]: :_guard "[0-7]#" "permissions in octal": :->after_perms'
'--pidns[Use this user namespace (as parent namespace if using --unshare-pid)]: :'
'--proc[Mount new procfs on DEST]:mount point for procfs:_files -/'
diff --git a/tests/meson.build b/tests/meson.build
index 87bf709e..69db7ef3 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -18,6 +18,7 @@ executable(
test_scripts = [
'test-run.sh',
'test-seccomp.py',
+ 'test-specifying-netns.sh',
'test-specifying-pidns.sh',
'test-specifying-userns.sh',
]
diff --git a/tests/test-specifying-netns.sh b/tests/test-specifying-netns.sh
new file mode 100755
index 00000000..e8d762f0
--- /dev/null
+++ b/tests/test-specifying-netns.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+set -xeuo pipefail
+
+srcd=$(cd $(dirname "$0") && pwd)
+. "${srcd}/libtest.sh"
+
+echo "1..1"
+
+# This test needs user namespaces
+if test -n "${bwrap_is_suid:-}"; then
+ echo "ok - # SKIP no setuid support for --unshare-user"
+else
+ # Start the server inside a bubblewrap sandbox with a new user and net namespace
+ $RUN --info-fd 42 --unshare-user --unshare-net python3 -c "
+import socket
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.bind(('127.0.0.1', 0))
+port = s.getsockname()[1]
+with open('server-port.txt', 'w') as f:
+ f.write(str(port))
+s.listen(1)
+conn, addr = s.accept()
+conn.sendall(b'hello from netns\n')
+conn.close()
+" 42>info.json &
+
+ # Wait for the server to write the port file
+ while ! test -f server-port.txt; do sleep 0.1; done
+ SANDBOX1PID=$(extract_child_pid info.json)
+
+ # Run the client inside another bubblewrap sandbox, joining the user and net namespaces of the first
+ ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=detect_leaks=0 \
+ $RUN --userns 11 --netns 12 python3 -c "
+import socket
+with open('server-port.txt', 'r') as f:
+ port = int(f.read().strip())
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect(('127.0.0.1', port))
+data = s.recv(1024)
+print(data.decode().strip())
+s.close()
+" 11< /proc/$SANDBOX1PID/ns/user 12< /proc/$SANDBOX1PID/ns/net > client-output.txt
+
+ # Verify the output
+ echo "hello from netns" > expected.txt
+ assert_files_equal expected.txt client-output.txt
+
+ rm -f info.json server-port.txt client-output.txt expected.txt
+
+ echo "ok - Test --netns"
+fi