8

If I try to deliver the Swagger UI using Flask RestPlus over HTTPS, I see only the "No spec provided" error message at the root URL, and the full Swagger UI never loads. However, if I visit the API endpoints they return responses as expected.

Looking at the source HTML for the error page, I noticed that swagger.json was being fetched from http://myhost/ rather than https://myhost/

I've discovered exactly the same issue on the restplus Github issues

I've fixed my issue temporarily with the monkey-patch mentioned on that page. The Swagger UI loads, and looking at the HTML source I see that swagger.json is indeed fetched from https://myhost.

Why is this happening, and how can I fix it without the monkey-patching?

HTTPS is courtesy of Cloudflare's "flexible" HTTPS service.

My app is behind Nginx which is configured thus, and hasn't been causing any issues as far as I'm aware:

...
http {
  ...
  server {
    location / {
      charset UTF-8;
      try_files $uri @proxy_to_app;
    }
    location @proxy_to_app {
      charset UTF-8;
      proxy_intercept_errors on;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://127.0.0.1:5000;
    }
  }
}
ukosteopath
  • 343
  • 5
  • 14

3 Answers3

4

I have used below to get it worked. You can view the stable example in below link.

http://flask-restplus.readthedocs.io/en/stable/example.html

from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
Yusuf
  • 139
  • 7
0

I am not sure this is completely secure, but here is how I've fixed it in Nginx:

sub_filter "http://$host/" "https://$host/";
sub_filter_once off;
proxy_redirect    off;

I am offloading SSL on Nginx and this works without any issues for me. It also removes the need to monkey patch application code.

The method you've listed from flask-restplus issues is definitely considered insecure:

Please keep in mind that it is a security issue to use such a
middleware in a non-proxy setup because it will blindly trust
the incoming headers which might be forged by malicious clients.
n3rV3
  • 1
  • 2
  • The monkey-patch I'm applying does seem safe, since it simply ensures that Flask-RestPlus re-writes the scheme/protocol to https instead of http. The warning you've cited relates to the `ProxyFix` middleware for Werkzeug, which is not the approach I'm taking. The RestPlus author says it detects headers set by Nginx. I believe those headers are correct in my setup, so I'm stumped. I'll continue debugging this and see what I can find. For now I'm sticking with the monkey-patch. I notice that [others are doing similar](https://github.com/noirbizarre/flask-restplus/issues/132). – ukosteopath Jul 13 '18 at 08:56
0

I had this issue.

When I viewed the source of the "No spec provided" error page I could see this at the bottom:

 window.onload = function() {
            const ui = window.ui = new SwaggerUIBundle({
                url: "http://127.0.0.1:5000/api/v1/swagger.json",
                validatorUrl: "" || null,
                dom_id: "#swagger-ui",
                presets: [
                    SwaggerUIBundle.presets.apis,
                    SwaggerUIStandalonePreset.slice(1) // No Topbar
                ],
                plugins: [
                    SwaggerUIBundle.plugins.DownloadUrl
                ],
                displayOperationId: false,
                displayRequestDuration: false,
                docExpansion: "none"
            })

Note that the uri is http://127.0.0.1:5000. This indicates that the hostname and protocol isn't being passed to the server (in my case Gnuicorn).

To fix this, I added the following to my ngnix config:

        location / {
                # new lines
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $host;

                # this already existed
                proxy_pass http://127.0.0.1:5000;
        }

Note that I had to add this inside the ssl server block. When I first tried it I put it in the non-SSL part and it made no difference.

Nick Lothian
  • 1,221
  • 11
  • 23