There are two issues that cause this to fail.
TL;DR
To get this to work, you need to:
- Run
sandbox with a --level option. - Install the policy I show below.
Problem 1: MCS Ranges
When you start the service from the command line as a normal user, you are probably running in the following SELinux context. You can verify with id -Z.
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
There are five parts to that context.
- Your SELinux user (unconfined_u)
- Your role (unconfined_r)
- Your domain (unconfined_t)
- Your MLS range (s0-s0)
- Your MCS range (c0.c1023)
By default, the latest version of sandbox requires that the user executing the sandbox command has an MCS range defined (see commit 78b077c and commit 6c2ad1c from 2011). When you're running as the normal user, everything's OK, because you have an MCS range defined. However, look at the context that systemd services run in by default. (I got this by making a script that printed out its SELinux context to the syslog.)
system_u:system_r:unconfined_service_t:s0
Whoops! We don't have an MCS range! This is why you got your error when running sandbox in a systemd service.
Fortunately, sandbox has a command line flag that you can use to explicitly set the MLS and MCS parts of the execution context: --level. That is, when you run
sandbox --level "s0" /path/to/my/command
then sandbox will no longer attempt to extract your current context's MCS range.
Problem 2: Sandbox Domain Pairings
If you make the above change and try re-running your service, however, you will get a new error.
/usr/bin/sandbox: Could not set exec context to system_u:system_r:sandbox_t:s0. Invalid argument
That error means that SELinux will not let you transition from the systemd context to the sandbox context. The reason for this is the pairings between the two different roles (system_r/unconfined_r) and the sandbox domain (sandbox_t).
The command seinfo -rXXXXX -x shows you a list of the domain pairings that are legal with the role "XXXXX". Let's look for sandbox_t.
$ sudo seinfo -runconfined_r -x | grep sandbox_t chrome_sandbox_t sandbox_t $ sudo seinfo -rsystem_r -x | grep sandbox_t sshd_sandbox_t chrome_sandbox_t
So, "sandbox_t" is available for pairing with "unconfined_r" but not "system_r". I don't know why this is the case; my best guess is that the Red Hat people wrote sandbox with the intention of only normal users running it. Fortunately, it's reasonably easy to add a pairing between "system_r" and "sandbox_t". Create a policy file (*.te extension) with the following content.
policy_module(sandbox_system, 1.0); require { type sandbox_t; } role system_r types sandbox_t;
If you name the file "sandbox_system.te", you can install it by running the following commands.
$ make -f /usr/share/selinux/devel/Makefile sandbox_system.pp $ sudo semodule -i sandbox_system.pp
Now, if you re-run seinfo, you should see the correct pairing.
$ sudo seinfo -rsystem_r -x | grep sandbox_t sshd_sandbox_t chrome_sandbox_t sandbox_t
Depending on your setup, you may need to add a few more rules to your policy file, but from this point forward, setroubleshoot and audit2allow will be able to do most of the work.
I hope this helps someone!