Credits: Digital Ocean for the WebDAV tutorial. hexeract for his post about multiple WebDAV users. Raymond Camden for the responsive directory index.

For a project I’ve been working on, I needed some place for users to upload and access files. I started off using Google Drive, Sharepoint etc, but I also wanted to have the option of hosting the files myself.

So, I looked at all sorts of options (Pydio, Seafile, Owncloud, FTP, WebDAV). I decided to have a stab at WebDAV, mainly due to it being included with Apache through mod_dav. Initially, I was using file based storage, but the Apache docs advise to use a dbm database instead due to being far faster. However, the same docs weren’t very helpful when it came to using dbm for group management / authorisation.

The following is what I did to get everything working. If you want to do something similar yourself, hopefully it will help. I’m using Debian 11, Apache 2.4.51.

First, create a directory for the credentials to live

# mkdir -p /usr/local/apache2/var

cd into the new directory and then create the dbm file that Apache will use for auth. I’ll add myself and also what groups I belong to. After running command below, you’ll be prompted for a password. I use pwgen for generating passwords.

# htdbm -ct users.dbm me@mydomain.com "project1,project2"

The -c is needed to create the database. -t is used to add a comment which Apache uses for the group credentials (it would have been nice if the Apache docs stated that!). The most important things to remember are: no spaces in group names, and separate each group with commas. Just stick with alphanumeric characters or an underscore, and you should be golden.

Next, we’ll add a second user who we only want to access project1

# htdbm -t users.dbm someone@somewhere.com "project1,"

The next user can only access project2

# htdbm -t users.dbm user@somewhere.com "project2,"

If you need to delete a user you can do that like so:

# htdbm -x users.dbm simon

I think you can see why setting up access control in this way is such a good idea. You’ve only got one file to manage. Adding, modifying or deleting users is just one line using htdbm. And the lookups are super fast. I’ve been using dbm for session management for years. You might want to install db-util as well for managing the database.

List database members:

htdbm -l users.dbm

Apache setup

You’ll need to enable the required modules.

# a2enmod dav
# a2enmod dav_fs
# a2enmod authn_dbm 
# a2enmod authz_dbm

The following is what I’m using for my apache.conf

DavLockDB /usr/local/apache2/var/DavLock
<IfModule mod_ssl.c>
<VirtualHost *:443>
    DocumentRoot "/var/www/files.yoursite.com"
    ServerName files.yoursite.com

    SSLCertificateFile /etc/letsencrypt/live/files.yoursite.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/files.yoursite.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    # Directory Listing Display Options
    IndexOptions +FancyIndexing
    IndexOptions +VersionSort
    IndexOptions +HTMLTable
    IndexOptions +FoldersFirst
    IndexOptions +IconsAreLinks
    IndexOptions +IgnoreCase
    IndexOptions +XHTML
    IndexOptions +NameWidth=*
    IndexOptions +SuppressDescription
    IndexOptions +SuppressHTMLPreamble
    IndexOptions +Charset=UTF-8
    # Make it responsive
    IndexHeadInsert "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"

    # project1 
    Alias /project1 /var/www/webdav/project1
    <Directory /var/www/webdav/project1>
        DAV                On
        SSLRequireSSL
        AuthType           Basic
        AuthName           "webdav"
        AuthBasicProvider  dbm
        AuthDBMUserFile    "/usr/local/apache2/var/users.dbm"
        AuthDBMGroupFile   "/usr/local/apache2/var/users.dbm"
        Require dbm-group  project1
    </Directory>

    # project2
    Alias /project2 /var/www/webdav/project2
    <Directory /var/www/webdav/project2>
        DAV                On
        SSLRequireSSL
        AuthType           Basic
        AuthName           "webdav"
        AuthBasicProvider  dbm    	
        AuthDBMUserFile    "/usr/local/apache2/var/users.dbm"
        AuthDBMGroupFile   "/usr/local/apache2/var/users.dbm"
        Require dbm-group  project2
    </Directory>

