The man page says that sudo allows a permitted user to execute a command as the superuser or another user, as specified by the security policy. However most of the sudo use that I've seen/done is really a privilege escalation with switched user being just a consequence of that. Even the famous XKCD 🥪 webcomic shows sudo being used in this way. Most of the time this small difference does not really matter, but sometimes it's really inconvenient.
This is mostly a note I've written for my self so that I can quickly recall how to sudo
when I really only want to do
something without the su
part. Let's have a look at another less popular but sometimes quite a bit more appropriate option.
A very common reason to use sudo is to access files that are only readable by root. Making backups is a good example:
sudo rustic backup /etc
This works and backups are made as expected, however one side effect is that by default rustic
makes a local cache in ~/.cache/rustic
. What that means is that other rustic commands (ie. snapshots
, repoionfo
, ...) that don't need to access local files (and thus are hopefully not being executed as root
) can't use that cache. We could configure some common directory in the rustic config, however the cache files would still have the wrong owner.
So we end up with two different caches for the same repository. This is quite minor issue in practice in this specific case, however it does illustrate the problem well enough.
What really happens in the background is that the command ran by sudo
is so called privileged
process (whose effective user ID is 0
) and for these processes all kernel permission checks are bypassed. This is how root
is able to read all files when doing the backup. However we don't only have this all-or-nothing option available. Linux also provides us with more granular control in the form of capabilities.
A thread can have one or more of these capabilities without bypassing all checks, so we can provide just enough access to do its work. But most conveniently for our use case, the effective user ID does not have to be 0
- we can run the command as any user and only provide the capabilities required.
For our backup there's one capability that's perfect for what we need:
CAP_DAC_READ_SEARCH
: Bypass file read permission checks and directory read and execute permission checks
This will let us read any files and browse contents of all directories without - for example - allowing execution of any binaries that the user can't normally execute or writing to any files. Exactly what we need.
capsh
🔗
The capsh
utility is relatively simple utility that we can use and it's likely already present on your system. Let's have a look at how it can be used:
1 sudo -E \
2 capsh \
3 --user=${USER} \
4 --inh=CAP_DAC_READ_SEARCH \
5 --addamb=CAP_DAC_READ_SEARCH \
6 -- -c "rustic backup ${directory}"
Well that's a lot. Let's go line by line:
-E
to preserve the environment variables as we will be dropping back to our user anyways.capsh
with some extra flags:$USER
variable is already set to your current user name. (So a user name before sudo is executed) In other words, switch back to current user from root
that's provided by sudo
.CAP_DAC_READ_SEARCH
inheritable, so that we can pass it to child process.--
is passed as a parameter to /bin/bash
executed by capsh
. In this case we tell it to run our backup.It all sounds a bit complicated, but let's just look at man page:
Inheritable: This is a set of capabilities preserved across an execve. Inheritable capabilities remain inheritable when executing any program, and inheritable capabilities are added to the permitted set when executing a program that has the corresponding bits set in the file inheritable set.
Aha, so these are the capabilities that can be inherited by child process. This allows capsh
to pass it on to the /bin/bash
it executes which will then pass it down to rustic
. Let's continue reading:
Because inheritable capabilities are not generally preserved across execve when running as a non-root user, applications that wish to run helper programs with elevated capabilities should consider using ambient capabilities
Right, this makes sense. The capability is inheritable, but that does not actually mean the child process will always inherit it. And in fact for non-root users the capability generally won't be inherited. So we need something more in our setup to make it all work. As suggested, let's look at ambient capabilities:
This is a set of capabilities that are preserved across an execve of a program that is not privileged.
Ah okay, this solves our problem then. Do we even need to make it inheritable then? Well yes, because:
no capability can ever be ambient if it is not both permitted and inheritable
💡 Aha, now it makes sense. We essentially tell capsh
to drop back to previous user, but make sure the CAP_DAC_READ_SEARCH
capability is passed to the command and that all of its child processes will automatically inherit it.
Which means that following command will drop us to a shell, that runs under current user, but any command executed within the shell will inherit the super power of being able to access any
file:
capsh --user=${USER} --inh=CAP_DAC_READ_SEARCH --addamb=CAP_DAC_READ_SEARCH -- -c bash
Let's try that:
# Can we see files in root home directory?
$ ls -la /root/.bash_history
-rw------- 1 root root 22896 Mar 18 13:25 /root/.bash_history
# Should anyone other than root be able to see the files?
$ stat /root | grep root
File: /root
Access: (0700/drwx------) Uid: ( 0/ root) Gid: ( 0/ root)
# Are we root?
$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
Neat, nobody
with super powers!
setcap
🔗
Another interesting option is to use setcap to assign capabilities to executable. There's example setup for restic to use this for root-less backups, however it's worth keeping in mind that anyone able to execute the binary will also get the capability. So this is more permanent setup while capsh
is perhaps more suitable for more ad-hoc usage or in situations where we don't want to have the capability every time a command is executed.
Overall capabilities look like pretty handy if maybe a bit unpolished way of providing limited access to powerful features. There's neat project called RootAsRole
, that's trying to bring this concept further. It's definitely on my watchlist. In general the UX side of things still isn't great in my opinion. But at least there's a choice and we aren't limited to UID=0
.
This article is part of Linux category. Last 16 articles in the category:
You can also see all articles in Linux category or subscribe to the RSS feed for this category.