Post

Openresty + WebDav 配置记录

Openresty + WebDav 配置记录

由 CLAUDE 润色。

使用 OpenResty + hacdias/webdav 搭建 WebDAV 服务

本文介绍如何在已有 OpenResty 的服务器上搭建带 HTTPS 和账号认证的 WebDAV 服务。核心思路是:用 hacdias/webdav 作为后端,OpenResty 通过反代转发请求。


一、下载 hacdias/webdav

前往 hacdias/webdav Releases 选择对应架构的二进制包,以 linux-amd64 为例:

1
2
wget https://github.com/hacdias/webdav/releases/latest/download/linux-amd64-webdav.tar.gz
tar -xzf linux-amd64-webdav.tar.gz && mv webdav /usr/local/bin/

二、创建存储目录和专用用户

为 WebDAV 服务创建独立的系统用户,避免使用 nobody

1
2
3
4
5
6
7
8
9
10
# 创建无登录 shell 的专用用户
useradd -r -s /usr/sbin/nologin -d /opt/webdav webdav

# 创建存储目录并设置权限
mkdir -p /opt/webdav
chmod 750 /opt/webdav
chmod g+s /opt/webdav
chown -R webdav:webdav /opt/webdav
setfacl -R -d -m other::0 /opt/webdav
mkdir -p /opt/webdav/data

三、配置 hacdias/webdav

1
2
3
4
5
6
7
8
9
10
11
mkdir -p /etc/webdav
cat <<EOF > /usr/local/etc/webdav/config.yaml
address: 127.0.0.1
port: 6065

# 认证由 OpenResty 的 auth_basic 负责,这里关闭
auth: false

directory: /opt/webdav/data
permissions: CRUD
EOF

auth: false 表示 WebDAV 后端本身不做认证,认证完全交给前面的 OpenResty 处理,外部无法直接访问 127.0.0.1:6065


四、配置 systemd 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat <<EOF > /etc/systemd/system/webdav.service
[Unit]
Description=WebDAV Server
After=network.target

[Service]
ExecStart=/usr/local/bin/webdav --config /usr/local/etc/webdav/config.yaml
Restart=on-failure
User=webdav
Group=webdav

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now webdav
systemctl status webdav

五、申请 SSL 证书

使用常用的 certbot 即可,


六、创建 Basic Auth 密码文件

1
2
3
4
5
6
7
8
9
10
dnf install -y httpd-tools

# 创建第一个用户(-c 新建文件)
htpasswd -c /usr/local/openresty/.htpasswd youruser

# 添加更多用户(去掉 -c)
# htpasswd /usr/local/openresty/.htpasswd anotheruser

# 设置权限
chmod 640 /usr/local/openresty/.htpasswd

七、配置 OpenResty

创建配置文件,这里只展示反代部分。

推荐使用 Nginx 配置工具生成基础配置。

1
2
mkdir -p /usr/local/openresty/nginx/conf/conf.d
vi /usr/local/openresty/nginx/conf/conf.d/webdav.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
# HTTP → HTTPS 跳转
server {
    listen 80;
    server_name xxx;

    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS WebDAV 主服务
server {
    listen 443 ssl;
    http2 on;
    server_name xxx;

    location / {
        # Basic Auth 认证
        auth_basic "WebDAV";
        auth_basic_user_file /usr/local/openresty/.htpasswd;

        # 反代到 hacdias/webdav 后端
        proxy_pass http://127.0.0.1:6065;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass_request_headers on;

        # 大文件支持,关闭请求缓冲直接流式转发
        client_max_body_size    0;
        client_body_timeout     600s;
        send_timeout            600s;
        proxy_request_buffering off;
    }
}

80 和 443 端口与博客共享,nginx 通过 server_name 自动区分流量,博客配置无需改动。

检查配置并重载:

1
openresty -t && openresty -s reload

八、验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export WEBDAV_URL="https://xxx"
export WEBDAV_USER="youruser"
export WEBDAV_PASS='yourpassword'

# PROPFIND
curl -u $WEBDAV_USER:$WEBDAV_PASS -X PROPFIND "$WEBDAV_URL/" -H "Depth: 1" -v
curl -u $WEBDAV_USER:$WEBDAV_PASS -X PROPFIND "$WEBDAV_URL/" -H "Depth: 1" -L -v

# 上传文件,返回 Created
curl -u $WEBDAV_USER:$WEBDAV_PASS -T /etc/hostname "$WEBDAV_URL/test.txt"

# 下载文件,返回文件内容
curl -u $WEBDAV_USER:$WEBDAV_PASS "$WEBDAV_URL/test.txt"

# 未认证访问
curl -I "$WEBDAV_URL/"

九、灰云(optional)

通过 cloudflare workers 判断 ipv4 cloudflare 代理还是 ipv6 直连,设置 cloudflare workers(需要被绑定的域名走橙云代理):

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
// 代码源自 REF,做了些修改避免信息丢失认证失败
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const clientIP = request.headers.get('CF-Connecting-IP') || '';
  const isIPv6 = clientIP.includes(':');

  if (!isIPv6) {
    return fetch(request);
  }

  const originalURL = new URL(request.url);
  // 替换 hostname 为灰云域名
  originalURL.hostname = 'xxx.com';

  // 透传原始请求方法,头,body,避免认证丢失
  const proxyRequest = new Request(originalURL.toString(), {
    method: request.method,
    headers: request.headers,
    body: ['GET', 'HEAD'].includes(request.method) ? null : request.body,
    redirect: 'manual',
  });

  return fetch(proxyRequest);
}

然后需要更新已有 ssl 证书,将其 extend 到灰云域名上:

1
certbot certonly --expand --dns-cloudflare --dns-cloudflare-credentials /path/to/cloudflare_credentials.ini -d xx.com -d gray.xx.com

更新 oprensty 配置,server 块的 server_name 中添加新的域名。

不过 cloudflare free 橙云代理限制了每个连接最大上传100MB,可能导致部分文件无法上传(比如 zotero,看起来目前并没有实现分块上传)。

REF:

This post is licensed under CC BY 4.0 by the author.