-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
systemd: Lock down cockpit-ws and -tls #21305
systemd: Lock down cockpit-ws and -tls #21305
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good first chunk of stuff, but I think we can go a lot further.
src/systemd/[email protected]
Outdated
SystemCallFilter=@system-service | ||
|
||
# cockpit-tls does all our outside networking | ||
PrivateNetwork=yes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I tried turning on PrivateNetwork in cockpit-tls
and it didn't seem to break anything. That's not surprising: systemd hands its listener socket to it and its outbound connections are via UNIX sockets on the filesystem.
Which is to say: we should probably also add more sandboxing to cockpit-tls
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aww dang -- we can't do that. cockpit-ws runs beiboot and ssh for remote hosts/bastion, and that does need networking. That's what's breaking all the multi-machine tests.
RestrictAddressFamilies=AF_UNIX | ||
|
||
# extra protection for our TLS keys -- only cockpit-tls should read them | ||
InaccessiblePaths=-/etc/cockpit/ws-certs.d |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't care for this at all. We're a dynamic user, so the only way we can read those is if they were world-readable. But then we could read everyone else's incorrectly world-readable data.
I'd prefer if you take a much more aggressive approach here and move to whitelisting: we should only be allowed to see:
/usr
/etc/cockpit/cockpit.conf
- the
/run/cockpit/session
socket
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, I just pushed an initial attempt after some light testing. There are already some failures, so some work to do.
Locking this down further is nice of course. Need to see how that plays out with PrivateTmp=
, i.e. which one wins.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.. plus, need to allow whatever ssh needs to read, i. e. at least /etc/ssh/. It likely also requires reading $HOME for ssh keys..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bastion host configurations have no business reading anyone's ssh keys from $HOME
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started with InaccessiblePaths=/
but this is too much of a ratsnest. (Plus, it's not even documented to work, as these options don't have an ordering/priority). Think /etc/nsswitch.conf an ld.so.conf and whatnot. ProtectSystem=strict
is a very good starting point, and together with ProtectHome
(which is implied by DynamicUser) is a robust basis. You could spend endless hours tracking it down to a list of 50 files, but this will also change over time, is too specific to be reliable across different system configurations, and just isn't worth it IMHO -- having pretty much everything being read-only gives us what we need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think about some people (cough, cough) using a Yubikey for SSH auth and requiring extra configs/libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think about some people (cough, cough) using a Yubikey for SSH auth and requiring extra configs/libraries.
This wouldn't require access to the Yubikey on the bastion host and, indeed, we'd actually want to very strictly isolate SSH on the bastion host for exactly this reason.
But there are some plausible causes where ssh on the bastion host would need to see more of the system:
- we might imagine ssh ends up using systemd-resolved and needing to talk to it via D-Bus. That's a whole lot of stuff that ends up getting pulled in.
- we might imagine some sssd-type setup where we feed hostkeys to ssh in some dynamic way backed by some kind of a network database. This could end up using a whole lot of random stuff around.
So, rough mid/longterm plan:
- get cockpit-beiboot separated from -ws via the same socket activation tricks
- after
ssh
is out of the picture, we can go insane on restrictions on -ws
75feac2
to
042df74
Compare
MemoryDenyWriteExecute=true | ||
SystemCallFilter=@system-service | ||
|
||
# cockpit-tls does all our outside web related networking, but ws also calls ssh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed I forgot about this, but.... this is a bit sad when ConnectTo
isn't enabled by default...
I wonder if we could do something about that. Isolating cockpit-ws
to its own network namespace would be a pretty big win.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearly we need socket-activated beiboot 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite honestly, that sounds right. It'd isolate SSH sessions in the same manner as local PAM ones, and would allow us to fully lock down ws. (But I don't want to do that in this PR)
For tracking back: commit 042df74 is a set of working restrictions which is green, but still without path restrictions. Trying these next. |
042df74
to
9095b22
Compare
Now that cockpit-ws does not directly fork cockpit-session, and sessions run in their own service/cgroup, we can heavily lock down our webserver (which is the weakest component in Cockpit). It only needs to talk to cockpit-tls over stdin/out and cockpit-session over the Unix socket, and call SSH for remote host support (so we can't lock down networking completely). `DynamicUser=` already implies the biggest restrictions, such as `ProtectSystem=full`, `ProtectHome`, `PrivateTmp`, and more. We can even work with `ProtectSystem=strict` (i.e. everything being read-only except the service's /run directory and tmp dir). Fixes cockpit-project#21299 https://issues.redhat.com/browse/COCKPIT-1206
Consistently use "yes" for boolean options. `DynamicUser` already implies `PrivateTmp` and `ProtectHome`.
cockpit-tls speaks to the outside world via socket activation, and to cockpit-ws via Unix socket. So it doesn't require much networking on its own.
9095b22
to
fe6813f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, this all looks like a huge win. I'm sure we could turn the screws even tighter, but let's get this stuff in now so that it makes the release.
Next steps:
- beiboot isolation
- network isolation for -ws
- filesystem-based restrictions
|
||
# cockpit-tls speaks to the outside world via socket activation, and to cockpit-ws via Unix socket | ||
PrivateIPC=yes | ||
PrivateNetwork=yes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that we deprive our network-facing component of the ability to interact with the network. This feels really nice.
Now that cockpit-ws does not directly fork cockpit-session, and sessions
run in their own service/cgroup, we can heavily lock down our webserver
(which is the weakest component in Cockpit). It only needs to talk to
cockpit-tls over stdin/out and cockpit-session over the Unix socket,
and call SSH for remote host support (so we can't lock down networking
completely).
DynamicUser=
already implies the biggest restrictions, such asProtectSystem=full
,ProtectHome
,PrivateTmp
, and more. We can evenwork with
ProtectSystem=strict
(i.e. everything being read-only exceptthe service's /run directory and tmp dir).
Fixes #21299
https://issues.redhat.com/browse/COCKPIT-1206