Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Capsudo: Rethinking sudo with object capabilities (ariadne.space)
79 points by fanf2 19 hours ago | hide | past | favorite | 48 comments




My most recent mini-adventure with sudo was on the Steam Deck; with OS updates, everything (or at least most?) of what's outside of your home directory is replaced. (In fact, you have to manually opt into being able to write to those directories at all, e.g. to use the system package manager instead of flatpak, by running `sudo steamos-readonly disable`). There are a couple sudo settings I change from the defaults, and because `/etc/sudoers and `/etc/sudoers.d/` (as in the entire directory) were restored to the base versions, my custom settings don't get preserved when updates occur. However, I was surprised to find out that using `visudo` to try to update the settings wasn't actually causing the settings I changed to take effect, which I eventually tracked down to some extra configs getting shipped by the base system in `/etc/sudoers.d`. I looked up what the precedence rules are for sudo rules, and apparently the rule is that anything in `/etc/sudoers.d/` will override `/etc/sudoers`, and the files in `/etc/sudoers.d/` are evaluated in lexigraphical order.

That's how I got where I am today, with a file called `zzz` that I copy into `/etc/sudoers.d/` every time there's a system update.


There's probably a way you can just set it and forget it with overlayfs.

OpenWRT solved the problem of updates via immutable firmware image flashing while maintaining customizations 15 years ago using it.


Probably! At the end of the day, this is the only change I need to make, and the updates are fairly infrequent, so this solution is good enough for me. I only brought it up as an example of how even the meta-rules around how sudo rules are interpreted might be more complex than people realize.

It's nice to see other people writing about the capability transfer feature of Unix domain sockets. File paths are not object capabilities, but file descriptors are. Using a privileged daemon on top of an ambient authority system like Linux seems to be a good way to retrofit object capabilities onto the operating systems we already use. This is the same approach we took in Goblins[0] for our Unix domain socket netlayer for the OCapN[1] protocol.

[0] https://spritely.institute/news/spritely-goblins-v0-16-0-rel...

[1] https://ocapn.org


Whenever I think about writing a central privileged daemon to grant capabilities to other processes, I'm puzzled by the choice to remove the old version of CAP_SETPCAP in 2.6.24: "grant or remove new capabilities to/from an existing running process" - sadly it still exists but means something else in newer kernels with filesystem capabilities.

