Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django rest framework gives 403 when behind nginx but not directly

Using django-rest-framework. I'm getting HTTP 403 errors when running in production behind nginx. When I call a particular view, which inherits from APIView to support a GET operation, I get:

{"detail": "Invalid username/password"}

But... I only get this in a browser. I don't get it when I use curl for the very same URL. I get this error whether I hit the URL directly, or load the URL via AJAX, in both Chrome and Firefox.

I do not get the error if I log in via the Django Admin with an admin account first.

Also, I only get this is I'm running from behind nginx. If I run with either the Django dev server, or gunicorn, and hit the port directly, I'm fine, and can happily hit the URL anonymously. If I then put nginx in front of this, to forward to the same gunicorn/runserver I get this error.

Maybe it's something to do with my nginx proxy_pass settings?

location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

I'm running django rest framework 2.2.6, Django 1.5 and nginx 1.2.7.

I set throttling to a silly high number in rest framework, and looked at the permissions which all seemed open by default (but set as so explicitly as well).

Can anyone point me in the right direction?

Thanks!

Ludo.

like image 625
Ludo Avatar asked Apr 12 '13 15:04

Ludo


3 Answers

In my case, I had HTTP basic authentication configured in Apache, but not in my Django project. Because the request had authentication headers, the Django app thought I wanted to authenticate with it. I disabled authentication in the Django REST framework using these settings:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': []
}
like image 130
Sjoerd Avatar answered Oct 12 '22 14:10

Sjoerd


If it's a basic http auth (like in @Sjoerd's case) and you don't need a django authentication, you can also remove Authorization header in your nginx reverse proxy config:

location / {
    ...
    proxy_set_header   Authorization    "";
}
like image 32
rinat.io Avatar answered Oct 12 '22 16:10

rinat.io


When you send a request to the site, which is behind basic authentication, client (browser) sends 'Authorization' header (as it should - see: "Basic access authentication" on wikipedia).

Ngnix passes it to your application.

Django Rest Framework also supports: BasicAuthentication, and it is enabled by default.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

source: http://www.django-rest-framework.org/api-guide/authentication/

So django-rest-framework sees 'Authorization' header, and thinks that is should be django user with password.

If such user does not exist, then you got: "HTTP 401 Unauthorized". See: http://www.django-rest-framework.org/api-guide/authentication/#basicauthentication

Soultions

Pick one: a) Add to your ngnix site config

location / {
    ...
    proxy_set_header    Authorization    "";
    ...
}

so DRF will not get 'Authorization' header, so it won't try to match django user.

After this change - basic auth can not be used on django side (empty header!

b) Get rid of "rest_framework.authentication.BasicAuthentication" from REST_FRAMEWORK (in your django settings).

If not defined, add to your django settings:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    )
}

Probbably best solution.

c) Use your django user/password for basic auth?

Realy? - Don't!

like image 24
user1045483 Avatar answered Oct 12 '22 16:10

user1045483