WebDAV (Web-based Distributed Authoring and Versioning) 是一种基于 HTTP 1.1 协议的通信协议, 在GET、POST、HEAD等几个HTTP标准方法以外, 它扩展了 HTTP 1.1, 增加了支持写文件锁定 (Locking) 、解锁 (Unlock) 等方法, 使应用程序可直接对 Web Server 直接读写。
WebDAV 就是通过 Restful API , 实现对服务端文件的 创建 / 删除 / 读取 / 修改。与其他文件传输协议相比, WebDAV 使用 HTTP 传输, 不容易被当作不明流量被砍掉。支持 HTTPS 数据加密, 避免流量被阻断, 并支持 HTTP 2 和范围请求 (RFC7233)
正是因为这些好处, 很多系统和软件都提供了对 WebDAV 的支持。比如说 OS X 的 finder 支持远程连接到 WebDAV 服务器。IOS 的播放器 nPlayer 能够播放 WebDAV 上的视频文件, 且传输速度高于 FTP / SMB 等协议。
准备工作
- 操作系统: CentOS 7.9.2009 (Core)
- Nginx 版本: 1.20.2
- 备用下载: nginx-1.20.2.tar.gz
- 模块支持
- nginx-dav-ext-module
- 添加了 PROPFIND、OPTIONS、LOCK、UNLOCK 方法
- 版本: git.r112.f5e3088 (release-v3.0.0)
- headers-more-nginx-module
- 修复了 iOS、Windows 默认客户端在使用 COPY、MOVE 方法时,
$http_destination
中的 URI 缺少/
的问题, 导致出现无法删除、重命名文件或文件夹等错误家。该模块可以修复该错误, 兼容更多的客户端 - 版本: git.r259.a4a0686 (v0.33)
- 修复了 iOS、Windows 默认客户端在使用 COPY、MOVE 方法时,
- nginx-dav-ext-module
- 其他依赖:
- OpenSSL 1.1.1m (TLS 1.3 支持)
- Zlib 1.2.11 (Gzip 支持)
- PCRE 8.45 (正则表达式)
- GCC 9.3.1 20200408 (Red Hat 9.3.1-2)
安装编译环境
yum install epel-release expat-devel httpd-tools unzip wget centos-release-scl git libxslt-devel libxml2-devel -y
yum install devtoolset-9-gcc* -y
yum groupinstall "Development tools" -y
创建非特权服务帐户
创建一个与 EPEL 仓库中相同的 nginx 服务帐户
groupadd -g 994 nginx
useradd -g 994 -u 996 -c "nginx user" -d /var/cache/nginx -s /sbin/nologin nginx
如果你得到下面的输出, 可以忽略此步骤。这只是意味着 nginx 用户已经或已经从仓库安装时创建
[root@centos7 ~]# groupadd -g 994 nginx
groupadd: group 'nginx' already exists
[root@centos7 ~]# useradd -g 994 -u 996 -c "Nginx web server" -d /var/lib/nginx -s /sbin/nologin nginx
useradd: user 'nginx' already exists
下载源码
# 创建工作目录并进入
mkdir nginx-webdav
cd nginx-webdav
# download nginx 1.20.2 source
wget wget https://nginx.org/download/nginx-1.20.2.tar.gz
# download pcre 8.45 / zlib 1.2.11 / openssl 1.1.1m dependency
wget https://sourceforge.net/projects/pcre/files/pcre/8.45/pcre-8.45.tar.gz
wget http://zlib.net/zlib-1.2.11.tar.gz
wget http://www.openssl.org/source/openssl-1.1.1m.tar.gz
# download nginx-dav-ext-module git.r112.f5e3088
git clone https://github.com/arut/nginx-dav-ext-module.git
# download headers-more-nginx-module git.r259.a4a0686
git clone https://github.com/openresty/headers-more-nginx-module.git
# Extract source file
tar -zxf pcre-8.45.tar.gz
tar -zxf zlib-1.2.11.tar.gz
tar -zxf openssl-1.1.1m.tar.gz
tar -zxf nginx-1.20.2.tar.gz
准备好的文件列表
[root@centos7 nginx-webdav]# tree -L 1
├── headers-more-nginx-module
├── nginx-1.20.2
├── nginx-1.20.2.tar.gz
├── nginx-dav-ext-module
├── openssl-1.1.1m
├── openssl-1.1.1m.tar.gz
├── pcre-8.45
├── pcre-8.45.tar.gz
├── zlib-1.2.11
└── zlib-1.2.11.tar.gz
6 directories, 4 files
编译安装 Nginx
详细参数配置参数
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx \
--group=nginx \
--with-zlib=../zlib-1.2.11 \
--with-zlib-opt='-g -Ofast -fPIC -m64 -march=native -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
--with-pcre=../pcre-8.45 \
--with-pcre-opt='-g -Ofast -fPIC -m64 -march=native -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
--with-pcre-jit \
--with-compat \
--with-file-aio \
--with-threads \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--add-module=../nginx-dav-ext-module \
--add-module=../headers-more-nginx-module \
--with-openssl=../openssl-1.1.1m \
--with-http_xslt_module \
--with-http_flv_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-mail \
--with-mail_ssl_module \
--with-stream \
--with-stream_realip_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' \
--with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'
调整文件名长度显示 (可选)
实现网页目录不裁剪文件名
修改 nginx-1.20.2/src/http/modules/ngx_http_autoindex_module.c
#define NGX_HTTP_AUTOINDEX_PREALLOCATE 50
#define NGX_HTTP_AUTOINDEX_NAME_LEN 50
修改为
#define NGX_HTTP_AUTOINDEX_PREALLOCATE 110
#define NGX_HTTP_AUTOINDEX_NAME_LEN 110
nginx: Long filenames in directory listing
Stop nginx from trimming file name in directory list?
综上所述整理好的执行代码
[root@centos7 nginx-webdav]# cd nginx-1.20.2
[root@centos7 nginx-1.20.2]# scl enable devtoolset-9 "./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-zlib=../zlib-1.2.11 --with-zlib-opt='-g -Ofast -fPIC -m64 -march=native -fstack-protector-strong -D_FORTIFY_SOURCE=2' --with-pcre=../pcre-8.45 --with-pcre-opt='-g -Ofast -fPIC -m64 -march=native -fstack-protector-strong -D_FORTIFY_SOURCE=2' --with-pcre-jit --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --add-module=../nginx-dav-ext-module --add-module=../headers-more-nginx-module --with-openssl=../openssl-1.1.1m --with-http_xslt_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'"
[root@centos7 nginx-1.20.2]# scl enable devtoolset-9 "make -j"
[root@centos7 nginx-1.20.2]# ./objs/nginx -V
nginx version: nginx/1.20.2
built by gcc 9.3.1 20200408 (Red Hat 9.3.1-2) (GCC)
built with OpenSSL 1.1.1m 14 Dec 2021
TLS SNI support enabled
configure arguments: ......
[root@centos7 nginx-1.20.2]# make install
安装完检查 nginx 的版本 nginx -V
[root@centos7 ~]# nginx -V
nginx version: nginx/1.20.2
built by gcc 9.3.1 20200408 (Red Hat 9.3.1-2) (GCC)
built with OpenSSL 1.1.1m 14 Dec 2021
TLS SNI support enabled
configure arguments: ......
编译报错处理方法
objs/ngx_modules.o \
-Wl,-z,relro -Wl,-z,now -pie -ldl -lpthread -lpthread -lcrypt ../pcre-8.45/.libs/libpcre.a ../openssl-1.1.1m/.openssl/lib/libssl.a ../openssl-1.1.1m/.openssl/lib/libcrypto.a -ldl -lpthread ../zlib-1.2.11/libz.a -lxml2 -lxslt -lexslt \
-Wl,-E
/usr/bin/ld: ../pcre-8.45/.libs/libpcre.a(libpcre_la-pcre_compile.o): relocation R_X86_64_32S against hidden symbol `_pcre_OP_lengths' can not be used when making a shared object
/usr/bin/ld: ../pcre-8.45/.libs/libpcre.a(libpcre_la-pcre_config.o): relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../pcre-8.45/.libs/libpcre.a(libpcre_la-pcre_exec.o): relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../pcre-8.45/.libs/libpcre.a(libpcre_la-pcre_fullinfo.o): relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../pcre-8.45/.libs/libpcre.a(libpcre_la-pcre_jit_compile.o): relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../pcre-8.45/.libs/libpcre.a(libpcre_la-pcre_study.o): relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../zlib-1.2.11/libz.a(deflate.o): relocation R_X86_64_32S against hidden symbol `_length_code' can not be used when making a shared object
/usr/bin/ld: ../zlib-1.2.11/libz.a(inflate.o): relocation R_X86_64_32S against hidden symbol `zcfree' can not be used when making a shared object
/usr/bin/ld: ../zlib-1.2.11/libz.a(inftrees.o): relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../zlib-1.2.11/libz.a(trees.o): relocation R_X86_64_32S against hidden symbol `_length_code' can not be used when making a shared object
/usr/bin/ld: ../zlib-1.2.11/libz.a(zutil.o): relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../zlib-1.2.11/libz.a(crc32.o): relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: ../zlib-1.2.11/libz.a(inffast.o): relocation R_X86_64_32S against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
make[1]: *** [objs/nginx] Error 1
make[1]: Leaving directory `../nginx-1.20.2'
make: *** [build] Error 2
如果出现这个错误解决方法是在 ./configure
中加入两行参数
--with-pcre-opt='-g -Ofast -fPIC -m64 -march=native -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
--with-zlib-opt='-g -Ofast -fPIC -m64 -march=native -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
libpcre throwing a “.rodata can not be used when making a shared object” error while compiling NGINX
创建 Nginx 系统服务
创建并编辑系统服务配置文件 /usr/lib/systemd/system/nginx.service
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/sh -c "/bin/kill -s HUP $(/bin/cat /var/run/nginx.pid)"
ExecStop=/bin/sh -c "/bin/kill -s TERM $(/bin/cat /var/run/nginx.pid)"
[Install]
WantedBy=multi-user.target
设置开机启动并启动 Nginx 服务
systemctl enable nginx
systemctl start nginx
配置 Nginx
站点目录
创建站点目录
mkdir /etc/nginx/conf.d
创建默认 nginx.conf
修改文件 vim /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# Specifies the value for maximum file descriptors that can be opened by this process.
worker_rlimit_nofile 51200;
# PCRE JIT can speed up processing of regular expressions significantly.
pcre_jit on;
events {
use epoll;
worker_connections 51200;
multi_accept on;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 50m;
charset utf-8;
sendfile on;
server_tokens off;
tcp_nodelay on;
tcp_nopush on;
real_ip_header X-Forwarded-For;
types_hash_max_size 2048;
keepalive_timeout 60;
access_log /var/log/nginx/access.log main;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
server {
listen 80 default_server;
listen [::]:80 default_server;
if ($host ~ "\d+\.\d+\.\d+\.\d+") {
return 404;
}
return 301 https://$host$request_uri;
}
include /etc/nginx/conf.d/*.conf;
}
WebDAV 账户管理
使用 htpasswd
创建 WebDAV 访问账户
yum install httpd-tools -y
# 创建用户名为 rbq 的用户
htpasswd -c /home/SSL/webdav.htpasswd 'rbq'
都配置完之后重启 nginx
nginx -s reload
设置文件夹访问权限
WebDAV 主目录在 /home
下访时, 需要调整权限
将 nginx
用户加入 qaq
用户组, 并将 qaq
用户加入 nginx
组
[root@centos7 ~]# id qaq
uid=1000(qaq) gid=1000(qaq) groups=1000(qaq)
[root@centos7 ~]# id nginx
uid=996(nginx) gid=994(nginx) groups=994(nginx)
[root@centos7 ~]# usermod -G qaq nginx
[root@centos7 ~]# usermod -G nginx qaq
[root@centos7 ~]# id nginx
uid=996(nginx) gid=994(nginx) groups=994(nginx),1000(qaq)
修改文件夹权限为 774
, 确保共享目录的读写权限
chmod -R 774 /home/qaq
创建 WebDAV 站点
修改文件 vim /etc/nginx/conf.d/webdav.conf
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate "/home/SSL/example.com.crt";
ssl_certificate_key "/home/SSL/example.com.key";
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 30m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access-example.com.log main;
error_log /var/log/nginx/error-example.com.log error;
location / {
# 设置 webdav 目录, 注意 Nginx worker 用户对该目录需有读/写/执行权限
root /home/qaq;
auth_basic "closed site";
auth_basic_user_file /home/SSL/webdav.htpasswd;
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS;
# 启用完整的创建目录支持
create_full_put_path on;
dav_access user:rw group:rw;
autoindex on;
autoindex_localtime on;
autoindex_exact_size off;
# 不限制文件大小
client_max_body_size 0;
# 为各种方法的URI后加上斜杠, 解决各平台webdav客户端的兼容性问题
set $dest $http_destination;
if (-d $request_filename) {
rewrite ^(.*[^/])$ $1/;
set $dest $dest/;
}
if ($request_method ~ (MOVE|COPY)) {
more_set_input_headers 'Destination: $dest';
}
if ($request_method ~ MKCOL) {
rewrite ^(.*[^/])$ $1/ break;
}
}
# Mac 挂载 webdav 后会自动写入很多文件, 可以通过 nginx 配置屏蔽掉, 保持 webdav 目录的干净
location ~ \.(_.*|DS_Store|Spotlight-V100|TemporaryItems|Trashes|hidden|localized)$ {
access_log off;
error_log off;
if ($request_method = PUT) {
return 403;
}
return 404;
}
location ~ \.metadata_never_index$ {
return 200 "Don't index this drive, Finder!";
}
}
本套配置经过 4 篇文章整理而成
- 开启
TLSv1.2 TLSv1.3
, 并且平衡了兼容性和安全性. SSL Labs 评分为 A - 开启 http 基本验证
- dav 权限设置为 770
dav_access user:rw group:rw;
- 启用 nginx 自带的网页目录列表
- 修复某些客户端路径结尾没有斜杠
- 屏蔽 Mac 挂载 webdav 后会自动写入很多文件
Nginx 安装和基本配置完成后, WebDAV 服务已生效。如果你在 Web 浏览器中访问 WebDav, 应该能够看到该目录中现有文件的目录列表, 但你无法通过 Web 浏览器上传新文件。需要使用 WebDAV 客户端 (例如 macOS Finder、Windows 中的 rclone 等) 管理文件。
配置代码解析
某些 WebDAV 客户端 (如 OS X 下的 ForkLift) 在 创建文件夹 / 复制文件夹 / 移动文件夹 失败的问题
Nginx 默认要求对文件夹操作时路径结尾需要有 /
, 而部分客户端没有遵守这一要求。解决方法是用 url rewrite
, 如果操作的是文件夹, 则补上 /
2018/06/03 08:03:58 [error] 7#7: *1 MKCOL can create a collection only, client: 8.8.8.8, server: example.com, request: "MKCOL /sp HTTP/2.0", host: "example.com"
2018/06/03 08:17:20 [error] 7#7: *18 "/sp" is collection, client: 8.8.8.8, server: example.com, request: "MOVE /sp HTTP/2.0", host: "example.com"
以下代码是为了解决某些 WebDAV 客户端 (如 macOS 下的 ForkLift) 在创建文件夹、复制文件夹或移动文件夹时因路径结尾缺少斜杠而报错的问题。
set $dest $http_destination;
if (-d $request_filename) {
rewrite ^(.*[^/])$ $1/;
set $dest $dest/;
}
if ($request_method ~ (MOVE|COPY)) {
more_set_input_headers 'Destination: $dest';
}
if ($request_method ~ MKCOL) {
rewrite ^(.*[^/])$ $1/ break;
}
配置说明
- 路径补全: 使用
rewrite
补全文件夹路径末尾的/
, 确保兼容客户端的操作习惯 - MOVE / COPY 方法兼容性: 对 MOVE 和 COPY 操作, 将
Destination
头的路径末尾补上/
, 以满足 Nginx 的 WebDAV 需求 (需要headers-more-nginx-module
模块) - MKCOL 方法补全: 确保创建集合 (文件夹) 时路径的正确性, 避免路径缺少
/
导致的MKCOL
报错
WebDAV 支持的请求方法
OPTIONS
: 查询 WebDAV 服务支持的方法GET
: 获取文件PUT
、POST
: 上传文件DELETE
: 删除文件或集合COPY
: 复制文件MOVE
: 移动文件MKCOL
: 创建新的文件集合 (文件夹)PROPFIND
: 查询文件属性LOCK
、UNLOCK
: 加锁、解锁文件, 实现写保护
使用 curl
测试 WebDAV
使用 curl
测试时, URI后面一定要带 /
, 不然就会报错
创建目录
curl -X MKCOL -u USER:PASSWORD https://dav.example.com/test/
上传文件
curl -T FILE -u USER:PASSWORD https://dav.example.com/test/
重命名
curl -X MOVE -u USER:PASSWORD --header 'Destination:https://dav.example.com/test/newname' https://dav.example.com/test/File
删除
curl -X DELETE -u USER:PASSWORD https://dav.example.com/test/File
原文 - 编译贡献 (文章按照贡献价值排序)
在 CentOS 7 上从源代码安装 Nginx
How to Build Nginx from source on CentOS 7
Centos7.x 编译安装全功能的Nginx
原文 - 配置贡献 (仅配置部分可供参考 其余部分没啥用)
nginx配置功能完整的webdav服务器
使用 nginx 搭建 WebDAV 服务器
Install Nginx as a WebDav File Server on CentOS 7
如何在 Nginx 上启用 TLS 1.3