haiwen / seafile-docker

A Docker image for Seafile server
Other
544 stars 183 forks source link

"Contradictory scheme headers" when passing both X-Forwarded-Proto and X-Forwarded-Protocol #226

Closed daliborfilus closed 8 months ago

daliborfilus commented 4 years ago

When using this nginx configuration:

    location / {
        proxy_http_version 1.1;
        proxy_pass_request_headers on;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
        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_set_header X-Forwarded-Protocol $scheme;
        #proxy_set_header X-Forwarded-Ssl on;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://127.0.0.1:8080/;
        proxy_buffering off;
    }

Seafile throws Bad request "Contradictory scheme headers". I removed every single header one by one and the error disappears when you either remove X-Forwarded-Proto OR X-Forwarded-Protocol. If you have only one of them, it works. If both, it doesn't.

I know it isn't necessary to use all these headers, but they are surely not contradictory. (I use all of them just because each app uses different one and I use this as a template for every app.)

Seafile version: Docker seafileltd/seafile-mc:latest, tag: e0edf79c2d14,

rene-s commented 3 years ago

I am able to reproduce this. My vhost config:

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

  server_name test.*;

  ssl_certificate     /etc/acme.sh/certs/test.example.com.cert.pem;
  ssl_certificate_key /etc/acme.sh/certs/test.example.com.key.pem;

  include includes/security_tls.conf;
  include includes/security.conf;
  include includes/error_pages_default.conf;

  client_max_body_size 0;

  location / {
    include includes/proxy.conf;
    resolver 127.0.0.11 valid=30s;
    set $upstream_app 127.0.0.1;
    set $upstream_port 12080;
    set $upstream_proto http;
    proxy_pass $upstream_proto://$upstream_app:$upstream_port;
  }
}

proxy.conf:

# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

# Proxy Connection Settings
proxy_buffers 32 4k;
proxy_connect_timeout 240;
proxy_headers_hash_bucket_size 128;
proxy_headers_hash_max_size 1024;
proxy_http_version 1.1;
proxy_read_timeout 240;
proxy_redirect  http://  $scheme://;
proxy_send_timeout 240;

# Proxy Cache and Cookie Settings
proxy_cache_bypass $cookie_session;
#proxy_cookie_path / "/; Secure"; # enable at your own risk, may break certain apps
proxy_no_cache $cookie_session;

# Proxy Header Settings
proxy_set_header Connection $connection_upgrade;
proxy_set_header Early-Data $ssl_early_data;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Real-IP $remote_addr;

Result when I request the Seafile login form:

Bad Request
Contradictory scheme headers 

However, when I change X-Forwarded-Ssl from on to off (thus making it indeed contradictory to X-Forwarded-Proto) or when I omit X-Forwarded-Ssl the error interestingly disappears.

jonbranan commented 1 year ago

For those sick and tired of being sick and tired:

If you use linuxservers SWAG or older letsencrypt server, the issue is slightly more nuanced. The challenge is that proxy_set_header X-Forwarded-Ssl ""; or proxy_hide_header X-Forwarded-Ssl; doesn't work. Apparently, you cannot allow SWAG's /config/nginx/proxy.conf to set the header and hide it in your new .conf file. You have to essentially comment out include /config/nginx/proxy.conf; and copy the contents minus the X-Forwarded-Ssl header.

Here is my working example:

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

    server_name seafile.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    location / {
        # Timeout if the real server is dead
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

        # Proxy Connection Settings
        proxy_buffers 32 4k;
        proxy_connect_timeout 240;
        proxy_headers_hash_bucket_size 128;
        proxy_headers_hash_max_size 1024;
        proxy_http_version 1.1;
        proxy_read_timeout 240;
        proxy_redirect  http://  $scheme://;
        proxy_send_timeout 240;

        # Proxy Cache and Cookie Settings
        proxy_cache_bypass $cookie_session;
        #proxy_cookie_path / "/; Secure"; # enable at your own risk, may break certain apps
        proxy_no_cache $cookie_session;

        # Proxy Header Settings
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Early-Data $ssl_early_data;
        proxy_set_header Host $host;
        proxy_set_header Proxy "";
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto https;
        # DON'T ENABLE
        # proxy_set_header X-Forwarded-Ssl on;
        proxy_set_header X-Real-IP $remote_addr;

        # Only way to remove the X-Forwarded-Ssl header is to not set it in the first place.
        # include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app seafile;
        set $upstream_port 80;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }
}

Reason proxy_set_header or proxy_hide_header doesn't work.