[Bug 295882] www/seahub: seafdav does not log real client IP address

From: <bugzilla-noreply_at_freebsd.org>
Date: Fri, 05 Jun 2026 18:26:39 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=295882

            Bug ID: 295882
           Summary: www/seahub: seafdav does not log real client IP
                    address
           Product: Ports & Packages
           Version: Latest
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: Individual Port(s)
          Assignee: ports-bugs@FreeBSD.org
          Reporter: lloydsystems1@tpg.com.au

Seafile server version 11.0.12 running on FreeBSD 14 with WebDAV extension
enabled.

The seafdav.log file records all client requests as coming from 127.0.0.1 (the
Apache reverse proxy) rather than the real client IP address. Given that
Seafile will be running behind an Apache or Nginx proxy 99% of the time, I
can’t imagine any case where this would be at all useful.

I have this in the Apache virtual host:

<Location /seafdav>
    ProxyPass http://127.0.0.1:8081/seafdav
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
</Location>

Expected behaviour is to look for and log the X-Forwarded-For header, but it
does not.

The Seafile manual recommends using Fail2Ban to protect the server. This should
logically apply to the SeafDAV service also, but it needs the remote IP
address.

By searching the code I found the routine responsible for logging the client
requests is
/usr/local/www/haiwen/seafile-server/seahub/thirdpart/wsgidav/wsgidav_app.py
and patched it to add xff as shown below.

class WsgiDAVApp:
...
    def __call__(self, environ, start_response):
    # util.log(“SCRIPT_NAME={!r}, PATH_INFO={!r}”.format(
    #    environ.get(“SCRIPT_NAME”), environ.get(“PATH_INFO”)))

        path = environ["PATH_INFO"]

        # --- Added ---
        xff = environ.get("HTTP_X_FORWARDED_FOR")
        if xff:
            environ["REMOTE_ADDR"] = xff.split(",")[0].strip()
        # --- End ---

Now seafdav.log shows the real client IP address, but more needs to be done to
properly protect SeafDAV with Fail2Ban.

Due to the default behaviour of WebDAV, valid clients will generate “401 Not
Authorized” errors in the log before they authenticate. When processed by
Fail2Ban, these false positives could block legitimate users. A more reliable
indicator for Fail2Ban would be the “Authentication (basic) failed for user”
messages, but these do not include the IP address.

The file
/usr/local/www/haiwen/seafile-server/seahub/thirdpart/wsgidav/http_authenticator.py
is responsible for logging authentication failures. The code was patched to
read REMOTE_ADDR and append it to the log entries.

class HTTPAuthenticator(BaseMiddleware):
    ...
    def handle_basic_auth_request(self, environ, start_response):
        realm = self.domain_controller.get_domain_realm(environ["PATH_INFO"],
environ)
        auth_header = environ["HTTP_AUTHORIZATION"]
        auth_value = ""
        try:
            auth_value = auth_header[len("Basic ") :].strip()
        except Exception:
            auth_value = ""

        auth_value = base64.decodebytes(util.to_bytes(auth_value))
        auth_value = util.to_str(auth_value)
        user_name, password = auth_value.split(":", 1)

        # --- Get client IP ---
        client_ip = environ.get("REMOTE_ADDR", "unknown")

        if self.domain_controller.basic_auth_user(realm, user_name, password,
environ):
            environ["wsgidav.auth.realm"] = realm
            environ["wsgidav.auth.user_name"] = user_name
            return self.next_app(environ, start_response)

        # --- Append client IP ---
        _logger.warning(
            "Authentication (basic) failed for user {!r}, realm {!r}, IP
{!r}.".format(
                user_name, realm, client_ip

The seafdav.log can now be parsed with a Fail2Ban filter like this:

[Definition]

failregex = ^.*WARNING\s+:\s+Authentication \([^)]*\) failed for user '[^']*',
realm '[^']*', IP '<HOST>'\.$

ignoreregex = 

I think the SeafDAV code should be fixed to make the logging useful and
workable for Fail2Ban, as it is recommended by Seafile.

-- 
You are receiving this mail because:
You are the assignee for the bug.