Allowing non-root execution of a jailed application
Jailed programs can generally be executed by using
Motivation
Suppose you have created a jail
Suppose this program is installed inside the jail under
On the host, you can simply execute it by doing:
You have to be root, however, to do that.
I'll now explain how to make this application accessible by a non-privileged user. Let's call that hypothetical user
Entering
If
This is fine, but
Sure, you could prevent
Command-based
The solution is to allow
What could that command be? Probably not
So, first we create a custom command in the form of a simple shell script. “By abuse of notation”, let's call this script
In our first approach, the script is just the one-liner from above:
Now here comes the trick: We create a
Let it have content of the following form:
where
Once you have saved the file,
Non-privileged user inside the jail
All fine and good. The only thing to worry about is that
We'll fix that now.
First, enter the jail (as root) and create a jailed user for
Now we have to tell our wrapper script on the host,
So we mingle that with our
The general solution
Now
A first idea might be to replace
Fortunately,
will give us the user name belonging to
So, the final version of our wrapper-script
Now both
Jailed programs can generally be executed by using
jexec(8)
. However, you have to be root in order to do that. In this short article I present an approach on how you can allow a specific set of non-privileged users to execute a particular jailed application.Motivation
Suppose you have created a jail
jailedfoo
whose only purpose is to run exactly one specific program foo
. You don't want it on your host system, either because it needs an emulation layer (like Linuxulator) or you just don't want to pollute your host-system with lots of dependecies.Suppose this program is installed inside the jail under
/opt/bloatware.com/foo
. The executable in that directory is foo-bin
.On the host, you can simply execute it by doing:
Code:
jexec jailedfoo /opt/bloatware.com/foo/foo-bin
You have to be root, however, to do that.
I'll now explain how to make this application accessible by a non-privileged user. Let's call that hypothetical user
klaus
.Entering
sudo
If
klaus
is allowed to execute programs using sudo(8)
, she could do
Code:
sudo jexec jailedfoo /opt/bloatware.com/foo/foo-bin
This is fine, but
sudo(8)
generally asks for a password. This is very inconvenient, especially if foo
is a graphical application that is supposed to be run by a desktop launcher and not by a terminal.Sure, you could prevent
sudo
from asking for a password for klaus
in general, but you may not want to do that, because then she could also run any privileged programs (even on the host) without ever being asked for a password. If an attacker gets somehow access to klaus
' terminal, that would be hazardous.Command-based
sudoer
fileThe solution is to allow
klaus
the sudo
ed execution of one specific command and to disable asking for a password for that specific command only.What could that command be? Probably not
jexec
itself, because that would allow klaus
to do that trick on any jail, not just jailedfoo
.So, first we create a custom command in the form of a simple shell script. “By abuse of notation”, let's call this script
foo
and place it under /usr/local/bin
on the host.In our first approach, the script is just the one-liner from above:
Code:
#!/bin/sh
jexec jailedfoo /opt/bloatware.com/foo/foo-bin
Now here comes the trick: We create a
sudoer
file for that specific script. Call that sudoer
file foo
(like the command it is refering to) and place it in the directory /usr/local/etc/sudoers.d
. Do this as root
with the visudo
command:
Code:
visudo -f /usr/local/etc/sudoers.d/foo
Code:
<user> <host> = (root) NOPASSWD: <cmd>
<user>
is our non-privileged user, namely klaus
, <host>
is the host we're on (assume that host is called mercury
), and <cmd>
is the actual command we want to execute; using the full path is important here. So, in our example, the finally content will look like this:
Code:
klaus mercury = (root) NOPASSWD: /usr/local/bin/foo
Once you have saved the file,
klaus
will be able to do the following without being asked for a password:
Code:
sudo foo
Non-privileged user inside the jail
All fine and good. The only thing to worry about is that
foo
inside the jail is executed as root
. It's still inside the jail, so there is probably no problem with that, but some applications just don't like being run as root
.We'll fix that now.
First, enter the jail (as root) and create a jailed user for
klaus
:
Code:
jexec jailedfoo /bin/sh
adduser klaus
Now we have to tell our wrapper script on the host,
/usr/local/bin/foo
, to execute the program as the jailed user klaus
, who has to have the same name as the host-klaus
. jexec(8)
has a special option -U
for that, but this won't work, if the jail is an Ubuntu running on Linuxulator, for example. So we will use a more portable solution using su(1)
. The general syntax will be as follows:
Code:
su -c /opt/bloatware.com/foo/foo-bin - klaus
So we mingle that with our
jexec
invocation in /usr/local/bin/foo
:
Code:
jexec jailedfoo "su -c /opt/bloatware.com/foo/foo-bin - klaus"
The general solution
Now
klaus
is fine. But what about other users, like marie
, for exmple? He might want to run foo
as well. Creating another entry in /usr/local/etc/sudoers.d/foo
will be easy, but what about the non-privileged execution inside the jail? With our current solution, marie
would run foo
inside the jail as the user klaus
, which is probably not what we want (unless you replace klaus
inside the jail by a more general user like foouser
, or something like that).A first idea might be to replace
klaus
in /usr/local/bin/foo
by a simple call to whoami(1)
. The problem is, however, that the script /usr/local/bin/foo
is executed by sudo
, so the user returned by whoami
will always be root.Fortunately,
sudo
is wise enough to provide us with the user-ID of the caller in the form of an environment variable called SUDO_UID
. The only thing we have to do is to convert that UID to the actual user name. The program id(1)
is the right tool for that job:
Code:
id -n -u <UID>
<UID>
, e.g. klaus
or marie
.So, the final version of our wrapper-script
/usr/local/bin/foo
looks as follows:
Code:
#!/bin/sh
jexec jailedfoo "su -c /opt/bloatware.com/foo/foo-bin - $(id -n -u ${SUDO_UID})"
Now both
klaus
and marie
both can launch foo
from the host by doing a sudo foo
without being asked for a password.