SELinux Policies for Mono (.NET) Web Applications

I’m curious to see how production ready Mono on Linux is. What better way to find out than to take an existing .NET web app and run it on Mono? I tried – the web app wouldn’t even run – reporting an internal server error. After a little delving it turns out that SELinux was blocking Apache mod-mono from spawning the mono process. From /var/log/messages:

May 14 15:18:29 slave kernel: type=1400 audit(1374675509.274:34550): avc:  denied  { search } for  pid=5744 comm="mono" name="/" dev=dm-2 ino=2 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:file_t:s0 tclass=dir
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.325:34551): avc:  denied  { search } for  pid=5744 comm="mono" name="/" dev=dm-2 ino=2 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:file_t:s0 tclass=dir
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.325:34552): avc:  denied  { search } for  pid=5744 comm="mono" name="/" dev=dm-2 ino=2 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:file_t:s0 tclass=dir
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.610:34553): avc:  denied  { search } for  pid=5744 comm="mono" name="/" dev=dm-2 ino=2 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:file_t:s0 tclass=dir
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.610:34554): avc:  denied  { search } for  pid=5744 comm="mono" name="/" dev=dm-2 ino=2 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:file_t:s0 tclass=dir
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.611:34555): avc:  denied  { search } for  pid=5744 comm="mono" name="/" dev=dm-2 ino=2 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:file_t:s0 tclass=dir
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.657:34556): avc:  denied  { execmem } for  pid=5744 comm="mono" scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=unconfined_u:system_r:httpd_sys_script_t:s0 tclass=process
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.658:34557): avc:  denied  { execmem } for  pid=5744 comm="mono" scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=unconfined_u:system_r:httpd_sys_script_t:s0 tclass=process
May 14 15:18:29 slave kernel: type=1400 audit(1374675509.863:34558): avc:  denied  { ptrace } for  pid=29877 comm="gdb" scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=unconfined_u:system_r:httpd_sys_script_t:s0 tclass=process

I was later to find out that this was only the first thing SELinux would prevent it from doing!

The mono guys recommend that you switch SELinux off for mono apps, but that won’t really cut it for a production system. I wanted to create an SELinux policy that could be applied to allow the mono web app to work. SELinux is very fine-grained in the permissions that can be applied, so I can see why the mono guys won’t provide a standard mono SELinux policy to install. The policy depends very much on what the individual mono app needs to be able to do. If you tried to create an overall policy for mono apps it would have to allow so many permissions that you might as well switch SELinux off.

So how to derive a custom SELinux policy for a mono app?

To start with, try to run the app. Any SELinux enforcement messages are logged to the system log. The SELinux audit2allow command is helpful in deriving policy rules to resolve the enforcement errors.

cat /var/log/messages | audit2allow

The output will give you a list of rules to apply, e.g.:

[Bren@slave devel]$ sudo cat /var/log/messages | audit2allow

#============= httpd_sys_script_t ==============
allow httpd_sys_script_t file_t:dir search;
allow httpd_sys_script_t self:process execmem;
allow httpd_sys_script_t self:process ptrace;
allow httpd_sys_script_t tmpfs_t:file unlink;

To create a policy module start by creating a type enforcement (.te) file and enter the audit2allow discovered permissions into that file.

vi /usr/share/selinux/devel/mod_mono.te

After making any alteration to the .te file the selinux policy module needs to be re-compiled.

make /usr/share/selinux/devel/mod_mono.pp

Install the new policy module as follows (you’ll need root permissions to do these things).

sudo semodule -i /usr/share/selinux/devel/mod_mono.pp

When you do all that and run the app again, it should get further, but will only encounter new enforcement errors. Unfortunately I found no easy of discovering all the permissions for an app but to iteratively test it, run audit2allow, add new rules to the .te file, re-compile, and try the app again.

Some rules will be common to all mono web apps, and some as unique as the web app itself. It is a process that will be determined by the individual app’s requirements. For example, does it use pipes or TCP ports to connect to a database server? If new features are added to the app in later releases, it could require addition policy rule changes to work.

In the end, here is the full policy I needed to get my web app functionality to work as expected. (Note that the samba_share_t permissions are needed because my web app views are locate on a development samba share.)

policy_module(mod_mono,1.0.0)

gen_require(`
   type lib_t;
   type tmp_t;
   type file_t;
   type tmpfs_t;
   type mono_exec_t;
   type samba_share_t;
   type inotifyfs_t;
   type httpd_t;
   type httpd_sys_script_t;
   type httpd_sys_rw_content_t;
   type postgresql_port_t;
   type port_t;
   class process { execmem ptrace };
   class capability { sys_admin ipc_owner };
   class sock_file { write create unlink };
   class sem create;
   class dir { open search getattr read write add_name };
   class filesystem getattr;
   class file { open read getattr execute_no_trans };
   class tcp_socket { name_connect };
')

#============ httpd_sys_script_t =============
allow httpd_sys_script_t self:process execmem;
allow httpd_sys_script_t file_t:dir search;
allow httpd_sys_script_t inotifyfs_t:dir { search read };
allow httpd_sys_script_t samba_share_t:dir { open search getattr read };
allow httpd_sys_script_t samba_share_t:file { open read getattr };
allow httpd_sys_script_t tmpfs_t:dir { search read write open add_name remove_name };
allow httpd_sys_script_t tmpfs_t:filesystem getattr;
allow httpd_sys_script_t tmpfs_t:file { read write open create unlink } ;
allow httpd_sys_script_t postgresql_port_t:tcp_socket name_connect;
allow httpd_sys_script_t port_t:tcp_socket name_connect;

allow httpd_t lib_t:file execute_no_trans;
allow httpd_t mono_exec_t:file { read execute_no_trans };
allow httpd_t self:process ptrace;
allow httpd_t tmp_t:sock_file { write create };
allow httpd_t httpd_sys_rw_content_t:sock_file unlink;
allow httpd_t self:capability { sys_admin ipc_owner };
allow httpd_t file_t:dir search;
allow httpd_t samba_share_t:dir search;
allow httpd_t samba_share_t:file { read open getattr };

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.