89luca89/distrobox

[Feature] Sandboxed mode

89luca89 opened this issue Β· 39 comments

Right now the distrobox's containers are created in privileged mode and share a lot of sensitive host's folder.

This is done because the aim is tight integration with the host, not sandboxing.


It would be nice to have an optional (see: disabled if not specified) --unprivileged or a --sandbox flag in distrobox-create to have a more isolated container to work with.

Sandboxed distrobox should not integrate with the host as tightly as the normal one

$HOME should not be bind mounted
Host's rootfs should be mounted in readonly (or ideally not mounted at all)
Sockets should not be passed to the container

Sandboxed distrobox should not use --privileged and should not use --ipc host, --pid host

Thinking of functionality:

  • --sandbox seems an understandable flag to use
  • --allow-path could be used to allow some path to be mounted as read-write

Depends on #70

I think only implementing "fully sandboxed+unprivileged" and "fully integrated+privileged" modes are not the way to go, but rather adding options to configure it finer-grained like making only a specified set of devices available, only select sockets, mapping a custom selection of directories or other things possible for Flatpaks using Flatseal.

Based on that you could then add presets for integrated vs. sandboxed.

Well there is a big difference between distrobox and Flatpak, distrobox is not an application distribution method.
On one hand I agree having some more fine grained support is good
On the other, it's an extreme work to add support to filter specific devices (like, usb pen1 passes, usb pen2 does not pass etc etc)

I was thinking more on the lines of

Sanbox mode:

1 - custom home
2 - you specify which path can the container access
3 - you decide if devices in /dev/ are accessible or not
4 - you decide if you share:
- network namespace
- process namespace
- ipc namespace
- mount namespace
- (others)

And on that one can do a couple of profiles (low to high sandboxing)

Adding to before
we should give options to:

  • mount in overlay, or not mount at all
    • /home
    • /
    • /dev
  • disable additional group mappings: run.oci.keep_original_groups=0
  • disable stuff like
    • network namespace
    • process namespace
    • ipc namespace
    • mount namespace
    • (others)

I had some ideas on sandboxing modes.
This gives overview of the modes I thought of, describing general use cases and overall access to Host.
The technical implementation specifics (what to mount / allow or exclude etc) of each mode shall be predetermined[?].

