Seafdav - MOVE command causing 502

Hi, the corresponding bug in the wsgidav is 183. Unfortunately, I can’t add links here, but it looks like we need to rewrite the Destination header in nginx (or patch wsgidav).

@Erik_Tews A couple of weeks ago I was reading every issue in wsgidav and already applied the workaround, here is how it looks like nginx in my case for seafdav

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

    proxy_set_header   X-Forwarded-Proto $scheme; # wsgidav issue 183 https://github.com/mar10/wsgidav/issues/183

    client_max_body_size 0;
    proxy_connect_timeout  36000s;
    proxy_read_timeout  36000s;
    proxy_send_timeout  36000s;
    send_timeout  36000s;
    proxy_http_version                 1.1;

    # This option is only available for Nginx >= 1.8.0. See more details below.
    proxy_request_buffering off;      # works well with mp4
    proxy_buffering off;

But even that, the js could not be served properly. The pictures (wsgidav logo) are missing and not properly displayed the pages in chrome browser. Just going without nginx in between works fine. If someone has hacked that (regex in nginx) to display properly the pages, go ahed pls. and share it.

Hi, I’m sorry, but as far as I understand the source code of wsgidav, the X-Forwarded-Proto header is never evaluated, so adding this header doesn’t help. There was a fix suggested in the bug to also evaluate the X-Forwarded-Proto header, but then another solution (rewriting the Destination) header was proposed, but that is more difficult with nginx.

Hi,
i use apache 2.4.38 as reverse proxy and did also get a 502 error when the webdav client from my KeePass try to initiate a MOVE.

Try to set the X-Forwarded-Proto header in my vhost config, but could not get it to work.

RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
RequestHeader set "X-Forwarded-SSL" expr=%{HTTPS}

This is the link to the bug in wsgidav: https://github.com/mar10/wsgidav/issues/183
Anybody else maybe has a hint or idea to fix this or where to raise a bug report?

In reading the issue and examining the code the problem is that WSGIDav examines the requested scheme (http or https) vs the actual scheme. In otherwords, when you contact the server you use https://server.org. This request is SSL terminated by the NGINX reverse proxy which forwards the request as http://127.0.0.1:8080. The headers passed along all indicate a scheme of HTTPS, but the actual communication is on HTTP (non-SSL). The WSSGIDav code sees the incongruity and rejects the request with the 502 error.

This is correct behavior according to the standard. The issue is the code should be checking the X-Forwarded-Proto and then act accordingly, but currently does not. So adjusting the X-Forwarded-Proto will have no affect at the moment.

The only other options are to rewrite the headers so it looks like the original request was HTTP or to enable HTTPS on the WebDav backend - neither of which is trivial. Or you just do not use a reverse proxy.

As I have time I’ll research options and see if a solution presents itself.

Adding something like this in the nginx vhost should rewrite the Destination header :

      set $destination $http_destination;
      if ($destination ~* ^https(.+)$) {
         set $destination http$1;
      }
      proxy_set_header Destination $destination;

If using Apache,

    RequestHeader edit Destination ^https: http:

You will need to enable mod_headers.

I can confirm that modifying the Destination header like so fixes the problem.

Have a tested working copy for Nginx (updated slightly):

add the following outside of the server context:

map $http_destination $nossl_destination {
"~^https:(.+)$" $1;
"~^http:(.+)$" $1;
}

then in location /seafdav add

proxy_set_header Destination "http:$nossl_destination";

Explanation:
The nginx map function operates by “mapping” a value to a variable based on a string (or other variable). In Nginx http headers are stored as variables with prefix $http_ and lowercase name of the header. Hence we are looking at the Destination header and storing a value based on what is there in $nossl_destination. The regex expression inside the map captures the “https:” part of the Destination header (we capture the : so we can evaluate for http as well as https). Everything after https is stored in an accessible variable called “$1” which we indicate as the value to be used for $nossl_destination by adding whitespace and stating it. We also have a value for straight http (based on configuration this may not be necessary but it is a good safety). Now in our location /seafdav we simply substitute a new Destination header with the “proxy_set_header Destination” and add http:$nossl_destination and everything should work.