</VirtualHost>
</IfModule>

As always check your config is correct

# apachectl -t
Syntax OK

Restart Apache

# systemctl restart apache2

Each time you have a new project, you can just copy the Alias and directory block, at the bottom like so.

DavLockDB /usr/local/apache2/var/DavLock
<IfModule mod_ssl.c>
<VirtualHost *:443>
    DocumentRoot "/var/www/files.yoursite.com"
    ServerName files.yoursite.com

    SSLCertificateFile /etc/letsencrypt/live/files.yoursite.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/files.yoursite.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    # Directory Listing Display Options
    IndexOptions +FancyIndexing
    IndexOptions +VersionSort
    IndexOptions +HTMLTable
    IndexOptions +FoldersFirst
    IndexOptions +IconsAreLinks
    IndexOptions +IgnoreCase
    IndexOptions +XHTML
    IndexOptions +NameWidth=*
    IndexOptions +SuppressDescription
    IndexOptions +SuppressHTMLPreamble
    IndexOptions +Charset=UTF-8
    # Make it responsive
    IndexHeadInsert "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"

    # project1 
    Alias /project1 /var/www/webdav/project1
    <Directory /var/www/webdav/project1>
        DAV                On
        SSLRequireSSL
        AuthType           Basic
        AuthName           "webdav"
        AuthBasicProvider  dbm
        AuthDBMUserFile    "/usr/local/apache2/var/users.dbm"
        AuthDBMGroupFile   "/usr/local/apache2/var/users.dbm"
        Require dbm-group  project1
    </Directory>

    # project2
    Alias /project2 /var/www/webdav/project2
    <Directory /var/www/webdav/project2>
        DAV                On
        SSLRequireSSL
        AuthType           Basic
        AuthName           "webdav"
        AuthBasicProvider  dbm    	
        AuthDBMUserFile    "/usr/local/apache2/var/users.dbm"
        AuthDBMGroupFile   "/usr/local/apache2/var/users.dbm"
        Require dbm-group  project2
    </Directory>

    # project3
    Alias /project3 /var/www/webdav/project3
    <Directory /var/www/webdav/project3>
        DAV                On
        SSLRequireSSL
        AuthType           Basic
        AuthName           "webdav"
        AuthBasicProvider  dbm    	
        AuthDBMUserFile    "/usr/local/apache2/var/users.dbm"
        AuthDBMGroupFile   "/usr/local/apache2/var/users.dbm"
        Require dbm-group  project3
    </Directory>

</VirtualHost>
</IfModule>

Hopefully, this will save someone a few hours of digging around Apache mailing lists. Or looking through the source code for comments.

In a future post, I’ll tackle another item on my todo list – making the directory listing look a bit better. It’s not unbearable, but it’s a bit disappointing that in 2021, the default page is not even responsive. Hence the IndexHeaderInsert override in the apache conf.

October 2023 Update

I forgot to mention I’m now using mod_macro which simplifies setting up new shares:

<Macro WebDAVShare $alias>
    Alias /$alias /var/www/myserver.tld/$alias
    <Directory /var/www/myserver.tld/$alias>
        DAV                On
        SSLRequireSSL
        AuthType           Basic
        AuthName           "webdav"
        AuthBasicProvider  dbm
        AuthDBMUserFile    "/usr/local/apache2/var/users.dbm"
        AuthDBMGroupFile   "/usr/local/apache2/var/users.dbm"
        Require dbm-group  admin $alias
    </Directory>

    <Directory /var/www/myserver.tld/$alias/cgi-bin/*>
        Options ExecCGI
        SetHandler cgi-script
        DirectoryIndex
    </Directory>
</Macro>

Use WebDAVShare simon
Use WebDAVShare bob
Use WebDAVShare alice
Use WebDAVShare some_other_site

I’ve also started using an admin group. This group can access any webdav share (as shown in the conf). I add myself to that group, so if there’s a problem with a share, I can access it and troubleshoot any problems.

$ htdbm -t users.dbm simon@mydomain.com "admin"