bubblewrap
bubblewrap copied to clipboard
Feature request: join network namespace created by 'ip netns create'
The ip
utility has a subcommand to create persistent network namespaces, and to run processes in these namespaces. It would be really handy if bubblewrap could put processes into network namespaces created in this manner. I specifically want this because I have scripts that bring up VPN tunnels and associate them with these namespaces, and then run specific programs in those namespaces (so, at the same time, process A can be talking to VPN proxy X, process B to proxy Y, etc) and I'd like to have reliable sandboxing for these processes.
For more information on the ip
feature:
Looks like a dup of https://github.com/projectatomic/bubblewrap/issues/61 ?
Pretty much, yeah. I'll comment there about things ip netns exec
does that might not be obvious.
+1 to this feature request
Another +1 to thie feature request. Being able to run flatpak in an existing network namespace would be perfect, for example for running a specific firefox session through VPN.
Pretty much, yeah. I'll comment there about things ip netns exec does that might not be obvious.
Right. I found this to be a clean and minimal stand-alone example of what netns exec
does:
https://github.com/minus7/netns-switch/blob/master/netns-switch.c
https://github.com/rootless-containers/slirp4netns may solve this issue.
Here is a patch I come up that add a switch --netns
for suid bubblewrap to switch into a netns created by ip netns create
.
NOTE:
- There is absolutely no warranty. While I do make some effort to avoid common pitfalls about parsing filenames, it may still introduce vulnerabilities and it is on your own.
- No access control is performed --- Now all users with access to bwrap binary will be able to enter any netns under
/run/netns
, which may not be desired. - It does not bind
/etc/netns/NAME/whatever
to/etc/whatever
asip netns exec
does, you can do your own, through. -
ip netns exec
remounts a new sysfs on/sys
to reflect netns change (for example, entries under/sys/class/net
should reflect network interfaces in current netns), but there is currently no way to mount a new sysfs in bubblewrap - It will not work well with
ip-vrf(8)
--- bubblewrap-0.5.0.orig/bubblewrap.c 2022-01-10 19:47:22.733904138 +0800
+++ bubblewrap-0.5.0/bubblewrap.c 2022-01-10 21:08:26.937312403 +0800
@@ -18,6 +18,7 @@
#include "config.h"
+#include <dirent.h>
#include <poll.h>
#include <sched.h>
#include <pwd.h>
@@ -38,6 +39,8 @@
#include "network.h"
#include "bind-mount.h"
+#define IPROUTE_NETNS_DIR "/run/netns"
+
#ifndef CLONE_NEWCGROUP
#define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */
#endif
@@ -86,6 +89,7 @@
int opt_seccomp_fd = -1;
const char *opt_sandbox_hostname = NULL;
char *opt_args_data = NULL; /* owned */
+const char *opt_netns_name = NULL;
int opt_userns_fd = -1;
int opt_userns2_fd = -1;
int opt_pidns_fd = -1;
@@ -240,6 +244,7 @@
" --userns FD Use this user namespace (cannot combine with --unshare-user)\n"
" --userns2 FD After setup switch to this user namespace, only useful with --userns\n"
" --pidns FD Use this user namespace (as parent namespace if using --unshare-pid)\n"
+ " --netns NAME Use this network namespace (in " IPROUTE_NETNS_DIR ")\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"
@@ -2101,6 +2106,19 @@
argv += 1;
argc -= 1;
}
+ else if (strcmp (arg, "--netns") == 0)
+ {
+ if (argc < 2)
+ die ("--netns takes an argument");
+
+ if (argv[1][0] == '\0')
+ die ("Invalid netns name: %s", argv[1]);
+
+ opt_netns_name = argv[1];
+
+ argv += 1;
+ argc -= 1;
+ }
else if (strcmp (arg, "--clearenv") == 0)
{
xclearenv ();
@@ -2396,6 +2414,21 @@
}
}
+static int
+run_netns_filter (const struct dirent * dir)
+{
+ const unsigned char type = dir->d_type;
+ const char * name = dir->d_name;
+
+ if (!(type & (DT_REG | DT_UNKNOWN)))
+ return 0;
+
+ if (strcmp(opt_netns_name, name) == 0)
+ return 1;
+
+ return 0;
+}
+
int
main (int argc,
char **argv)
@@ -2419,6 +2452,7 @@
struct sock_fprog seccomp_prog;
cleanup_free char *args_data = NULL;
int intermediate_pids_sockets[2] = {-1, -1};
+ int netns_fd = -1;
/* Handle --version early on before we try to acquire/drop
* any capabilities so it works in a build environment;
@@ -2475,6 +2509,9 @@
if (opt_userns_fd != -1 && opt_unshare_user_try)
die ("--userns not compatible --unshare-user-try");
+ if (opt_netns_name && opt_unshare_net)
+ die ("--netns not compatible --unshare-net");
+
/* Technically using setns() is probably safe even in the privileged
* case, because we got passed in a file descriptor to the
* namespace, and that can only be gotten if you have ptrace
@@ -2504,6 +2541,27 @@
opt_unshare_user = TRUE;
#endif
+ if (opt_netns_name)
+ {
+ if (!is_privileged)
+ die ("--netns works only in setuid mode");
+
+ struct dirent **nsnamelist;
+ int nsdirfd = -1;
+
+ if ((nsdirfd = open(IPROUTE_NETNS_DIR, O_DIRECTORY | O_PATH)) == -1)
+ die ("Failed query netns dir: %s", strerror(errno));
+
+ if (scandirat(nsdirfd, ".", &nsnamelist, run_netns_filter, alphasort) != 1)
+ die ("Invalid netns name: %s", opt_netns_name);
+
+ if ((netns_fd = openat(nsdirfd, nsnamelist[0]->d_name, O_RDONLY)) == -1)
+ die ("Failed open netns: %s", strerror(errno));
+
+ free(nsnamelist);
+ close(nsdirfd);
+ }
+
if (opt_unshare_user_try &&
stat ("/proc/self/ns/user", &sbuf) == 0)
{
@@ -2758,6 +2816,10 @@
close (intermediate_pids_sockets[1]);
}
+ if (netns_fd > 0 && setns (netns_fd, CLONE_NEWNET) != 0)
+ die_with_error("Setting netns failed");
+ close (netns_fd);
+
/* Child, in sandbox, privileged in the parent or in the user namespace (if --unshare-user).
*
* Note that for user namespaces we run as euid 0 during clone(), so
slirp4netns may solve this issue.
Docker's rootless mode uses slirp4netns, and network namespaces accept their own firewall rules, both without privileges.
If bubblewrap made it easy to use them (perhaps with setup done in advance or automated by something like flatpak) the resulting network access control would be a great improvement. For example, Steam games could be allowed to reach their servers but forbidden from scanning the local network.