362 lines
11 KiB
Markdown
362 lines
11 KiB
Markdown
---
|
|
title: "WebDAV with nginx"
|
|
date: 2016-03-26T00:00:00+01:00
|
|
draft: false
|
|
share: false
|
|
---
|
|
|
|
This website has been hosted on an [Online.net](https://www.online.net) dedicated server since its creation. I've been one of their customers for the past 3 years now, and I still don't have anything bad to say about them.
|
|
|
|
They recently upgraded their personnal range, and I took the opportunity to upgrade from a single server running all of my services to 2 servers running LXC containers that are hosting my services.
|
|
|
|
It took me 2 days to migrate everything, but it was worth it. If I decide to switch servers again, I'll have to migrate the containers instead of the services themselves. Considering they are stored on a separate BTRFS volume, it shouldn't take me more than a few hours at most.
|
|
|
|
During the migration, I realized that I needed to make files that were hosted on one server accessible to the other. I could have gone with CIFS or NFS, but I wanted to have encryption built-in instead of having to rely on a VPN for that. Since I figured it was a good opportunity to learn something new, I ended up going with WebDAV.
|
|
|
|
In this tutorial, I'll explain how I've configured a read-only WebDAV share using [nginx](https://www.nginx.com/) and [Let'sEncrypt](https://letsencrypt.org/) SSL certificates between two Debian Jessie containers.
|
|
|
|
## Server configuration
|
|
|
|
### Installing the required packages
|
|
|
|
First thing first, we need to install the packages we'll need for this configuration :
|
|
|
|
```bash
|
|
apt update
|
|
|
|
apt -t jessie-backports install nginx letsencrypt
|
|
apt install apache2-utils
|
|
```
|
|
|
|
### Getting our first certificate from letsencrypt
|
|
|
|
#### letsencrypt configuration
|
|
|
|
Let's create a configuration file for letsencrypt :
|
|
|
|
```bash
|
|
mkdir /etc/letsencrypt
|
|
|
|
echo 'rsa-key-size = 3072
|
|
renew-by-default
|
|
text = True
|
|
agree-tos = True
|
|
renew-by-default = True
|
|
authenticator = webroot
|
|
email = admin@example.com
|
|
webroot-path = /var/www/letsencrypt/' > /etc/letsencrypt/cli.ini
|
|
```
|
|
|
|
*Please do modify admin@example.com by your actual e-mail address.*
|
|
|
|
We also need to create the directory structure where letsencrypt ACME challenge temporary files will be stored :
|
|
|
|
```
|
|
mkdir -p /var/www/letsencrypt/.well-known
|
|
```
|
|
|
|
#### nginx configuration
|
|
|
|
We now need to configure nginx by adding the following in the `/etc/nginx/sites-available/default` file, anywhere in the `server{}` block that is configured to listen on port 80.
|
|
|
|
```
|
|
location /.well-known/acme-challenge {
|
|
root /var/www/letsencrypt;
|
|
}
|
|
```
|
|
|
|
Let's make sure that we haven't done anything wrong :
|
|
|
|
```bash
|
|
nginx -t
|
|
```
|
|
|
|
The command should give you the following output :
|
|
|
|
```
|
|
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
|
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
|
```
|
|
|
|
If that's the case, you can safely reload the nginx daemon :
|
|
|
|
```
|
|
nginx -s reload
|
|
```
|
|
|
|
#### Certificate request
|
|
|
|
Now that letsencrypt and nginx are properly configured, we can request our certificate from letsencrypt :
|
|
|
|
```bash
|
|
letsencrypt --config /etc/letsencrypt/cli.ini certonly -w /var/www/letsencrypt -d www.example.com
|
|
```
|
|
|
|
*Please do modify www.example.com by your server's FQDN, and please note that the letsencrypt servers need to be able to resolve that name to your server's IP.*
|
|
|
|
If everything goes well, your certificates will be generated and stored in the /etc/letsencrypt folder.
|
|
|
|
### WebDAV configuration
|
|
|
|
Now that we've obtained our certificate from letsencrypt, we can begin configuring nginx.
|
|
|
|
First, we need to comment two SSL directives from the default nginx configuration :
|
|
|
|
```
|
|
sed -i '/ssl_/ s/^/#/' /etc/nginx/nginx.conf
|
|
```
|
|
|
|
Let's now create a `/etc/nginx/conf.d/ssl.conf` with the following content :
|
|
|
|
```
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:50m;
|
|
ssl_session_tickets off;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
|
|
ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
|
|
|
|
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
|
|
|
|
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
|
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
|
|
ssl_prefer_server_ciphers on;
|
|
|
|
add_header Strict-Transport-Security max-age=15768000;
|
|
add_header X-Frame-Options DENY;
|
|
add_header X-Content-Type-Options nosniff;
|
|
|
|
ssl_stapling on;
|
|
ssl_stapling_verify on;
|
|
resolver 127.0.0.1 valid=300s;
|
|
resolver_timeout 5s;
|
|
```
|
|
|
|
*This configuration will work if you're using a single certificate on your server. If not, you'll have to remove the `ssl_certificate`, `ssl_certificate_key` and `ssl_trusted_certificate` directives from this file and move them to the correct `server{}` block.*
|
|
|
|
We now need to generate a `dhparam.pem` file :
|
|
|
|
```bash
|
|
mkdir /etc/nginx/ssl && chmod 700 /etc/nginx/ssl
|
|
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 3072
|
|
chmod 600 /etc/nginx/ssl/dhparam.pem
|
|
```
|
|
|
|
Let's now generate a HTTP basic authentication file. This example creates a user named example :
|
|
|
|
```
|
|
mkdir /etc/nginx/auth
|
|
|
|
htpasswd -c /etc/nginx/auth/webdav example
|
|
New password:
|
|
Re-type new password:
|
|
Adding password for user user
|
|
```
|
|
|
|
This file has to be readable by the user running your webserver. For security reasons, we'll make it readable only by him :
|
|
|
|
```
|
|
chown -R www-data:nogroup /etc/nginx/auth
|
|
chmod 700 /etc/nginx/auth
|
|
chmod 400 /etc/nginx/auth/webdav
|
|
```
|
|
|
|
Let's now modify our `/etc/nginx/sites-available/default` file with the following content :
|
|
|
|
```
|
|
server {
|
|
listen 80 default_server;
|
|
listen [::]:80 default_server ipv6only=on;
|
|
server_name "";
|
|
|
|
return 444;
|
|
}
|
|
|
|
server {
|
|
listen 443 default_server ssl http2;
|
|
listen [::]:443 default_server ipv6only=on ssl http2;
|
|
server_name "";
|
|
|
|
return 444;
|
|
}
|
|
```
|
|
|
|
We now have to create a `/etc/nginx/sites-available/example` file that will contain our actual webdav configuration. This example makes a `data` folder stored in `/var/www/` accessible.
|
|
|
|
```
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name www.example.com;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
server_name www.example.com;
|
|
|
|
root /var/www;
|
|
|
|
location / {
|
|
index index.html;
|
|
}
|
|
|
|
location /.well-known/acme-challenge {
|
|
root /var/www/letsencrypt;
|
|
}
|
|
|
|
location /data {
|
|
client_body_temp_path /tmp;
|
|
dav_methods PUT DELETE MKCOL COPY MOVE;
|
|
dav_ext_methods PROPFIND OPTIONS;
|
|
create_full_put_path on;
|
|
dav_access user:r group:r;
|
|
|
|
auth_basic "Restricted access";
|
|
auth_basic_user_file auth/webdav;
|
|
|
|
limit_except GET {
|
|
allow <YOUR IP HERE>;
|
|
deny all;
|
|
}
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
The last thing we have to do is to create a symlink so that nginx will load our configuration :
|
|
|
|
```
|
|
ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
|
|
```
|
|
|
|
Like before, let's make sure our configuration is correct and then reload the daemon :
|
|
|
|
```
|
|
nginx -t
|
|
nginx -s reload
|
|
```
|
|
|
|
That's it for the WebDAV configuration server-side !
|
|
|
|
### nginx monitoring
|
|
|
|
If you're using monit, you can easily monitor the nginx daemon by copying the following in `/etc/monit/conf.d/nginx` :
|
|
|
|
```
|
|
check process nginx
|
|
with pidfile "/run/nginx.pid"
|
|
start program = "/bin/systemctl start nginx"
|
|
stop program = "/bin/systemctl stop nginx"
|
|
alert monit@example.com
|
|
```
|
|
|
|
### Certificates auto-renewal
|
|
|
|
This goes beyond the scope of the article, but since letsencrypt certficates are only valid for 3 months, you'll need to renew them regularily. You can do so manually or you can setup a cron that does it for you.
|
|
|
|
I personnaly use the following script :
|
|
|
|
```
|
|
#!/bin/bash
|
|
|
|
PRG="/usr/bin/letsencrypt"
|
|
CONFIG="/etc/letsencrypt/cli.ini"
|
|
MAILDEST="admin@example.com"
|
|
GLOBAL=0
|
|
|
|
# www.example.com
|
|
$PRG --config $CONFIG certonly -w /var/www/letsencrypt -d www.example.com
|
|
[[ $? != 0 ]] && GLOBAL=$(( $GLOBAL + 1 ))
|
|
|
|
if [[ $GLOBAL == 0 ]]; then
|
|
/usr/sbin/nginx -s reload
|
|
else
|
|
echo "Something went wrong while renewing the certificates on $(hostname -f)
|
|
Manual action needed." | mail -s "Letsencrypt error on $(hostname -f)" $MAILDEST
|
|
fi
|
|
```
|
|
|
|
You can add multiple domains in the script. As long as you add all 3 lines for each domain, it will not automatically reload nginx if one or more certificate could not be renewed and will send an e-mail to the address configured in the `MAILDEST` variable.
|
|
|
|
You can configure this script in the root user crontab using the `crontab -e` command :
|
|
|
|
```
|
|
## LETSENCRYPT CERTIFICATE AUTORENEWAL
|
|
30 03 01 */2 * /root/bin/tlsrenew
|
|
```
|
|
This will run the script every two months, on the first day of the month, at 3:30 AM.
|
|
|
|
## Client configuration
|
|
|
|
### Installing the required packages
|
|
|
|
A single package is required to mount a webdav volume on Debian :
|
|
|
|
```
|
|
apt update && apt install davfs2
|
|
```
|
|
|
|
### Mounting the share manually
|
|
|
|
If like me, you want to mount your webdav share in a LXC container, you'll first need to make sure that the following line is present in its configuration file :
|
|
|
|
```
|
|
lxc.cgroup.devices.allow = c 10:229 rwm
|
|
```
|
|
|
|
You'll also need to create the `/dev/fuse` node in the container :
|
|
|
|
```
|
|
mknod /dev/fuse c 10 229
|
|
```
|
|
|
|
In any case, we have to edit the `/etc/davfs2/secrets` file to add the mount point, username and password that will be used to mount the share :
|
|
|
|
```
|
|
echo '/data webdav notanactualpassword' >> /etc/davfs2/secrets
|
|
```
|
|
|
|
Once that's done, we can mount our share with the following command :
|
|
|
|
```
|
|
mount -t davfs https://www.example.com/data /data -o ro,dir_mode=750,file_mode=640,uid=root,gid=root
|
|
```
|
|
|
|
You might need to edit the parameters depending on which users you want to make the share available to.
|
|
|
|
### Mouting the share on boot
|
|
|
|
A davfs volume can be mounted via the `/etc/fstab` file, but I decided to use monit instead so that the volume would be mounted again automatically should my WebDAV server reboot.
|
|
|
|
In order to do so, I first created a `davfs.txt` file in the `/var/www/data` folder on my WebDAV server :
|
|
|
|
```
|
|
touch /var/www/data/davfs.txt
|
|
```
|
|
|
|
I then created the following `/root/bin/mount_davfs` script :
|
|
|
|
```
|
|
#!/bin/bash
|
|
|
|
mknod /dev/fuse c 10 229
|
|
mount -t davfs https://www.example.com/data /data -o ro,dir_mode=750,file_mode=640,uid=root,gid=root
|
|
```
|
|
|
|
The last thing I did was create a `/etc/monit/conf.d/davfs` file with the following content :
|
|
|
|
```
|
|
check file davfs with path /data/davfs.txt
|
|
alert monit@example.com
|
|
if does not exist then exec "/root/bin/mount_davfs"
|
|
```
|
|
|
|
That way, if monit notices that the `/data/davfs.txt` file becomes inaccessible for some reason, it will try remouting the share.
|
|
|
|
## Conclusion
|
|
|
|
That's all ! Hopefully this has been useful to someone. Please do comment below if you have any question or if this has been helpful !
|