背景
科技云盘(CAS Cloud Box)提供了 WebDAV 协议接口,可用于第三方应用访问和管理文件。然而,科技云盘的 WebDAV 服务器仅支持 Digest 认证 ,而 rclone 的 WebDAV 后端仅支持 Basic 认证 ,两者不兼容会导致 401 Unauthorized 错误。
为解决此问题,本方案通过一个运行在本地的 Python 反向代理来桥接 rclone 和科技云盘,由代理负责处理 Digest 认证。
架构
rclone (无认证) ──> 本地代理 (127.0.0.1:18080) ──Digest认证──> pan.cstcloud.cn/DAV/
文件清单
| 文件 | 用途 |
|---|---|
~/.config/rclone/rclone.conf |
rclone 配置文件,指向本地代理 |
~/.config/rclone/webdav_digest_proxy.py |
Python 反向代理脚本,处理 Digest 认证 |
~/Library/LaunchAgents/com.rclone.webdav-proxy.plist |
macOS launchd 服务配置,开机自动启动代理 |
/etc/systemd/system/webdav-proxy.service |
Linux systemd 服务配置,开机自动启动代理 |
配置详情
1. rclone 配置 (~/.config/rclone/rclone.conf)
ini
[remote]
type = webdav
url = http://127.0.0.1:18080/个人文件/
vendor = other
url指向本地代理地址,路径/个人文件/对应科技云盘的个人文件目录- 无需配置
user和pass,认证由代理处理
2. WebDAV Digest 代理 (~/.config/rclone/webdav_digest_proxy.py)
代理脚本的核心配置参数位于文件顶部:
python
UPSTREAM = "https://pan.cstcloud.cn/DAV/" # 科技云盘 WebDAV 地址
USER = "XXXX@XXXX" # 科技云盘账号
PASS = "XXXXXXXX" # WebDAV 授权密码
PORT = 18080 # 本地监听端口
代理的工作流程:
- 监听
127.0.0.1:18080,接收 rclone 发来的 HTTP 请求 - 将请求转发到
pan.cstcloud.cn,自动附加 Digest 认证头 - 将响应中的绝对路径 (
https://pan.cstcloud.cn/DAV/...) 改写为相对路径 (/...),使 rclone 能正确解析目录结构 - 返回处理后的响应给 rclone
依赖:Python 3 + requests 库(系统已安装)。
3. 服务管理(开机自启)
macOS --- launchd
服务配置文件:~/Library/LaunchAgents/com.rclone.webdav-proxy.plist
- RunAtLoad: 登录时自动启动
- KeepAlive: 进程退出后自动重启
- 日志路径 :
/tmp/webdav-proxy.log
Linux --- systemd
服务配置文件:/etc/systemd/system/webdav-proxy.service(系统级)或 ~/.config/systemd/user/webdav-proxy.service(用户级)
推荐使用用户级服务,无需 root 权限。创建步骤:
bash
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/webdav-proxy.service << 'EOF'
[Unit]
Description=CSTCloud WebDAV Digest Auth Proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 %h/.config/rclone/webdav_digest_proxy.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
# 安全加固
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=default.target
EOF
%h是 systemd 变量,会自动替换为当前用户的 home 目录。
启用并启动服务:
bash
systemctl --user daemon-reload
systemctl --user enable webdav-proxy.service # 开机自启
systemctl --user start webdav-proxy.service # 立即启动
如果希望在用户未登录时也保持服务运行(适用于服务器环境):
bash
sudo loginctl enable-linger $USER
Linux(无 systemd 用户会话)--- nohup + crontab
在共享实验室服务器、HPC 集群等环境中,普通用户通常没有 systemd 用户会话(systemctl --user 报 Failed to get D-Bus connection: Connection refused)。此时使用 nohup + crontab 方案:
启动代理:
bash
nohup python3 ~/.config/rclone/webdav_digest_proxy.py > /tmp/webdav-proxy.log 2>&1 &
设置开机/重启后自动启动(crontab):
bash
(crontab -l 2>/dev/null; echo '@reboot nohup python3 $HOME/.config/rclone/webdav_digest_proxy.py > /tmp/webdav-proxy.log 2>&1 &') | crontab -
验证 crontab 是否写入成功:
bash
crontab -l | grep webdav
管理代理进程:
bash
# 查看代理是否运行
ps aux | grep webdav_digest_proxy | grep -v grep
# 停止代理
kill $(pgrep -f webdav_digest_proxy)
# 重启代理
kill $(pgrep -f webdav_digest_proxy) 2>/dev/null
nohup python3 ~/.config/rclone/webdav_digest_proxy.py > /tmp/webdav-proxy.log 2>&1 &
# 查看日志
tail -f /tmp/webdav-proxy.log
如何判断用哪种方案? 运行
systemctl --user status,如果报Failed to get D-Bus connection就用 nohup + crontab 方案,否则用 systemd 方案。
常用操作
使用 rclone 访问云盘
由于本机配置了 HTTP 代理(
http://127.0.0.1:7890),访问本地代理时需设置no_proxy环境变量以避免请求被转发到外部代理。
bash
# 列出目录
no_proxy='*' rclone lsd remote:
# 列出所有文件
no_proxy='*' rclone ls remote:
# 列出指定目录的文件
no_proxy='*' rclone ls remote:科研/
# 上传文件到云盘
no_proxy='*' rclone copy /path/to/local/file remote:目标目录/
# 从云盘下载文件
no_proxy='*' rclone copy remote:科研/paper.pdf /path/to/local/
# 双向同步
no_proxy='*' rclone sync /path/to/local/ remote:目标目录/
# 挂载为本地磁盘(前台运行,Ctrl+C 卸载)
no_proxy='*' rclone mount remote: /tmp/cstcloud --vfs-cache-mode full
可在 ~/.zshrc 中添加别名简化操作:
bash
alias rpan='no_proxy="*" rclone'
之后即可直接使用 rpan ls remote: 等命令。
管理代理服务
macOS
bash
# 查看代理运行状态
launchctl list | grep webdav-proxy
# 查看代理日志
cat /tmp/webdav-proxy.log
# 停止代理服务
launchctl unload ~/Library/LaunchAgents/com.rclone.webdav-proxy.plist
# 启动代理服务
launchctl load ~/Library/LaunchAgents/com.rclone.webdav-proxy.plist
# 重启代理服务(停止 + 启动)
launchctl unload ~/Library/LaunchAgents/com.rclone.webdav-proxy.plist && \
launchctl load ~/Library/LaunchAgents/com.rclone.webdav-proxy.plist
# 确认代理正在监听
lsof -i :18080
Linux
bash
# 查看代理运行状态
systemctl --user status webdav-proxy
# 查看代理日志
journalctl --user -u webdav-proxy -f
# 停止代理服务
systemctl --user stop webdav-proxy
# 启动代理服务
systemctl --user start webdav-proxy
# 重启代理服务
systemctl --user restart webdav-proxy
# 确认代理正在监听
ss -tlnp | grep 18080
更新 WebDAV 授权密码
科技云盘的 WebDAV 授权密码可能会过期,需要重新生成:
-
打开 科技云盘应用管理页面
-
点击顶部的「应用管理」标签
-
如果已有密码,点击「删除密码」,确认删除
-
点击「设置密码」→「生成密码」→「保存并复制」
-
编辑代理脚本,更新密码:
bashvim ~/.config/rclone/webdav_digest_proxy.py # 修改 PASS = "新密码" -
重启代理服务:
bash# macOS launchctl unload ~/Library/LaunchAgents/com.rclone.webdav-proxy.plist launchctl load ~/Library/LaunchAgents/com.rclone.webdav-proxy.plist # Linux systemctl --user restart webdav-proxy -
验证连接:
bashno_proxy='*' rclone lsd remote:
访问群组文件
默认配置访问的是「个人文件」目录。如需访问「群组文件」,修改 rclone.conf:
ini
[remote-group]
type = webdav
url = http://127.0.0.1:18080/群组文件/
vendor = other
然后使用 no_proxy='*' rclone ls remote-group: 访问。
Linux 完整部署指南
以下是在一台全新 Linux 机器(Ubuntu/Debian/CentOS 等)上从零部署的完整步骤。
第一步:安装依赖
bash
# Ubuntu / Debian
sudo apt update && sudo apt install -y python3 python3-pip rclone
pip3 install requests
# CentOS / RHEL / Fedora
sudo dnf install -y python3 python3-pip rclone
pip3 install requests
# Arch Linux
sudo pacman -S python python-requests rclone
如果系统源中的 rclone 版本过旧,可使用官方安装脚本:
bash
curl https://rclone.org/install.sh | sudo bash
第二步:部署代理脚本
bash
mkdir -p ~/.config/rclone
cat > ~/.config/rclone/webdav_digest_proxy.py << 'PYEOF'
#!/usr/bin/env python3
"""Local reverse proxy: rclone (no auth) -> CSTCloud WebDAV (Digest auth)."""
import re
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
import requests
from requests.auth import HTTPDigestAuth
UPSTREAM = "https://pan.cstcloud.cn/DAV/"
USER = "XXXX@XXXX" # <-- 替换为你的账号
PASS = "XXXXXXXX" # <-- 替换为你的 WebDAV 授权密码
PORT = 18080
session = requests.Session()
session.auth = HTTPDigestAuth(USER, PASS)
class ProxyHandler(BaseHTTPRequestHandler):
def _proxy(self):
path = self.path.lstrip("/")
url = UPSTREAM + path
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length) if content_length else None
headers = {}
for key in ("Depth", "Destination", "Overwrite", "Content-Type",
"If-Match", "If-None-Match", "Range"):
val = self.headers.get(key)
if val:
headers[key] = val
if "Destination" in headers:
dest = headers["Destination"]
dest = dest.replace(f"http://127.0.0.1:{PORT}/", UPSTREAM)
dest = dest.replace(f"http://localhost:{PORT}/", UPSTREAM)
headers["Destination"] = dest
try:
resp = session.request(
method=self.command,
url=url,
headers=headers,
data=body,
allow_redirects=False,
stream=False,
timeout=300,
)
except Exception as e:
self.send_error(502, str(e))
return
content = resp.content
if resp.headers.get("Content-Type", "").startswith("application/xml"):
text = content.decode("utf-8", errors="replace")
text = text.replace("https://pan.cstcloud.cn/DAV/", "/")
text = text.replace("http://pan.cstcloud.cn/DAV/", "/")
content = text.encode("utf-8")
self.send_response(resp.status_code)
for k, v in resp.headers.items():
low = k.lower()
if low in ("transfer-encoding", "connection",
"content-encoding", "content-length"):
continue
self.send_header(k, v)
self.send_header("Content-Length", str(len(content)))
self.end_headers()
self.wfile.write(content)
do_GET = _proxy
do_PUT = _proxy
do_POST = _proxy
do_DELETE = _proxy
do_HEAD = _proxy
do_OPTIONS = _proxy
do_PROPFIND = _proxy
do_PROPPATCH = _proxy
do_MKCOL = _proxy
do_COPY = _proxy
do_MOVE = _proxy
do_LOCK = _proxy
do_UNLOCK = _proxy
def log_message(self, format, *args):
pass
if __name__ == "__main__":
port = int(sys.argv[1]) if len(sys.argv) > 1 else PORT
server = HTTPServer(("127.0.0.1", port), ProxyHandler)
print(f"WebDAV Digest proxy listening on http://127.0.0.1:{port} -> {UPSTREAM}",
flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()
PYEOF
chmod +x ~/.config/rclone/webdav_digest_proxy.py
第三步:配置 rclone
bash
cat > ~/.config/rclone/rclone.conf << 'EOF'
[remote]
type = webdav
url = http://127.0.0.1:18080/个人文件/
vendor = other
EOF
第四步:启动代理并设置自启
先测试 systemd 用户会话是否可用:
bash
systemctl --user status
如果可用(正常输出状态信息),使用 systemd 方案:
bash
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/webdav-proxy.service << 'EOF'
[Unit]
Description=CSTCloud WebDAV Digest Auth Proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 %h/.config/rclone/webdav_digest_proxy.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now webdav-proxy.service
如果报 Failed to get D-Bus connection: Connection refused(常见于共享实验室服务器、HPC 集群),使用 nohup + crontab 方案:
bash
# 立即启动
nohup python3 ~/.config/rclone/webdav_digest_proxy.py > /tmp/webdav-proxy.log 2>&1 &
# 设置开机自启
(crontab -l 2>/dev/null; echo '@reboot nohup python3 $HOME/.config/rclone/webdav_digest_proxy.py > /tmp/webdav-proxy.log 2>&1 &') | crontab -
第五步:验证连接
bash
# 检查代理是否在运行
systemctl --user status webdav-proxy
# 测试 rclone 连接
no_proxy='*' rclone lsd remote:
# 预期输出示例:
# -1 2026-02-06 19:06:01 -1 models
# -1 2026-01-06 15:43:28 -1 zotero
# -1 2026-01-05 10:32:32 -1 个人信息
第六步(可选):服务器环境保活
如果在无图形界面的服务器上使用,需要启用 linger 使用户服务在未登录时也能运行:
bash
sudo loginctl enable-linger $USER
第七步(可选):设置 shell 别名
bash
# 添加到 ~/.bashrc 或 ~/.zshrc
echo 'alias rpan='\''no_proxy="*" rclone'\''' >> ~/.bashrc
source ~/.bashrc
# 之后可以直接使用
rpan ls remote:
rpan copy localfile.pdf remote:科研/
跨平台配置差异总结
| 项目 | macOS | Linux (systemd) | Linux (无 systemd 用户会话) |
|---|---|---|---|
| Python 路径 | /usr/bin/python3 |
/usr/bin/python3 |
/usr/bin/python3 |
| rclone 安装 | brew install rclone |
包管理器或官方脚本 | 同左 |
| requests 安装 | pip3 install requests |
pip3 install requests |
同左 |
| 服务管理 | launchd (launchctl) |
systemd (systemctl --user) |
nohup + crontab |
| 服务配置位置 | ~/Library/LaunchAgents/*.plist |
~/.config/systemd/user/*.service |
crontab -e |
| 开机自启 | plist RunAtLoad=true |
systemctl --user enable |
@reboot crontab |
| 进程保活 | plist KeepAlive=true |
service Restart=on-failure |
需手动重启 |
| 未登录时运行 | 不适用 | loginctl enable-linger |
crontab @reboot 自动覆盖 |
| 查看日志 | cat /tmp/webdav-proxy.log |
journalctl --user -u webdav-proxy |
tail -f /tmp/webdav-proxy.log |
| 检查端口 | lsof -i :18080 |
`ss -tlnp | grep 18080` |
| 停止服务 | launchctl unload ... |
systemctl --user stop ... |
kill $(pgrep -f webdav_digest_proxy) |
故障排查
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
401 Unauthorized |
授权密码过期 | 按上述步骤重新生成密码 |
connection refused (端口 18080) |
代理未运行 | macOS: launchctl load ...;Linux: systemctl --user start webdav-proxy |
502 Bad Gateway |
代理无法连接科技云盘 | 检查网络连接和 DNS 解析 |
| 目录列表为空但无报错 | rclone 请求走了系统代理 | 确保设置了 no_proxy='*' |
Item with unknown path |
代理未正确改写路径 | 检查代理脚本中的 URL 替换逻辑 |
ModuleNotFoundError: requests |
缺少 Python 依赖 | pip3 install requests |
systemd 报 Failed to connect to bus |
缺少 D-Bus 用户会话 | export XDG_RUNTIME_DIR=/run/user/$(id -u) |
| 服务器重启后代理未运行 | 未启用 linger | sudo loginctl enable-linger $USER |
科技云盘 WebDAV 服务信息
| 项目 | 值 |
|---|---|
| WebDAV 地址 | https://pan.cstcloud.cn/DAV/个人文件/ |
| 账户名 | XXXX@XXXX |
| 认证方式 | HTTP Digest (realm="pan", algorithm=MD5) |
| WebDAV 引擎 | IT Hit WebDAV Server v4.4.3821 |
| DAV 支持级别 | 1, 2 |