sudo without a setuid
binary or SSH over a UNIX socket
In this post, I will detail how to replace sudo
(a setuid binary) by using SSH over a local UNIX socket.
I am of the opinion that setuid
/setgid
binaries are a UNIX legacy that should be deprecated. I will explain the security reasons behind that statement in a future post.
This is related to the work of the Confined Users SIG in Fedora.
Why bother?
The main benefit of this approach is that it enables root
access to the host from any unprivileged toolbox / distrobox container. This is particularly useful on Fedora Atomic desktops (Silverblue, Kinoite, Sericea, Onyx) or Universal Blue (Bluefin, Bazzite) for example.
As a side effect of this setup, we also get the following security advantages:
- No longer rely on
sudo
as asetuid
binary for privileged operations. - Access control via a physical hardware token (here a Yubikey) for each privileged operation.
Setting up the server
Create the following systemd units:
/etc/systemd/system/sshd-unix.socket
:
/etc/systemd/system/sshd-unix@.service
:
Create a dedicated configuration file /etc/ssh/sshd_config_unix
:
Enable and start the new socket unit:
Add your SSH Key to /root/.ssh/authorized_keys
.
Setting up the client
Install socat
and use the following snippet in /.ssh/config
:
Test your setup:
Shell alias
Let’s create a sudohost
shell “alias” (function) that you can add to your Bash or ZSH config to make using this command easier:
2024-01-12 update: Fix quoting and array expansion (thanks to o11c).
Test the alias:
We’ll keep a distinct alias for now as we’ll still have a need for the “real” sudo
in our toolbox containers.
Security?
As-is, this setup is basically a free local root for anything running under your current user that has access to your SSH private key. This is however likely already the case on most developer’s workstations if you are part of the wheel
, sudo
or docker
groups, as any code running under your user can edit your shell config and set a backdoored alias for sudo
or run arbitrary privileged containers via Docker. sudo
itself is not a security boundary as commonly configured by default.
To truly increase our security posture, we would instead need to remove sudo
(and all other setuid
binaries) and run our session under a fully unprivileged, confined user, but that’s for a future post.
Setting up U2F authentication with an sk-based SSH key-pair
To make it more obvious when commands are run as root, we can setup SSH authentication using U2F with a Yubikey as an example. While this, by itself, does not, strictly speaking, increase the security of this setup, this makes it harder to run commands without you being somewhat aware of it.
First, we need to figure out which algorithm are supported by our Yubikey:
If the value is 5.2.3
or higher, then we can use ed25519-sk
, otherwise we’ll have to use ecdsa-sk
to generate the SSH key-pair:
Add the new sk-based SSH public key to /root/.ssh/authorized_keys
.
Update the server configuration to only accept sk-based SSH key-pairs:
/etc/ssh/sshd_config_unix
:
Restricting access to a subset of users
You can also further restrict the access to the UNIX socket by configuring classic user/group UNIX permissions:
/etc/systemd/system/sshd-unix.socket
:
1
2
3
4
5
6
7
8
...
[Socket]
...
SocketUser=tim
SocketGroup=tim
SocketMode=0660
...
Then reload systemd’s configuration and restart the socket unit.
Next steps: Disabling sudo
Now that we have a working alias to run privileged commands, we can disable sudo
access for our user.
Important backup / pre-requisite step
Make sure that you have a backup and are able to boot from a LiveISO in case something goes wrong.
Set a strong password for the root
account. Make sure that can locally log into the system via a TTY console.
If you have the classic sshd
server enabled and listening on the network, make sure to disable remote login as root
or password logins.
Removing yourself from the wheel
/ sudo
groups
Open a terminal running as root
(i.e. don’t use sudo
for those commands) and remove you users from the wheel
or sudo
groups using:
You can also update the sudo
config to remove access for users that are part of the wheel
group:
# Comment / delete this line
%wheel ALL=(ALL) ALL
Removing the setuid binaries
To fully benefit from the security advantage of this setup, we need to remove the setuid
binaries (sudo
and su
).
If you can, uninstall sudo
and su
from your system. This is usually not possible due to package dependencies (su
is part of util-linux
on Fedora).
Another option is to remove the setuid
bit from the sudo
and su
binaries:
You will have to re-run those commands after each update on classic systems.
Setting this up for Fedora Atomic desktops is a little bit different as /usr
is read only. This will be the subject of an upcoming blog post.
Conclusion
Like most of the time with security, this is not a silver bullet solution that will make your system “more secure” (TM). I have been working on this setup as part of my investigation to reduce our reliance on setuid
binaries and trying to figure out alternatives for common use cases.
Let me know if you found this interesting as that will likely motivate me to write the next part!
References
- Pull request for openssh-portable to enable the SSH client to connect directly to a UNIX socket (will remove the need for socat): enable ssh connection to a unix socket
- SSH connect to a UNIX socket instead of hostname
- How to configure SSH with YubiKey Security Keys U2F Authentication on Ubuntu
Comments
You can also contact me directly if you have feedback.