(In a sense, not having this capability in processes running as root is theatre anyway: you have /dev/kmem access so could just edit the kernel data structures. It's just doing so cleanly that is no longer possible.)

Being able to briefly escalate my editor to have the capabilities to write /etc/wibble.conf when I started editing it as a non-privileged user, then take away the capability again would be more convenient that always needing to run the editor as root. (So convenient, in fact, that people fake this with little editor helpers that do the equivalent of 'really tee FILE-TO-WRITE >/dev/null', but that's an ugly hack.)


> you have /dev/kmem access so could just edit the kernel data structures.

Not anymore: since kernel 2.6.26 /dev/kmem only exists if CONFIG_DEVKMEM is enabled, and it was removed completely in 5.13.

[1] https://lwn.net/Articles/851531/ [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...


I am somewhat cautious to comment as I know the author is way more experienced than I am and I fear that I may be missing something. However, let me try to accomplish the same with my elementary doas(1) knowledge.

Allowing mounting for a specific group is simple with doas.conf(5):

    permit :mountd cmd /sbin/mount
    permit :mountd cmd /sbin/umount
We can of course tighten it further as the author did:

    permit :mount-usb cmd /sbin/mount /dev/sdb1
    permit :umount-usb cmd /sbin/umount /media/usb
If you want to go more complex than specifying arguments, we could of course create a shell script and specify it instead of a binary.

Likewise, we can do something similar for a service account:

    permit :www-deployment as www-deployment cmd /var/www/bin/build /var/www/application
The key difference here would be that www-deployment can not delegate as easily to arbitrary users, as they would need to ask someone with root access to add additional users to the www-deployment group. But I am left wondering if this use case (if it is important enough) is not equally well served by specifying a location for non-root users to add permissions akin to what we see in doas.conf(5), but with the constraint that they of course can only allow other users to run commands with their privileges. Yes, it would "bloat" doas(1), but these code paths are not that long as long as you keep your scope constrained (doas(1) has a core of just over 500 lines and with environment handling and configuration format parsing we arrive a a final line count at just over 1,300).

At this point, the main advantage I see with capsudod is that you can more easily drop privileges and put in restrictions like pledge(2) before the binary is ever called upon by whatever user we have granted permissions. While with the doas(1) thinking above you have to run over plenty of code that could be exploited. Still, this feels like a rather minor relative improvement to what we already have.

Am I missing something in my ignorance? Lastly, let me also say that I am sure that sudo(8) has the ability to do the same things I proposed to do with doas(1) above, but I know the latter far better.


The whole problem is mapping privilege to users and groups, so doas doesn't solve the issues explained in the article.

> The key difference here would be that www-deployment can not delegate as easily to arbitrary users, as they would need to ask someone with root access to add additional users to the www-deployment group. But I am left wondering if this use case (if it is important enough)...

Delegation is the killer feature of the object capability model. It's not just important enough, it's the most important. Keep in mind that the ACL model allows delegation, too, it's just unsafe. Users share credentials all the time. Capabilities allow delegation in a way that can be attenuated, revoked, and audited.


Firstly, thank you for engaging and trying to enlighten me.

I do understand why capability delegation is useful and I am familiar with using Unix sockets to delegate the control of daemons using socket permissions, which feels similar to what we see here with capsudod (I have not read the code sadly, too much other code to read today).

However, I am still puzzled what the advantage of having a herd of capsudod instances running is to say my proposal of allowing users to set up their own doas.conf(5)s to delegate capabilities. Yes, we still need SUID and we will need to be darn sure 1,000 or so lines are properly secured, but it is attenuable, revocable, auditable, and feels (perhaps wrongly, because I have a bias towards text files describing the state of a system?) more natural to me than putting it all into the running state of a daemon.

Is there some other strength/weakness of these approaches that I am failing to see? I am no systems programmer, but I find topics like this interesting and dream of a day when I could be one.


> However, I am still puzzled what the advantage of having a herd of capsudod instances running is to say my proposal of allowing users to set up their own doas.conf(5)s to delegate capabilities. Yes, we still need SUID and we will need to be darn sure 1,000 or so lines are properly secured, but it is attenuable, revocable, auditable, and feels (perhaps wrongly, because I have a bias towards text files describing the state of a system?) more natural to me than putting it all into the running state of a daemon.

I think two separate discussions are being mixed here. The above seems mostly concerned with the chosen interface of capsudo. Imperative vs. declarative is orthogonal to the discussion about object capabilities vs. ACLs.


If you squint hard enough, this is really a pretty similar idea to Polkit [0] but with a much simpler communication layer.

[0]: https://polkit.pages.freedesktop.org/polkit/


You don't need to elevate privileges if you give things the right privileges from the start.

Sudo is just a hack to avoid setting up proper capabilities / permissions in the first place.


I am the owner and only user of the computer. Does that mean I should run everything with root? Of course not. It’s simply better to start with little privileges and then elevate when needed. Using any additional privileges should be an intentional act. I also do it the other way: reduce my privileges via sudo -u nobody.

No, you should run every program with only the privileges it needs. The very concept of running your programs with all your privileges as a user by default is wrong-headed to begin with. To strain the "user" model you should have a distinct "user" for every single program which has only the resources and privileges needed by/allocated to that program. The actual user can allocate their resources to these "users" as needed. This is a fairly primitive version of the idea due to having to torture fundamentally incompatible insecure building blocks to fit, but points in the direction of the correct idea.

I have used systemd services before to do this to run an application, I had a user created specifically for the application, and I defined the capabilities the application needed via CapabilityBoundingSet and AmbientCapabilities [0] and I used a lot of stuff from [1] to restrict the application e.g. the sandboxing facilities, restricting the allowed syscalls [2], ...etc. systemd also comes with a useful command systemd analyze security [3]

[0] https://www.freedesktop.org/software/systemd/man/latest/syst...

[1] https://www.freedesktop.org/software/systemd/man/latest/syst...

[2] https://www.freedesktop.org/software/systemd/man/latest/syst...

[3] https://www.freedesktop.org/software/systemd/man/latest/syst...


The root account shouldn't exist either. Having god accounts is a bad idea security wise. Instead everything should follow the principle of least privilege.

The problem is how do you set up those permissions without a god object? How do you fix ones that are broken on a running system?

Ultimately the security systems that introduce high complexity in the name of fine grain permission controls end up being the most fragile and hardest to verify. People get stuff wrong then break it further trying to get their job done. The better system is sometimes the one that doesn’t have all of the features but is comprehensible to humans.


>how do you set up those permissions without a god object

Let the operating system define default granted permissions for OS apps.

Have the OS let the user grant permissions at install / runtime for apps.


If an app requires a permission, how does OS know that it's OK to grant it? For example, I want to backup my system, so I install app which needs a permission called "bypass any file access control and let me read every file". How does OS know it's legitimate and not malware trying to steal data?

It could be "this requires special digital signature from OS manufacturer" -> then the private key of this digital signature is a "god object"

It could be "this requires confirmation from the physically present user" -> then you basically have passwordless sudo

It could be "this requires users pin/password/biometrics" -> then you have regular sudo

Either way, there is some source of authority in here, even if it's called "root key" or "user pin" instead of "root account".


>then the private key of this digital signature is a "god object"

You could instead require the app to be part of the OS. The next gotcha would from you I imagine is that the build farm for the next OS update is a god object and at that point I think this is a meaningless tangent. I'll concede and say you have to trust your OS creator. But you always have to trust your OS creator for any OS.

>then you basically have passwordless sudo

If sudo couldn't be used from other programs / she'll scripts and doesn't give access to a god account, but instead did simple things like let you use ping, then that seems fine to me. But why require people to manually wrap programs when it could be handled automatically.

>Either way, there is some source of authority in here

Sure, but it's a system that's much better than sudo.


> You could instead require the app to be part of the OS.

That almost sounds like you're advocating for the abolishment of third party or user-made apps that can make changes to the system without the approval of the manufacturer.


This is about being able to read any file on the system including things like the user's bank authentication tokens. No 3rd party developers should be able to read bank authentication tokens. The OS should create a safer API for 3rd parties to use for the use case they want.

Doesn't this just move the bucket: which processes should the OS grant access to that API?

In any case, if the purpose is to make a backup of the system, it seems the possibility to read all and every file as original as possible seems rather critical, in particular if we want to take advantage of e.g. content-based addressing -based deduplication in the backup application. And we in any case want to restore that backup to an empty computer, so there really are no places to hide the encryption keys in such a way that they cannot be read from the backup.


> Letting the operating system define granted permission for OS apps.

We're heading that direction right now, and it will be the OS vendors who decide what programs you have permissions to run and which ones you can't.

That's a concept that HN seems to detest.


Selinux and AppArmor?

Android has it figured out too.


Have you ever tried to fix an application that was getting denied based on SELinux policies? It's a cryptic nightmare. You run a tool that gives you some magic string and hope that it works because nobody really understands what's going on. If that doesn't work you're in a world of pain. Almost as bad as Microsoft's ACLs.

If you have a privilege to replace the kernel or bootloader, you effectively have all privileges on that system. Therefore, there's no need to complicate the access limitations when you get full access anyway.

I own the computer. The least privilege I have encompasses every privilege.

You could own a microwave, but there doesn't have to be a button that makes it run with the door open. The UI of devices doesn't let just anything happen. Similarly an operating system doesn't need to make accessible a way to do everything to the user.

> Similarly an operating system doesn't need to make accessible a way to do everything to the user.

Then who is it available to, if not me, the owner of the computer? What if the operating system isn't doing the things it should that I don't have access to? Do I have to bring it to someone and beg them to fix the computer for me?


I instinctually agree with nkrisc, but this is an interesting line of thought.

What's an example of something that nobody should be allowed to do e.g. on a laptop? If I buy a system with OS stuff set up from the get-go. What abilities do you withdraw from the user?


>What's an example of something that nobody should be allowed to do e.g. on a laptop?

Clearing required efi variables, bricking the motherboard.

https://www.phoronix.com/news/UEFI-rm-root-directory


> You could own a microwave, but there doesn't have to be a button that makes it run with the door open. The UI of devices doesn't let just anything happen.

And where is the UI capability that prevents microwave users from putting liquids (e.g. grape juice) that generate plasma storms inside the microwave and often result in fires? Or, as a bonus, crinkled foil.

To state the matter bluntly – the entire diatribe concerning the system’s role in defining capabilities is as constructive as insisting that every computing device and appliance on the planet must implement B2-level RBAC and capability-based controls – an argument so unmoored from practical reality that one wonders whether its proponent has ever been burdened by implementation.


The UI is missing because the law doesn't require it. That's why it's possible to by tablesaws without a SawStop like safety mechanism despite it being superior to have (ignoring price). Some people will choose the cheaper and less safe option because they don't value safety as much.

"an operating system doesn't need to make accessible a way to do everything to the user"

Microsoft and Apple both seem to think this way. Questionable results.


But do you want your web browser to have the privilege to read your SSH private key? That's the risk of running programs "as you".

You should read over NIST 800-53 AC-2 and AC-6. They go over why privileged accounts are important, why they are used, and how they protect users and organizations.

JIT access should be the goal.

Scroll down to: Implementation Guidance

https://csf.tools/reference/cloud-controls-matrix/v4-0/iam/i...


>JIT access should be the goal.

Individual privileges for specific things should be given access to instead of giving god access to a system.


I hear what you are saying but many, many people who have dedicated their life to this topic disagree with you. Onions have layers for a reason.

RBAC by nature requires a Creator. ZeroTrust networks still require gateways.


This sounds good in theory, but in practice it doesn't work. You always end up with an object that has all the privileges.

Which is a well known anti-pattern. https://en.wikipedia.org/wiki/God_object

That one seems unrelated with OS security model and root account. (One must squint really hard to see root and oop god object as same)

Can you please elaborate on the use of sudo -u nobody? Do you use it interactively? I’m intrigued.

Yea I do. I use it for programs where I’m unsure whether it will read or modify my filesystem. I still allow the program to run arbitrary computation and use the network. It’s just the filesystem part that I want to isolate.

Every time somebody wants to do something in Linux and they mention Objects, I turn around and go the other way.

These people still do not understand why that userspace became so powerful and so useful.

I also think that's the "solution", which is to craft a new optional userspace experience that leaves traditional unix strings and pipes alone. It's not for me, but I'm sure many would like it. I mean, look at PowerShell on Linux :/


That's functionally equivalent to using sudo but only allowing a certain shell script that's a wrapper for what needs to be done by a given user (to avoid the whole syntax mess). But somehow with more boilerplate.

What's missing in this writeup is if/how capsudod can be configured to drop/acquire capabilities (in the capabilities(7) sense, in a namespace sense, or using openbsd's pledge(2)) before executing the command. From looking at the code it seems to rely on filesystem permissions to access the capsudod socket; when in order to make mounting USB drives actually safe via this kind of permission proxying method, you'd want to only give it very limited access to operations, like only the ability to make the mount(2) syscall. I generally agree, this is sudo in a different format and with a daemon; perhaps that's better. And it's written in C, which doesn't speak to avoiding the classic C-related privilege escalation risks (although this is a small amount of code compared to sudo).

If I'm a user who's been given access to run such a wrapper script via sudo, how do I further delegate that access?



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: