recipes icon indicating copy to clipboard operation
recipes copied to clipboard

CSRF issue using Traefik

Open voryzen opened this issue 2 years ago • 30 comments
trafficstars

Tandoor Version

1.5.6

Setup

Docker / Docker-Compose

Reverse Proxy

Traefik

Other

No response

Bug description

Issue:

Forbidden (403)

CSRF verification failed. Request aborted.
Help

Reason given for failure:

    Origin checking failed - null does not match any trusted origins.
    

In general, this can occur when there is a genuine Cross Site Request Forgery, or when [Django’s CSRF mechanism](https://docs.djangoproject.com/en/4.1/ref/csrf/) has not been used correctly. For POST forms, you need to ensure:

    Your browser is accepting cookies.
    The view function passes a request to the template’s [render](https://docs.djangoproject.com/en/dev/topics/templates/#django.template.backends.base.Template.render) method.
    In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
    If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
    The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.

You’re seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.

You can customize this page using the CSRF_FAILURE_VIEW setting.

recipe.conf

server {
  listen 80;
  server_name localhost;

  client_max_body_size 128M;

  # serve media files
  location /media/ {
    alias /media/;
  }
  # pass requests for dynamic content to gunicorn
  location / {
    #proxy_set_header Host $host;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://tandoor:8080;
    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #proxy_set_header X-XSRF-TOKEN $http_x_xsrf_token;
    #proxy_set_header Host $host;
    proxy_redirect http://127.0.0.1:8080 <website redacted>;

    error_page 502 /errors/http502.html;
  }

  location /errors/ {
    alias /etc/nginx/conf.d/errorpages/;
    internal;
  }
}

.env file

DEBUG=1
SQL_DEBUG=0
DEBUG_TOOLBAR=0
TANDOOR_PORT=8080
ALLOWED_HOSTS=*
SECRET_KEY=<redacted>
TIMEZONE=<redacted>
DB_ENGINE=django.db.backends.postgresql
POSTGRES_HOST=tandoor_db
POSTGRES_PORT=5432
POSTGRES_USER=ThatUser
POSTGRES_PASSWORD=ThatPassword
POSTGRES_DB=ThatDB
FRACTION_PREF_DEFAULT=0
COMMENT_PREF_DEFAULT=1
SHOPPING_MIN_AUTOSYNC_INTERVAL=5
REVERSE_PROXY_AUTH=0
ENABLE_PDF_EXPORT=1

nginx container: tandoor_web recipes container: tandoor postgres container: tandoor_db

Its fairly obvious that I've tried a few different options in the Recipes.conf file, but I'm still getting the CSRF issue. Any suggestions?

Relevant logs

No response

voryzen avatar Sep 28 '23 11:09 voryzen

Can you please share the header details provided at /system/

smilerz avatar Sep 28 '23 11:09 smilerz

which container?

voryzen avatar Sep 28 '23 11:09 voryzen

Tandoor

smilerz avatar Sep 28 '23 11:09 smilerz

That directory does not exist.

Here's a screenie

Screenshot_20230928_214716

voryzen avatar Sep 28 '23 11:09 voryzen

Sorry for the confusion. It's a webpage at the root of you Tandoor url.

smilerz avatar Sep 28 '23 11:09 smilerz

Oh right, sorry.

I'm not logged in, so I can't view that page; and I can't log in due to the CSRF issue

Screenshot_20230928_215653

voryzen avatar Sep 28 '23 11:09 voryzen

can you connect bypassing traefik?

smilerz avatar Sep 28 '23 12:09 smilerz

Not really; I've too many services on the machine. The things I've tried, I haven't had much luck.

What else did you want me to try?

voryzen avatar Sep 28 '23 13:09 voryzen

The problem is probably with your traefik setup. Your options are:

  • start with a known working configuration and incrementally add capabilities to identify which one is causing the issue
  • search the forums for working traefik configs to see if you spot any differences
  • use traefik or django support channels to get assistance on how to troubleshoot traefik and django

smilerz avatar Sep 28 '23 13:09 smilerz

Seriously doubt that it is an issue with Traefik, as mentioned earlier the machine runs several services (all of which are behind Traefik); I appreciate your candor, though.

Most of my research points to django, and its Cross Site Request Forgery Protection, which would point to the structure of Tandoor.. hence I opened this issue. What makes you say that Traefik is the issue?

voryzen avatar Sep 28 '23 13:09 voryzen

django is extremely opinionated about the headers that are provided. Unless you are serving django apps behind traefik the fact that other services are working isn't an indication that you have set it up correctly.

smilerz avatar Sep 28 '23 14:09 smilerz

Most probably a duplicate of https://github.com/TandoorRecipes/recipes/issues/518. What solved it for me is defining the CSRF_TRUSTED_ORIGINS env var on my container. Hope it works for you too.

alecgerona avatar Sep 30 '23 15:09 alecgerona

Hi @alecgerona, thanks for the suggestion, probably not a duplicate of that issue.. my reasoning here, is that it concerns nginx proxy manager and not traefik..

Can you give some examples on your fix? I've been playing with the env file, and mine reads (similar to) as follows:

CSRF_TRUSTED_ORIGINS="https://recipes.domain.tld","http://recipes.domain.tld"

voryzen avatar Sep 30 '23 15:09 voryzen

@voryzen Hi, reason I mentioned it is because I'm also using Nginx Proxy Manager and ran into your problem. My env var is just CSRF_TRUSTED_ORIGINS="https://recipes.domain.tld". I only need to whitelist one. I think if you have multiple you just have to use space as a delimiter.

CSRF_TRUSTED_ORIGINS="https://recipes.domain.tld http://recipes.domain.tld"

As an extra warning, while I got past the CSRF error, I ran into the Mixed Content issue since my https is being provided by Cloudflare and Tandoor still thinks it's being served by http so I can't use it still. I haven't put in the time to solve it yet.

Might be helpful to you.

alecgerona avatar Oct 02 '23 02:10 alecgerona

Hi @alecgerona, thanks for the suggestion, probably not a duplicate of that issue.. my reasoning here, is that it concerns nginx proxy manager and not traefik..

@alecgerona sorry, that was my fault; I wasn't clear there. When I referenced 'that' earlier, I was referring to the cited issue, not my own.

To be clearer, I'm having this issue with Traefik, not with Nginx Proxy Manager.

If I manage to get past this error (I've been looking at adamchainz cors module) I'll keep an eye out for that mixed content issue.

Thank you

voryzen avatar Oct 02 '23 04:10 voryzen

Yeah no worries had a mindfart of my own back then. Hope you solve this. The env var should be enough.

alecgerona avatar Oct 02 '23 05:10 alecgerona

@voryzen A few things I noticed in your config:

  • I think the X-Forwarded-Proto in your nginx configuration cannot work. It will always be http from the perspective of nginx. The default config works fine in my traefik setup. Nginx should forward all X-Forwarded-* headers by default.
  • It is weird that you get null does not match any trusted origins. You should try to run tcpdump inside the nginx container to see which headers come from traefik. You should see Host and X-Forwarded-Host with a value matching your domain.

swnf avatar Oct 07 '23 09:10 swnf

Hi @swnf

Thank you very much for your input into this issue. I've done as you asked, and I've pasted the output to pastebin (redacting personal information) for your perusal. https://pastebin.com/PJsPz3sR

I can verify that the Host and X-Forwarded-Host does point to the correct domain.

I look forward to hearing your opinion on the matter, hope you're well.

voryzen avatar Oct 07 '23 10:10 voryzen

I don't see anything wrong with your headers. If this is a new deployment, you should probably try to reset tandoor by deleting all containers/volumes and recreating them.

swnf avatar Oct 07 '23 11:10 swnf

@swnf yea, new deployment. deleting and recreating did not yield results. I had a look through the tandoor code, and I'm fairly convinced now that its an issue with nginx.

I'll have some more time to work on this over the weekend

voryzen avatar Oct 13 '23 15:10 voryzen

@voryzen were you able to figure this out? I have a similar issue where only Chrome gives me this error, but Firefox and Edge work fine.

"There was an error updating a resource! {"detail":"CSRF Failed: CSRF token missing."}"

I have added CSRF_TRUSTED_ORIGINS = "https://exmaple.com" but it makes no difference to Chrome.

blknight88 avatar Nov 20 '23 04:11 blknight88

No, haven't had an luck.

TBH, I've moved on to other programs, due to the lack of help. Sorta been made feel like it's an issue with something I've done; so I'm a little unwilling to approach the devs or their posse.

Good luck though, I would love to see a fix for it.

voryzen avatar Dec 20 '23 05:12 voryzen

Just for reference, here is a complete config with traefik that works perfectly fine for me. You can try to remove any additional configuration you made to the environment/nginx/traefik to figure out what triggers this behaviour. It might also help to delete the website's data in your browser because Edge and Chrome are nearly identical.

version: '3.8'

services:
  db_recipes:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: "djangouser"
      POSTGRES_PASSWORD: "djangpassword"
      POSTGRES_DB: "djangodb"
    volumes:
      - postgresql:/var/lib/postgresql/data

  web_recipes:
    image: vabene1111/recipes:1
    environment:
      SECRET_KEY: "myverysecretkey"
      DB_ENGINE: "django.db.backends.postgresql"
      POSTGRES_HOST: "db_recipes"
      POSTGRES_PORT: "5432"
      POSTGRES_USER: "djangouser"
      POSTGRES_PASSWORD: "djangpassword"
      POSTGRES_DB: "djangodb"
    volumes:
      - staticfiles:/opt/recipes/staticfiles
      - nginx_config:/opt/recipes/nginx/conf.d
      - mediafiles:/opt/recipes/mediafiles
    depends_on:
      - db_recipes
  
  nginx_recipes:
    image: nginx:mainline-alpine
    volumes:
      - nginx_config:/etc/nginx/conf.d:ro
      - staticfiles:/static
      - mediafiles:/media
    depends_on:
      - web_recipes
    labels:
      traefik.enable: "true"
      traefik.http.routers.recipes.rule: "Host(`tandoor.example.com`)"

  traefik:
    image: traefik:2.10
    command: --providers.docker --providers.docker.exposedByDefault=false
    ports:
      - '80:80'
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      
volumes:
  postgresql:
  staticfiles:
  nginx_config:
  mediafiles:

swnf avatar Dec 20 '23 10:12 swnf

Thanks for sharing, as of the most recent version "1.5.10" I no longer have this issue with Chrome so long as I have the following in my .env file. Granted I am not using Taefik but an Apache reverse proxy.

CSRF_TRUSTED_ORIGINS = "https://exmaple.com/"

blknight88 avatar Dec 22 '23 15:12 blknight88

@alecgerona did you ever solve the Cloudflare Mixed Content issue? I just hit it and was hoping for an easy fix.

tasmith039 avatar Jan 12 '24 17:01 tasmith039

Anyone else that comes across this and has the Cloudflare Mixed Content issue. You will need a custom Recipes.conf and add the two lines below.

proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Scheme https;

tasmith039 avatar Jan 12 '24 18:01 tasmith039

@tasmith039 thank you for this tip! I have added your changes to Recipes.conf and restarted the container, but I'm still getting errors from Axios (still using HTTP instead of HTTPS, like http://tandoor.domain.com/api/space/1/).

Context: I'm using Traefik + Docker (Traefik config through Docker labels) + Cloudflare.

My config looks like below:

server {
  listen 80;
  server_name localhost;

  client_max_body_size 128M;

  # serve media files
  location /media/ {
    alias /media/;
  }
  # pass requests for dynamic content to gunicorn
  location / {
    proxy_set_header Host $http_host;
    proxy_pass http://web_recipes:8080;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Scheme https;
    
    error_page 502 /errors/http502.html;
  }

  location /errors/ {
    alias /etc/nginx/conf.d/errorpages/;
    internal;
  }
}

Do yours look different?

goniszewski avatar Jan 15 '24 14:01 goniszewski

Hey @goniszewski I am also using Traefik + Docker + Cloudflare. I will say I did have some weird issues where dont always work quite right. For example I always get the error below on startup which gives a cloudflare error. I also had had to make sure to clear our my old volumes from initial setup. Are you only getting the Mixed Content issue but the page loads fine otherwise?

2024-01-15 11:47:49 2024/01/15 16:47:49 [error] 21#21: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 172.25.0.2, server: localhost, request: "GET /view/recipe/1 HTTP/1.1", upstream: "http://172.23.0.3:8080/view/recipe/1", host: "tandoor.mydomain.com"

Below is my docker compose config


version: "3.9"
services:
  db_recipes:
    restart: always
    image: postgres:15-alpine
    volumes:
      - ./config/tandoor/postgresql:/var/lib/postgresql/data
    env_file:
      - ./config/tandoor/.env
    networks:
      - tandoor

  web_recipes:
    restart: always
    container_name: "web_recipes"
    image: vabene1111/recipes
    env_file:
      - ./config/tandoor/.env
    volumes:
      - staticfiles:/opt/recipes/staticfiles
      # Do not make this a bind mount, see https://docs.tandoor.dev/install/docker/#volumes-vs-bind-mounts
      - ./config/tandoor/nginx:/opt/recipes/nginx/conf.d
      - ./config/tandoor/mediafiles:/opt/recipes/mediafiles
    depends_on:
      - db_recipes
    networks:
      - tandoor
  nginx_recipes:
    image: nginx:mainline-alpine
    container_name: "nginx_recipes"
    restart: always
    env_file:
      - ./config/tandoor/.env
    volumes:
      - ./config/tandoor/nginx:/etc/nginx/conf.d:ro
      - staticfiles:/static:ro
      - ./config/tandoor/mediafiles:/media:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.tandoornginx.rule=Host(`tandoor.mydomain.com`)"
      - 'traefik.http.services.tandoornginx.loadBalancer.server.port=80'
    depends_on:
      - web_recipes
    ports:
      - "8086:80"
    networks:
      - tandoor
      - frontend

Below is my Recipes.conf

server {
  listen 80;
  server_name localhost;

  client_max_body_size 128M;

  # serve media files
  location /media/ {
    alias /media/;
  }
  # pass requests for dynamic content to gunicorn
  location / {
    proxy_set_header Host $http_host;
    proxy_pass http://web_recipes:8080;
    error_page 502 /errors/http502.html;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Scheme https;
   # proxy_redirect http://127.0.0.1:8080 https://tandoor.mydomain.com; # replace port and domain
  }

  location /errors/ {
    alias /etc/nginx/conf.d/errorpages/;
    internal;
  }
}

tasmith039 avatar Jan 15 '24 16:01 tasmith039

Thank you for your reply @tasmith039 :) Yes, the page loads fine otherwise, but is unable to fetch any data from the API. I have tried to create override rules in CF, but didn't succeed.

I will try again later with the proxy_redirect option you have commented out. Meanwhile, the other app I tried worked right away with my setup. I only needed it to show my wife the possibilities, so I'll just explore it for now and see how it compares to Tandoor.

goniszewski avatar Jan 16 '24 12:01 goniszewski

@goniszewski good luck man. Let me know if there is anything else I can do to help. We can jump On a zoom or something if you want. I hope you get it working.

tasmith039 avatar Jan 16 '24 13:01 tasmith039