axnsan12 / drf-yasg

Automated generation of real Swagger/OpenAPI 2.0 schemas from Django REST Framework code.
https://drf-yasg.readthedocs.io/en/stable/
Other
3.39k stars 434 forks source link

Running Django behind a proxy, mixed content error #504

Open DenLilleMand opened 4 years ago

DenLilleMand commented 4 years ago

The interactive part of the API is trying to do HTTP, when my site is running HTTPs.

I think it might be my fault, because my Django instance is only running HTTP between it self and a proxy. I have tried to do environ['HTTPS'] = 'on' but it doesn't quite work, i get 500 errors.

But i don't need Django to generate URLs anyway, relative paths work just fine, but swagger tries to actually do API calls based on them instead of using a relative path.

So I think it could be resolved if the URL argument to openapi.Info or putting a DEFAULT_API_URL = "https://example.com/" would change the HTTP schema being used, but it is just always HTTP regardless of these values. Do you have any suggestions on what to do if those arguments/settings don't work?

etene commented 4 years ago

Hello, Take a look at #491 as for why the DEFAULT_API_URL is ignored. In your case, the proxy config might be wrong & thus Django might simply not know it is behind a HTTPS proxy. The issue looks completely unrelated to drf-yasg.

I can't guarantee this would work for you, but I have a similar (working) setup somewhere and a quick look at Django Debug Toolbar tells me that the proxy sets the X-Forwarded-For and X-Forwarded-Proto HTTP headers. That might be the solution to your problem, especially the second header !

DenLilleMand commented 4 years ago

Hello, Take a look at #491 as for why the DEFAULT_API_URL is ignored. In your case, the proxy config might be wrong & thus Django might simply not know it is behind a HTTPS proxy. The issue looks completely unrelated to drf-yasg.

Yes i know that it is unrelated and that my Django doesn't know anything about HTTPs, i have a pretty hardcore setup of NGINX(Ingress K8s) -> NGINX(Docker) -> Gunicorn -> Django, i have tried to set all of the settings about forwarding headers correctly in all of them, but my request tracing is non-existent in my system, so it is hard to know where they get striped if they do.

I can't guarantee this would work for you, but I have a similar (working) setup somewhere and a quick look at Django Debug Toolbar tells me that the proxy sets the X-Forwarded-For and X-Forwarded-Proto HTTP headers. That might be the solution to your problem, especially the second header !

X-Forwarded-For and X-Forwarded-Proto is some headers i have been trying to get through to Django including making sure that Django trusts forwarded headers. But i think that the Django Debug Toolbar is a great suggestion for getting some extra info, i had forgotten about that tool.


The reason why i thought to post it here is that i feel like the documentation on this area might be leaking a bit, it could be more clear that the only thing that matters is the Django settings. Ehm.. it is also unclear to me why i cannot just force yasg to use HTTPs urls just because Django doesn't have it enabled locally. Maybe because it is a dirty way of doing it?

etene commented 4 years ago

request tracing is non-existent in my system, so it is hard to know where they get striped if they do.

Since everything behind the proxy is HTTP-only, I typically use tcpdump at various points in the chain. In verbose mode, it prints HTTP requests headers. Something like this:

# tcpdump -nvi <interface name> tcp port http
[ lots of stuff ]
08:43:49.892319 IP (tos 0x0, ttl 64, id 19284, offset 0, flags [DF], proto TCP (6), length 849)
    10.0.0.18.42680 > 10.0.0.21.80: Flags [P.], cksum 0x9c5c (correct), seq 1:798, ack 1, win 457, options [nop,nop,TS val 3260241910 ecr 295902270], length 797: HTTP, length: 797
    GET /static/favicon.ico HTTP/1.0
    Host: the.host.name
    X-Forwarded-For: 10.3.0.22
    X-Forwarded-Proto: https
    Connection: close
        [ other headers ]

The reason why i thought to post it here is that i feel like the documentation on this area might be leaking a bit, it could be more clear that the only thing that matters is the Django settings

Since you're the second person to have this issue in a few weeks, I think you're probably right and the documentation could be made clearer on that subject. I'll see if I can come up with a PR when I have some time.

Ehm.. it is also unclear to me why i cannot just force yasg to use HTTPs urls just because Django doesn't have it enabled locally. Maybe because it is a dirty way of doing it?

It's supposed to work transparently whether you're using HTTP or HTTPS, assuming everything is set up properly. So I guess forcing the protocol could be considered "dirty", and should not be necessary.

YemreGurses commented 4 years ago

I'm getting the same error as well. I try endpoints from swagger but mixed content error killed me. Neither, 'DEFAULT_API_URL' nor USE_X_FORWARDED_HOST = True, SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') worked. I'm stuck on what to do.

DenLilleMand commented 4 years ago

I'm getting the same error as well. I try endpoints from swagger but mixed content error killed me. Neither, 'DEFAULT_API_URL' nor USE_X_FORWARDED_HOST = True, SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') worked. I'm stuck on what to do.

Have you also told any proxies that they have to pass on the headers to Django?

I have this in my nginx config:

      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Host $http_host;

If you use Gunicorn like i do, i can see that there is some options like:

secure_scheme_headers
{'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}

and

forwarded_allow_ips = "*"

etc. Clearly mine is not working yet, but i have a feeling that passing on the right headers to Django, will make Django return the absolute URLs in HTTPs instead of HTTP.

I am really not a experienced Django developer, so i was looking at setting the environment variable HTTPS = on in the WSGI file, but this definitely crashes my server, because i think that it tries to make Django it self run the HTTPs encryption maybe.

YemreGurses commented 4 years ago

Yea, I have tried your nginx settings, nothing worked. I dont get it, it is working fine completely in the demo.

DenLilleMand commented 4 years ago

Yea, I have tried your nginx settings, nothing worked. I dont get it, it is working fine completely in the demo.

Arg ok, i'll let you know if i fix it. Hope you'll do the same.

YemreGurses commented 4 years ago

Yea, I have tried your nginx settings, nothing worked. I dont get it, it is working fine completely in the demo.

Arg ok, i'll let you know if i fix it. Hope you'll do the same.

Alright, we have two different Django repos in my company and I had installed drf-yasg to other one and deployed and it worked perfectly! Thus, we can make sure that the problem is not related to drf-yasg but server, nginx etc. This sucks because these DevOps things aren't exactly what I understand. Any help could be perfect.

mchetelat commented 4 years ago

Hi all

I am running my Django application behind an Apache2 Reverse Proxy and I had the same issue. The following configuration allows me to connect behind the proxy with the original host name and with the "X-Forwarded-Proto"-header set to "https" which drf-yasg considers to generate the base url.

    ProxyRequests Off
    ProxyPreserveHost On

    RequestHeader set X-Forwarded-Proto "https"

    ProxyPass           /static/ !
    ProxyPass           /        http://127.0.0.1:8000/
    ProxyPassReverse    /        http://127.0.0.1:8000/

You need to tell Django to trust the X-Forwarded-Proto header that comes from your proxy by setting

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

in your Django settings.py (https://docs.djangoproject.com/en/3.0/ref/settings/#secure-proxy-ssl-header). Please read the attached warning in the documentation.