From f0b992053c113a3575728bceb0444756a33d659d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 14 Apr 2024 13:05:23 +0200 Subject: [PATCH 1/1] add a nicer way to declare host access --- bubblebox.py | 57 ++++++++++++++++++++++++++++++++++++++++++---------- profiles.py | 30 +++++++++++++++++++-------- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/bubblebox.py b/bubblebox.py index 1af0c87..628107e 100644 --- a/bubblebox.py +++ b/bubblebox.py @@ -16,9 +16,6 @@ def flat_map(f, xs): ys.extend(x_mapped) return ys -def globexpand(base, names): - return flat_map(lambda x: glob.glob(base + "/" + x), names) - def randname(): # choose from all lowercase letter letters = string.ascii_lowercase @@ -73,19 +70,57 @@ def bubblebox(*flags): #pprint(args) os.execvp(args[0], args) -# Convenient methods to give access to the host file system -def ro_host_access(*names): - return bwrap_flags(*flat_map(lambda x: ["--ro-bind", x, x], names)) -def rw_host_access(*names): - return bwrap_flags(*flat_map(lambda x: ["--bind", x, x], names)) -def dev_host_access(*names): - return bwrap_flags(*flat_map(lambda x: ["--dev-bind", x, x], names)) - # Give all instances of the same box a shared XDG_RUNTIME_DIR def shared_runtime_dir(boxname): dirname = BUBBLEBOX_DIR + "/" + boxname os.makedirs(dirname, exist_ok=True) return bwrap_flags("--bind", dirname, XDG_RUNTIME_DIR) +# Convenient way to declare host access +class Access: + Read = 0 + Write = 1 + Device = 2 + + def flag(val): + if val == Access.Read: + return "--ro-bind" + elif val == Access.Write: + return "--bind" + elif val == Access.Device: + return "--dev-bind" + else: + raise Exception(f"invalid access value: {val}") +def host_access(dirs): + def expand(root, names): + """`names` is one or more strings that can contain globs. Expand them all relative to `root`.""" + if isinstance(names, str): + names = (names,) + assert isinstance(names, tuple) + for name in names: + assert not (name.startswith("../") or name.__contains__("/../") or name.endswith("../")) + path = root + "/" + name + # prettification + path = path.replace("//", "/") + path = path.removesuffix("/.") + # glob expansion + yield from glob.glob(path) + def recursive_host_access(root, dirs, out): + for names, desc in dirs.items(): + for path in expand(root, names): + if isinstance(desc, dict): + # Recurse into children + recursive_host_access(path, desc, out) + else: + # Allow access to this path + out.extend([Access.flag(desc), path, path]) + # Start the recursive traversal + out = [] + recursive_host_access("", dirs, out) + #pprint(out) + return bwrap_flags(*out) +def home_access(dirs): + return host_access({ HOME: dirs }) + # Profile the profiles when importing bubblebox. import profiles diff --git a/profiles.py b/profiles.py index fdb2f67..ff685f9 100644 --- a/profiles.py +++ b/profiles.py @@ -13,23 +13,37 @@ DEFAULT = collect_flags( # merged-usr symlinks bwrap_flags("--symlink", "usr/lib", "/lib", "--symlink", "usr/lib64", "/lib64", "--symlink", "usr/bin", "/bin", "--symlink", "usr/sbin", "/sbin"), # folders we always need access to - ro_host_access("/usr", "/sys", "/etc"), + host_access({ ("/usr", "/sys", "/etc"): Access.Read }), # make a basic shell work - ro_host_access(*globexpand(HOME, [".bashrc", ".bash_aliases", ".profile", "bin"])), + home_access({ + (".bashrc", ".bash_aliases", ".profile"): Access.Read, + "bin": Access.Read, + }), ) # https://github.com/igo95862/bubblejail is a good source of paths that need allowing. # We do not give access to pipewire, that needs a portal (https://docs.pipewire.org/page_portal.html). DESKTOP = collect_flags( # Access to screen and audio - dev_host_access("/dev/dri", "/dev/snd"), - ro_host_access("/tmp/.X11-unix/", os.environ["XAUTHORITY"]), - ro_host_access(*globexpand(XDG_RUNTIME_DIR, ["wayland*", "pulse"])), - # Access to some key global configuration - ro_host_access(*globexpand(HOME, [".config/fontconfig", ".XCompose"])), + host_access({ + "dev": { + ("dri", "snd"): Access.Device, + }, + "/tmp/.X11-unix/": Access.Read, + os.environ["XAUTHORITY"]: Access.Read, + XDG_RUNTIME_DIR: { + ("wayland*", "pulse"): Access.Read, + }, + }), + # Access to some key user configuration + home_access({ + (".config/fontconfig", ".XCompose"): Access.Read, + }), # Access to basic d-bus services (that are hopefully safe to expose...) dbus_proxy_flags("--talk=org.kde.StatusNotifierWatcher.*", "--talk=org.freedesktop.Notifications.*", "--talk=org.freedesktop.ScreenSaver.*", "--talk=org.freedesktop.portal.*"), # Make it possible to open websites in Firefox - ro_host_access(*globexpand(HOME, [".mozilla/firefox/profiles.ini", ".local/share/applications"])), + home_access({ + (".mozilla/firefox/profiles.ini", ".local/share/applications"): Access.Read, + }), dbus_proxy_flags("--talk=org.mozilla.firefox.*"), ) -- 2.30.2