9
votes

I'm following this tutorial to run Flask on an Nginx server. I've almost got it to work, wherein the page loads when SELinux is set as Permissive but shows a 502 Bad Gateway when SELinux is in the Enforcing mode.

Here are some relevant files:

myproject.ini

[uwsgi]
module = wsgi

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

die-on-term = true

myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=thisuser
Group=nginx
WorkingDirectory=/home/thisuser/public_html
Environment="PATH=/home/thisuser/thisuser_env/bin"
ExecStart=/home/thisuser/thisuser_env/bin/uwsgi --ini myproject.ini

[Install]
WantedBy=multi-user.target

thisuser.com.conf (Nginx configuration)

server {
    listen  80;

    server_name thisuser.com www.thisuser.com;
    access_log /home/thisuser/logs/access.log;
    error_log /home/thisuser/logs/error.log;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/home/thisuser/public_html/myproject.sock;
        try_files $uri $uri/ =404;
    }

}

The location of the Flask files+dirs is /home/thisuser/ and it's contexts are set like so:

[root@dev ~]# ls -ldZ /home/thisuser/
drwx--x--x. thisuser thisuser unconfined_u:object_r:user_home_dir_t:s0 /home/thisuser/
[root@dev ~]# ls -ldZ /home/thisuser/public_html/
drwxrwxr-x. thisuser thisuser unconfined_u:object_r:httpd_sys_content_t:s0 /home/thisuser/public_html/

The errors are as follows:

/var/log/audit/audit.log

type=AVC msg=audit(1498880449.864:156): avc:  denied  { write } for  pid=2667 comm="nginx" name="myproject.sock" dev="dm-2" ino=67165858 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(1498880449.864:156): arch=c000003e syscall=42 success=no exit=-13 a0=f a1=7f526e12e548 a2=6e a3=7ffdf52991b0 items=0 ppid=2666 pid=2667 auid=4294967295 uid=997 gid=995 euid=997 suid=997 fsuid=997 egid=995 sgid=995 fsgid=995 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)

and

/home/thisuser/logs/error.log

2017/06/30 23:40:49 [crit] 2667#0: *1 connect() to unix:/home/thisuser/public_html/myproject.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.1.15, server: thisuser.com, request: "GET / HTTP/1.1", upstream: "uwsgi://unix:/home/thisuser/public_html/myproject.sock:", host: "thisuser.com"

Steps tried:

  • tried changing the sock permissions to chmod-socket = 666
  • used setsebool -P httpd_can_network_connect 1
  • changed FROM user=thisuser to user=nginx
  • added thisuser to the nginx group

The only thing that works is changing SELinux to Permissive. Are there some changes/additions I can make, so that SELinux stays Enforcing?

Edit: http(s) has already been allowed in firewalld

[root@dev ~]# firewall-cmd --permanent --zone=public --add-service=https
[root@dev ~]# firewall-cmd --permanent --zone=public --add-service=http
[root@dev ~]# firewall-cmd --reload
5

5 Answers

1
votes

Not sure if the below will work but:

  • The socket needs to be associated with the httpd_sys_content_rw_t type so that processes associated with httpd_t can write it. create "myproject/runtime" and associate type httpd_sys_content_rw_t with "runtime" so that the socket gets created with the httpd_sys_content_rw_t type

  • Make systemd manually associate the uwsgi app process with the httpd_sys_script_t type so that the webapp is targeted by SELinux (not sure whether systemd is allowed to do this as is in the policy)

The gist is that:

avc:  denied  { write } for  pid=2667 comm="nginx" name="myproject.sock" dev="dm-2" ino=67165858 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=sock_file

Indicates that nginx process associated with type httpd_t was not allowed to write the myproject.sock sock file because it was associated with the "read only" httpd system content type.

It should have been associated with the "read and write" httpd system content type instead. ini:

[uwsgi]
module = wsgi

master = true
processes = 5

socket = /home/thisuser/public_html/myproject/runtime/myproject.sock
chmod-socket = 660
vacuum = true

die-on-term = true

unit:

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=thisuser
Group=nginx
WorkingDirectory=/home/thisuser/public_html/myproject
Environment="PATH=/home/thisuser/thisuser_env/bin"
ExecStart=/home/thisuser/thisuser_env/bin/uwsgi --ini myproject.ini
SELinuxContext=system_u:system_r:httpd_sys_script_t:s0

[Install]
WantedBy=multi-user.target

conf:

server {
    listen  80;

    server_name thisuser.com www.thisuser.com;
    access_log /home/thisuser/logs/access.log;
    error_log /home/thisuser/logs/error.log;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/home/thisuser/public_html/myproject/runtime/myproject.sock;
        try_files $uri $uri/ =404;
    }

}

Associate labels:

chcon -t httpd_sys_script_exec_t /home/thisuser/thisuser_env/bin/uwsgi
chcon -Rt httpd_sys_content_rw_t /home/thisuser/public_html/myproject/runtime
0
votes

You need to enable the port 80 on the semanage in order to be able to send traffic through that port.

semanage port -a -t http_port_t -p tcp 80

you may need to enable the port on firewalld too:

firewall-cmd --zone=public --permanent --add-port=80/tcp
0
votes

Change the uwsgi.ini file as following:

[uwsgi]
plugins = python
project = /home/thisuser/public_html/myproject
chdir = %(project)
module = application  # This is the application.py in myproject, you should replace it.
callable = app  # This is the call module name of application.py you run
master = true
process = 5
socket = /home/thisuser/public_html/myproject/runtime/myproject.sock
chmod-socket = 666
vacumm = true
die-on-term = true
0
votes

Try to add this to your myproject.ini file :

plugins = python
project = /home/thisuser/public_html/myproject
chdir = %(project)
0
votes

Thank you all for your answers. My goal was to run Python Flask application with NGINX through uWSGI with SELINUX enabled with enforcing mode. I used Unix socket initially but NGINX cannot access socket file when SELINUX is enforced. There is a workaround to set httpd_t in permissive using the command "semanage permissive -a httpd_t". But I wanted no security loophole so only option is to use HTTP as protocol for uwsgi to talk to my application. This is how I made it happen using the ideas suggested in this thread:

myapp.ini file

;This is uWSGI config file
[uwsgi]
module = wsgi:app
; Allow the Python processes to spawn threads
; uWSGI disables Python threads by default
enable-threads = true
callable = app  # This is the call module name of application.py you run

master = true
processes = 1

plugins = python3
project = /usr/local/myapp/bin
chdir = %(project)

socket = 127.0.0.1:3031

chmod-socket = 660
chown-socket=bma:nginx
uid = bma
gid = nginx

socket-timeout = 3600
harakiri = 3600

; remove the socket when the process stops
vacuum = true

; uWSGI will kill the process instead of reloading it
die-on-term = true

My NGINX Conf has:

location /rest/ {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:3031;
}

To launch the uWSGI, I used this command (from my .service file)

/usr/local/bin/uwsgi --socket 127.0.0.1:3031 --ini bma.ini

Finally run the following command to allow httpd to act as a relay:

# setsebool -P httpd_can_network_relay 1

Thats all I had to do. SELinux is fully enforcing. No need to set httpd_t in permissive mode.