JimOfAllTrades
JimOfAllTrades

Reputation: 65

CSRF verification error from dockerized Django behind dockerized nginx

I have two docker containers running together, one for a Django app using gunicorn, and one for nginx serving static files and redirecting requests to the Django app. I have used different ports for

  1. the Django/gunicorn server, exposed only to nginx (8000),
  2. the nginx server, exposed in the docker container (80), and
  3. externally when using the docker container (so I mapped the port to a different one, say 8888). I also access it with a "xyz.local" host name from the device where docker runs. This is all happening in a home network, no HTTPS involved.

Now when I submit a form, e.g. the admin interface's login form, I get a CSRF verification failure saying xyz.local:8888 is not a trusted origin.

I understand that I can add "http://xyz.local:8888" to the CSRF_TRUSTED_ORIGINS to make it work, or disable CSRF protection altogether, but I'm wondering if that is the correct/best way in this setup.

I would like to have the Django docker container be as unaware of the outside world as possible, so that I can just run it on another device having a different host name or with a different docker-compose configuration using a different port, without having to rebuild the docker container or having to introduce an ENV variable for the host and port used outside of docker.

I'm new to nginx and docker. Can I make it work using different settings in the nginx.conf or Django's settings.py?

Here is what I think are the relevant parts of the nginx.conf (otherwise mostly stuff to handle static files):

    server {
        listen 80;

        location / {
            proxy_pass http://web:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_redirect off;
        }
        ...
    }

docker-compose.yml:

version: '3'
services:
  web:
    image: mydjangoapp
    expose:
      - "8000"
    volumes:
      ...

  nginx:
    image: nginx
    ports:
      - "8888:80"
    volumes:
      - /volume1/myapp/nginx.conf:/etc/nginx/nginx.conf
      ...
    depends_on:
      - web

My Django's settings.py is pretty standard as it comes when initializing a new Django project.

I've read through several related questions here and elsewhere - most seem to have their problems only when introducing HTTPS, but some seem to have a similar problem I have - and tried including different settings in the nginx.conf and settings.py, e.g. proxy_set_header X-Forwarded-Port $server_port; and proxy_set_header X-Forwarded-Host $host; in nginx.conf and USE_X_FORWARDED_HOST = True and USE_X_FORWARDED_PORT = True in settings.py, or I included CSRF_COOKIE_SECURE = False in the settings.py. Frankly, not really knowing what I was doing there, just trying out what helped others in similar situations.

Upvotes: 0

Views: 478

Answers (1)

0urz4g
0urz4g

Reputation: 483

I understand that I can add "http://xyz.local:8888" to the CSRF_TRUSTED_ORIGINS to make it work

In my point of view, what you are doing is a correct way to do.

Some configuration of Django is related to its running environment, like ALLOWED_HOSTS or CSRF_TRUSTED_ORIGINS. And this cannot be avoided.

As you suggest, you can use environment variables for changing this configuration easily from one environment to another.

Another way to do is to have multiple settings files, and use the DJANGO_SETTINGS_MODULE environment variable to determine which file to use depending the environment.

Be aware that for sensibles settings like the SECRET_KEY or the database password for exemple, it is absolutely recommended to not use the settings file for security reasons.

Upvotes: 1

Related Questions