There are various ways to write the regex and you can also use something akin to what @dani posted.

8 Likes

thanks! this is the only solution that worked for me

Confirmed working with @eruditewriter solution, thank you!

Thanks @eruditewriter, it works for me as well!

I had the same problem here and fixed it with Apache Reverse Proxy like this:

  #
  # seafile webdav
  #
  <Location "/seafdav">
    RequestHeader edit Destination ^https: http:
    ProxyPass http://127.0.0.1:8080/seafdav
    ProxyPassReverse http://127.0.0.1:8080/seafdav
  </Location>

@Jonathan maybe this should be in the upgrade notes: https://download.seafile.com/published/seafile-manual/upgrade/upgrade_notes_for_7.1.x.md

MOVE command is still failed ,after I add change the conf in nginx. Could you help me find the reason?
I am using seafdav in docker with my raspberry pi2.
seafdav.log shows that it is using http now:

2020-09-04 11:11:10.313 - <1996277120> wsgidav.wsgidav_app         INFO    :  172.19.0.6 - myMail@live.com - [2020-09-04 11:11:10] "HEAD /mydata/pass.kdbx" depth=0, elap=0.229sec -> 200 OK
2020-09-04 11:11:12.699 - <1996277120> wsgidav.wsgidav_app         INFO    :  172.19.0.6 - myMail@live.com - [2020-09-04 11:11:12] "PUT /mydata/.pass.kdbx.1599217869287" length=5241, elap=1.428sec -> 201 Created
2020-09-04 11:11:14.191 - <1996277120> wsgidav.wsgidav_app         INFO    :  172.19.0.6 - myMail@live.com - [2020-09-04 11:11:14] "HEAD /mydata/pass.kdbx" depth=0, elap=0.320sec -> 200 OK
2020-09-04 11:11:15.571 - <1996277120> wsgidav.wsgidav_app         INFO    :  172.19.0.6 - myMail@live.com - [2020-09-04 11:11:15] "MOVE /mydata/.pass.kdbx.1599217869287" dest="http://mydomain.com:843/seafdav/mydata/pass.kdbx", depth=0, overwrite=T, elap=0.262sec -> 502 Bad Gateway

Hi, for some unknown reason I couldn’t get the solutions posted by @eruditewriter and @dani to work at all (but huge thanks anyway for pointing in the right direction!).
After much trial and error I noticed that MOVE no longer fails when I completely strip scheme & hostname in the Destination header. More precisely:

map $http_destination $nossl_destination {
        "~^https?.*(/seafdav/.*)$" $1;
}

and then for the seafdav location:

proxy_set_header   Destination "$nossl_destination";

I’m not sure if this is approach is technically correct / intended behaviour but at least it’s working for me. I’m also sure there’s an easier way to strip out the hostname than using the mapping above, but that’s for more seasoned nginx admins. (At the very least I’ll rename the nossl_destination variable to something more appropriate, e.g. plain_url or so.)
Any feedback is still appreciated of course.

The regexp I gave is not quite right, it also matches (sub)directories named seafdav. I’ve now settled for this:

        # in the "location /seafdav" block:
        set $destination $http_destination;
        if ($destination ~* ^https?://[^/]+(/seafdav/.+)$) {
                set $destination $1;
        }
        proxy_set_header Destination $destination;

I’d very much appreciate some official guidance in the manual. (That is, something more detailed than just a simple reference to the present thread…) I don’t think using TLS in the reverse proxy is only an edge case. It’s important enough to merit proper documentation.

@andi-blafasl This isn’t working for me. Is there anything else you did? I am using the exact same Apache Directive as you, just with my port changed (I am using a different WebDAV port).

This solution worked for us! Please add it to the documentation!

This has been added long time ago.

You’re right. I just didn’t see it.