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
sudoIf
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 sudoed 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.