We use proxy servers all the time: we have a main server (eg http://edwards.sdsu.edu/) that serves applications (eg. http://edwards.sdsu.edu/GenomePeek) but the application itself runs on different hardware than the webserver.
Here, we show how to host a Django project on a proxy server using the apache web server and make it accessible.
First, you need your Django application up and running in your development location. Second, you need an apache server running. You should ensure that you have installed mod_wsgi
(for that, your operating system distro probably has a library you can install [e.g. with apt
or dnf
]). On the host that is exposed to the internet, you need mod_proxy
installed as well.
I am not going to show you how to do those here, there are plenty of tutorials on setting up servers. This is explicitly how to set the widgets on your host, your server, and within Django.
My setup:
- The external server: https://edwards.sdsu.edu is exposed to the internet
- The server behind the proxy: http://12.34.56.78 (not its real IP addresss!)
- On that server (12.34.56.78) there is a user called
rob
with a Django web application calledphages
in/home/rob/phages
- We are going to make the web application appear on https://edwards.sdsu.edu/phages (it doesn’t appear there at the moment)
On the external server
This is the machine exposed to the internet, and is just a proxy server. It is going to accept the requests to https://edwards.sdsu.edu/phages and return the pages from the Django web application.
In /etc/httpd/conf.d
create a file called proxypass.conf
that has these two lines:
ProxyPass /phages http://130.191.93.89/phages ProxyPassReverse /phages http://130.191.93.89/phages
And now restart the httpd
server
service httpd restart
or
systemctl restart httpd
On the application server
The main things that we need to do are:
- Set up the application server (optional)
- Set up mod_wsgi for the server
- Change permissions so that the apache user can access the application
- Configure the application
Set up the application server
This is a completely optional step. I am starting from a plain CentOS8 install and had to make a couple of tweaks:
# install apache and python virtual environment dnf install httpd mod_wsgi virtualenv # allow access through the firewall firewall-cmd --zone=public --permanent --add-service=http --add-service=https firewall-cmd --reload firewall-cmd --list-all # install elinks to test the server yum --enablerepo=PowerTools install elinks # start and enable the httpd server systemctl start httpd.service systemctl enable httpd.service
Set up mod_wsgi
There are two good tutorials that help with this set up, on the Django documentation page and on Digital Oceans. Most of the information here comes from those two sites, but I had to tweak some issues.
We are going to set up the Django wsgi call as a Daemon Process
In /etc/httpd/conf.d
create a file called django.conf
WSGIDaemonProcess phages python-path=/home/rob/phages:/home/rob/phages/venv/lib/python3.6/site-packages WSGIProcessGroup phages WSGIScriptAlias /phages /home/rob/phages/phages/wsgi.py Alias /static /home/rob/phages/static/ <Directory /home/rob/phages/static> Require all granted </Directory> <Directory /home/rob/phages/phages> <Files wsgi.py> Require all granted </Files> </Directory>
This sets up several items:
WSGIDaemonProcess phages python-path=/home/rob/phages:/home/rob/phages/venv/lib/python3.6/site-packages
is the WSGI Daemon. This provides it access to the root level of the phages application, and also the specific location of the site-packages in the virtual environment for this applicationWSGIProcessGroup phages
just sets up a specific process group for this application. It means that if I have two web applications on this server, they won’t stomp over each other (see the Django documentation for more information)WSGIScriptAlias /phages /home/rob/phages/phages/wsgi.py
tells the server that if a request is made to/phages
to redirect it to thewsgi.py
script in my application (this is what Django uses to process your requests).Alias /static /home/rob/phages/static/
sets up any static pages in your application. Don’t forget to runcollectstatic
(see below)<Directory /home/rob/phages/static>
provides access to all the files to everyone<Directory /home/rob/phages/phages>
only provides access towsgi.py
to everyone
Set up permissions
We need to set the permissions and ownership of some of our files so that apache can access them. Note, I strongly recommend you ls -l
each of these before and after chmodding
them to check the permissions!
Add the apache
user to your user’s group
usermod -a -G rob apache
Provide access to the users home directory
chmod 710 /home/rob
Provide access to the sqlite database in the web application
chmod 664 ~rob/phages/db.sqlite3
Allow the user to write to the database
chown :apache /home/rob/phages/db.sqlite3
Allow apache to access the project
chown :apache /home/rob/phages
Enable apache
to access the home directories in SELinux. If you are using SELinux (you should), you will need to set the appropriate boolean. First, find out which one:
grep httpd /var/log/audit/audit.log | audit2allow
and then check and set the status of that boolean:
getsebool httpd_read_user_content setsebool httpd_read_user_content on getsebool httpd_read_user_content
You may need to revisit this step later if you get an error when running the apache server.
Set additional permissions
Another approach to checking SELinux booleans is to use
audit2why -a | less
This will show you more detailed error messages. One in particular is worth paying attention to if it refers to a library (for example, a file ending cpython-36m-x86_64-linux-gnu.so
)
Missing type enforcement (TE) allow rule.
This probably means that you need to set the appropriate settings to ensure that apache is allowed to execute this code block.
chcon -R -h -t httpd_sys_script_exec_t ./venv/lib/python3.6/site-packages/FastaValidator.cpython-36m-x86_64-linux-gnu.so
Set up the Django application
Clone your repository using git
git clone
Set up a new virtual environment for your application and then activate it and use pip to install the requirements
cd phages virtualenv venv source venv/bin/activate pip3 install -r requirements.txt
Note at this point you might need to find your site-packages
to confirm the entries you added to django.conf
above:
ls -d $PWD/$(find . -name site-packages -printf '%P\n')
Now you should be able to use the normal manage.py
to run a server on the local machine, changing the IP address as appropriate
python3 manage.py runserver 12.34.56.78
and then test that server. Note, if you are using a terminal I recommend using elinks
to access the local server. It will show you that you have access, although some of the javascript and other things may not work properly.
You may need to edit ALLOWED_HOSTS
in settings.py
to make sure you can access the machine. You will need to add your outside server (the one that has the proxy on it) to ALLOWED_HOSTS
too!
We will collect all the static files so that they are served properly:
python3 manage.py collectstatic
Now you should be able to access your site using mod_wsgi
and apache.
Start at your base URL:
links http://12.34.56.78
If you can not access the website:
- Check you
/var/log/httpd/error_log
while loading the page. If you see aclient denied by server configuration
then:- Check the file that is actually trying to be accessed by that error
- Use
audit2allow
to make sure SELinux is not blocking access (see above)
- restart the httpd server to make sure all changes have been applied
systemctl restart httpd.service
Cleaning up Django
Reset your SECRET_KEY and turn of DEBUG
The SECRET_KEY
is used in a variety of places to control access, but it is used on a per-instance basis. You should be able to reset your secret key between runs of the server.
First, generate a new secret key by starting a python console in your virtual env that has Django installed:
from django.core.management.utils import get_random_secret_key print(get_random_secret_key())
This will give you a new secret key that you can use in your application. We will put this in local_settings.py
which, by default, is ignored in version control. Create a file called in your applications (in the same directory as settings.py
) called local_settings.py
that has these two lines:
DEBUG = False SECRET_KEY = '(v3u!gc)nf-qbwka=2%p!rsmvyjs9w8mwzv!sd1fu)w2#0ch%_'
Note: Change the SECRET_KEY to be one that is not published on a website!
Next, edit settings.py
and remove the two lines that define DEBUG and SECRET_KEY and instead, add this line
from .local_settings import SECRET_KEY, DEBUG
Now you can save settings.py
in version control with no issues. You just need to remember to make a local_settings.py
file on your development machine!
Note: You may also want to move ALLOWED_HOSTS
from settings.py
to local_settings.py
too!
Check for deployment
Start by running the django built in tests for deployment:
python manage.py check --deploy
Writing to the server
The directions above are great for a standalone server that is going to only read from the SQL database, however there are some additional considerations that you should think about if you intend to write to the server.
First, SELinux (for good reasons) makes it very hard for apache to write to a user’s home directory. There are workarounds online, but the more appropriate solution is to move your application out of user space and into its own space, for example under /var/www/
.
Once you have moved the directory, you will need to set the SELinux settings:
chcon -Rv --type=httpd_sys_rw_content_t /var/www/phages/
and ensure the permissions are set as above, and then you should be able to write to db.sqlite3