Currently, services default to running as root with unlimited network & file system access. Service authors must actively take away capabilities, which is error prone and requires extensive knowledge of systemd directives. Writing least privilege services requires the most effort, it should be the other way around.
The solution is two-fold:
User=nobody
, enable ProtectHome
, PrivateTmp
, and ideally also PrivateNetwork
and PrivateDevices
. We WANT stuff to break if the author hasn't taken the time to enumerate what the service actually needs.capabilities = [ "mknod" "net_admin" ]
, privateTmp = true
Eventually, all existing services should be least privilege and all new services default to least privilege.
https://wiki.archlinux.org/index.php/DeveloperWiki:Security#Service_Isolation contains useful information and links to further reading about service isolation.
A dump of things that could be included in a hypothetical service writing chapter: https://gist.github.com/joachifm/022ca74fd447bd8bb2f80a133c0ab3a9
Big +1 from me.
We shouldn't default to User=nobody
, because then all services which don't specify a user will be running as the same UID, breaking isolation. Instead, we should have no default at all and instead require a value to be set (I'm not 100% sure on how to do this - maybe a required function argument?)
@aneeshusa that is certainly something to consider. It could be solved by a simple assert. I have thought about setting default based on the name of the service; with dynamic users/groups there's no real danger of running out, the only possible issue is encroaching on users and groups created by the user.
That said, User=nobody
services would be hamstrung to the point of not having much useful information to leak.
The ideal configuration would have each daemon running with a distinct filesystem, network, device, and process namespace with explicit namespace sharing, so users and groups would mostly be used to regulate access to filesystem state. Any process without CAP_SYS_ADMIN would then be more or less confined, regardless of DAC.
big +1 from me as well.
@joachifm i am worried how would the migration of services be like. we dont want to break every service right? would this be possible to introduce gradually?
@garbas I have a few options in mind.
First is to only do docs + reify some systemd directives to make it more convenient to specify privileges, but not change any defaults.
Second is to change the defaults, but make them conditional on an internal flag that is set for all (or some) existing services, making new services least privilege by default while minimizing disruption. Equivalently, introduce a new service option namespace that's least privilege by default.
Third is to change the defaults and hope that the fallout isn't too bad. It's probably wishful thinking on my part, but I'm convinced that only a minority of services would actually _break_ due to this.
In any event, getting to least privilege is a massive undertaking, so I'm content to close this once we have some docs and have reified the most important systemd directives, anything beyond that isn't realistic, in my opinion.
@joachim +1
I really don't see obsticales here except getting it done. Switching the defaults to restrictive can only be the last step, otherwise lots of modules will break. Maybe a goal for post 16.09?
Has any work been done on this?
I don't think so but I plan on doing so after getting #12895 merged if no one beats me to it.
I have some private patches, but I'm not sure what extent of potential breakage/how invasive changes are going to be accepted, so I've been somewhat hesitant to actually do anything with this.
Some work in progress stuff here: https://github.com/joachifm/nixpkgs/tree/capable-services
Looks nice and straightforward. Should be easy for maintainers to handle!
How does this relate to grsec RBAC? Are the capabilities mapped in systemd?
@spacekitteh grsec RBAC might impose stricter controls but is otherwise orthogonal. If we had a declarative RBAC policy interface, perhaps this information could be re-used there to achieve consistency. With policy generated from learning logs, it gets trickier. Most of this stuff is mainly going to benefit non-RBAC users, in any case, I think. For now I'm just doing the stupidest thing that can work ...
It might be worth thinking about how to make an interface which works with both grsec RBAC and SELinux, eventually.
(triage) has there been progress on this front?
Close?
I have cleaned up a few services but progress is slow. We should however, not close IMHO.
Most helpful comment
Some work in progress stuff here: https://github.com/joachifm/nixpkgs/tree/capable-services