ossf / wg-best-practices-os-developers

The Best Practices for OSS Developers working group is dedicated to raising awareness and education of secure code best practices for open source developers.
https://openssf.org
Apache License 2.0
766 stars 131 forks source link

Project idea: Security sandboxing guidelines #631

Open l0kod opened 1 month ago

l0kod commented 1 month ago

As talked about in today's meeting, it would be good to have guidelines to help developers sandbox their applications.

Landlock is the Linux sandboxing mechanism designed for such use case which requires an unprivileged access control system. It can be used through 3 dedicated syscalls to create and enforce a security policy on the calling task (e.g. the developer's application) and its future children.

We already have:

Who is interested in this topic?

david-a-wheeler commented 1 month ago

I like the idea of creating guidelines for Landlock, especially if there are examples.

Configuring Landlock for a specific use seems to involve writing code. If that code is built into an application, I'd want to see how to write the code so the application will still run on other systems (e.g., *BSDs, MacOS, Windows) and quietly skip Landlock. This does run the risk of an application's sandboxing quietly failing - how do you prevent that? (I bet there are easy answers, guidance would be helpful).

I see that Landlock expects to be configured by creating & compiling code. A lot of higher-security systems don't allow arbitrary code to be brought in & compiled. Is there a way to create a separate configuration file to control Landlock for a specific application? Maybe, instead of guidelines, we should build such a general tool so people can easily (re)configure Landlock for various users without recompilation.

We might want to later generalize to other sandboxing systems, e.g., bubblewrap, which I believe is used by Flatpak, libgnome-desktop, and sandwine. It's okay to give separate guidance for each separately, but cross-comparisons might help improve guidance for each.

l0kod commented 1 month ago

I like the idea of creating guidelines for Landlock, especially if there are examples.

Sure, this tutorial should help.

Configuring Landlock for a specific use seems to involve writing code. If that code is built into an application, I'd want to see how to write the code so the application will still run on other systems (e.g., *BSDs, MacOS, Windows) and quietly skip Landlock. This does run the risk of an application's sandboxing quietly failing - how do you prevent that? (I bet there are easy answers, guidance would be helpful).

This is indeed important, as well as backward compatibility. Detecting non-Linux systems may already be done at build time for compiled software, so it would only be a matter of a #if check. Backward compatibility can be checked with a landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION) call. This is much easier with the high-level libraries though.

I see that Landlock expects to be configured by creating & compiling code. A lot of higher-security systems don't allow arbitrary code to be brought in & compiled. Is there a way to create a separate configuration file to control Landlock for a specific application?

Landlock can be seen as a building block to create sandboxes. There are two use cases:

  1. sandboxer tools (designed to launch arbitrary applications) leveraging Landlock and defining their own configuration,
  2. any other applications willing to sandbox themselves.

These two use cases are complementary. Systems need to be compiled at some point and this is where Landlock can be used for the second use case. The first use case can help when we don't own applications/services and cannot change their code (for various reasons). Of course these notions are relative and may mean something different for a developer, a sysadmin, or a distro/product maintainer.

Maybe, instead of guidelines, we should build such a general tool so people can easily (re)configure Landlock for various users without recompilation.

I'd like to help developers, who may not depend on tools installed on the system, but they know very well how their applications work and what access is legitimate and required. This is what I mean by application sandboxing: to integrate security into applications. Two advantages of this approach are that it can be transparent for users (but not for attackers) and it can be tested in a CI like any other features relying on the kernel (i.e. most of them).

There is already a few sandboxer tools that leverage Landlock, and I also plan to work on a new one, but such tool will never be as transparent and well-integrated as embedded sandboxing.

We might want to later generalize to other sandboxing systems, e.g., bubblewrap, which I believe is used by Flatpak, libgnome-desktop, and sandwine. It's okay to give separate guidance for each separately, but cross-comparisons might help improve guidance for each.

Bubblewap is a very useful sandboxer tool, but it mainly relies on namespaces, which were not designed for security and are not an access control system. This tool needs to be SUID, the use of namespaces increases the kernel attack surface, and there is no fine-grained access control. Some parts of Bubblewrap could be implemented with Landlock though.

Guidance on these tools would be nice too, but my main goal is to help developers, that can then build secure apps/tools for users, sysadmins, and distro maintainers. My understanding was that this working group is dedicated to developers, not users/sysadmins/distros.

I think application sandboxing should be part of secure development guidelines. It is complementary to the use of secure APIs/libraries, hardening compiler options, input sanitization, and other good practices.

valoq commented 1 month ago

The idea of providing a developer guideline for sandbox technologies is something I have also been thinking about for several years and I would be happy to contribute to such a guide.

To start with a bit of feedback on existing guides based on my own experience: Landlock is quite easy to implement in small applications and the existing documentation already does a decent job in my opinion. I have recently implemented landlock in zathura and also just opened a PR for ouch and while I found it quite easy to get started with landlock (especially compared to seccomp) there are possibly checks and cases that are missing to provide a optimal implementation. A review of the mentioned implementations may reveal potential issues that need to be addressed more in documentations, assuming I am not the only one to miss them.

On a more general note, I strongly feel like the topic of sandboxing needs a fundamental documentation that distinguished between the different approaches and meaning of the term. As the above discussion already indicates, and so do many discussions on this topic, it is easy to misunderstand what the different tools accomplish and what their limits are. For example there is a significant different between a well designed sandbox that separates privileges within an application to run dangerous code in a well restricted child process (like OpenSSH, chromium and firefox do it) and a simple container that provides a runtime to start arbitrary applications in, which is what bubblewrap/flatpak, firejail and an even docker represent.

In many discussions there seems to be no differentiation between these two kinds of sandbox implementations, but while the first kind that implements sandboxing as part of the application code itself can potentially provide a significant resilience even against dedicated adversaries, any sandboxing tools that build around existing applications provide far less protection against adversaries that actively try to circumvent the isolation. For example, even if syscall filter are used with bubblewrap/flatpak/firejail, the system calls that need to be whitelisted are any and all system calls that the application may require during its entire lifetime. On the other hand, even a rather simple seccomp implementation like we did for zathura can deny access to most system calls, even without the use of a broker architecture that uses a separate child process. (Though I would still like to implement one in zathura eventually). In the case of zathura, the current seccomp filter is simply enabled after the application has initialized, just before a target document is opened (read untrusted data is parsed)

Ideally it would become normal to split applications into different process to implement strong sandboxing like the role model applications OpenSSH/Chromium do it, though it may be overkill for some, more simple tools. In order to approach this process splitting/broker architecture I found the sandboxing-api project to be a most interesting approach that should not be missing in a sandbox developer guide. This approach targets libraries and allows to run the parsing code inside a different process while providing a simple stub api for any applications that want to make use of it, thereby requiring no significant changes to the application using the sandboxed library api. There are several example implementations for different open source projects like curl for example, but none of these have made it into the actual upstream repos.

One additional benefit from landlock that I personally see is that is makes the first step towards linux application sandboxing very easy. Implementing seccomp filter can be quite difficult and it requires a lot of testing since different architectures and even libc backends may use different syscalls for the same functionality. Additionally new versions of dependencies may introduce new syscalls as well, which may break the application if the used policy is too strong (can be avoided by using errno instead of kill policy) Landlock on the other hand is quite simple to understand and the side effects are far less severe. It makes for a good first step that addresses the most obvious target for adversaries/malware (filesystem access). And once that is implemented tested and accepted, further improvements with seccomp can provide a full sandbox implementation that makes any attempt of meaningful exploitation an expensive if not impossible endeavor.

To recap: