so, you own a raspberry pi. that's cool. maybe you have a spare hard drive at hand. and you might wonder, what your next software project could be. well, why not running a lightweight and secure webdav server with user authenticationon your pi? here's a tutorial for you …

table of contents

installation

open your favourite command line application (like terminal.app on mac; prompt on ios; etc.) and connect to your raspberry pi via ssh: $ ssh pi@raspberrypi.local; once connected, please install lighttpd – that's an open source and lightweight server, perfect for your pi:

$ sudo apt-get install lighttpd lighttpd-mod-webdav apache2-utils


configuration

create some folder and adjust permissions, by running these command line by line:

$ sudo chown www-data:www-data /var/run/lighttpd/
$ sudo lighty-enable-mod auth
$ sudo mkdir -p /var/www/webdav/web
$ sudo chown www-data:www-data /var/www/webdav/web

this can be used as a default configuration. feel free to change the webroot path to any other path, i.e. external hard drives, etc.

if you want to continue without transport encryption, i suggest to initially set it up like this, to make sure it's working, but then tweak the setup with some nice encryption. skip to those sections, if you already know how to setup lighttpd: self-signed ssl certificate, or let's encrypt certificate.

edit the lighttpd.conf file $ sudo nano /etc/lighttpd/lighttpd.conf to make it look like this (change server.name's value to match raspberr pi's hostname):

server.modules = (
	"mod_access",
	"mod_alias",
	"mod_compress",
	"mod_redirect",
	"mod_setenv",
	"mod_webdav",
)

server.document-root        = "/var/www/html"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 80

index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

$SERVER["socket"] == ":80" {
	server.name = "raspberrypi.local"
	server.document-root = "/var/www/webdav/web"
	
	alias.url = ("/webdav" => "/var/www/webdav/web")
	$HTTP["url"] =~ "^/webdav($|/)" {
		webdav.activate = "enable"
		webdav.is-readonly = "disable"
		webdav.sqlite-db-name = "/var/run/lighttpd/lighttpd.webdav_lock.db"
		auth.backend = "htpasswd"
		auth.backend.htpasswd.userfile = "/var/www/webdav/passwd.dav"
		auth.require = ( "" => ( "method" => "basic",
			"realm" => "webdav",
			"require" => "valid-user" ) )
	}
}

user authentication

as you might have seen in the configuration file above, this sample setup already includes user authentication. please run the following commands to add login credentials. you can run the first line multiple times, with different usernames, to create multiple user accounts:

$ sudo htpasswd -c /var/www/webdav/passwd.dav USERNAME
$ sudo chown root:www-data /var/www/webdav/passwd.dav
$ sudo chmod 640 /var/www/webdav/passwd.dav

finally, you should enable authentication and restart the server with:

$ sudo lighty-enable-mod auth
$ sudo /etc/init.d/lighttpd restart

now you can connect to your webdav server with url http://raspberrypi.local/webdav. if you upload a file to the webdav share, it will become accessible through http://raspberrypi.local/yourfilename.


self-signed ssl certificate

adding at least a self-signed certificate for transport encryption is a good option. a self-signed certificate has the same strong encryption as any certificate issued by a certificate authority. if you followed the above steps, you need to make some adjustments to lighttpd.conf. basically, you need to change the port and add these two lines:

ssl.engine  = "enable"
ssl.pemfile = "/etc/lighttpd/certificates/raspberrypi.local.pem"

please run $ sudo nano /etc/lighttpd/lighttpd.conf and adapt your configuration to this example (remember to change your hostname if necessary. i assume, you will do this in the following sections without a further reminder, on your own):

server.modules = (
	"mod_access",
	"mod_alias",
	"mod_compress",
	"mod_redirect",
	"mod_setenv",
	"mod_webdav",
)

server.document-root        = "/var/www/html"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 80

index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

$HTTP["scheme"] == "http" {
	$HTTP["host"] =~ "raspberrypi.local" {
		url.redirect = ( "^/(.*)" => "https://raspberrypi.local/$1" )
	}
}

$SERVER["socket"] == ":443" {
	server.name = "raspberrypi.local"
	server.document-root = "/var/www/webdav/web"
	
	ssl.engine  = "enable"
	ssl.pemfile = "/etc/lighttpd/certificates/raspberrypi.local.pem"
	
	alias.url = ("/webdav" => "/var/www/webdav/web")
	$HTTP["url"] =~ "^/webdav($|/)" {
		webdav.activate = "enable"
		webdav.is-readonly = "disable"
		webdav.sqlite-db-name = "/var/run/lighttpd/lighttpd.webdav_lock.db"
		auth.backend = "htpasswd"
		auth.backend.htpasswd.userfile = "/var/www/webdav/passwd.dav"
		auth.require = ( "" => ( "method" => "basic",
			"realm" => "webdav",
			"require" => "valid-user" ) )
	}
}

