Open SomeAB opened 1 year ago
The recommendations in the first StackOverflow post are still valid.
As to Thomas Ward's post, do keep in mind that that post relates to static HTML or maybe PHP code. You should never put Python project code of your application under the Apache DocumentRoot
directory (eg., /var/www/html
, but can differ based on Linux/Apache distribution). The only exception to this might be the single WSGI script file which loads your application. The code in that should be kept to an absolute minimum and should only import the application
object from the real WSGI application entry point for your Python web application, kept outside of the Apache document root.
The location of the virtual environments in the third post are an example only and not a recommendation. You can keep it collocated with your project code if you want.
Important thing is not to rely on anything under your personal home directory. Do not open up permissions on your home directory or use groups to provide access to it. So do not put a distinct virtual environment under your home directory and do not use per user Python package installs.
@GrahamDumpleton
So following on your advice, I plan to do the following:
Am I missing anything?
If when you say "non-admin" you mean a user that does not have "sudo" access, then yes. It should not be a user that has any extra privileges.
As to a WSGI script file, it doesn't need to be, and shouldn't be under the Apache document root (/var/www/html) if possible as then it could be viewed as a static raw file.
Create a separate directory (eg., /var/www/wsgi-scripts) under the Apache root and the WSGIScriptAlias
can refer to it from that separate directory.
The only time would put the WSGI script file under the Apache document root is if you had no choice and had to use AddHandler
directive for some reason, which wouldn't happen in your case.
@GrahamDumpleton yeah, thats very helpful & clear. But I ran into an interesting problem/situation as a result:
I created a new user called djangouser
Logged in as djangouser and created the 2 folders:
/home/djangouser/mypyenv
-- to store python environments in one place
/home/djangouser/djangoprojects
-- to store Django projects
where
/home/djangouser/djangoprojects/project_01/project_01/wsgi.py
is the location of the wsgi file of django sample project
Now I'm guessing the since I'm going to run Django as this non-privileged user, now I need to install HTTPD/Apache for this user, and mod_wsgi as well? or maybe not? I already installed it in the main administrator account ..
I'm hoping to not to have to start all over again, since I already have httpd+mod_wsgi installed as the admin user called someab
in my case.
sudo systemctl start httpd
.. and nothing is required. Thats basically the command to start a service on Fedora/Redhat Linux.
PS: Please rest assured, I am compiling all the knowledge you are providing into a video tutorial/walkthrough .. to help those who may want to install the whole stack or just a part of it. I am hoping it would be helpful to the community at large. Thank you so far!
You don't need a separate Apache/httpd installation or instance.
When systemd
starts Apache/httpd, it starts it as root
, but after initial configuration and forking of child processes it drops privileges and the Apache worker processes run as a separate Apache user.
When you specify user=name
for mod_wsgi daemon processes, those separate mod_wsgi processes (which are also created and managed by Apache httpd parent process), instead of being run as the Apache user will run as the user you specify instead.
It is normal that Apache runs as root
initially as that is the only way it can acquire use of system ports such as 80/443, open log files, and run child processes as a different named user etc. So you don't need to be concerned about that and it is secure.
@GrahamDumpleton
Cool. A small question that arises at the end of that is ... what is the standard or ideal way of checking that mod_wsgi is actually running as the user we defined i.e., the directive is being 'ignored' or 'implemented'.
@GrahamDumpleton So I am in last stage of Deployment, and I ran into another file permissions scenario or two.
First the CONTEXT:
Since django is being served from filesystem of second non-priveledged user called djangouser
the filesystem is like this: /home/djangouser/djangoprojects/pyproject_01
But I'm getting this error when I try to access my 127.0.0.1
[core:error] [pid 7092:tid 7264] (13)Permission denied: [client 127.0.0.1:57598] AH00035:
access to / denied (filesystem path '/home/djangouser/djangoprojects')
because search permissions are missing on a component of the path
this is my custom additional httpd config file:
#Custom Httpd Config File by SomeAB for Django
#Since WSGI Daemon Process directive is outside of VirtualHost container below
#Any WSGI app can run under processes created by it, & vice-versa
#And any errors get logged in apache main errorlog, & vice-versa
WSGIDaemonProcess djangodaemon_p user=djangouser processes=4 threads=4 maximum-requests=10000 request-timeout=180 lang='en_US.UTF-8' locale='en_US.UTF-8' python-home=/home/djangouser/mypyenv/pyenv_01 python-path=/home/djangouser/djangoprojects/pyproject_01
#Use mod_wsgi in daemon mode, with given process name
WSGIProcessGroup djangodaemon_p
#Set Application Group to empty string, so all apps use same Python Interpretor
WSGIApplicationGroup %{GLOBAL}
#Linking folders from where to serve public static & media files
Alias /static/ /home/djangouser/djangoprojects/pyproject_01/static/
Alias /media/ /home/djangouser/djangoprojects/pyproject_01/media/
#Linking URLs to Django WSGI File
WSGIScriptAlias / /home/djangouser/djangoprojects/pyproject_01/pyproject_01/wsgi.py
#Limit request size to server to 100MB, written in bytes here. Optional Safety feature. If exceeded Error 413 is thrown.
LimitRequestBody 1048576
<VirtualHost *:80>
ServerName sampleapp.com
ServerAlias www.sampleapp.com
ServerAdmin admin@sampleapp.com
#Identify files with .wsgi extention as WSGI Scripts
AddHandler wsgi-script .wsgi
<Directory /home/djangouser/djangoprojects/pyproject_01/pyproject_01>
#Strict no override in this directory
AllowOverride None
Options ExecCGI MultiViews Indexes
MultiViewsMatch Handlers
Require all granted
#Only open up access to wsgi.py
#<Files wsgi.py>
#</Files>
</Directory>
#Opening up access on the public static files folder
<Directory /home/djangouser/djangoprojects/pyproject_01/static>
Require all granted
#Options Indexes FollowSymLinks
</Directory>
#Opening up access on the public media files folder
<Directory /home/djangouser/djangoprojects/pyproject_01/media>
Require all granted
#Options Indexes FollowSymLinks
</Directory>
</VirtualHost>
QUESTION 1: I'm not 100% sure (but pretty sure), that it seems like Apache is not able to access the .wsgi file? I already tried setting permissions of 755 to the folders & wsgi.py for testing purpose. There are 3 solutions I heard about for this situation: A. Someone mentioned that Apache should be made 'GROUP' owner of the djangoprojects folder. B. Someone mentioned that Apache should be added to the personal 'GROUP' of djangouser C. I myself am considering CREATING a custom GROUP and ADDING BOTH Apache & djangouser to that GROUP, and SETTING that as the owner GROUP. Such that .. this group would only have access in the limited areas.. instead of everywhere Apache or djangouser has access to.
Am I missing something here?
QUESTION 2: Interesting according to Django documentation for SCRIPTALIAS we use wsgi.py .. which is a python script and not a wsgi script. Is this serving as an entry point that you talked about earlier? or is it still treated as a wsgi script..
So I am guessing in this case I don't need 'Add Handler wsgi-script .wsgi ' directive at all? Or is this still expected by mod_wsgi somewhere?
QUESTION 3: As you may notice in my httpd config, to further close down permissions, I was planning on using
My main concern is that the project's settings.py
is also in that folder, and exposing that doesn't seem like a good idea. That is why I prefer to use
You definitely do not need Add Handler wsgi-script .wsgi
.
What do you get when you run:
ls -alsd /home/djangouser
The safest way is NOT to have WSGIScriptAlias
directly map to wsgi.py
in your project code area.
As I said before, have a /var/www/wsgi-scripts
which contains project_01.wsgi
and then use:
WSGIScriptAlias / /var/www/wsgi-scripts/project_01.wsgi
<Directory /var/www/wsgi-scripts>
AllowOverride None
Require all granted
</Directory>
You don't need the Directory
block for /home/djangouser/djangoprojects/pyproject_01/pyproject_01
then and not opening up any access to where your code is.
The project_01.wsgi
file would include only:
# Import wsgi.py file from Django project.
from project_01.wsgi import application
and if necessary this could configure host specific environment variable configs etc.
@GrahamDumpleton
When I do the ls
command (while logged in as djangouser) .. I get
0 drwx------. 1 djangouser djangouser 554 Sep 4 11:08 /home/djangouser
Q1:Does this mean I have to open up the home directory even at top level?
I'm doing it the way you suggested.. since I want to have the safest route.
Q2: Now since project.wsgi is supposed to have .wsgi extension.. so we need the Add Handler? If not, how will it recognize wsgi scripts? Is there something else that is already doing it?
Q3: I created project_01.wsgi under /var/www/wsgi-scripts both the folder and the file have 755 permissions
About what is inside it (project_01.wsgi), I think you meant
import os
from wsgi.py import application
and NOT just
from project_01.wsgi import application
(because that would be referencing the file from within itself?)
Also, I made the changes to the django_httpd.conf (as you suggested) https://paste.pythondiscord.com/ZMRA
When the home directory is not readable to others like that, the Apache user cannot serve static files out of there when using Alias
directive. The WSGIScriptAlias
would also likely fail. If you want the home directory to be locked down, you would need to have the static
and media
directories outside of the user home directory and accessible to the Apache user somehow.
You do not need AddHandler
even if the file uses a .wsgi
extension when used with WSGIScriptAlias
. You could also the extension .python-script
if you want with WSGIScriptAlias
and it would still work without needing to set AddHandler
for .python-script
. The AddHandler
is only required when you want .wsgi
scripts in a directory referenced using Alias
directive to be handled in a special way.
No I did mean:
from project_01.wsgi import application
That will look for the Python package with directory name project_01
, containing the file wsgi.py
and import the function called application
within it.
It would work because you had:
python-path=/home/djangouser/djangoprojects/pyproject_01
thus it will find /home/djangouser/djangoprojects/pyproject_01/project_01./wsgi.py
.
If you used:
from wsgi.py import application
It would look for a Python package with directory name wsgi
, with a file called py.py
inside of it.
So Python package/module imports are different and wsgi.py
in that context does not refer to a file called wsgi.py
.
You also should not need:
Options ExecCGI MultiViews Indexes
MultiViewsMatch Handlers
in the Directory
block when using WSGIScriptAlias
.
Also a bad idea to use maximum-requests=10000
.
If you site experiences very high throughput then this only serves to cause the processes to restart more often, which is bad when you are under load.
If you want to restart periodically, you are better off looking at restart-interval
. Look at using graceful-timeout
in conjunction with this to avoid breaking running requests.
@GrahamDumpleton Most interesting indeed!!
I get the rest of it, but the question about where to serve the static and media files from remains. This 'djangouser' does not have any sensitive files (since it was created for the sole purpose of running mod_wsgi+Django as ) except the python virtual env and Django files themselves
I am assuming that the static/media folder is being accessed as 'Apache' because we are using Alias ? So Apache is serving them directly?
One possible solution that comes to mind is to make a static/media folder under /var/www (chown djangouser? 755 for perms?) to do Django's 'collectstatic' command on it
Or opening up home folder of djangouser is fine?
This is my current django_httpd.conf https://paste.pythondiscord.com/OW4Q
Edit: For testing only (until your answer) I did go ahead and chmod /home/djangouser to 755, and fixed a typo that was causing me issue.. and now django starter page is perfectly fine.. on 127.0.0.1
@GrahamDumpleton I am hoping to hear from you regarding this part. I am not sure if opening up perms is the right way.
Not much else I can tell you. There is no ideal folder structure as each has it's own pros and cons.
Opening up permissions for the home directory of the user reduces security a little, but might be more convenient than having to generate static files to a directory under /var/www from your Django project.
The safest scenario is where the Apache user would never be allowed to serve up or even read directories containing your Python source code. Also that whatever user runs your code under Apache cannot write to directories holding the source code. The only thing the user your code runs as might need write access to is media upload directories or a database directory if using sqlite.
The thing is though, is that Apache itself is unlikely to be your weakest point. What you have to worry about more is that the Python code of your web application is written correctly and doesn't have vulnerabilities, especially if it interfaces with a database. You seemed to be spending a lot of time worrying about Apache when you should be worrying about your Python web application code more.
Hi, I'm working on Fedora 38, already have apache with mod_wsgi installed (built mod_wsgi from source, instead of pip). MariaDB for DB.
Now my question is this that what is the ideal folder structure for securely running Django with virtual environment
Currently: /var/www is owned by root:root /var/www/mypy/pyvenv_01 is the python virtual environment, where /var/www/mypy is owned by user:user /var/www/djangosite/project1 is the first python project created, where /var/www/djangosite is owned by user:user
I was looking at your answer here, from 10 years ago.. but not sure if its still completely correct https://stackoverflow.com/questions/16408079/secure-django-file-permissions
I was additionally looking at Thomas Ward's reasoning about file permissions here https://askubuntu.com/questions/767504/permissions-problems-with-var-www-html-and-my-own-home-directory-for-a-website/767534#767534
mod_wsgi documentation briefly mentions placing the venv in /usr/local/venvs/example https://modwsgi.readthedocs.io/en/develop/user-guides/virtual-environments.html
Django documentation, just says put it in /home/mycode .. HOWEVER I read somewhere, that this is not good. https://docs.djangoproject.com/en/4.2/intro/tutorial01/
For now I'm planning to run only Django based website/app .. but I need room to be able to add other stuff as well (nothing particular in mind yet)
It would be really helpful, if you can shed some light on this Mr. Graham.