Configure a WebDAV enabled webserver for multiple user folders and one shared folder

I recently was requested to set up an apache webserver to provide WebDAV folders for multiple users with individual folders. Additionally, all users should be able to use a shared WebDAV folder. After some extensive research I was unable to find any good hints on how to actually do this.

My first approach was to set up the WebDAV folders within the DocumentRoot and hence only one <Directory> configuration item was needed with multiple <Location> configuration items. At first sight, this seemed to provide what was requested.

After some testing it showed this approach had a major security issue: If the user just accessed http://webdav.example.com and authenticated successfully, the user was able to see and write to all available folders. This is obviously an undesirable behaviour.

So I decided to move the WebDAV folders out of the DocumentRoot and providing an Alias, <Directory> and <Location> configuration item for each folder and setting up access to that folder in the <Location> configuration item. Additionaly, this frees up the http://webdav.example.com which can provide further information on how to use the service.

And voilà, every user has his own WebDAV folder and can not see or access the folders of other users.

In the following example, three WebDAV folders are configured, one for each user and a shared folder for all users.

DAVLockDB /serv/webdav.example.org/auth/DAVLock
DAVMinTimeout 180 

NameVirtualHost 10.1.1.1
<VirtualHost webdav.example.org>

    ServerName  webdav.example.org
    ServerAdmin webmaster@example.org

    DocumentRoot /serv/webdav.example.org/htdocs/

    LogLevel warn 

    ErrorLog /serv/webdav.example.org/logs/error.log
    CustomLog /serv/webdav.example.org/logs/access.log combined

    # user1
    Alias /user1 /serv/webdav.example.org/webdav/user1

    <Directory /serv/webdav.example.org/webdav/user1>
        DAV             On
        AuthType        Basic 
        AuthName        "My WebDav Directory"
        AuthUserFile    /serv/webdav.example.org/auth/webdav.user
        Require         valid-user 
    </Directory>

    <Location /user1/>
        Require     user user1
    </Location>

    # user2
    Alias /user2 /serv/webdav.example.org/webdav/user2

    <Directory /serv/webdav.example.org/webdav/user2>
        DAV             On
        AuthType        Basic 
        AuthName        "My WebDav Directory"
        AuthUserFile    /serv/webdav.example.org/auth/webdav.user
        Require         valid-user 
    </Directory>

    <Location /user2/>
        Require     user user2 
    </Location>

    # transfer 
    Alias /transfer /serv/webdav.example.org/webdav/transfer

    <Directory /serv/webdav.example.org/webdav/transfer>
        DAV             On
        AuthType        Basic 
        AuthName        "My WebDav Directory"
        AuthUserFile    /serv/webdav.example.org/auth/webdav.user
        Require         valid-user 
    </Directory>

    <Location /transfer/>
        Require    valid-user 
    </Location>

</VirtualHost>

If you want to allow the user to access his WebDAV directory using an Internet browser you can add the following lines to the corresponding <Location> configuration item.

 Options +Indexes
 IndexIgnore ..
 IndexOptions -IconsAreLinks NameWidth=* FancyIndexing SuppressLastModified FoldersFirst 
 IndexOrderDefault Ascending Name

