SELinux woes with Tomcat on CentOS 7.4


Update: make sure to check out the relevant post for CentOS 7.6, too!


After the SELinux Crash Course it’s time to put that knowledge to good use. Keep that article open coz I’ll reference it continuously. If you haven’t read that one yet, make sure you do before proceeding here. It’s totally to-the-point, I promise.

So anyway, even though the RHEL 7.4 release notes still fail to mention this, there’s a pretty crucial change regarding Tomcat: the previously unconfined process now runs under the tomcat_t context. That means anything excluded from the default policies will fail after the upgrade. And there’s plenty excluded. Well, if you don’t document your stuff, I’ll do it I guess.

Sidenote: SELinux isn’t the only issue with Tomcat on 7.4. 2 months after release, font detection is still broken. You would uninstall the stix fonts causing the issue, except for the fact that now Tomcat, and even OpenJDK depends on them. Neat, huh?

Anyway, let’s fix Tomcat.

Network access


Webapps have this habit of producing network traffic. They might even go as fas as to connect to other web resources. Apparently the folks at RH don’t think so. The logs might contain entries similar to these:

type=AVC msg=audit(1505388256.060:145109): avc:  denied  { name_connect } for  pid=5674 comm="java" dest=43 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:whois_port_t:s0 tclass=tcp_socket
type=AVC msg=audit(1505388350.195:145121): avc:  denied  { name_connect } for  pid=5813 comm="java" dest=587 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:smtp_port_t:s0 tclass=tcp_socket
type=AVC msg=audit(1505394939.649:48244): avc:  denied  { name_connect } for  pid=39262 comm="java" dest=1433 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:mssql_port_t:s0 tclass=tcp_socket

This is just a cherry-picked bunch for WHOIS, SMTP and MSSQL respectively, your mileage will vary so check the logs and act accordingly. After you collected the relevant ones, you can allow them with audit2allow and semodule as explained in the crash course.


Sometimes web services open non-standard ports to receive traffic. For example a Java Git server like Gerrit, GitBucket or Gitblit, which can receive pushes via SSH on port 29418, which seems to be the de facto standard for such purposes (but of course it could be any other port you set). Then the denied message will look like this:

type=AVC msg=audit(1505407348.112:4253): avc:  denied  { name_bind } for  pid=26397 comm="java" src=29418 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket

You can allow this the same way as the send part (audit2allow + semodule).

File access


If you happen to store your webapp files under a non-default folder, you’ll prolly get a lot of file access problems. Check out the available types which we can turn to with seinfo:


From this list, tomcat_var_lib_t sounds pretty good to me. Assuming your files are under the /data directory:

semanage fcontext -a -t tomcat_var_lib_t "/data(/.*)?"
restorecon -rv /data

Font cache

Java wants to refresh its font cache every now and then, but thanks to the recent changes it won’t succeed either. The audit log will contain entries like this:

type=AVC msg=audit(1505839995.508:158113): avc:  denied  { write } for  pid=9494 comm="java" name="fonts" dev="vda1" ino=1039773 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:usr_t:s0 tclass=dir
type=AVC msg=audit(1505840318.716:158118): avc:  denied  { add_name } for  pid=10016 comm="java" name="1.8.0_144" scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:usr_t:s0 tclass=dir
type=AVC msg=audit(1505841062.952:158123): avc:  denied  { create } for  pid=11027 comm="java" name="1.8.0_144" scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:usr_t:s0 tclass=dir

Let’s fix that too:

semanage fcontext -a -t tomcat_cache_t "/usr/share/tomcat/.java(/.*)?"
restorecon -rv /usr/share/tomcat/.java

Native libraries

Sometimes, clever folks decide to speed things up by bundling native libraries with Java libraries. This results in the .so file being extracted to a random place in Tomcat’s cache directory, then being executed. That’s right, Tomcat, and thus Java executes the cache. I couldn’t find a way to manually extract the native libs beforehand and tell Tomcat to look in that fixed folder before resorting to the cache, so I had no other choice than to allow the cache to be executed, however bad this may sound. The related log entry:

type=AVC msg=audit(1505394015.226:145396): avc:  denied  { execute } for  pid=17642 comm="java" path="/var/cache/tomcat/temp/" dev="vda1" ino=519697 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:tomcat_cache_t:s0 tclass=file

Again, allow it with audit2allow and semodule.

CIFS shares

If, for whatever reason, you store certain files Tomcat needs to access on a CIFS share, things will get a bit more complicated. On NFS you can specify the context via a mount option, but that’s not supported in mount.cifs. If it was a native filesystem, you could also relabel all the files to something that Tomcat can access, but again, that’s not the case here. So your best bet is to explicitly tell SELinux what it should allow by writing your own policy module. Example:

module tomcat-cifs 1.0;

require {
        type tomcat_t;
        type cifs_t;
        class dir { open getattr read search };
        class file { open getattr read };

allow tomcat_t cifs_t:dir { open getattr read search };
allow tomcat_t cifs_t:file { open getattr read };

This does exactly what it seems to do: allow processes running under the tomcat_t context to perform the listed actions on dirs and files existing under the cifs_t context. Save this as tomcat-cifs.te, then you can deploy it with the script I posted in the crash course: tomcat-cifs

Naturally, you can pick any other name, tomcat-cifs is just an example. Just make sure you modify the filename and the module definition as well.