Basically, the SUID bit makes a program get the permissions of the owner when executed. If you set /bin/bash as SUID, suddenly every bash shell would be a root shell, kind of. Processes on Linux have a real user ID, an effective user ID, and also a saved user ID that can be used to temporarily drop privileges and gain them back again later.
So tools like sudo and doas use this mechanism to temporarily become root, then run checks to make sure you’re allowed to use sudo, then run your command. But that process is still in your user’s session and process group, and you’re still its real user ID. If anything goes wrong between sudo being root and checking permissions, that can lead to a root shell when you weren’t supposed to, and you have a root exploit. Sudo is entirely responsible for cleaning the environment before launching the child process so that it’s safe.
Run0/systemd-run acts more like an API client. The client, running as your user, asks systemd to create a process and give you its inputs and outputs, which then creates it on your behalf on a clean process tree completely separate from your user session’s process tree and group. The client never ever gets permissions, never has to check for the permissions, it’s systemd that does over D-Bus through PolKit which are both isolated and unprivileged services. So there’s no dangerous code running anywhere to exploit to gain privileges. And it makes run0 very non-special and boring in the process, it really does practically nothing. Want to make your own in Python? You can, safely and quite easily. Any app can easily integrate sudo functionnality fairly safely, and it’ll even trigger the DE’s elevated permission prompt, which is a separate process so you can grant sudo access to an app without it being able to know about your password.
Run0 takes care of interpreting what you want to do, D-Bus passes the message around, PolKit adds its stamp of approval to it, systemd takes care of spawning of the process and only the spawning of the process. Every bit does its job in isolation from the others so it’s hard to exploit.
(I’ll attempt this based on my understanding of both)
Pouring a cup of juice is something an adult needs to be involved with.
sudo is when you ask for permission to pour your own cup of juice. You ask an adult, they give you the cup and the juice, and then you’re responsible for pouring it. If the adult isn’t paying attention they may leave the fridge open for you to go back for more juice or another beverage, but otherwise you’re limited to the amount of juice the adult has given you.
run0 is when the adult just gets you a cup of juice. You tell them what you want, they go and pour the juice, and just give you the cup with the juice in it. You never enter the kitchen, so you don’t have access to the fridge, just your cup of juice.
Some executables are special. When you run them, they automagically run as root instead! But if sudo isn’t very, very careful, you can trick it into letting you run things as root that you shouldn’t be able to.
Run0 DM’s systemd asking it to go fork a process as root for you, and serves as the middleman between you and the other process.
But I’ve had so many issues with D-Bus fucking shit up on my systems that I’d be very reluctant to hinge my only way of recovering from failures upon something so brittle.
Granted, D-Bus hasn’t given me any trouble since moving to NixOS. The hell of trying to recover my arch systems from a perpetually failing D-Bus would make me very apprehensive to adopt this. I could see myself using run0 by default, but keeping sudo-rs or doas around with a much stricter configuration as a failsafe until the run0 + D-Bus + PolKit is absolutely stable and bulletproof.
I haven’t had D-Bus problems in quite a while but actually run0 should help with some of those issues. Like, systemctl --user will actually work when used with run0, or at least systemd-run can.
Haven’t used it yet so it’s all theoretical, but it makes sense to me especially at work. I’ve used systemd-run to run processes in very precise contexts, it’s worth using even if just to smush together schedtool, numactl, nice, taskset and sudo in one command and one syntax. Anything a systemd unit can do, systemd-run and run0 can do as well.
I’m definitely going to keep su around just in case because I will break it the same I’ve broken sudo a few times, but I might give it a shot and see if it’s any good just for funsies.
Just trying to explain what it does and what it can do as accurately as possible, because out of context “systemd adds sudo clone” people immediately jump to conclusions. It might not be the best idea in the end but it’s also worth exploring.
Some people are opposed to sudo being a fairly complex program with an awkward to understand configuration language and a couple of methods that can fetch config from elsewhere. Fixing upstream sudo can’t happen because those features exist and are presumably used by some subset of people, so straight up removing them is not good, but luckily doas and sudo-rs exist as alternatives with a somewhat stripped featureset and less footguns.
Others are opposed to the concept of SUID. Underneath all the SUID stuff lies far more complexity than is obvious at first sight. There’s a pretty decent chunk of code in glibc’s libdl that will treat all kinds of environment variables differently based on whether an executable is SUID, and when that goes wrong, it’s reported as a glibc bug (last year’s glibc CVE-2023-4911 was this). And that gets all the more weird when fancy Linux features like namespaces get involved.
Removing SUID requires an entirely different implementation and the service manager is the logical place for that. That’s not just Lennart’s idea; s6, as minimal and straight to the point as it tends to be, also implements s6-sudo{,d,c}. It’s a bit more awkward to use but is a perfectly “Unix philosophy” style implementation of this very same idea.
Basically, the SUID bit makes a program get the permissions of the owner when executed. If you set
/bin/bash
as SUID, suddenly every bash shell would be a root shell, kind of. Processes on Linux have a real user ID, an effective user ID, and also a saved user ID that can be used to temporarily drop privileges and gain them back again later.So tools like
sudo
anddoas
use this mechanism to temporarily become root, then run checks to make sure you’re allowed to use sudo, then run your command. But that process is still in your user’s session and process group, and you’re still its real user ID. If anything goes wrong between sudo being root and checking permissions, that can lead to a root shell when you weren’t supposed to, and you have a root exploit. Sudo is entirely responsible for cleaning the environment before launching the child process so that it’s safe.Run0/systemd-run acts more like an API client. The client, running as your user, asks systemd to create a process and give you its inputs and outputs, which then creates it on your behalf on a clean process tree completely separate from your user session’s process tree and group. The client never ever gets permissions, never has to check for the permissions, it’s systemd that does over D-Bus through PolKit which are both isolated and unprivileged services. So there’s no dangerous code running anywhere to exploit to gain privileges. And it makes run0 very non-special and boring in the process, it really does practically nothing. Want to make your own in Python? You can, safely and quite easily. Any app can easily integrate sudo functionnality fairly safely, and it’ll even trigger the DE’s elevated permission prompt, which is a separate process so you can grant sudo access to an app without it being able to know about your password.
Run0 takes care of interpreting what you want to do, D-Bus passes the message around, PolKit adds its stamp of approval to it, systemd takes care of spawning of the process and only the spawning of the process. Every bit does its job in isolation from the others so it’s hard to exploit.
Can someone ELI3?
(I’ll attempt this based on my understanding of both)
Pouring a cup of juice is something an adult needs to be involved with.
sudo is when you ask for permission to pour your own cup of juice. You ask an adult, they give you the cup and the juice, and then you’re responsible for pouring it. If the adult isn’t paying attention they may leave the fridge open for you to go back for more juice or another beverage, but otherwise you’re limited to the amount of juice the adult has given you.
run0 is when the adult just gets you a cup of juice. You tell them what you want, they go and pour the juice, and just give you the cup with the juice in it. You never enter the kitchen, so you don’t have access to the fridge, just your cup of juice.
This is an extremely good explanation.
Okay now please ELI1
Gagagoogoo Gagaga
when in need, cry out for mommy!
caseyweederman is not in the sudoers file. This incident will be reported.
Some executables are special. When you run them, they automagically run as root instead! But if sudo isn’t very, very careful, you can trick it into letting you run things as root that you shouldn’t be able to.
Run0 DM’s systemd asking it to go fork a process as root for you, and serves as the middleman between you and the other process.
You had me rolling, bud.
Dude, you need a prize for this comment. Very well explained!
Sounds good in theory.
But I’ve had so many issues with D-Bus fucking shit up on my systems that I’d be very reluctant to hinge my only way of recovering from failures upon something so brittle.
Granted, D-Bus hasn’t given me any trouble since moving to NixOS. The hell of trying to recover my arch systems from a perpetually failing D-Bus would make me very apprehensive to adopt this. I could see myself using run0 by default, but keeping sudo-rs or doas around with a much stricter configuration as a failsafe until the run0 + D-Bus + PolKit is absolutely stable and bulletproof.
I haven’t had D-Bus problems in quite a while but actually run0 should help with some of those issues. Like,
systemctl --user
will actually work when used with run0, or at least systemd-run can.Haven’t used it yet so it’s all theoretical, but it makes sense to me especially at work. I’ve used systemd-run to run processes in very precise contexts, it’s worth using even if just to smush together schedtool, numactl, nice, taskset and sudo in one command and one syntax. Anything a systemd unit can do, systemd-run and run0 can do as well.
I’m definitely going to keep
su
around just in case because I will break it the same I’ve broken sudo a few times, but I might give it a shot and see if it’s any good just for funsies.Just trying to explain what it does and what it can do as accurately as possible, because out of context “systemd adds sudo clone” people immediately jump to conclusions. It might not be the best idea in the end but it’s also worth exploring.
At that point just set a break-glass root password and don’t use sudo or doas.
Thank you, i didnt really understand what this was about ubtil now
Thanks a lot for this detailed, understandable and kind answer :)
Why not just fix sudo then?
Or just use
doas
, it’s still more secure than sudoSome people are opposed to
sudo
being a fairly complex program with an awkward to understand configuration language and a couple of methods that can fetch config from elsewhere. Fixing upstreamsudo
can’t happen because those features exist and are presumably used by some subset of people, so straight up removing them is not good, but luckilydoas
andsudo-rs
exist as alternatives with a somewhat stripped featureset and less footguns.Others are opposed to the concept of SUID. Underneath all the SUID stuff lies far more complexity than is obvious at first sight. There’s a pretty decent chunk of code in glibc’s libdl that will treat all kinds of environment variables differently based on whether an executable is SUID, and when that goes wrong, it’s reported as a glibc bug (last year’s glibc CVE-2023-4911 was this). And that gets all the more weird when fancy Linux features like namespaces get involved.
Removing SUID requires an entirely different implementation and the service manager is the logical place for that. That’s not just Lennart’s idea; s6, as minimal and straight to the point as it tends to be, also implements
s6-sudo{,d,c}
. It’s a bit more awkward to use but is a perfectly “Unix philosophy” style implementation of this very same idea.Never had an issue. Might look for a replacement should an issue arise. Been driving Linux since sarge.