Test an SELinux Command Context Safely with runcon

Before trusting an SELinux policy change in production, runcon lets you test one throwaway command under a specific context. This guide covers inspecting the current context and launching a command with a specified one. Allow about ten minutes. It needs GNU coreutils and an SELinux-enabled host with a context that the policy permits. The inspection step is harmless; launching a process under a changed context is security-sensitive and should be tested with a short-lived command first.

Checkpoint: by the end, you should know whether this host can perform a transition, what exit status means when it cannot, and how to keep the child command separate from runcon's options.

1. Confirm the installed command

This guide follows the installed GNU coreutils 9.4 manual on this machine. Check the executable and package version before relying on a script or deployment note:

$ command -v runcon
/usr/bin/runcon
$ runcon --version
runcon (GNU coreutils) 9.4
$ dpkg-query -W -f='${Package} ${Version}\n' coreutils
coreutils 9.4-3ubuntu6.3

The package revision is distribution-specific. The important version-specific point here is the installed command's GNU coreutils version, while the available security contexts and transitions come from the host's SELinux policy and kernel.

2. Inspect the current context without starting a command

With neither a complete context nor a command, runcon prints the current security context. This is the safest first check because it does not ask for a transition:

$ runcon
unconfined

Your output may contain several colon-separated SELinux fields instead. Do not copy the example value into a production command unless it is the exact context you intend to use on this host. An output such as unconfined describes the current process; it does not demonstrate that a requested confined context exists.

If the command reports that runcon may be used only on an SELinux kernel, stop at this checkpoint. The utility cannot create an SELinux transition on a host without the required kernel support. Do not try to fix that by adding sudo.

3. Try a harmless complete-context launch

The first positional argument is treated as a complete context when none of -c, -t, -u, -r or -l is present. Use a context copied from a trusted local policy or an administrator's documented test procedure, not a guessed string. A command that exits immediately keeps the test bounded:

$ CONTEXT='USER:ROLE:TYPE:LEVEL'
$ runcon "$CONTEXT" /usr/bin/true
$ printf 'runcon status: %s\n' "$?"
runcon status: 0

The value shown is a placeholder, not a valid context to paste. Replace it only after checking the policy on the target host. A status of 0 means that runcon launched true and that true returned 0. It does not prove that a longer-running program will have the access you expect.

On this machine, the local check stops earlier because SELinux support is unavailable:

$ runcon unconfined /usr/bin/true
runcon: runcon may be used only on a SELinux kernel
$ printf 'runcon status: %s\n' "$?"
runcon status: 125

Exit status 125 means that runcon itself failed. It is different from the child's status. The exact diagnostic is environment-dependent, so treat the message as the useful part of this check.

4. Modify only the parts of a context you need

The option form starts with the command, then changes one or more fields relative to the current or transitioned context. The manual provides these fields:

For example, the shape of a type test is:

$ TYPE='DOCUMENTED_TYPE_FROM_POLICY'
$ runcon --type="$TYPE" /usr/bin/true
$ printf 'runcon status: %s\n' "$?"
runcon status: 0

Use a real type from the policy before running this. The placeholder is deliberately not a claim about what your system permits. A carefully chosen context can still fail because the policy rejects the transition, the field combination is invalid, or the kernel does not provide SELinux support.

5. Keep child arguments after the command

Everything after the command name is passed to that command. Quote values that may contain spaces, and keep untrusted text out of the context options. This example prints a fixed argument from inside the child:

$ runcon -- /usr/bin/printf '%s\n' 'child-argument-ok'
child-argument-ok

The -- marker makes the boundary visible when the command or its arguments begin with a hyphen. It does not bypass SELinux checks and it does not make a guessed context safe.

6. Diagnose failures without escalating blindly

Exit status 126 means the command was found but could not be invoked. Exit status 127 means the command could not be found. If runcon itself fails, it returns 125. Otherwise it returns the child's status, including a non-zero status deliberately returned by that child.

Check the executable and policy inputs before using elevated privileges:

$ command -v /usr/bin/true
/usr/bin/true
$ runcon --help
$ runcon --version

Security boundary: do not run an application as root merely to make a transition succeed. A different identity can change file access, audit records and the impact of a policy mistake. If a service must use runcon, test the exact service command as its normal account, record the previous command for rollback, and make the change during a maintenance window. The examples here change no persistent policy or service configuration, so there is nothing to undo after the short-lived test.

Done means