35

I am developing a django API which will be running on top of Apache2 via WSGI on a server running Ubuntu.

Users will be able to upload pictures they take to the server using a POST request. The API processes this request and then attempts to write the image to /var/www/media/animals/user_uploads/<animal_type>/<picture_name>.jpg. In case there is no directory /var/www/media/animals/user_uploads/<animal_type>/ it will create it.

When testing during development everything was fine, both using Windows and Scientific Linux. When testing on the deployment server, I receive this error:

Django Error

From what I understand, the Apache2 server is running using the user www-data. In my case, running cat /etc/passwd to get the list of users, this is what I get for www-data:

www-data:x:33:33:www-data:/var/www:/bin/sh

I am assuming this means that www-data has access to everything in /var/www/. I have tried:

chmod 777 -R media

This worked but it is obviously a very bad way to solve this. Is there a better way to solve this?

This is my wsgi.py:

import os, sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "serengeti.settings")
sys.path.append('/serengeti/django/serengeti')
sys.path.append('/serengeti/django')

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

I have this in my settings.py file:

MEDIA_ROOT = '/var/www/media/'
MEDIA_URL = os.path.join(BASE_DIR,'/media/')

My vhost.conf contains this:

Alias /media/ /var/www/media/
Vlad Schnakovszki
  • 8,434
  • 6
  • 80
  • 114

6 Answers6

81

I have solved this myself in the end.

When running on the development machines, I am in fact running using my current user's privileges. However, when running on the deployment server, I am in fact running through wsgi, which means it's running using www-data's privileges.

www-data is neither the owner nor in the group of users that own /var/www. This means that www-data is treated as other and has the permissions set to others.

The BAD solution to this would be to do:

sudo chmod -R 777 /var/www/

This would give everyone full access to everything in /var/www/, which is a very bad idea.

Another BAD solution would be to do:

sudo chown -R www-data /var/www/

This would change the owner to www-data, which opens security vulnerabilities.

The GOOD solution would be:

sudo groupadd varwwwusers
sudo adduser www-data varwwwusers
sudo chgrp -R varwwwusers /var/www/
sudo chmod -R 770 /var/www/

This adds www-data to the varwwwusers group, which is then set as the group for /var/www/ and all of its subfolders. You could set it to 750 to make it more secure but then you won't be able to use Django's collectstatic functionality so stick to 770 unless you're very confident about what you're doing.