47 thoughts on “Configure a WebDAV enabled webserver for multiple user folders and one shared folder

    • Thank you!

      There is RFC 4331 discussing quotas for WebDAV but reading the mod_dav FAQ there is no actual implementation/support in mod_webdav.

      Another approach to implement a type of quota could be to create a file system specific to each user and size that file system to how much file system space you want the user to be able to use.

      • The problem seems to be that the webdav-folders are not user-specific. So all the quotas should be addressed on ‘www-data’-user.

        • I know, hence the idea of a unique file system per user configuration item.

          Although, as you correctly observe, all WebDAV users are basically www-data on the system it should still be possible to limit each WebDAV users file system usage by providing a dedicated file system for each configured WebDAV user.

          To demonstrate this, I have set up a temorary file system as show below:

          $ sudo bash
          $ mkdir -p /mnt/tmpfs/user1
          $ mount -osize=2m tmpfs /mnt/tmpfs/user1 -t tmpfs
          $ chown -R www-data:www-data /mnt/tmpfs/user1

          Also, have changed the <Location> and <Directory> configuration items to point to /mnt/tmpfs/user1

          To check if it works as expected, 2 files with each 1MB in size have been created using dd:

          $ dd if=/dev/zero of=test.img bs=1024 count=0 seek=1024
          $ dd if=/dev/zero of=test2.img bs=1024 count=0 seek=1024

          Using cadaver as client trying to upload all the 1MB image files:

          dav:/user1/> put test.img
          Uploading test.img to `/user1/test.img’:
          Progress: [=============================>] 100.0% of 1048576 bytes succeeded.
          dav:/user1/> put test2.img
          Uploading test2.img to `/user1/test2.img’:
          Progress: [=============================>] 100.0% of 1048576 bytes failed:
          507 Insufficient Storage
          dav:/user1/>

          And then checking the file system using df:

          $ df -h /mnt/tmpfs/user1
          Filesystem Size Used Avail Use% Mounted on
          tmpfs 2.0M 1.1M 1016K 51% /mnt/tmpfs/user1

          To me, it looks as if that approach could actually work :)

  1. cadaver is able to connect just fine but browsers can not, though. Takes some time to accomplish this one.

    • A full example as hinted in the bottom of the blog posting:

      <Directory /serv/webdav.example.org/webdav/user2>
      Options +Indexes
      IndexIgnore ..
      IndexOptions -IconsAreLinks NameWidth=* FancyIndexing SuppressLastModified FoldersFirst
      IndexOrderDefault Ascending Name

      DAV On

      AuthType Basic
      AuthName “My WebDav Directory”
      AuthUserFile /serv/webdav.example.org/auth/webdav.user
      Require valid-user
      </Directory>

      For this to work the apache module mod_autoindex has to be activated.
      The browser is limited to read-only when accessing the WebDAV location.

  2. My apologies for the inaccurency. Browsers are not able to connect to this ‘/mnt/tmpfs/user1’. Some settings might be wrong on my server.

    • Please check the example I have provided.
      The browser should use the exact same URL as used with cadaver.

      Warning:
      The tmpfs is really very temporary. As soon as you reboot the machine the data will be lost!
      I chose tmpfs to conceptually show how to use a file system together with WebDAV as means
      to implement a quota.

  3. For some – so far – unknown reason browser is continiously looking for the ‘DocumentRoot’-folder even though it is set to be ‘/mnt/tmpfs/userX’ for userX. And for another user ‘userY’ which has a webdav foldern on the ‘DocumentRoot’-folder everything works fine.

  4. The example I have provided in the previous comment only showed the <Directory> configuration item.
    For it to all work you need three parts as shown in the blog post

    – Alias directive
    – <Directory> configuration item
    – <Location> configuration item

    My guess is that you are missing the Alias directive mapping /user1 to /mnt/tmpfs/user1
    Can you check that?

  5. For some reason the quotations are dropped out.

    The problem were:

    On Location I had:
    "
    ....

    and after correction there is:


    ....

    • Well, I’ll write the reply without the start and end marks:

      The faulty version was:
      Location /mnt/tmpfs/user1/

      And the sound one is:
      Location /user1/

  6. so, is that script for manual folders? I have about 2200 users. I reeeeeaaaallly don’t want to have to take that script and insert lines for each user.. My issue is that if a user is using CyberDuck or BitKinex, the entire tree is viewable, not just their folder branch. Basically this means that User A can see User B’s folders and files, but can’t write to them. Not being able to write to another user’s folder is a great thing, but the fact that they can drill down through the other user’s files is not.. It causes slow load time with either WebDAV client. Is there a way for them to only see their files/folders?

    • Can you provide an example of your configuration?
      Did you try to set up my example?
      I was unable to reproduce the behaviour you report using the setup described in my blog posting. After being authenticated and using a different URL I was prompted for a username and password (and thus had to reauthenticate with the correct credentials).

      In any case, if I had to set up WebDAV for 2200 users I would create a VirtualHost file and use the Apache2 Includedirective to include the 2200 snippets which can be automatically generated using a simple shell script and a template that replaces @@USER@@ with the actual user name and also creates the corresponding /serv/webdav.example.org/webdav/@@USER@@ user directory. The snippets are best named as the corresponding user for which the WebDAV service is provided.

      The template could look like this:

      # @@USER@@
      Alias /@@USER@@ /serv/webdav.example.org/webdav/@@USER@@

      <Directory /serv/webdav.example.org/webdav/@@USER@@>
      DAV On
      AuthType Basic
      AuthName "My WebDav Directory"
      AuthUserFile /serv/webdav.example.org/auth/webdav.user
      Require valid-user
      </Directory>

      <Location /@@USER@@/>
      Require user @@USER@@
      </Location>

      Technically, you could even push it one step further by creating complete VirtualHosts if you can setup subdomains with the name of each individual user so your users could access their WebDAV service using an URL like http://username.webdav.example.org

      Does that help you?

  7. It works fine for me, but on my debian server, www-data is the owner of the files and folder.
    How can i connect to the server with ssh, with my user login and pwd ? (for example to add files in my webdav directory with rsyn). Or is it possible to mount the user’s home directory in the webdav directory ?

    • WebDAV does not provide file system access control on a per user basis. As you have observed, everything belongs to www-data.

      You could do it the other way around: mounting the WebDAV directory using a bind mount into the user’s directory. The drawback here is, if you have many users each need
      to be part of the www-data group and if the users disregard the bind mount they can simply walk the directory tree deleting everything www-data group can write to :)

      Another approach could be:
      Use a recent version of OpenSSH (>= 4.9 I think) and use the chroot sftp feature (which is not that easy to configure as it may seem) and mount bind the specific user’s WebDAV directory
      into the user specific chroot directory. Although every user is still part of the www-data group the chroot and mount should ensure the user only sees the part of the directory tree they should see
      (and have access to).

      Does that help you?

  8. The browser access part :
    ======================
    Options +Indexes
    IndexIgnore ..
    ndexOptions -IconsAreLinks NameWidth=* FancyIndexing SuppressLastModified FoldersFirst
    IndexOrderDefault Ascending Name
    ======================
    should be placed in the -section and not inside the -section, because you can access the other user directories with leaving the traling slash.

    • “In the -section and not inside the -section”? I do not quite understand, can you please provide me with further details?

  9. WordPress has eaten my tags. :-) The browser access part should be placed in the Location-section, not in the Directory-section. Sorry for the mistake.

    • Thanks for the hint!
      I have updated the blog posting and have changed the reference from Directory to Location.

  10. Pingback: The Linux Self-Learner _1 – Learning Webdav | TBlueLinux
  11. Hi,
    Looks great, however, simple things are not clear to me. If location of DocRoot in filesstem is /serv/site.com/htdocs/ but webdav parent folder is /serv/site.com/webdav how then a user can reach that? What address he should go? site.com/webdav returns “Not found!”.
    Thank you.

    • No problem. I hope I can resolve your issues/questions.

      There are two things happening. First, you have a simple webserver with its DocumentRoot pointing to /serv/webdav.example.org/htdocs/. Second, you have a set of aliases defined (and the corresponding Directory configurations which in turn set up the authentication for _each_ directory.
      The key here to understand is that the users “DocumentRoot” is not contained _within_ the main webserver DocumentRoot but resides “next” to it.

      This will avoid a “crossover” from the simple webserver to the WebDAV enabled “parts” of the webserver.

      When you look at the configuration you can see that WebDAV is enabled for each user directory and also authentication is set up.

      To access the simple webserver:

      http://webdav.example.org (this is where you could create a “landing page” for your users)

      And the WebDAV locations:

      http://webdav.example.org/user1 (this is the WebDAV enabled “space” for user1)
      http://webdav.example.org/user2 (this is the WebDAV enabled “space” for user2)
      http://webdav.example.org/transfer (this is the WebDAV enabled “space” for any user configured in /serv/webdav.example.org/auth/webdav.user

      Does this information make it clearer?

      • Thank you a lot for the detailed explanation! So far I managed to make a common webDAV directory and learned how to add operate with symlinks. Next error I get, is when I try to implement a user owened/controlled webdav dir.
        My config now is:

        DAVLockDB /serv/webdav.dhtpc.org/auth/DAVLock
        DAVMinTimeout 180

        NameVirtualHost *

        ServerAdmin webmaster@localhost
        LogLevel warn

        DocumentRoot /serv/webdav.dhtpc.org/htdocs/

        Options Indexes MultiViews
        AllowOverride None
        Order allow,deny
        allow from all

        # Public WebDAV
        Alias /webdav /serv/webdav.dhtpc.org/webdav/public

        DAV On
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
        AuthName “webdav”

        # private WebDAV. For user chuk
        Alias /chuk /serv/webdav.dhtpc.org/webdav/chuk

        DAV On
        AuthType Basic
        AuthName “My WebDav Directory”
        AuthUserFile /serv/webdav.dhtpc.org/auth/webdav.users
        Require valid-user

        Require user chuk

        Then I created pawwd file for this user and set permissions for www-data
        ls -l /serv/webdav.dhtpc.org/auth/webdav.users
        -rw-r—– 1 root www-data 44 Feb 18 23:13 /serv/webdav.dhtpc.org/auth/webdav.users

        But when I try to access the folder I get Internal server error which is reflected in logs as:
        [Tue Feb 19 08:13:51 2013] [crit] [client 127.0.0.1] configuration error: couldn’t perform authentication. AuthType not set!: /chuk/
        [Tue Feb 19 08:19:47 2013] [crit] [client 192.168.0.11] configuration error: couldn’t perform authentication. AuthType not set!: /chuk/

        I would appreciate if you can point me toward source of the problem.
        Thank you.

        • Sadly, the blog stripped away some Apache configuration tags so it is hard to tell if they are really missing or just stripped away.
          I have sent you an email with a proposal on how to help you.

  12. Great post, which I will put to use – but please, oh please, use HTTPS with this. Unless you really want your user’s credentials, and their files with it, to be up for grabs.

    • Good point! I should upgrade it to use https.
      To my defense I must say the emphasis of the post was on how to configure WebDAV for multiple users, not on how to make it secure…

  13. hexeract: I hate to say it but I’m a true newbie around WebDAV and I’m learning slowly. I really like your posting and it falls in line with what I want to do. I don’t want to take up too much of your time but I have a few questions and was wondering if you might help mw with my Webdav.conf file.

    I want to create the ability for users on a server to either use the browser or the davfs2 or a Windows tool like Expandrive or something like. In essence a shared file system approach ala’ SSHFS. This is all on an internal network right now (just testing).

    On my internal network the web server is 192.168.1.4. The users have home directories like /home/layton or /home/susy. my webdav.conf looks like the following:

    DAVLockDB /etc/httpd/DAVLock
    DAVMinTimeout 180

    NameVirtualHost 192.168.1.4

    ServerName 192.168.1.4
    ServerAdmin webmaster@e192.168.1.4

    DocumentRoot /etc/httpd/htdocs/

    LogLevel warn

    ErrorLog /etc/httpd/logs/error.log
    CustomLog /etc/httpd/logs/access.log combined

    # laytonjb
    Alias /laytonjb /home/laytonjb

    DAV On
    AuthType Basic
    AuthName “My WebDav Directory”
    AuthUserFile /etc/httpd/webdav.users.pwd
    Require valid-user

    Require user laytonjb
    Options +Indexes
    IndexIgnore ..
    IndexOptions -IconsAreLinks NameWidth=* FancyIndexing SuppressLastModified FoldersFirst
    IndexOrderDefault Ascending Name

    # susy
    Alias /susy /home/susy

    DAV On
    AuthType Basic
    AuthName “My WebDav Directory”
    AuthUserFile /etc/httpd/webdav.users.pwd
    Require valid-user

    Require user susy
    Options +Indexes
    IndexIgnore ..
    IndexOptions -IconsAreLinks NameWidth=* FancyIndexing SuppressLastModified FoldersFirst
    IndexOrderDefault Ascending Name

    When I test the configuration by using the URL: 192.168.1.4/laytonjb it says it can’t find the URL. I also tried cadaver and got the following:

    [root@home4 etc]# cadaver http://192.168.1.4/laytonjb
    Could not access /laytonjb/ (not WebDAV-enabled?):
    405 Method Not Allowed
    Connection to `192.168.1.4′ closed.

    I hate to ask you to debug my webdav.conf file but I’m pretty lost at this point and any help is greatly appreciated. Thanks!

    • Just a wild guess:

      Can the user www-data (or whatever user your apache runs as) read the home directories of the users?

      I would strongly advise against doing this.
      Set up a filesystem and have a bind mount into each users directory with their corresponding WebDAV directory.

      Does this information help you?

  14. I think I understand. I’ve never really used bind mounts before. But what you are saying is that I should bind mount each user’s home directory to a webdav directory? I’ll see what I can do.

    Thanks!

    • Not quite. The other way around.

      Have a directory in the users directory called webdav (for example). Have the actual users webdav directory in something like /data/webdav/johndoe.
      Then bind-mount /data/webdav/johndoe to /home/johndoe/webdav.

      As long as your users have shell access and you are not careful with your user/group/world directory/file flags you will allow people to “peek” into the other peoples webdav directory…

      WebDAV, on a file system level, does not know about users/groups. It all belongs to ‘www-data’.

      In any case, your www-data user needs write access
      to the users webdav directory (how else can it deposit/delete files?)

    • No idea. I would try to bindmount the directory of user2 into the directory of user1…

  15. Very helpful post! I did some more research and got it down to below. As these are just directories, I got rid of the blocks. Also, using a group file, you can have someone be a member of more than one group (https://httpd.apache.org/docs/2.4/howto/auth.html#lettingmorethanonepersonin). All the best.

      # Project 1
        Alias /project1 /var/www/webdav/project1
       
            DAV             On
    SSLRequireSSL
            AuthType        Digest
            AuthName        “webdav”
            AuthUserFile    “/usr/local/apache2/var/passwords”
    AuthGroupFile   “/usr/local/apache2/var/groups”
    Require group   project1
       
       
        # Project 2
        Alias /project2 /var/www/webdav/project2
       
            DAV             On
    SSLRequireSSL
            AuthType        Digest
            AuthName        “webdav”
            AuthUserFile    “/usr/local/apache2/var/passwords”
    AuthGroupFile   “/usr/local/apache2/var/groups”
    Require group   project2
       

    • Very interesting! I never thought about grouping user and have the access restricted by group membership!
      The use case I had was to have the users separate and sharing one directory was the exception.
      Thank you very much for your feedback and solution!

      • I stumbled on it by accident mate. Works perfectly though. As I’m working on a project that will (hopefully) eventually create about a hundred of these webdav shares, the next challenge is coming up with a way of automating the creation of the directories and adding to the apache.conf. Any ideas?

        • This really depends on how complex your group relations will be and how many uses will have access to how many different shares…

          A full blown configuration management framework can help but if you start from scratch, this can be a very steep learning curve for “just” this specific use case.

          Depending on our exact requirements, things can get either very easy or very difficult.

          A more manual approach would be to really understand and define the requirements and write some tools yourself…

          Projects are fixed (over a period of time) and there is a 1:1 relationship between groups and projects:
          You can set up all the projects/configuration and “just” need to update the users and group file using a tool that creates the file from whatever source you have which tracks users -> projects.

          And maybe SHA2 versions of the passwords, depending on how users sign up you may directly ask your users to submit the hash, caveat here is that you can not impose any password policy (i.e password must be 20+ characters of length)

          Projects and users constantly change:
          This is the nightmare scenario :-) where you basically have to set up a tool chain that creates the apache configuration snippets (preferably located in their own subdirectory so you can easily delete all of them at once) the users and group file and which also takes care of unreferenced project directories.

          This will require a second tool chain to “work through” the newly created configuration snippets to check which project directories remain intact and which need to be archived. For the projects that are new you need to check if you have an archived directory and move it to the right location again.

          I hope some hints here are helpful to decide on how to proceed with your project.

          • Hmmm. I think it might be time to learn some bash scripting. It’s not the end of the world to do it manually as I’m currently just testing with a couple of users. Thanks for the pointers though.

          • Any scripting language will do, for example perl, ruby, python or even php.
            If you feel very adventurous you can even try powershell :)

            Just remember, perl was written to have a “better” sed/awk tool :-)

Leave a reply to Jeff Layton Cancel reply