Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions bubblewrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 */

Expand Down
4 changes: 4 additions & 0 deletions bwrap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@
<listitem><para>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. </para>
<para>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.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--netns <arg choice="plain">FD</arg></option></term>
<listitem><para>Use an existing net namespace instead of creating one. This cannot be combined with <option>--unshare-net</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--uid <arg choice="plain">UID</arg></option></term>
<listitem><para>Use a custom user id in the sandbox (requires <option>--unshare-user</option>)</para></listitem>
Expand Down
1 change: 1 addition & 0 deletions completions/bash/bwrap
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ _bwrap() {
--hostname
--info-fd
--lock-file
--netns
--overlay
--overlay-src
--perms
Expand Down
1 change: 1 addition & 0 deletions completions/zsh/_bwrap
Original file line number Diff line number Diff line change
Expand Up @@ -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 -/'
Expand Down
1 change: 1 addition & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
Expand Down
52 changes: 52 additions & 0 deletions tests/test-specifying-netns.sh
Original file line number Diff line number Diff line change
@@ -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