3

In AlmaLinux9 I have a small ExectStart systemd service which runs a java process (Java Temurin 21). I am getting SELinux errors due to the service not having the execmem permission (already checked the AVC logs).

The audit2allow tool recommends giving the unit_t domain execmem permissions, this is the recommended policy to install:

module my-java 1.0; require { type init_t; class process execmem; } #============= init_t ============== allow init_t self:process execmem; 

I do not want to give every init_t process execmem privileges so how do I define a custom SELinux policy / policy transition that just applies to this specific service?.

Also the java executable works just fine if I run it directly, this are the associated SELinux domains.

system_u:object_r:lib_t:s0 /usr/lib/jvm/temurin-21-jre/bin/java 
0

1 Answer 1

2

From your question I am making the following assumptions:

  • Your service is running as init_t
  • Your service's unit file has ExecStart=/usr/lib/jvm/temurin-21-jre/bin/java -jar something.jar
  • /usr/lib/jvm/temurin-21-jre/bin/java's type is lib_t.

That being the case, the first thing to fix is to ensure your service is not running as init_t. init_t is supposed to be used only by the init system. Individual services are supposed to use types created specially for them (e.g., web servers should run as httpd_t). There is a type for user services, called unconfined_service_t and, as its name implies, it's not subject to much confinement.

So, why is your service running as init_t? It's because the executable is labelled with lib_t and there is no type transition rule causing an automatic transition from init_t to anything else when an executable labelled with lib_t is executed:

# sesearch -T -s init_t -c process -t lib_t | wc -l 0 

Let's search for transition rules that would allow init_t to transition to unconfined_service_t:

$ sudo sesearch -T -s init_t -c process -D unconfined_service_t type_transition init_t bin_t:process unconfined_service_t; type_transition init_t usr_t:process unconfined_service_t; 

It follows that if your executable is labelled with bin_t or usr_t then your service will end up running as unconfined_service_t. This makes sense, as these types are used for files in /usr/bin and /usr/libexec and so on. So the next thing to solve is ensuring that the executable is labelled with one of these types.

This is where one of the most important guidelines for managing a SELinux system comes into play. The summary is: don't put things in weird places.

For instance, httpd_t is allowed to read files labelled httpd_sys_content_t, and the default file system labelling is configured so that this label is applied to files in /var/www by default. It follows that it's a bad idea to put files intended to be served by a web server outside of this directory. That is not to say that you can't do it; only that if you insist on doing it then you've made extra work for yourself in order to ensure that the files get the right labels.

The same applies to your JVM. It's in an unconventional location, and therefore the default file system labels for its executables is lib_t (wrong) instead of bin_t (correct). To demonstrate this, compare the outputs of the following two commands, which look up the specified paths to determine their default file contexts. The first path is shipped by java-21-openjdk-headless, the second is from your JVM:

$ matchpathcon /usr/lib/jvm/java-21-openjdk-21.0.5.0.11-1.fc41.x86_64/bin/java /usr/lib/jvm/java-21-openjdk-21.0.5.0.11-1.fc41.x86_64/bin/java system_u:object_r:bin_t:s0 $ matchpathcon /usr/lib/jvm/temurin-21-jre/bin/java /usr/lib/jvm/temurin-21-jre/bin/java system_u:object_r:lib_t:s0 

So if your JVM is in the wrong location, where do we need to move it? We can figure this out by examining the list of paths and their default file contexts to find out:

$ sudo semanage fcontext -l | grep /usr/lib/jvm | grep bin_t /usr/lib/jvm/java(.*/)bin(/.*) all files system_u:object_r:bin_t:s0 

So if you're OK with moving your JVM, all you need to do is put it into a patch that matches that regular expression, run restorecon -rv on /usr/lib/jvm (which resets the labels of the files on disk to match their defaults) and restart your service, and it should now properly transition to unconfined_service_t. We can test a suitable file path with matchpathcon:

$ matchpathcon /usr/lib/jvm/java-temurin-21-jre/bin/java /usr/lib/jvm/java-temurin-21-jre/bin/java system_u:object_r:bin_t:s0 

If you're not able to move the JVM then you can configure the default filesystem labelling for the executables in their existing path with a command like semanage fcontext --add -t bin_t /usr/lib/jvm/temurin-\d+-jre/bin(/.*), followed by a restorecon and restart of your service as described above.

So now that your service is running with the proper label (unconfined_service_t), let's check if it's allowed the execmem permission:

# sesearch -A -s unconfined_service_t -c process -p execmem allow unconfined_service_t unconfined_service_t:process execmem; [ deny_execmem ]:False 

It seems that it will be allowed, as long as the boolean deny_execmem is not set. This boolean is described in the unconfined_service_selinux(8) man page:

If you want to deny user domains applications to map a memory region as both executable and writable, this is dangerous and the executable should be reported in bugzilla, you must turn on the deny_execmem boolean. Disabled by default.

setsebool -P deny_execmem 1

You can check if it's enabled on your system with getsebool deny_ptrace; as long as it's off, SELinux won't prevent your service, running as unconfined_service_t, from mapping its pages as both writable and executable.

(I'm surprised actually that this is not disabled by default in 2024...)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.