Nginx搭建WebDAV服务

  迫于无法忍受现成的 NAS 系统的限制,Alliot 正在着手将最常用的一些服务剥离出来,方便迁移与定制, WebDAV 首当其冲, Alliot 在许多场景下的同步与备份都依赖它。
  WebDAV 作为一种基于HTTP/HTTPS协议的网络通信协议,预想是非常简单的,然而在具体动手的过程中还是遇到了挺多坑,Obsidian 的 Remotely-save 便是其中一个。
  本文将基于 Nginx/Tengine 手把手构建一个 WebDAV 服务。

前言

  Obsidian 的 Remotely-Save 插件在尝试使用 Nginx + nginx-dav-ext-module 搭建的 WebDAV 服务上同步时出现了 “405 Method Not Allowed” 的错误:

1
2
 *26 mkdir() "/webdav/obsidian/note/.obsidian" failed (17: File exists)  
request: "MKCOL /webdav/obsidian/note/.obsidian/ HTTP/2.0"

  之前是直接使用的群晖 WebDAV 服务,没有遇到过这个问题, 而群晖使用的是 httpd 作为 WebDAV 服务端。

经过一轮测试与研究,发现了如下 Github Issue:
https://github.com/remotely-save/remotely-save/issues/81

RootOfCase 在于 nginx-dav-ext-module 这个拓展模块没有正确的对 . 开头的文件(即隐藏文件)处理,而 Obsidian 的配置文件夹 .obsidian 刚好是 . 开头:
https://github.com/arut/nginx-dav-ext-module/issues/41

不过这个项目已经年久失修,从评论区我们找到了一个修复这个问题的 Fork 仓库:
https://github.com/mid1221213/nginx-dav-ext-module

编译安装

注: Alliot 本文是以 Tengine 为例来进行编译安装的,Nginx 与 Openresty 基本一致,在此文中可以同等看待。

编译 Nginx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 下载 nginx-dav-ext-module
git clone https://github.com/mid1221213/nginx-dav-ext-module

# 下载Nginx源码
wget https://tengine.taobao.org/download/tengine-3.0.0.tar.gz
tar -zxf tengine-3.0.0.tar.gz
cd tengine-3.0.0


# 安装编译源码必要的依赖
apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev libgd-dev libxml2 libxml2-dev uuid-dev


./configure --prefix=/var/www/html \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--user=www --group=www \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--lock-path=/var/lock/nginx.lock \
--pid-path=/var/run/nginx.pid \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_v2_module \
--with-pcre \
--with-http_dav_module \
--add-module=../nginx-dav-ext-module
make -j
make install

Nginx 配置文件如下, 可以直接追加到 /etc/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
dav_ext_lock_zone zone=webdav:10m;
server {
listen 443 ssl http2;
server_name www.iots.vip;
access_log /var/log/nginx/webdav.log;

# SSL 证书配置
ssl_certificate /etc/letsencrypt/live/www.iots.vip/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.iots.vip/privkey.pem;

ssl_session_timeout 5m;
ssl_session_cache shared:MozSSL:10m;
ssl_dhparam /etc/ssl/dhparam.pem;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_stapling on;
ssl_stapling_verify on;
client_max_body_size 0;

error_page 497 https://$host:$server_port$request_uri;

location /webdav/ {
create_full_put_path on;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;

dav_access user:rw group:rw all:rw;
dav_ext_lock zone=webdav;

client_max_body_size 0;

client_body_temp_path /tmp/nginx_client-bodies;

# 用户名 密码
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/htpasswd;

# webdav存储路径
alias /data/sync/webdav/;
}

这里的用户名密码使用 apache2-utils 中的 htpasswd 来生成:

1
2
3
4
apt install -y apache2-utils

htpasswd -c /etc/nginx/htpasswd <username>
# 输入密码即可

使用 Systemd 来托管 Nginx 进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat > /lib/systemd/system/nginx.service << EOF
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

# 重载systemd配置,开机启动Nginx
systemctl daemon-reload
systemctl enable --now nginx.service

测试同步

Alliot 使用 Obsidian 的 Remotely-save 配置:
服务器地址: https://domain/webdav/obsidian (这里注意,一定要加上obsidian子路径,因为这个插件不像其他的客户端,它不会帮你创建目录,如果不加obsidian,便会将所有的文件直接同步到webdav下)
用户名,密码
鉴权类型: basic
同步配置文件夹: 开启

PS

2023-10-23: 就在今天,蚂蚁旗下的云笔记——语雀,不出意外的出现了重大故障,故障时间长达八小时(五个 9 的 SLO 直接炸了),各个技术群里一片骂声。
笔记作为 IT 人的核心生产力工具,我始终相信最简单的 Markdown + Local-First + 云备份,才是最优解。