Bug: KopiaUI v0.19.0: WebDAV: Unable to authenticate against SHA256 Digests/RFC 7616 (HTTP userhash extension)

tags: webdav, auth, authentication, digest, htdigest, realms, rfc-7616, rfc-2617, rfc-1945, sha2, sha256, hashing, salting, encryption, encrypting, user accounts, message-digest-algorithm, lighttpd, mod_webdav, mod_auth, mod_authn_file, auth.backend

I’m unable to connect KopiaUI to a WebDAV target. The endpoint runs Lighttpd (1.4.69) using mod_authn_file htdigests. It is configured to use SHA256 hashing. It also uses RFC 7616’s HTTP Digest auth userhash extension.[1]

Note RFC 7616 (2015) obsoletes RFC 2617 HTTP Authentication: Basic and Digest Access Authentication (1999).[2]

Attempting to connect to it as a repository returns

Connect Error: INTERNAL: internal server error: unable to complete GetBlob(kopia.repository,0,-1) despite 10 retries: error determining sharded path: error getting sharding parameters for storage: ReadStream .shards: Authorize .shards: 401

Cadaver (0.26-1) and WebDAV Browser (1.2.2) (a Firefox, Edge, Chromium extension) operate as expected.[3] IE:

cadaver https://$sub.$domain.internal:$port/dav

This capability is important to allow using HTTP/WebDAV realms to ensure partitioning appropriate permissions as described in RFC 7616,[4] first described in RFC 1945 (HTTP/1.0).[5]

I’ve also looked over the CLI options but find no indication that Kopia supports this.[6]

I am attemping to ensure this functions before beginning to integrate a database backend for properly encrypted (ie: salted & hashed) accounts via mod_authn_dbi. It too uses the SHA-256 for the message digest algorithm.[7]

The Lighttpd conf follows (where $domain is the domain; mimetype.assign block heavily truncated). Syntax can be validated by lighttpd -f $confLocation -t.

Please confirm repoducability and advise.

KopiaUI Version v0.19.0 1f8f728c4133d4f419df93e58a5b54c3bf9c75e3 kopia/htmlui 16dfe3793a020835bc6a98d75e7c2550210954e1 built on Sat Dec 28 09:10:28 UTC 2024 fv-az798-492 (appimage)

  1. See Lighttpd’s Docs, htdigest (mod_authn_file) for description, examples, templates
  2. RFC 2617, HTTP Authentication: Basic and Digest Access Authentication, June 1999, IETF
  3. Cadaver, Joe Orton, GitHub; WebDAV Browser 1.2.2, WebDAVDevs, GitHub
  4. RFC 7616, HTTP Digest Access Authentication, September 2015, IETF
  5. http - What is the “realm” in basic authentication, Stack Overflow
  6. repository create webdav, Kopia documentation
  7. dbi (mod_authn_dbi) (since lighttpd 1.4.56)

lighttpd.conf.20250318T180337UTC

{
    var.log_root                   = "/var/log/lighttpd/"
    var.server_root                = "/www/"
    var.state_dir                  = "/var/run/"
    var.home_dir                   = "/var/run/lighttpd"
    var.conf_dir                   = "/etc/lighttpd"
    var.vhosts_dir                 = "/www//vhosts"
    var.cache_dir                  = "/var/cache/lighttpd"
    var.socket_dir                 = "/var/run/lighttpd/sockets"
    server.document-root           = "/www/"
    server.upload-dirs             = ("/tmp")
    server.errorlog                = "/var/log/lighttpd/error.log"
    server.pid-file                = "/var/run/lighttpd.pid"
    server.username                = "http"
    server.groupname               = "www-data"
    server.port                    = "2080"
    index-file.names               = ("index.php", "index.html", "index.htm", "default.htm")
    static-file.exclude-extensions = (".php", ".pl", ".fcgi")
    mimetype.assign                = (
        ".sarif.json"                     => "application/sarif+json",
        ".sarif-external-properties.json" => "application/sarif-external-properties+json",
        ".1905.1"                         => "application/vnd.ieee.1905",
        ".tar.bz2"                        => "application/x-gtar-compressed",
        ".tar.gz"                         => "application/x-gtar-compressed",

        < snip >

        ".sisx"                           => "x-epoc/x-sisx-app",
        "README"                          => "text/plain;charset=utf-8",
        "Makefile"                        => "text/x-makefile;charset=utf-8",
        ""                                => "application/octet-stream",
    )
    setenv.add-response-header     = (
        "Referrer-Policy"           => "same-origin",
        "Strict-Transport-Security" => "max-age=31536000; includeSubDomains; preload",
        "X-Frame-Options"           => "DENY",
        "X-Content-Type-Options"    => "nosniff",
        "Server"                    => "stor",
    )
    server.modules                 = (
        "mod_auth",
        "mod_authn_file",
        "mod_openssl",
        "mod_setenv",
        "mod_webdav",
        "mod_openssl",
    )


    $HTTP["url"] =~ "^/dav(?:/|$)" {
        # block 1
        webdav.activate                = "enable"
        webdav.sqlite-db-name          = "/var/run/lighttpd/webdav-lock.db"
        auth.backend                   = "htdigest"
        auth.backend.htdigest.userfile = "/etc/lighttpd/webdav-digest.user"
        auth.require                   = (
            "/dav" => (
                "method"    => "digest",
                "algorithm" => "SHA-256",
                "userhash"  => "enable",
                "realm"     => "dav",
                "require"   => "valid-user",
            ),
        )
        auth.cache                     = (
            "max-age" => "3600",
        )
        dir-listing.activate           = "enable"
        dir-listing.encoding           = "utf-8"

    } # end of $HTTP["url"] =~ "^/dav(?:/|$)"

    $SERVER["socket"] == ":2443" {
        # block 2
        ssl.engine               = "enable"
        ssl.openssl.ssl-conf-cmd = (
            "MinProtocol" => "TLSv1.3",
            "Options"     => "-ServerPreference",
        )
        ssl.read-ahead           = "disable"
        ssl.pemfile              = "/etc/ssl/private/$domain.internal.pem"
        ssl.privkey              = "/etc/ssl/private/$domain.internal-key.pem"

    } # end of $SERVER["socket"] == ":2443"

    $HTTP["host"] == "stor.$domain.internal" {
        # block 3
        server.document-root = "/mnt/sda1/"

    } # end of $HTTP["host"] == "stor.$domain.internal"
}