1. 26
  1.  

    1. 10

      I still don’t understand why pledge(2)/unveil(2) have not been ported to other kernels. They have proven to work very well for a almost a decade now, as they were introduced in 2015.

      1. 9

        SerentityOS [1] [2] has ported pledge+unveil. Reply thread here mentions NanoVMS [3] too.

        But i agree. It’s a really easy-to-use, simple protection that seems like a no-brainer for pretty much everything to have now.

        1: https://man.serenityos.org/man2/pledge.html
        2: https://awesomekling.github.io/pledge-and-unveil-in-SerenityOS/
        3: https://nanovms.com/dev/tutorials/applying-sandbox-security-node-js-unikernels-openbsd-pledge-unveil

        1. 6

          We’re doing a capstone project with one of my students where we’re porting pledge to FreeBSD. We’re not doing unveil because I don’t know the internals as good as pledge, but hopefully something good comes out of this experiment.

          1. 5

            You might want to reach out to some of the HardenedBSD community members. We have a port of pledge that is nearing completion and will likely be merged into HardenedBSD on the inside of a year, quicker if we have help. :-)

          2. 4

            The Linux approach, for better or worse, is to provide more generic/complex kernel APIs, the two most similar to pledge/unveil being respectively seccomp-bpf and landlock. Given those, you can port pledge/unveil in userspace. But that obviously results in less uptake than an API blessed/mandated by the OS developers in the OpenBSD style.

            edit: Although see previous discussion for more caveats.

            1. 1

              From the porting link:

              For example, let’s say you want to do something on Linux like control whether or not some program you downloaded from the web is allowed to have telemetry.

              This seems pretty easy on Linux with systemd, am I missing something? The program itself doesn’t even have to know about any control mechanisms like pledge or whatever, we can enforce it from outside.

              1. 10

                pledge and unveil are specifically designed as tools for the programmer. They aren’t meant to be imposed from the outside as a sandbox. Theo has repeatedly rejected the idea of adding a utility to OpenBSD that runs an arbitrary command line under a particular pledge, for example. The intended use is for the developer (or knowledgeable porter) to pledge/unveil down to the resources that they know the program needs. It’s a tool for defensive programming: if your pledged process gets owned, it has as little ambient authority as possible.

                1. 3

                  I agree. The programmer knows better than the user what resources a program needs.

                  Besides, the external approach imposes a static set that stays the same during the entire runtime, which is sort of critical, mostly because many programs require more privileges during the initialization which can then be dropped later.

                  1. 2

                    The programmer knows better than the user what resources a program needs.

                    I agree, but OTOH the user has the much greater incentive to care about sandboxing programs than their programmers (when the user and the programmer are not the same).

                  2. 1

                    OK but my point is that the linked post says that this functionality is extremely difficult in Linux, but if we shift our thinking slightly to work with Linux’s execution resource controls, it seems to be quite easy.

                    1. 2

                      It…depends. Linux sandboxing solutions are not particularly unified and depend on stitching a lot of pieces together, sometimes with confusing or bizarre edge cases. It’s not awful, but it’s still a fair bit of effort and a surprising amount of room for error.

                      (I will say that Landlock helps a lot here by being a relatively high-level set of access controls for things that are otherwise more difficult to sandbox.)

                      1. 1

                        No, it really looks quite simple. Check the link I posted earlier. It’s as simple as adding a couple of lines to the systemd unit file or a couple of flags to the systemd-run command line.

                        1. 8

                          There’s a significant structural difference between the two. I honestly don’t know why the Internet keeps lumping them together. Yes, both apply restrictions, but they’re completely different mechanisms. It’s like lumping email and keyloggers together because both ultimately boil down to delivering a series of keypresses to a third-party.

                          pledge and unveil are things that the program does, which has two major implications:

                          1. It’s generally expected that the program is written in such a manner that pledgeing a particular restriction is a supported mode of operation.
                          2. Breaching a pledge kills the program with SIGABRT and you get a stack trace pointing to wherever the promise was broken.
                          3. You can pledge or unveil whenever you want during a program’s execution (but there are restrictions to what you can “unpledge” or “un-unveil”). So you can e.g. start with full access rights to the user’s home path in order to read configuration, and you can drop filesystem access privileges entirely once you’ve read configuration data.

                          “External” sandboxing solutions allow you to apply the same restrictions, but:

                          1. They’re going to apply regardless of whether the program supports them or not. E.g. you can IPAddressDeny= an address, but if sending telemetry to that address is a blocking operation, you’ll just get a blocked program. That’s obviously not on the sandboxing method itself, but…
                          2. …breaching a restriction doesn’t necessarily give you any useful debugging context. The failure mechanism for breaching a restriction is usually operation-dependent. E.g. IPAddressDeny= packets just get dropped.
                          3. Those restrictions apply throughout a program’s execution lifecycle. So if you need higher privileges at start-up you’re out of luck, since external tools have no insight into what a program is doing.

                          The whole point of pledge and unveil is that you write your program so that you make certain promises about how it’s going to work, and sandboxing works as protection against accidentally breaking those promises, either straight as a bug, or through a bug getting exploited. They’re design-by-contract tools, more or less.

                          1. 3

                            I’m a bit confused, I wouldn’t expect “Linux’s execution resource controls” to include systemd-specific APIs? Tbc these toggles do not cleanly map to OS primitives at all; you can’t just go “ah yes I want systemd’s IP address restrictions myself” without writing your own BPF filtering.

                            If you did just mean systemd specifically, note that it’s quite a bit trickier to, say, sandbox things in a flexible way with that. In particular, to sandbox something that’s not a service with systemd-run has the overhead of spawning the child under the service manager and piping the output, and you lose the ability to easily control and kill the child (because you’re just killing the proxying process, not the actual child).

                            1. 1

                              If you did just mean systemd specifically

                              Yes, that’s what I meant.

                              you lose the ability to easily control and kill the child

                              But the whole point of systemd is that it makes it easy to control and kill the child processes that it runs…

                              1. 2

                                Yes but these things are happening on different levels and mechanisms.

                                One is baked into the software by design, the other is not.

                                systemd is just flat out not a replacement for pledge+unveil, end of story. Completely different mechanisms.

                                1. 2

                                  I didn’t say it’s a ‘flat out replacement’, I said it achieves the same goals that were mentioned in the linked post ie preventing a piece of software I downloaded off the internet from ‘phoning home’ and uploading telemetry.

                                  1. 3

                                    Okay I understand now.. The way I see it, the example in that post was a one-off example, and a pretty bad one, because I think from this you’ve misunderstood the purpose of pledge. While there may be some overlap in the functionaility of BPF filters they are far, far, from 1-1.
                                    To help clear this up:

                                    • pledge is not for sandboxing existing/binary (i.e. already complied) software, nor limiting network connections in software (the former would preferably be done with network controls like firewalls).
                                    • pledge must be added to the source code of the software itself (either by a dev or maintainer).
                                    • on a violation of “promises” the program is killed
                                    • you can limit network connection but it’s simply “all or nothing”; your software gets all network, or no network.

                                    So in this scenerio of telemetry:

                                    • if devs/maintainers have source access to add pledge then they also have source access to just rip out that telemetry code right?
                                      There would be no point in using pledge here.
                                    • in using pledge, as soon as the software tries to do any networking, the program is killed! The connection is blocked – only because your software is now not running at all!
                                    1. 1

                                      Yeah exactly, the overlap is what I’m talking about. This is what I was referring to earlier–if you shift your mindset a bit, it becomes pretty easy to externally impose similar security restrictions using systemd. If you don’t mind that the exact way it’s done is a bit different, you can achieve very similar goals.

                                      1. 4

                                        Part of the pledge/unveil model is that you can progressively drop privileges during program execution. The observation is that programs may need a lot of privileges in order to start themselves up, and then hardly any for the remainder of runtime. For example, consider the recommendations for using pledge when opening handles to sndiod in OpenBSD:

                                        If the sndio library is used in combination with pledge(2), then the sio_open() function needs the stdio, rpath, wpath, cpath, inet, unix, dns, and audio pledge(2) promises.

                                        However:

                                        Once no further calls to sio_open() will be made, all these pledge(2) promises may be dropped, except for the audio promise.

                                        And in fact aucat(1) does exactly this, using a bunch of privileges at startup time (but dropping other very sensitive promises like proc and exec right away) and then dropping most of them when it’s time to start playback or recording from the sound device handle.

                                        The purpose of pledge is for the programmer to mitigate the privileges available to exploits, not for the user to defend against code they chose to run. As far as I know, these goals, which are the main point of pledge and unveil, do not overlap at all with what you can do by filtering access via systemd.

                                        1. 1

                                          They overlap with the goal that was stated in the linked post. That was what I was referring to. I agree that dropping complex combinations of privileges in Linux at different points within an application’s lifecycle doesn’t seem easy.

                                          I encourage you to read the rest of my messages in this thread to get the fuller context. It’s better than responding to one message at a time: https://lobste.rs/s/ny2s9f/openbsd_innovations#c_bds2yk

                                          1. 4

                                            Yeah but the linked post misunderstands what pledge is actually for; “sandboxing untrusted binaries” is not an actual design goal of pledge and if it’s useful for that goal it’s only useful by accident.

                                2. 1

                                  Yeah so this is kinda weird: it makes it easy to manage the child as a single entity and then interactively control it. But systemd-run is, ofc, just a proxy, spawning the child as a transient unit and monitoring it until exit.

                                  This effectively means that systemd-run does not itself control the spawned process’s lifetime. When you run it with –pty, Ctrl-C will kill the child normally… because it’s going through the tty. But if you’re running it detached from the tty (e.g. because you need to control stdout/stdin from your program), or you just signal the process directly, the actual service will remain running in the background. The only way of directly controlling that lifecycle is by grabbing the PID or (preferably) transient unit name and then targeting it directly or controlling it via the D-Bus interface.

                                  1. 1

                                    But it’s a transient unit, it shows up in the output of systemctl list-units, right? That means you can operate on it with eg systemctl stop the-unit just like other systemd units?

                                    1. 1

                                      It is, yeah, which is why I had mentioned the “it makes it easy to […] interactively control it”. It’s just a lot harder in turn to automatically do things like “oh the user did Ctrl-C, let me stop all my processes”.

                        2. 1

                          How does pledge/unveil compare to Capsicum?

                          1. 4

                            From my admittedly passing acquaintance with both of them: Capsicum is much finer grained.

                            Pledge/unveil solves the 80% use case, Capsicum aims to solve the 100% use-case.

                            I.e. it would be very difficult, perhaps impossible to implement capsicum in pledge/unveil, but easy enough to do with capsicum.

                            That said, there is probably more veil/pledge actually implemented in binaries out in the wild. I think all of the base OpenBSD system is done now. Last I checked only some of FreeBSD has Capsicum implemented in base.