Issue
I deployed a Django 4 app with Daphne (ASGI) in a docker container. I use Caddy as a reverse proxy in front. It works, except I can't fill in any form because the CSRF protection kicks in. So no admin login, for example.
I can currently access the admin interface in two ways:
- Directly through docker, via a SSH tunelled port
- Through Caddy, which is then forwarding to the Docker container.
Option 1 works. I can log into the admin interface just as if I was running the development server locally. All is working as expected.
However, option 2 (caddy reverse proxy) doesn't work. I can access Django and load pages, but any form submission will be blocked because the CSRF protection kicks in.
CSRF verification failed. Request aborted.
Reason given for failure:
Origin checking failed - https://<mydomain.com> does not match any trusted origins.
My Caddyfile contains this:
<mydomain.com> {
reverse_proxy localhost:8088
}
localhost:8088 is the port exposed by my docker container.
In an effort to eliminate potential issues, I've set the following to false in my config file:
SECURE_SSL_REDIRECT
(causes a redirect loop, probably related to the reverse proxying)SESSION_COOKIE_SECURE
(I'd rather have it set to True, but I don't know at this point)CSRF_COOKIE_SECURE
(same remark)
The only Django-Caddy examples I could find online are outdated and refer to older versions of Caddy and/or Django. Django is deployed on ASGI with Daphne.
I've seens posts suggesting to change CSRF_TRUSTED_ORIGINS
, but it doesn't seem right that I would have to add a host that is already in the ALLOWED_HOSTS list. That also wouldn't explain why it works directly on the docker container, unless localhost is a special case for CSRF.
Versions:
- Caddy: 2.5.1
- Django: 4.0.5
- Daphne: 3.0.2
- Python: 3.10.5
Any idea what goes wrong, and how I should go about debugging such issues?
Solution
Finally found out what was happening.
I first wanted to know the exact HTTP request that was sent from caddy to django:
sudo tcpdump -i lo -A -n port 8088
This confirmed that:
- the
Origin
andReferer
headers were set properly - the
csrftoken
cookie was sent properly
Once that was known, I could dig in the code from django. Specifically, this function in the CSRF middleware.
In conclusion:
- Caddy forwards the http request to django unencrypted (so HTTP-non-S between caddy and django).
- Django considers that request non-secure
- The CSRF protection expects the
Origin
header sent by the browser to behttp://
because the request is not secure. In my case, it ishttps://
because my browser is talking to Caddy over https - Because the
Origin
header does not match what the CSRF middleware expects, the request is rejected
It's actually a simple fix.
Since we know that Caddy will always ignore X-Forwarded-Proto
from the browser and sets it itself, we can add SECURE_PROXY_SSL_HEADER to the settings.py
in django:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
And voilĂ !
Now I can also set these to true:
- SECURE_SSL_REDIRECT
- SESSION_COOKIE_SECURE
- CSRF_COOKIE_SECURE
EDIT Here's the Caddyfile, as per requested:
service.mywebsite.com {
reverse_proxy localhost:8088
}
Answered By - aspyct
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.