next step for capsudo is to add socket activation support (supporting both the systemd and s6 protocols)
this allows you to not have a giant herd of long running capsudod processes
related to this is allowing capsudod to receive already connected sockets in general, which will allow for extending the capsudo protocol with additional features like requiring a secret to mint the capability
@ariadne I wonder if sealed capabilities, like in CHERI, would be useful. If you're not familiar, sealed caps represent the ability to do something just like any other cap, but cannot be used until they are unsealed by the correct "unsealing capability".
This allows for separation of concerns, so one service might have access to sealed caps for various commands and file accesses, thus being able to send those caps for other services, while being unable to actually use the caps.
Another service might have an unsealing cap for a particular domain, but only be able to access the items of that domain when given sealed caps of that domain. And not be able to use sealed caps belonging to some other domain.
@navi @ariadne And I really wish people would stop using the "socket activation" vocable because it is not a well-defined concept.
I wrote this 10 years ago, and it is still relevant today: https://forums.gentoo.org/viewtopic-t-994548-postdays-0-postorder-asc-start-25.html#7581522
@navi @ariadne Like https://skarnet.org/software/s6/s6-ipcserver.html? or do you want to pass the listening socket to the daemon, so the daemon itself can do the accepts?
@navi @ariadne Yeah, I am unconvinced as well (and that's a euphemism), but if you're willing to experiment with it and test various metrics, why not.
The reason why systemd does this in the first place is to cut corners with dependencies. When a service is accessible over a socket, in the absence of Type=notify, systemd considers the service ready as soon as something listens to the socket, and dependent services can then start. So, in order to make the boot appear faster, systemd itself listens to the socket, so it can start dependent services immediately in parallel.
Never mind that the service itself might then take a long time to start up when a client wants to connect; or even might fail. It's all about the appearance of speed. It's fake. And that's why I don't like it.
@dakkar @ariadne @navi as long as the server drops privileges after binding, this is no different from the supervisor needing to have privileges to bind to the socket.
(supervisor, not super-server: a super-server binds to a socket and listens, and spawns servers with the accepted socket as stdin and stdout)
You could say, well, this dispenses the server author from thinking about it. And I'd answer: I want my server authors to think about the level of privilege they need, I want them to know these things and handle them correctly, and I'd rather have them do that than learn how to use sd_listen_fds which isn't even a good API to begin with.
@noisytoot @ska @navi @dakkar yes, some services should be restricted to root.
in 2025 we have better answers to this problem than "only root/cap_net_bind_service can use ports below 1024"
netfilter's UID matching being one example, eBPF being another
@noisytoot @ska @navi @dakkar basically, the <1024 thing is top-down imposed policy, while software freedom is better served by bottom-up policy
@ariadne @noisytoot @navi @dakkar I think the question is, what's the problem with "only cap_net_bind_service can bind to 1023 and lower"? Why is it a bad solution, why is this a problem that needs solving?
@ariadne @noisytoot @navi @dakkar I'm usually with you but I don't follow that. What does "ports 1023 and below are restricted to root" have to do with software freedom and imposing policy? it only looks like a technical convention to me.
@ska @noisytoot @navi @dakkar yes, an outdated one. it encourages spawning services as root that would not need to start as root to begin with otherwise.
@ska @noisytoot @navi @dakkar basically i am saying that this should be handled by the firewall at this point
@navi @noisytoot @ska @dakkar it is literally a thing that was implemented in Unix before firewalls were invented
@noisytoot @ska @navi @dakkar also, while here, i will point out that identd in 2025 is also incredibly stupid, disclosing information about system users based on a 16-bit secret is madness
@ariadne @ska @navi @noisytoot the way it was described to me (ages ago), was: if I’m talking to some random machine on the network, on a low-numbered port, I can have some assurance I’m talking to a service that the administrator of that machine set up, and not something that a user started
still weird, still outdated, but not much to do with firewalls or eBFP
and it had to be a non-configurable thing, because it was for the benefit of other machines
@dakkar @ska @navi @noisytoot it does have much to do with both, as both are ways of enforcing that policy
@ska you’re not wrong
but also consider: code that’s not written can’t have bugs
if the service never needs to run at higher privileges, it doesn’t need to care about dropping them
and I would trust you, or @navi, or even Lennart, to run a child process with the correct priveleges from your supervisors, than I would trust most developers (e.g. me) to do the whole set*id dance correctly in all execution paths…
I agree that it’s not a great reason, but maybe it’s not a bad reason either?
@dakkar @navi I trust the shortest code path. Going through sd_listen_fds lengthens the code path. If you want to trust random devs with the least possible amount of code (which is a sensible position!), give them a super-server and tell them to only read from stdin and write to stdout. That way, they're not even in charge of the accept() and the main loop. And that factorization actually shortens the code path.
@ariadne @mcrees ah yes, let non-root users race to bind their RCE in a can to port 443, perfect.
The point of making low ports special is that there are global conventions on what services should be running where and restricting ports is essential to correctness in addition to security. Whether you're hardcoding the split or using nonportable kernel communication mechanisms for more fine-grained control doesn't change that.
@ska @navi @ariadne Agreed here. I always have to think when I see that term to remember which combination of behaviors it means. And the idea of passing the _listening_ socket into a service and having it do the accept()s is to me very weird and unrelated to a super-server. That's more of a daemonize-type wrapper to avoid rolling-your-own privilege-dropping code.
@ariadne @ska @mcrees Why the requirement for a <1024 port number? The only one I can think of is when the well-known port is already assigned to a low port number, and IMO writing a small application that deals only with opening the listening socket and dropping privileges before passing the socket to the real service makes for a smaller amount of code subject to breakage.