Vlad Schnakovszki
  • 8,434
  • 6
  • 80
  • 114
  • 1
    How do I know which user I need to add? – User May 02 '15 at 03:53
  • If you're using the default Apache2 configuration, it's www-data. If you've changed the user, it's the one you've set. If you're using another server, you'll need to add the one specific to your server. – Vlad Schnakovszki May 08 '15 at 19:08
  • 11
    I don't understand why the "GOOD" solution is any more secure than the `sudo chown -R www-data /var/www/ option`. In both options you are giving the web server user (www-data) rwx access to the entire directory. One gives the group rwx, the other specifies the owner with rwx. How is using the group any more secure? – rsynnest Feb 03 '17 at 20:27
  • 2
    @rsynnest good point. I meant to have it as 760 to avoid allowing executions if the server somehow got hacked and some script was uploaded in there. I've updated it. See this for some details: https://security.stackexchange.com/a/65201/20064 – Vlad Schnakovszki Apr 06 '17 at 13:00
  • 4
    Permission `6` on directory makes it inaccessible. www-data won't be able to traverse or read anything from /var/www with `760` permission. An odd octal permission is must for directory if you want it to be accessible. – Jahid Aug 12 '17 at 18:11
  • @Jahid don't have any Django projects set up now to confirm this but this definitely worked before (hence the high number of upvotes). Why would the www-data user now need execute permissions to read and write to the /var/www folder? Are you sure you're not doing something you're not supposed to, like trying to execute stuff via www-data from the /var/www folder? – Vlad Schnakovszki Aug 16 '17 at 13:28
  • It's how linux permission works. You can not go inside a directory unless you have execute permission on that directory. Try this: `mkdir mytest; chmod 660 mytest; cd mytest` and `mkdir mytest; chmod 660 mytest; touch mytest/file`. Do notice that I am using `660` (not `760`) here, which gives *you* the owner a 6 permission on the directory so that you can check the outcome yourself. – Jahid Aug 16 '17 at 13:42
  • 2
    The upvotes I believe for your links, all of them talks about valid permission for directory, 750 or 770. And people definitely followed them and corrected them accordingly, thus this little mistake were overlooked. – Jahid Aug 16 '17 at 13:47
  • @Vlad Schnakovszki I believe the owner of the app I'm working on has changed the user (the user must login to even see the app that let's them upload a file). Do you know how can I find out what it is? – Renel Chesak Aug 16 '17 at 15:18
  • I don't get this, but I'm doing exactly how you describe and I'm getting the same error when saving a file: OSError: [Errno 13] Permission denied: ... – 0x4ndy Jan 31 '18 at 22:01
  • @Jahid – so how do you set permissions to allow traversing directories but not execute files? – Felix Böhme Mar 15 '18 at 20:26
  • @FelixBöhme : You apply different permissions for files and directories. directories should be 750 and files should be 640 for php and 644 for publicly available resources such as images, js, html etc. – Jahid Mar 16 '18 at 14:47
  • 1
    `sudo chmod -R 770 /var/www/` is the right solution. If you set 760, group users will get Permission denied on read or write attempts. https://stackoverflow.com/q/53815702/6143954 – NKSM Jul 19 '20 at 07:56
  • Don't copy paste this unless you know what you are doing. It messed up my whole app – Austin not from Boston Jan 07 '21 at 17:06
  • @AustinnotfromBoston sudo chmod -R a+rwx /path/to/folder use this command to reset the permissions it worked for me – Leman Kirme Mar 01 '21 at 08:29
  • Hi. I wrote " sudo chgrp -R varwwwusers . " " sudo chmod -R 760 . : and now my server is down. How can i fix it? – Kyoko Sasagava Mar 26 '22 at 13:42
  • @LemanKirme That allows all permissions for all files for all users which is terrible for security. You need execute permission only on the folders. – JakeD Oct 30 '22 at 01:41
  • this solution broke my server – Ricardinho Oct 31 '22 at 02:17
  • I've updated the answer to use 770/750 instead of 760/740 for the reasons detailed in the comments above. – Vlad Schnakovszki Nov 06 '22 at 19:33
14

Create a 'MEDIA' directory in the root of your project. Then set:

MEDIA_ROOT = os.path.join(BASE_DIR,'MEDIA')
Nic Scozzaro
  • 6,651
  • 3
  • 42
  • 46
11

To know which user you are logged on to:

$ whoami
ubuntu

And adding to your solution, if you are using an AWS Instance, you should add your user to the group to be able to access that folder:

Making a group for webservices users (varwwwusers)

$ sudo groupadd varwwwusers

Change the www folder and make it belong to varwwwusers

$ sudo chgrp -R varwwwusers /var/www/

www-data is the server making django requests, add that to the group

$ sudo adduser www-data varwwwusers

Change folder policy

$ sudo chmod -R 770 /var/www/

Add ubuntu to the group of varwwwusers

$ usermod -a -G varwwwusers ubuntu

Hope this helps!

  • 5
    Shouldn't it be `sudo usermod -a -G varwwwusers ubuntu` ? – dcolumbus May 29 '17 at 05:57
  • I'm still facing the permission denied error. I believe it might be due to apache configurations. Can you please share that file too. Also please share the settings conf just to make sure. thanks! – jitesh2796 Aug 16 '20 at 18:48
1

The resolution for this problem when dealing with the production server would be to make use of collectstatic as already mentioned that it used and resolved or give permissions to the folders. However if your solution is in a local environment, the solution can be acquired by configuring the local MEDIA directory in the settings.py file that acts on the local server.

So, would be adding these two lines in the local configuration file as @Nic Scozzaro mentioned:

MEDIA_ROOT = os.path.join (BASE_DIR, 'media')
STATIC_ROOT = os.path.join (BASE_DIR, 'static')

After the configuration restart the services to apply the fixes.

Lucas Coelho
  • 1,492
  • 1
  • 9
  • 14
1

instead of : MEDIA_ROOT = os.path.join(BASE_DIR, '\media\')

add the following: MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

0

Unlike Others it was the answer for me (ubantu with gunicorn nginx)

instead of : MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

add the following: MEDIA_ROOT = os.path.join(BASE_DIR, '/media/')

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/30367634) – ABN Nov 18 '21 at 01:59