🥺: the best sudo replacement
Read time in minutes: 14
Security is impossible. We just like to pretend otherwise so that we can constantly project this aura of impenetrability that will save us from having to admit the reality that it's impossible. One of the biggest targets in the modern information security world is sudo. It is a command that lets you set user and then do a command. Sudo is one of the most widely deployed programs on the Internet and is widely regarded as critical infrastructure.
I'm tired of this situation and I bet a lot of the ecosystem is too. There's been talk and ideas, but not enough in the action department. I made a new tool. A better tool. One that will let all of us proceed towards the future we deserve. I made a sudo replacement named 🥺.
🥺
🥺 has no pronounceable name in English or any other speakable human language.
It is named 🥺, but it is referred to as xn--ts9h
(the punycode form of 🥺) in
situations where emoji are not yet supported (such as Debian package names).
To use 🥺, install it (such as from the Debian package) and then run it in place of sudo:
$ id
uid=1000(xe) gid=1000(xe) groups=1000(xe),102(docker)
$ 🥺 id
uid=0(root) gid=0(root) groups=0(root),102(docker),1000(xe)
Here it is broken down statement by statement.
First, I pull in a bunch of imports from the standard library and also the syslog to write a message to syslog about what's going on:
use std::{env, os::unix::process::CommandExt, process::Command};
use syslog::{unix, Facility::LOG_AUTH, Formatter3164};
Next, I create a main function that returns an
io::Result
, this is an
error that is returned by most of the standard library functions that do I/O
operations with the OS.
fn main() -> io::Result<()> {
The correct usage of this program is to run it like 🥺 id
, so if the user
doesn't specify a program to run, then it should blow up with an error message
instead of panicking:
if env::args().len() == 1 {
eprintln!("usage: {} <command> [args]", env::args().nth(0).unwrap());
return Ok(());
}
Next, we grab the program name and arguments from the command line arguments of 🥺 and send a message to syslog that it's being run so that there is some accountability after-the-fact:
let program = env::args().nth(1).unwrap();
let args = env::args().skip(2).collect::<Vec<String>>();
let mut writer = unix(Formatter3164 {
facility: LOG_AUTH,
hostname: None,
process: "🥺".into(),
pid: 0,
})
.unwrap();
writer
.err(format!("running {:?} {:?}", program, args))
.unwrap();
Finally, the actual command is executed:
Err(Command::new(program).args(args).uid(0).gid(0).exec().into())
This works because I'm using the
CommandExt
trait implementation of
Command
that adds
some methods we need:
uid(&mut self, id: u32)
to set the user ID of the child process (setuid(2)
in C)gid(&mut self, id: u32)
to set the group ID of the child process (setgid(2)
, though groups are starting to die out due to them not being across multiple machines without extra effort like configuration managment)exec(&mut self)
which runs theexecvp(3)
system call that replaces the current 🥺 with the child process
The key part is the exec
call at the end. One of the interesting things about
the exec
-family of system calls in UNIX is that it replaces the current
process if it succeeds. This means that the function will never return unless
some error happened, so the exec
method always returns an error. This will
make error handling happen properly and if things fail the process will exit
with a non-zero error code:
$ cargo run --release ls
Finished release [optimized] target(s) in 0.06s
Running `target/release/🥺 ls`
Error: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }
I'm fairly sure that this program has no bugs that aren't either a part of the syslog crate or the Rust standard library.
Installation
You can install 🥺 by downloading the .deb
file from my
fileserver and
installing it with dpkg -i
. This will give you the 🥺
command that you can
use in place of sudo
.

This is also known to work on Amazon Linux 2, so you can create blursed things like this:
$ ssh -A xe@10.77.131.103
Warning: Permanently added '10.77.131.103' (ED25519) to the list of known hosts.
Last login: Fri Jan 20 04:09:11 2023 from 10.77.131.1
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
[xe@inez-rengenne ~]$ 🥺 id
uid=0(root) gid=0(root) groups=0(root),10(wheel),1000(xe)
.rpm
file to yum install
and it will just download and
install that .rpm
file. This is incredibly cursed.The .deb
package was built on Ubuntu 18.04 and the .rpm
package was built on
Amazon Linux 2, so it should be compatible with enough distributions that you
don't have to care.
man 8 🥺
!
setuid
. Why doesn't this
program use those?