14

I have nginx forwarding requests to gunicorn via a unix socket at /run/gunicorn/socket. By default, this behavior is not permitted by SELinux:

grep nginx /var/log/audit/audit.log
type=SERVICE_START msg=audit(1454358912.455:5390): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=nginx comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
type=AVC msg=audit(1454360194.623:7324): avc:  denied  { write } for  pid=9128 comm="nginx" name="socket" dev="tmpfs" ino=76151 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=sock_file
type=SYSCALL msg=audit(1454360194.623:7324): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=1f6fe58 a2=6e a3=7ffee1da5710 items=0 ppid=9127 pid=9128 auid=4294967295 uid=995 gid=993 euid=995 suid=995 fsuid=995 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1454361591.701:13343): avc:  denied  { connectto } for  pid=9128 comm="nginx" path="/run/gunicorn/socket" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=unix_stream_socket
type=SYSCALL msg=audit(1454361591.701:13343): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=1f6fe58 a2=6e a3=7ffee1da5950 items=0 ppid=9127 pid=9128 auid=4294967295 uid=995 gid=993 euid=995 suid=995 fsuid=995 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)

Everywhere I look (e.g., here and here), the instructions to enable this say to make a request to nginx, have the request be denied by SELinux, then run audit2allow to permit future requests. I can't figure out any chcon or semanage command that allows this behavior explicitly.

Is this the only way? It seems ridiculous that you can't set up a policy that allows nginx to write to a socket without first having an attempt denied and then running a tool that enables things that were denied. How do you know exactly what is being enabled? How is this supposed to work if your setting up machines under automation?

I'm using CentOS 7.

drs
  • 345
  • 1
  • 3
  • 11

3 Answers3

27

It seems ridiculous that you can't set up a policy that allows nginx to write to a socket without first having an attempt denied and then running a tool that enables things that were denied.

Well no, SELinux is Mandatory Access Control, things are denied by default and you have to explicitly allow something. If the policy authors have not considered a particular (franken) stack or a daemon's authors have not made it SELinux aware and written policy for it then you are on your own. You have to analyze what your services are doing and how they are interacting with SELinux and come up with your own policy to allow it. There are tools available to help you audit2why, audit2allow etc.

... Is this the only way?

No, but it depends upon what you are trying to do and how you are trying to do it as to what the solution is. For example you may want to bind nginx (httpd_t) to port 8010 (unreserved_port_t). When you start nginx it fails

Starting nginx: nginx: [emerg] bind() to 0.0.0.0:8010 failed (13: Permission denied)

and you (eventually) look in the audit log and find

type=AVC msg=audit(1457904756.503:41673): avc:  denied  { name_bind } for
pid=30483 comm="nginx" src=8010 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket

You might run this through audit2alllow and naively accept it's findings

allow httpd_t port_t:tcp_socket name_bind;

which then allows httpd_t to connect to any tcp port. This may not be what you want.

You can use sesearch to investigate the policy and see what port types httpd_t can name_bind to

sesearch --allow -s httpd_t | grep name_bind
...
allow httpd_t http_port_t : tcp_socket name_bind ;
allow httpd_t http_port_t : udp_socket name_bind ;
...

Amongst other types http_t can bind to http_port_t. Now you can use semanage to dig a bit deeper.

semanage port -l | grep http_port_t
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
...

Port 8010 isn't listed. As we want nginx to bind to port 8010 it is not unreasonable to add it to the http_port_t list

semanage port -a -t http_port_t -p tcp 8010

Now nginx will be allowed to name_bind to port 8010 nd not every tcp port as above.

How do you know exactly what is being enabled?

Changes to policy are fairly easy to read, running your messages above through audit2allow we get

allow httpd_t httpd_sys_content_t:sock_file write;
allow httpd_t initrc_t:unix_stream_socket connectto;

which seem fairly self explanatory.

The first of those refers to the file with inum 76151. You can use find to get it's name (find / -inum 76151) and then use semanage fcontext -a -t ... to change the policy and restorecon to fix the context.

The second relates to /run/gunicorn/socket which again has the wrong context. Using sesearch we can see that http_t can connectto unix_stream_sockets of type (amongst others) http_t. So we can change the context accordingly for example

semanage fcontext -a -t httpd_t "/run/gunicorn(/.*)?"
restorecon -r /run

This sets the context of /run/gunicorn and the tree| files below it to httpd_t.

How is this supposed to work if your setting up machines under automation?

You need to analyse the system and make appropriate changes in test. You then use your automation tools to deploy the changes, puppet and ansible have support for this.

Of course you can do it all in production with SElinux set to permissive. Collect all the messages, analyse them decide on your changes and deploy them them.

There is much more to know about SELinux but that's the limit of my skills, Michael Hampton is better and Mathew Ife is way better again, they may have more to add.

user9517
  • 114,104
  • 20
  • 206
  • 289
  • 1
    Your advice is thorough and gets me much closer to solving these issues myself, though still leaves me a bit short. `allow httpd_t httpd_sys_content_t:sock_file write;` is not as self-explanatory to me as you hoped. What is this saying the policy on that file needs to be changed to (i.e., what goes after `-t` in the `semanage` command? – drs Mar 14 '16 at 00:30
  • Also, I get usage instructions when using your `semanage` commands directly. I need to add an `--add` argument. – drs Mar 14 '16 at 00:30
  • Actually, I should also say that after changing the type of the socket file to `httpd_var_run_t` as Michael Hampton noted below, the `audit2allow` message is: `allow httpd_t var_run_t:sock_file write;` – drs Mar 14 '16 at 00:32
  • It looks like you set it to `var_run_t` not `httpd_var_run_t`. – user9517 Mar 14 '16 at 06:37
  • @lain, hmm.. no dice. Now `audit2allow` says `allow httpd_t var_run_t:sock_file write;` – drs Mar 14 '16 at 13:15
  • I'm not doing tech support for you via SF which is a Q&A site. You asked some questions about SELinux, I have answered them. _You_ now have a steep learning curve. BTW your last too comments are the same. – user9517 Mar 14 '16 at 13:55
3

The type you want to use is not httpd_sys_content_t. This is for static files that the web server is meant to serve to user agents.

For a socket used for interprocess communication, the type you are looking for is httpd_var_run_t.

Though, please note that because you ran gunicorn unconfined, there may be additional issues communicating with it.

Michael Hampton
  • 237,123
  • 42
  • 477
  • 940
  • 3
    Thanks! It looks like this took care of one of the SELinux issues. Any pointers on how to set up gunicorn (or any other service) confined? – drs Mar 14 '16 at 00:16
1

I tried the previous answers without success, in my case I'm using a nginx server as frontend for a uwsgi application using unix sockets to communicate them, my O.S. It's a Fedora server 26.

The unix sockets are created in the directory /var/local/myapp:

/var/local/myapp/server.sock    
/var/local/myapp/stats.sock

To configure SELinux I had to add the context type: httpd_sys_rw_content_t

semanage fcontext -at httpd_sys_rw_content_t "/var/local/myapp(/.*)?"
restorecon -R -v '/var/local/myapp' 
rsc1975
  • 81
  • 1
  • 8