eerotal/LibreSignage

Nginx reverse proxy configuration yields 400 Bad Request

TheAssassin opened this issue · 7 comments

I've set up an nginx reverse proxy in front of the Docker container (which works perfectly if used directly), as I do with all my web applications. Its main purpose is to handle TLS. Unfortunately, the config suggested in the docs doesn't work for me. I've set up a config just as described, and see the following page all the time:

Bad Request
Your browser sent a request that this server could not understand.
Additionally, a 400 Bad Request error was encountered while trying to use an ErrorDocument to handle the request.

Apache/2.4.25 (Debian) Server at 192.168.0.2 Port 80

I cannot explain why this is happening. Likely it's some security feature. I don't see any messages in neither error nor access log inside the container. I've tinkered with the options a bit, and removing the Host header makes LibreSignage generate redirects to the non-HTTPS URL, creating an infinite loop there. Any ideas what could be causing this behavior?

I'm using the latest LibreSignage container available as of today. My nginx config works well with most other containerized web apps I host, so I wonder why it would cause issues with apache2...

Is this the config you tried? Last time I tried that config it worked fine both with and without TLS but I'll have a look at it.

Yes, exactly that one. I even added proxy_redirects, as mentioned on the docs page, but to no avail.

Interestingly, at least the non-TLS nginx config seems to work just fine for me. The config I used was:

server {
	listen 80;
	server_name example.com;

	location / {
		proxy_http_version 1.1;
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_pass http://localhost:8080/;
	}	
}

Then I added example.com to my /etc/hosts for testing:

127.0.0.1	example.com

Finally I ran the Docker container with docker run -p 8080:80 eerotal/libresignage:latest. The web interface was then accessible at http://example.com and it didn't show any errors. Could you paste here the exact nginx config you used and the command you used to start the Docker container?

As mentioned above, removing the forwarding of Host makes the application redirect on the non-HTTPS version. I am not entirely surprised the non-TLS config works therefore. But I'm interested in getting TLS to work, that's what the nginx is there for.

By the way, I tend to use -p 127.0.0.1:port:80 for forwarding, otherwise the port will be forwarded globally. IMO that's an antipattern and Docker shouldn't behave like that (I'd rather opt in to opening ports on all interfaces). I will probably send a PR to the docs to add a note about that. I've seen too many Docker ports related misconfigurations recently...

server {
        listen 80;
        listen [::]:80;
        server_name signage.myserver.tld;

        return 301 https://signage.myserver.tld;
}

server {
        listen 443 ssl;
        listen [::]:443 ssl;

        server_name signage.myserver.tld;

        ssl_certificate /etc/letsencrypt/live/myserver.tld/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/myserver.tld/privkey.pem;
        ssl_dhparam /etc/nginx/dhparam.pem;

        location / {
                proxy_http_version 1.1;
                proxy_set_header Host $http_host;
                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_redirect http:// $scheme://;
                proxy_pass http://127.0.0.1:port/;
                include proxy_params;

                client_max_body_size 64M;
        }

        location ~ /\.ht {
                deny all;
        }
}

I am using docker-compose. Managing containers directly is too annoying IMO. My configuration is as simple as it could possibly be. Just docker-compose up -d to get it up and running.

version: "2"

services:
  libresignage:
    image: eerotal/libresignage:latest
    ports:
      - 127.0.0.1:port:80
    volumes:
      - ls_data:/var/www/html/data

volumes:
  ls_data:

Okay, this seems to be caused by the line include proxy_params; in your nginx config. At least on Debian, this includes the file /etc/nginx/proxy_params, which contains:

proxy_set_header Host $http_host;
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;

As these are already set in the main nginx config, you should remove the include line. I wonder if this also leads to the infinite redirects you mentioned though.

Having the settings twice in there shouldn't make a difference. It's set_header, not add_header. But for some weird reason, removing the include indeed makes a difference. I must be missing something... the headers are exactly the same in both the included and the server config...

I do get the redirect to non-HTTPS again, as you'd expected. Am I missing something here?

Does your code create the redirect to HTTP, or is it one of the libs or frameworks you use?

You can stop digging. It appears to work now. Don't ask me why... now I get the redirect to https://signage.myhost.tld/control just fine. I just use include proxy_params; only again now, too. I swear it used to be broken...

Thanks for your assistance nevertheless!