Running gunicorn from a Python virtual environment with systemd and selinux
There are a lot of tutorials out there for running a gunicorn server on systemd, but I wasn't able to find one that actually told me how to do so in selinux. Since I'd never used selinux before, this relatively simple task turned into hours of trial and error, but my loss is your gain. YMMV dependent on how custom your selinux setup is, but this worked for me:
Assuming your project is in /www/myproject
:
/www/myproject/gunicorn_config.py
:
bind = 'unix:/run/gunicorn.sock'
worker_class = 'sync'
workers = 4
timeout = 60
/etc/systemd/system/gunicorn.socket
:
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
/etc/systemd/system/gunicorn.service
:
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
# the specific user that our service will run as
User=someuser
Group=someuser
WorkingDirectory=/www/myproject
ExecStart=/www/myproject/.venv/bin/gunicorn -c /www/myproject/gunicorn_config.py applicationname.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Once these files are in place, just run:
systemd enable gunicorn.socket
systemd enable gunicorn.service
systemd start gunicorn
# label gunicorn within the virtualenv as a bin script
semanage fcontext -a -t bin_t "/www/myproject/.venv/bin(/.*)?"
restorecon -R -v .venv/bin
# label the gunicorn socket as a socketfile
semanage fcontext -a -t httpd_var_run_t /run/gunicorn.sock
restorecon -v /run/gunicorn.sock
systemd restart gunicorn
If you're operating on a super custom system, the key is just to set labels that the systemd
process can access for gunicorn (in my case systemd was of type init_t
and gunicorn needed to be of type bin_t
, and that your gunicorn.sock
file is labeled correctly as a socket. You can find labels for similar processes with semanage fcontext --list | grep 'process_type'
, so if you know a process or socket that's already running in your desired context, search for and apply it. You can get more verbose explanations of why selinux might be blocking execution on a file with audit2allow -aw
- use this to find the context that's trying to access a blocked file.
You'll need to fiddle with your nginx or Apache setup to make your server accessible from a client, which is out of scope for this little snippet, but this should allow you to run gunicorn with systemd without selinux throwing any type errors.
Member discussion