Mode Short see touch Comments
porous rw yes yes (default currently)
read-only ro yes nope
overlay-fork of touched up self (~fantasy host) ("just works" mode) [*]
isolate iso no no way! (fully locked down) [#]

[*]: try to be smoothe, smoother than porous even. does not conflict with sovereign Host. but [guest view] may diverge from ground truth (host state)
[#]: as close to completely seperate and airgapped [guest, host] as possible

[*], [#]: >
User should be able to successfully do as guest sudo rm -rf / (and deal with the consequences) without causing any hiccups on Host.
In effect overlay-fork can be thought to be like isolate mode but the starting point is Host state (in -of), as opposed to a blank slate (in -iso).

[?]: User would make a simple choice of mode and distrobox should make intelligent decisions in the backend.

Great analysis yes, that is what I had in mind, with rw being the default.
For this to work we will need first to implement #119 and #120

And we will need a way to fix the workdir stuff (probaby depending on the level, we completely omit that?)

bd4 commented

I was just thinking this, great to see it's already in the works!

Another possible use case here is running graphical windows apps with wine or native old / experimental proprietary applications, or even experimental open source apps. There is overlap with x11docker (and flatpak) here, so not sure what makes sense for distrobox; this use case requires more work re fine grained controls like sound, webcam, nested X server vs host X server, gpu vs not. Maybe makes sense to start simple with "isolate".

I was just thinking this, great to see it's already in the works!

Another possible use case here is running graphical windows apps with wine or native old / experimental proprietary applications, or even experimental open source apps. There is overlap with x11docker (and flatpak) here, so not sure what makes sense for distrobox; this use case requires more work re fine grained controls like sound, webcam, nested X server vs host X server, gpu vs not. Maybe makes sense to start simple with "isolate".

Yea starting with a "simpler" sandbox mode

The target (at least generally speaking) is some sort of general isolation (not going to provide super-capillar controls like flatpak for example)
Adding such fine grained controls would be too much and out of scope of the project (imho), let's keep in mind that the point of distrobox (and toolbx) is integration not isolation, obviously if you need complete isolation and security it is faster adding just the bits you need to a plain podman/docker than removing all the integration from here πŸ˜„

bd4 commented

Yes simple is good, I think that is what makes toolbox/distrobox so great!

I think an interesting distinction with distrobox vs something like x11docker is mutability and persistence. x11docker and similar are designed to have an immutable core, that is changed by creating a new image, e.g. Dockerfile with new layers, and have mutable data exposed via volumes. Distrobox and toolbox on the other hand have a mutable core by default, you can just sudo dnf/apt/zypper etc as much as you want, so it's more like working in a standard chroot. I think both have their pros and cons. What excites me about distrobox+isolate is that it can provide a more chroot like workflow while still providing sandbox/isolation. I think LXC and systemd-nspawn are in this category as well, but so far only toolbox and distrobox hit the too simple not to use threshold (even easier than setting up schroot and debootstrap/pacstrap really).

The cool thing is, with just isolate + the ability to bind mount specific files (pass directly to docker/podman -v?), a lot of more advanced use cases should be possible, e.g. mostly isolated but expose gpu for CUDA/ROCm/oneAPI development. They may not all be trivial with a specialized switch, but possible and still easier than doing it manually with podman.

Yes simple is good, I think that is what makes toolbox/distrobox so great!

I think an interesting distinction with distrobox vs something like x11docker is mutability and persistence. x11docker and similar are designed to have an immutable core, that is changed by creating a new image, e.g. Dockerfile with new layers, and have mutable data exposed via volumes. Distrobox and toolbox on the other hand have a mutable core by default, you can just sudo dnf/apt/zypper etc as much as you want, so it's more like working in a standard chroot. I think both have their pros and cons. What excites me about distrobox+isolate is that it can provide a more chroot like workflow while still providing sandbox/isolation. I think LXC and systemd-nspawn are in this category as well, but so far only toolbox and distrobox hit the too simple not to use threshold (even easier than setting up schroot and debootstrap/pacstrap really).

Yes the point of toolbox and distrobox is simplicity and well use the big number of well maintained container images on the vaiorus registries πŸ˜„

The cool thing is, with just isolate + the ability to bind mount specific files (pass directly to docker/podman -v?), a lot of more advanced use cases should be possible, e.g. mostly isolated but expose gpu for CUDA/ROCm/oneAPI development. They may not all be trivial with a specialized switch, but possible and still easier than doing it manually with podman.

As you can see in the docs here:

Distrobox already supports a --additional-flags/-a flag to pass directly the podman/docker flags to the container manager, and a --volume/-v flags to add additional volumes πŸ˜„

would leveraging sandbox tools like firejail work for the moment?

would leveraging sandbox tools like firejail work for the moment?

Didn't think of this, we could use bwrap maybe as it does not require root to run, but probably worth experimenting

Aex12 commented

Didn't think of this, we could use bwrap maybe as it does not require root to run, but probably worth experimenting

Doesn't podman already isolate the container from the host? Wouldn't it be redundant to bwrap podman?

It's a small nit but I think the issue description is misleading or outdated:

Right now the distrobox's containers are created in privileged mode

Currently podman boxes are unprivileged by default. They do have write access to host's home and other directories, though.

It's a small nit but I think the issue description is misleading or outdated:

Right now the distrobox's containers are created in privileged mode

Currently podman boxes are unprivileged by default. They do have write access to host's home and other directories, though.

Podman containers are indeed unprivileged by default, but distrobox creates privileged containers, otherwise it wouldn't be able to access bunch of devices and stuff.

Any news on this?

I honestly need this so much I might as well make a PR if it's not expected in the short term

ok so while we're at it, here's a hacky solution I just crafted. This does not substitute the mounts performed by distrobox-init as of yet, so it won't expose things like networking or GUI sockets as expected.

Just declare $NAME and $IMAGE and run the following

mkdir -p ~/.local/bin ~/.local/share/distrobox/$NAME
cp $(which distrobox)-{init,create,export,host-exec} ~/.local/bin/
sed -i '/--volume \/:\/run\/host:rslave/d' ~/.local/bin/distrobox-create
DBX_CONTAINER_CUSTOM_HOME=~/.local/share/distrobox/$NAME ~/.local/bin/distrobox-create --image $IMAGE --home ~/.local/share/distrobox/test --name $NAME

now you can just run it with distrobox enter --no-workdir $NAME πŸŽ‰

Update: if all you need is an Internet connection, you can further patch distrobox-create to bind into /run/host/etc/{hosts,localtime,resolv.conf}

@xerz-one

I'm not planning on working on this very soon

The plan anyway should be:

1 find what is needed to have the minimum possible to have working:

  • audio
  • video
  • xorg/wayland
  • internet/lan

2 create a "sandboxed" profile that just mounts the minimum

Anything more than that should not be in scope of the project, as the point of toolbox/distrobox type of software is the host's integration, and de-integrating too much would just be a waste of effort. If anyone needs further sandboxing, they can always use podman/docker directly

I was experimenting with this idea, and I was wondering if it would be benefitial to include a --disalow-path to compliment the --allow-path.

Personal update: I find myself no longer needing this. It turns out a more bare podman with Pods can easily achieve a sandboxed environment with support for desktop apps and nested desktop sessions. Leaving it here in case anyone is interested, still hoping the best for Distrobox!

Here's what's needed to get Podman to launch apps

  • XDG
    • Volumes
      • /run/user/1000:/run/user/1000, where the first 1000 is the host $UID (check $XDG_RUNTIME_DIR on host to make sure)
    • Environment variables
      • XDG_RUNTIME_DIR=/run/user/1000
  • Wayland
    • Requires XDG settings
    • Environment variables
      • WAYLAND_DISPLAY=wayland-0, where wayland-0 is the host $WAYLAND_DISPLAY (it represents a file at $XDG_RUNTIME_DIR)
  • X11
    • Volumes
      • /tmp/.X11-unix:/tmp/.X11-unix
    • Environment variables
      • DISPLAY=:0, where :0 is the host $DISPLAY
    • Commands to run on host
      • xhost +"local:podman@"
  • Pulseaudio
    • Requires XDG settings
    • Environment variables
      • PULSE_SERVER=/run/user/1000/pulse/native

It's worth keeping in mind that returning from the in-app terminal to the main UI will end the shell process and all of its children, so if you want to use it you should detach any process you want to keep alive.

@xerz-one that's great finding thanks!
This will surely be useful in the future when I (or someone else? πŸ‘€ ) will work on this

Thanks!

Any updates on this?

Since the container only can see the home of the user running distrobox (but it sees it even when another home folder is specified for the container), isn't one solution that a sandbox-mode just creates another user without password (e.g. named distrobox) that runs distrobox-enter, so that when you run an exported program it will run something like:
"su -l distrobox -c distrobox-enter -n debian -- /usr/bin/chromium %U"

juhp commented

It turns out a more bare podman with Pods can easily achieve a sandboxed environment with support for desktop apps and nested desktop sessions. [..]
Here's what's needed to get Podman to launch apps

@xerz-one, is the setup described in more detail somewhere?

Since the container only can see the home of the user running distrobox (but it sees it even when another home folder is specified for the container), isn't one solution that a sandbox-mode just creates another user without password (e.g. named distrobox) that runs distrobox-enter, so that when you run an exported program it will run something like: "su -l distrobox -c distrobox-enter -n debian -- /usr/bin/chromium %U"

This tool might help achieve this or be inspiration: https://github.com/intgr/ego

My 2 cents to the discussion: some services (like Bitbucket Pipelines Cloud) do not support --privileged (reference).
I was almost ready to deliver a reestructured pipeline and I could not finish because of this kind of limitation.

To sum up: --unprivileged could solve my problem.

Hi all
I did a basic draft that you can check here:

#818

Let me know how this works πŸ‘

This seems great for porting to macOS, actually (#36). On macOS I don’t need to interact with the base system from inside the distrobox for my use case, but only access to dev tools (and the internet). So podman running in a VM shouldn’t be an issue for this mode.

I have just now merged #818

As explained in the PR:

THIS IS NOT A PROPER DATA SANDBOX

This should ensure a basic unsharing between guest and hosts

This allows to:

-unshare-devsys: do not share host devices and sysfs dirs from host
-unshare-ipc: do not share ipc namemspace with host
-unshare-netns: do not share the net namespace with host
-unshare-process: do not share process namemspace with host

And an unshare-all to do all the above.

This allows to have only the minimal requirements to:

access user's HOME
launch applications with GUI/GPU/Audio/Video
Additional mountpoints can be declared with --volume

For now I'm not contemplating unsharing the home that would be a bit too much and denaturalise the purpose of distrobox itself.

i guess my suggestion #969 would fall around this

Is there a way to atleast deny the container access to a few paths such as the host's ~/.gnupg , ~/.password-store, ~/.ssh ?

For now I'm not contemplating unsharing the home that would be a bit too much and denaturalise the purpose of distrobox itself.
Is there a way to atleast deny the container access to a few paths such as the host's ~/.gnupg , ~/.password-store, ~/.ssh ?

In my opinion, going a whitelist route is the most sensible, something like Flatpak does. Only share what needs to be shared.
Certain images for Distrobox could declare their specific needs, like Waydroid would need binderfs for instance, but by default never allow, only deny, that is the point of sandboxing.
I wonder, is there a solution for running certain programs temporarily with a well defined privilage set? Like with polkit, only allowing certain actions for a given program in a container.

Hi,
First, thanks you a lot for your work!

I agree that a data sandbox mode with whitelist would be awesome, and I have the feeling that it would be really close to attain with options like custom home for container.
Is it really not an option to develop this feature in the future?
I find it sad that it got dismissed, it would really be useful for some containerized software (if not the majority of them) to not having access to home directory, and then having a whitelist system that permit to give access to one or few specifics folders.
Of course, this feature would not be a default, it would just be an option at the creation of the container.

I think it would be really, really useful to a lot of people, myself included.
Anyway, thanks you a lot for your work, I use it everyday on universalblue and it's really amazing, thanks you again!

I think it would be really, really useful to a lot of people, myself included.

The point of this would be a platform-independent implemention of something like firejail for programs not made for the given platform of with sandboxing in mind, like a snap or flatpak package, as far as I see.

Is there a way to atleast deny the container access to a few paths such as the host's ~/.gnupg , ~/.password-store, ~/.ssh ?

If you want to be as isolated as possible you can do this:

  1. Create a user for your distroboxes (-m creates a home folder for the new user): useradd -m distrobox
  2. Switch to the distrobox account with su -l distrobox (note that you want the -l flag, otherwise you will get permission errors).
  3. Run distrobox-create with whatever options you want and add --unshare-all if you wish to unshare as much as possible.
  4. Enter with distrobox-enter and install everything you want.

If you wish to run a GUI application you can just run the following inside a terminal where you aren't inside your distrobox
xhost +si:localuser:distrobox and run export DISPLAY=":0" in the terminal inside your distrobox.

I tried this with firefox and file:/// can just see the distrobox-user's home and not my regular home.

And you can of course populate your distrobox user's home with whatever files you wish to share with it (using sudo cp ... for example).

P.S. This message was written from the distrobox user using Fedora 39 with a host user called damiano using Arch. It just works.

P.P.S. There is a way to make your distrobox programs to run as the distrobox user even when they are launched from the regular user. Unfortunately (or perhaps fortunately?) it's hard to run as another user without providing the password even when the password is set to nothing. This is a (hacky) way around it. Here is an example for firefox:

  1. Create a script somewhere in your PATH (for example /bin/distrobox-firefox) with the following content:
#!/bin/bash
sudo -i -u distrobox bash -c "DISPLAY=:0 /usr/bin/distrobox-enter  -n my-distrobox  --   firefox --ProfileManager"

This switches user to distrobox and uses bash to set the DISPLAY-variable and run distrobox-enter and runs firefox (change my-distrobox to the name of your distrobox and firefox to whatever program you wish to launch).

  1. Make the script executable with chmod +x /bin/distrobox-firefox.

  2. Add the following to /etc/sudoers:

yourUserName ALL = NOPASSWD: /bin/distrobox-firefox

but obviously change yourUserName to your actual username, for me it's damiano ALL = NOPASSWD: /bin/distrobox-firefox.
Please note that this line must come after the line that gives your user sudo privileges (that line is either on the form yourUserName ALL=(ALL) ALL or %wheel ALL=(ALL) ALL depending on if your distro sets up sudo just for your user or for the wheel group), otherwise you will still be prompted for a password.

  1. Now you can use sudo distrobox-firefox to run firefox inside distrobox as the user distrobox. You can put this line in a .desktop file to make it appear in your desktop environment's menu.

@89luca89 do you believe there is an easier way for this (switching user and running distrobox)? If so distrobox could perhaps have an option to create a new user when creating a sandboxed environment and run everything as that user?

@damianoognissanti The problem with this is that there is no easy way to share files between the logged in user and the distrobox dedicated user. (Maybe ACL for the desired directories, but that involves a lot of work and the needed permissions.) Also, the user needs to have permission to use sudo. (But it can be scoped to only distrobox, so I guess you could make the appropriate configs as you want in sudoers.)