A life without sudo

- 3 mins read
Make me a sandwich.  What? Make it yourself.

Fig 1. Classic XKCD cartoon :-)

Please see the latest update, below!

Ever since my first stumbling steps with Linux back in ‘96, I’ve been learning about UNIX. The first obvious lesson was to not use the root account. Since then I’ve been using a combination of sudo command and suid root binaries to get the job done.

For the last ten years, however, I’ve been meaning to learn about Linux capabilities(7) and thanks to a friend and colleague of mine I now have :)

What you want is to grant capabilities per user and application. Most tutorials only tell you how to do one or the other.

First of all you need to figure out what capabilities an application requires to perform an action. Let’s use tcpdump as an example, it needs raw link access to sniff packets so your user (you), need to be listed in the system /etc/security/capability.conf file:

cap_net_raw     joachim

Second, you need to set this on the application, so that when joachim wants to run tcpdump he is granted the capability:

$ sudo /sbin/setcap cap_net_raw+ep /usr/sbin/tcpdump

Some applications require multiple capabilities, like Qemu when you use tap networking. Update /etc/security/capability.conf

cap_sys_ptrace,cap_net_bind_service,cap_net_raw,cap_net_admin     joachim

Place all capabilities on one line, separated with comma. Then add both capabilities to qemu, like this:

$ sudo /sbin/setcap cap_net_raw,cap_net_admin+ep /usr/bin/qemu-system-arm

Update Sep 15, 2024

As time has progressed so has Linux distributions. On Debian derived distributions like Ubuntu, or my personal favorite, Linux Mint, you now have to adjust your PAM setup slightly:

Edit the file /etc/pam.d/common-auth and modify the pam_cap.so line to include the keepcaps keyword:

# /etc/pam.d/common-auth
...
auth    optional         pam_cap.so keepcaps
...

Without it, your inherited capability mask will not be set properly! If you’re unlucky, you may also have to comment out pam_systemd.so in the file /etc/pam.d/common-session:

# /etc/pam.d/common-session
...
#session optional        pam_systemd.so
...

… or, if you want your desktop environment to work like you expect it to, then use this ugly hack instead:

# /etc/pam.d/common-session
...
# This gives *all users* the following capabilities, not what we want.
session optional        pam_systemd.so default-capability-ambient-set=CAP_NET_RAW,CAP_NET_ADMIN,CAP_NET_BIND_SERVICE,CAP_SYS_PTRACE,CAP_WAKE_ALARM
...

In the future, systemd-homed is supposed to be possible to set up to allow the same fine grained mask per user as capability.conf. It is unclear to the undersigned why pam_systemd.so, or distributions (?), cannot just use pam_cap.so and capability.conf

Helper Script

Here’s a little script I use to set capabilities. It used to be called wkz-caps.sh, but I’ve since refactored it. You need to run it every time after upgrading any of the packages on your system that provides the tools you need capabilities for.

#!/bin/sh

set_capabilities()
{
    capability="$1"
    shift
    for cmd in "$@"; do
        binary=$(command -v "$cmd" 2>/dev/null) || binary=$cmd
        path=$(realpath "$binary") # ip tool may be a symlink
        if [ -x "$path" ]; then
            sudo setcap "$capability" "$path"
        else
            echo "Warning: $cmd not found or not executable, skipping." >&2
        fi
    done
}

set_capabilities 'cap_net_admin,cap_net_raw,cap_net_bind_service+ep' \
    gdb-multiarch dnsmasq

set_capabilities 'cap_net_admin+ep' \
    ip bridge brctl ifconfig ethtool \
    qemu-system-aarch64 qemu-system-arm qemu-system-ppc \
    qemu-system-ppc64 qemu-system-x86_64 \
    /usr/lib/qemu/qemu-bridge-helper

set_capabilities 'cap_net_raw+ep' \
    tcpdump nemesis hping3 trafgen