generate certificate

to generate a certificate, you can run these commands in order:

$ sudo mkdir -p /etc/lighttpd/certificates
$ cd /etc/lighttpd/certificates
$ sudo openssl req -new -x509 -keyout raspberrypi.local.pem -out raspberrypi.local.pem -days 365 -nodes
$ sudo chown www-data:www-data raspberrypi.local.pem
$ sudo chmod 0600 raspberrypi.local.pem

enable ssl and restart the server:

$ sudo lighty-enable-mod ssl
$ sudo service lighttpd restart

let's encrypt certificate

if you choose to use a certificate from let's encrypt (please donate; they do a great job in securing our internet). to use the let's encrypt wizard for obtaining a certificate, you should have a hostname for your pi, which reachable from the world wide web (make sure, you forward ports 80 and 443 in your router's settings).

get the installation wizard using git (make sure it's installed: $ sudo apt-get install git) and run it:

$ cd ~/
$ git clone https://github.com/letsencrypt/letsencrypt && cd letsencrypt
$ ./letsencrypt-auto --server https://acme-v01.api.letsencrypt.org/directory auth

in this tutorial, i'll assume you choose this option in the wizard: 2: Place files in webroot directory (webroot). once the wizard asks your for the webroot directory, please enter: /var/www/webdav/web

lighttpd wants certificate to be combined. additionally we'll generate dh parameters (read about diffie–hellman key exchange on wikipedia). generating those parameters will take a long time. i mean, a really really long time, like up to hours. so plan wisely. go ahead and run these commands:

$ sudo -s
$ cd /etc/letsencrypt/live/raspberrypi.local
$ cat ./privkey.pem ./cert.pem > ./ssl.pem
$ cd /etc/ssl/certs
$ openssl dhparam -out dhparam.pem 4096
$ exit

next, you should update your lighttpd.conf by opening it ($ sudo nano /etc/lighttpd/lighttpd.conf) and make changes to the $SERVER["socket"] section:

server.modules = (
	"mod_access",
	"mod_alias",
	"mod_compress",
	"mod_redirect",
	"mod_setenv",
	"mod_webdav",
)

server.document-root        = "/var/www/html"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 80

index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

$HTTP["scheme"] == "http" {
	$HTTP["host"] =~ "raspberrypi.local" {
		url.redirect = ( "^/(.*)" => "https://raspberrypi.local/$1" )
	}
}

$SERVER["socket"] == ":443" {
	server.name = "raspberrypi.local"
	server.document-root = "/var/www/webdav/web"
	
	ssl.engine = "enable"
	ssl.pemfile = "/etc/letsencrypt/live/raspberrypi.local/ssl.pem"
	ssl.ca-file = "/etc/letsencrypt/live/raspberrypi.local/fullchain.pem"
	ssl.dh-file = "/etc/ssl/certs/dhparam.pem"
	ssl.ec-curve = "secp384r1"
	ssl.honor-cipher-order = "enable"
	ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
	ssl.use-sslv2 = "disable"
	ssl.use-sslv3 = "disable"
	setenv.add-response-header = (
		"Strict-Transport-Security" => "max-age=63072000; includeSubdomains; preload",
		"X-Frame-Options" => "DENY",
		"X-Content-Type-Options" => "nosniff")
	
	alias.url = ("/webdav" => "/var/www/webdav/web")
	$HTTP["url"] =~ "^/webdav($|/)" {
		webdav.activate = "enable"
		webdav.is-readonly = "disable"
		webdav.sqlite-db-name = "/var/run/lighttpd/lighttpd.webdav_lock.db"
		auth.backend = "htpasswd"
		auth.backend.htpasswd.userfile = "/var/www/webdav/passwd.dav"
		auth.require = ( "" => ( "method" => "basic",
			"realm" => "webdav",
			"require" => "valid-user" ) )
	}
}

after all this has done, please restart the server:

$ sudo service lighttpd restart

renew certificate

i suggest to setup automatic renewal of your certificate; let's create a script for that:

$ touch ~/etc/renew_letsencrypt.sh
$ chmod +x ~/etc/renew_letsencrypt.sh
$ nano ~/etc/renew_letsencrypt.sh

fill the file with:

#!/bin/sh

/home/pi/letsencrypt/certbot-auto renew

cd /etc/letsencrypt/live/raspberrypi.local
cat privkey.pem cert.pem > ssl.pem

service lighttpd reload

and create a new line in crontab -e to update weekly:

@weekly /home/pi/etc/renew_letsencrypt.sh

that's it. now you have a lightweight and secure webdav server running on your raspberry pi. enjoy!