From 9086767d7834e2129255b0e15956877e29e15834 Mon Sep 17 00:00:00 2001 From: Rahul Sandhu Date: Mon, 29 Sep 2025 23:38:04 +0100 Subject: [PATCH] Support scoping abstract unix sockets It's desirable in many cases to be able to allow a sandboxed program to exist with the current network namespace without permitting it access to all abstract unix sockets in said namespace. For example, X11 has an abstract unix socket @/tmp/.X11-unix/X0, which, using the abs scoping options this patch introduces, would be inaccessible to a sandboxed client that resides in the same network namespace as the X11 server. As we are relied on by various higher level sandboxing frameworks, such as Glycin and Flatpak, also introduce a `-try` variant that does not simply bail if unable to restrict access to said unix sockets. Closes: https://github.com/containers/bubblewrap/issues/330 Signed-off-by: Rahul Sandhu --- bubblewrap.c | 74 ++++++++++++++++++++++++++++++++++++++++++ bwrap.xml | 21 ++++++++++++ completions/bash/bwrap | 2 ++ completions/zsh/_bwrap | 2 ++ meson.build | 4 +++ 5 files changed, 103 insertions(+) diff --git a/bubblewrap.c b/bubblewrap.c index 69d319b7..991a8716 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -32,10 +32,15 @@ #include #include #include +#include #include #include #include +#ifdef HAVE_LANDLOCK_H +#include +#endif + #include "utils.h" #include "network.h" #include "bind-mount.h" @@ -92,6 +97,8 @@ static int opt_userns_fd = -1; static int opt_userns2_fd = -1; static int opt_pidns_fd = -1; static int opt_tmp_overlay_count = 0; +static bool opt_scope_abstract_unix_sockets = false; +static bool opt_scope_abstract_unix_sockets_try = false; static int next_perms = -1; static size_t next_size_arg = 0; static int next_overlay_src_count = 0; @@ -373,6 +380,8 @@ usage (int ecode, FILE *out) " --perms OCTAL Set permissions of next argument (--bind-data, --file, etc.)\n" " --size BYTES Set size of next argument (only for --tmpfs)\n" " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n" + " --scope-abstract-af-unix Prevent connecting to abstract unix sockets outside the sandbox\n" + " --scope-abstract-af-unix-try Try --scope-abstract-af-unix if possible else continue by skipping it\n" ); exit (ecode); } @@ -2736,6 +2745,14 @@ parse_args_recurse (int *argcp, argv += 2; argc -= 2; } + else if (strcmp (arg, "--scope-abstract-af-unix") == 0) + { + opt_scope_abstract_unix_sockets = true; + } + else if (strcmp (arg, "--scope-abstract-af-unix-try") == 0) + { + opt_scope_abstract_unix_sockets_try = true; + } else if (strcmp (arg, "--") == 0) { argv += 1; @@ -2867,6 +2884,26 @@ namespace_ids_write (int fd, } } +#ifdef HAVE_LANDLOCK_H +#ifndef landlock_create_ruleset +static inline int +landlock_create_ruleset (const struct landlock_ruleset_attr *attr, + size_t size, + uint32_t flags) +{ + return syscall (SYS_landlock_create_ruleset, attr, size, flags); +} +#endif + +#ifndef landlock_restrict_self +static inline int +landlock_restrict_self (int ruleset_fd, uint32_t flags) +{ + return syscall (SYS_landlock_restrict_self, ruleset_fd, flags); +} +#endif +#endif + int main (int argc, char **argv) @@ -3498,6 +3535,43 @@ main (int argc, die ("creation of new user namespaces was not disabled as requested"); } + if (opt_scope_abstract_unix_sockets) + { + #ifdef HAVE_LANDLOCK_H + static const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET + }; + const int abi = landlock_create_ruleset (NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 0) + die_with_error ("failed to check Landlock compatibility"); + if (abi < 6) + die ("supported kernel Landlock ABI too old, version 6 or above required"); + const int ruleset_fd = landlock_create_ruleset (&ruleset_attr, sizeof (ruleset_attr), 0); + if (ruleset_fd < 0) + die_with_error ("failed to create Landlock ruleset"); + if (landlock_restrict_self (ruleset_fd, 0) < 0) + die_with_error ("failed to enforce Landlock ruleset"); + #else + die ("Landlock not available at compile time, cannot implement --scope-abstract-af-unix"); + #endif + } + + #ifdef HAVE_LANDLOCK_H + if (opt_scope_abstract_unix_sockets_try) + { + static const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET + }; + const int abi = landlock_create_ruleset (NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 6) + { + const int ruleset_fd = landlock_create_ruleset (&ruleset_attr, sizeof (ruleset_attr), 0); + if (ruleset_fd < 0) + landlock_restrict_self (ruleset_fd, 0); + } + } + #endif + /* All privileged ops are done now, so drop caps we don't need */ drop_privs (!is_privileged, true); diff --git a/bwrap.xml b/bwrap.xml index f379f0fa..d2129ee7 100644 --- a/bwrap.xml +++ b/bwrap.xml @@ -617,6 +617,27 @@ command line. Please be careful to the order they are specified. + + + + Scope access to abstract unix7 sockets. + + This option will prevent the newly created sandbox from connecting to abstract AF_UNIX sockets + created outside the sandbox, + for example the X11 socket @/tmp/.X11-unix/X0, + even if the network namespace is the same. + + This has the same behaviour as LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: see + landlock7 for details. + + + + + + Try to do the same as , + but if that isn't possible, continue without that restriction. + + diff --git a/completions/bash/bwrap b/completions/bash/bwrap index e7a523c2..aa0b7b54 100644 --- a/completions/bash/bwrap +++ b/completions/bash/bwrap @@ -15,6 +15,8 @@ _bwrap() { --disable-userns --help --new-session + --scope-abstract-af-unix + --scope-abstract-af-unix-try --unshare-all --unshare-cgroup --unshare-cgroup-try diff --git a/completions/zsh/_bwrap b/completions/zsh/_bwrap index fbddda43..15c66f0f 100644 --- a/completions/zsh/_bwrap +++ b/completions/zsh/_bwrap @@ -60,6 +60,8 @@ _bwrap_args=( '--remount-ro[Remount DEST as readonly; does not recursively remount]:mount point to remount read-only:_files' '--ro-bind-try[Equal to --ro-bind but ignores non-existent SRC]:source:_files:destination:_files' '--ro-bind[Bind mount the host path SRC readonly on DEST]:source:_files:destination:_files' + '--scope-abstract-af-unix[Prevent connecting to abstract unix sockets outside the sandbox]' + '--scope-abstract-af-unix-try[Try --scope-abstract-af-unix if possible else continue by skipping it]' '--seccomp[Load and use seccomp rules from FD]: :_guard "[0-9]#" "file descriptor to read seccomp rules from"' '--setenv[Set an environment variable]:variable to set:_parameters -g "*export*":value of variable: :' '--size[Set size in bytes for next action argument]: :->after_size' diff --git a/meson.build b/meson.build index 78678d09..3f767243 100644 --- a/meson.build +++ b/meson.build @@ -57,6 +57,10 @@ if ( ], language : 'c') endif +if cc.check_header('linux/landlock.h') + add_project_arguments('-DHAVE_LANDLOCK_H', language : 'c') +endif + bash = find_program('bash', required : false) if get_option('python') == ''