纸翼 · 加载中
3104 words
16 minutes
RK3576 私有云盘部署实录:Headscale、Samba、FRP 与单 Key Web 访问

这篇文章记录一套真正部署并经过公网读写测试的私有云盘:RK3576 负责保存文件,Windows 电脑通过 Headscale/Tailscale 把 SSD、U 盘分别挂载为 Z:Y:,陌生设备则打开 HTTPS 域名、输入一个访问 Key 后使用网页文件管理器。

项目配置、鉴权程序、systemd 服务和更细的逐步命令已经整理到:

为了避免泄露真实环境,本文统一使用 cloud.example.comheadscale.example.com203.0.113.10<TOKEN> 等示例值。照着部署时必须替换成自己的信息。

一、我要实现什么#

硬件和环境如下:

  • RK3576,Debian 12 ARM64,4 GB 内存;
  • 128 GB NVMe SSD;
  • 64 GB U 盘;
  • 一台有公网 IP 的 Linux 服务器;
  • Windows 电脑;
  • 校园网或手机热点。

目标有两种访问方式:

Windows Z/Y 盘:
Windows -> Tailscale/Headscale -> RK -> Samba -> /srv/cloud
公网网页:
Browser -> HTTPS/Nginx -> FRP -> RK Nginx -> Key 鉴权
-> FileBrowser Quantum -> SSD/USB

两条链路操作的是同一份文件,不需要同步。公网服务器只转发流量,不保存云盘数据。

二、为什么要拆成这些组件#

Headscale 和 Tailscale#

Tailscale 客户端在设备之间建立基于 WireGuard 的加密网络,能穿过多数 NAT。Headscale 是自托管控制服务器,负责设备注册、密钥协调、地址分配和访问策略,但不承担普通文件流量。

两端网络允许时,文件数据点对点传输;校园网限制较严时,Tailscale 会退回 DERP 中继。控制服务器放在自己的公网服务器上,设备就不必依赖 Tailscale 官方控制平面。

Samba#

Samba 在 Linux 上实现 SMB 协议。Windows 原生支持 SMB,所以可以直接把共享目录映射为 Z:,使用体验和本地分区很接近。

我没有把 445 端口暴露到校园网或公网。Samba 只监听 RK 的 127.0.0.1:445,然后用 Tailscale Serve 把 Tailnet 内的 445 转发到这个回环端口。

FRP#

陌生设备未必安装 Tailscale,因此还要有公网 Web。RK 在 NAT 后面主动连接公网服务器上的 FRPS,建立反向隧道;公网 Nginx 收到 HTTPS 请求后,把它送入隧道。

FRP 的远端代理端口只绑定公网服务器的 127.0.0.1,外界只能走 Nginx 的 443,不能绕过 TLS 和站点规则。

FileBrowser Quantum 与 Key 网关#

FileBrowser 负责文件列表、上传、下载、重命名和删除。它没有直接暴露,而是信任 RK Nginx 注入的 X-Forwarded-User

前面再放一个很小的 Python 鉴权服务:用户输入 Key 后获得 HMAC 签名 Cookie,Nginx 用 auth_request 检查会话。Key 只以 scrypt 散列保存,支持过期、撤销、轮换和全局会话失效。

三、先检查开发板#

Terminal window
cat /etc/os-release
uname -a
uname -m
free -h
lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINTS,TRAN
df -hT
ip -br address
ip route

重点是通过型号、容量和传输类型分清 eMMC、NVMe 和 U 盘。本文假设系统在 mmcblk0,SSD 是 /dev/nvme0n1,U 盘是 /dev/sda

如果先用手机热点联网:

Terminal window
nmcli device wifi list
nmcli device wifi connect '<SSID>' password '<PASSWORD>' ifname wlan0
ping -c 3 1.1.1.1

校园网若是 802.1X,不能只填账号密码,还要知道 EAP 类型、二阶段认证和 CA 证书策略。我先用普通热点把环境配好,再迁移到校园网,这样排障变量更少。

四、为什么发现设备后还要挂载#

lsblk 能看到磁盘,只代表内核发现了块设备。应用只能通过 Linux 目录树访问文件系统,所以还必须把分区挂载到目录。

本方案没有把 SSD 和 U 盘做 RAID、LVM 或联合文件系统,而是分别挂载:

/srv/cloud
├── SSD -> NVMe ext4
└── USB -> U 盘 ext4

这样一块设备拔出或损坏不会连带破坏另一块盘。代价是它们不是一个可自动汇总容量的文件系统。

下列格式化命令会清空目标磁盘。务必先用 lsblkblkidfindmnt 确认设备,绝对不要对系统 eMMC 执行。

Terminal window
wipefs -a /dev/nvme0n1
parted -s /dev/nvme0n1 mklabel gpt mkpart primary ext4 0% 100%
mkfs.ext4 -L RK_SSD /dev/nvme0n1p1
wipefs -a /dev/sda
parted -s /dev/sda mklabel gpt mkpart primary ext4 0% 100%
mkfs.ext4 -L RK_USB /dev/sda1
blkid /dev/nvme0n1p1 /dev/sda1

创建服务用户和挂载点:

Terminal window
groupadd --system cloud
useradd --system --gid cloud --home-dir /nonexistent \
--shell /usr/sbin/nologin cloud
install -d -m 0770 -o cloud -g cloud /srv/cloud/SSD /srv/cloud/USB

把真实 UUID 写入 /etc/fstab

UUID=<SSD_UUID> /srv/cloud/SSD ext4 defaults,noatime,nofail,x-systemd.device-timeout=10s 0 2
UUID=<USB_UUID> /srv/cloud/USB ext4 defaults,noatime,nofail,x-systemd.device-timeout=10s 0 2
Terminal window
systemctl daemon-reload
mount -a
chown cloud:cloud /srv/cloud/SSD /srv/cloud/USB
chmod 0770 /srv/cloud/SSD /srv/cloud/USB
findmnt /srv/cloud/SSD /srv/cloud/USB
df -hT /srv/cloud/SSD /srv/cloud/USB

如果挂载失败却继续向空目录写文件,数据会落到 eMMC 上,这是必须防范的坑。

五、部署 Headscale#

先把 headscale.example.com 解析到公网服务器。Debian 12 可安装 Headscale 官方 DEB:

Terminal window
HEADSCALE_VERSION=0.29.3
wget -O /tmp/headscale.deb \
"https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb"
apt install -y /tmp/headscale.deb nginx certbot

配置的核心内容:

server_url: https://headscale.example.com
listen_addr: 127.0.0.1:8080
trusted_proxies:
- 127.0.0.1/32
- ::1/128
prefixes:
v4: 100.64.0.0/10
allocation: sequential
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
dns:
magic_dns: true
base_domain: tail.example.com

Headscale 只监听回环,公网由 Nginx 终止 TLS。Nginx 反向代理必须传递 Upgrade,因为 Tailscale Control Protocol 会通过 WebSocket POST 工作。

Terminal window
systemctl enable --now headscale
curl -fsS http://127.0.0.1:8080/health
headscale users create cloud

仓库中提供了完整的 Headscale 配置和 Nginx 示例

六、注册 RK 和 Windows#

RK 安装 Tailscale:

Terminal window
curl -fsSL https://tailscale.com/install.sh | sh
systemctl enable --now tailscaled

在 Headscale 服务器生成单次、短期预认证 Key:

Terminal window
headscale preauthkeys create --user cloud --expiration 1h --reusable=false

在 RK 使用一次后即可丢弃明文:

Terminal window
tailscale up \
--login-server=https://headscale.example.com \
--authkey='<PREAUTH_KEY>' \
--hostname=rk3576-cloud
tailscale status
tailscale ip -4

Windows 安装 Tailscale 后,用另一个单次 Key 注册:

Terminal window
& "$env:ProgramFiles\Tailscale\tailscale.exe" up `
--login-server=https://headscale.example.com `
--authkey='<ANOTHER_PREAUTH_KEY>' `
--hostname='windows-client'

这里容易误解:预认证 Key 只用于首次把设备登记到 Tailnet。登记完成后设备用自己的节点密钥自动连接,所以每次重启不需要登录账号。

七、Samba 和 Windows Z、Y 盘#

Terminal window
apt install -y samba

/etc/samba/smb.conf 添加:

[global]
interfaces = lo
bind interfaces only = yes
smb ports = 445
map to guest = never
[RKSSD]
path = /srv/cloud/SSD
browseable = yes
read only = no
valid users = cloud
force user = cloud
create mask = 0660
directory mask = 0770
[RKUSB]
path = /srv/cloud/USB
browseable = yes
read only = no
valid users = cloud
force user = cloud
create mask = 0660
force create mode = 0660
directory mask = 0770
force directory mode = 0770
Terminal window
smbpasswd -a cloud
testparm -s
systemctl enable --now smbd
tailscale serve --bg --tcp=445 tcp://127.0.0.1:445
tailscale serve status

我还给 smbd 和 FileBrowser 增加了 mountpoint 启动前检查。任意一块盘没有真正挂载时,文件服务会拒绝启动,避免数据误写进 eMMC 上的空目录。

在 Windows 管理员 PowerShell 中:

Terminal window
net use Z: "\\<RK_TAILSCALE_IP>\RKSSD" /user:cloud * /persistent:yes
net use Y: "\\<RK_TAILSCALE_IP>\RKUSB" /user:cloud * /persistent:yes

星号让 Windows 交互输入 Samba 密码,避免明文出现在命令历史。此后 Z: 是 SSD,Y: 是 U 盘,容量也会分别正确显示。

八、FileBrowser 为什么要配置两个 source#

最初我把 /srv/cloud 配成一个名为 RK Cloud 的 source,网页能看到 SSDUSB 文件夹,却无法正确显示容量。

原因是 /srv/cloud 本身位于 eMMC,SSD 和 U 盘只是它下面的两个挂载点。一个 source 对应一个文件系统统计,无法把两个独立挂载的容量自动相加。

最终配置改成:

server:
port: 18082
listen: "127.0.0.1"
sources:
- path: "/srv/cloud/SSD"
name: "SSD"
config:
defaultEnabled: true
private: true
- path: "/srv/cloud/USB"
name: "USB"
config:
defaultEnabled: true
private: true
auth:
methods:
password:
enabled: false
proxy:
enabled: true
header: "X-Forwarded-User"
frontend:
disableUsedPercentage: false

这样网页左侧会显示 SSD、USB 两个入口和各自的占用条。配置文件和 systemd 服务在仓库的 filebrowser/systemd/ 中。

安装 ARM64 版本:

Terminal window
wget -O /tmp/filebrowser-quantum \
https://github.com/gtsteffaniak/filebrowser/releases/download/v1.5.5-stable/linux-arm64-filebrowser
install -m 0755 /tmp/filebrowser-quantum /usr/local/bin/filebrowser-quantum
systemctl enable --now filebrowser-quantum

九、单 Key 登录的实现#

公网 Web 不开放 FileBrowser 的管理后台和密码登录。自定义网关提供:

  • rk1.<id>.<random> 形式的高熵 Key;
  • scrypt 散列存储;
  • HMAC 签名、SecureHttpOnlySameSite=Lax Cookie;
  • 12 小时会话;
  • 10 分钟内连续失败限速;
  • Key 过期、撤销、轮换;
  • 全局会话重置。

部署仓库中的 gateway/cloud_auth.py 后:

Terminal window
cloudkey init
cloudkey add owner --expires never
cloudkey list

addrotate 只显示一次明文,应该立刻保存到密码管理器。管理命令还有:

Terminal window
cloudkey add guest --expires 7d
cloudkey rotate owner
cloudkey revoke guest
cloudkey sessions-reset

RK Nginx 先向 cloud-auth 发内部鉴权请求,成功后才把 X-Forwarded-User 注入 FileBrowser。FileBrowser 只监听回环地址,否则任何能直连 18082 的人都能伪造认证头。

十、FRP 和公网 HTTPS#

公网服务器 FRPS 的关键配置:

bindAddr = "0.0.0.0"
bindPort = 10925
proxyBindAddr = "127.0.0.1"
auth.method = "token"
auth.token = "<RANDOM_64_HEX_TOKEN>"

token 可以现场生成:

Terminal window
openssl rand -hex 32

RK 的 FRPC:

serverAddr = "203.0.113.10"
serverPort = 10925
loginFailExit = false
auth.method = "token"
auth.token = "<SAME_TOKEN>"
[[proxies]]
name = "rk-cloud-web"
type = "tcp"
localIP = "127.0.0.1"
localPort = 18080
remotePort = 18082

loginFailExit = false 配合 systemd 的 Restart=always,校园网短暂断开后会自动恢复。

公网 Nginx 只需要把云盘域名代理到本机 127.0.0.1:18082

location / {
proxy_pass http://127.0.0.1:18082;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_request_buffering off;
proxy_buffering off;
client_max_body_size 0;
}

先把 cloud.example.com 解析到公网服务器,再用 Certbot 申请证书。仓库的 server/ 目录包含 HTTP、HTTPS、FRPS 和 unavailable 页面示例。

十一、这次实际遇到的几个问题#

1. 502 Bad Gateway#

公网 Nginx 返回 502,说明域名和 Nginx 已经工作,后端链路断在 FRP、RK Nginx、鉴权服务或 FileBrowser。

Terminal window
# 公网服务器
systemctl status frps nginx
ss -lntp | grep -E ':(10925|18082)\b'
# RK
systemctl status frpc-cloud nginx cloud-auth filebrowser-quantum
journalctl -u frpc-cloud -n 100 --no-pager
curl -I http://127.0.0.1:18080/login

有一次 502 的直接原因是 Python 登录页模板用了 str.format(),而 CSS 也包含花括号,结果 CSS 被误当成格式字段并抛出 KeyError。后来改成明确占位符的 .replace(),避免模板语法与 CSS 冲突。

2. 登录页 403#

403 来自鉴权服务的来源校验。浏览器翻译或某些跳转可能发送 Origin: null,两层代理如果又没有一致传递 Host,就会被拒绝。

最终逻辑是:普通 Origin 必须和可信 Host 一致;Origin: null 只有在 HostX-Forwarded-Host 归一化后唯一且一致时才放行。这样兼容浏览器,又不把 CSRF 防护完全关闭。

3. 页面一直显示“恢复连接”#

这是公网入口在线但 RK 到服务器的 FRP 隧道中断。校园网切换、Wi-Fi 省电或 DNS 暂时异常都可能触发。systemd 自动重启 FRPC,再加一个周期检查 NetworkManager、Tailscale 和 FRPC 的 timer,可以自动恢复大多数短暂故障。

4. 有些文件无法访问#

网页和 Samba 都以 cloud 用户操作。root 手动复制的文件若属于 root:root 且权限太紧,就会出现能看到但打不开或无法修改。

只修复明确目录:

Terminal window
chown -R cloud:cloud /srv/cloud/SSD/目标目录
chmod -R u+rwX,g+rwX,o-rwx /srv/cloud/SSD/目标目录

不要对系统根目录递归 chown,也不要用 chmod 777

5. 一个共享无法正确表示两块盘的容量#

Samba 共享根目录跨越两个文件系统时,Windows 只能显示共享根对应的一个文件系统统计,不能自然表示 SSD 与 U 盘之和。最终我把 Samba 也拆成 RKSSDRKUSB 两个共享,分别映射为 Z:Y:;网页则通过拆分 source 达到同样效果。

十二、验收与重启测试#

RK 本地:

Terminal window
systemctl --no-pager status \
tailscaled smbd filebrowser-quantum cloud-auth nginx frpc-cloud
findmnt /srv/cloud/SSD /srv/cloud/USB
df -hT /srv/cloud/SSD /srv/cloud/USB
tailscale serve status

公网服务器:

Terminal window
systemctl --no-pager status headscale frps nginx
curl -fsS https://headscale.example.com/health
curl -I https://cloud.example.com/login

最后分别在网页 SSD、USB 两个来源执行上传、读取、重命名、删除,再去 Z: 盘确认变化。仓库中的 tests/public_crud.ps1 可以自动完成两块盘的公网 CRUD 测试,并在 finally 中清理测试文件。

完成后必须重启 RK 再测一次。只有挂载、Tailnet、Samba、Tailscale Serve、FileBrowser、鉴权、FRPC 全部能自动恢复,才算真正可用。

十三、安全和备份#

这套方案缩小了暴露面,但它不是备份系统:

  • 445 只放在 Tailnet,绝不直接开放公网;
  • FileBrowser 与鉴权服务只监听回环;
  • FRP 远端代理只绑定公网服务器回环;
  • 所有公网文件流量经过 HTTPS;
  • Key、FRP token、Samba 密码、预认证 Key和 SSH 私钥绝不入库;
  • 网页和 SMB 删除的是同一份真实文件,默认没有回收站;
  • 重要文件必须另做离线或异地备份,并测试恢复。

结语#

这套架构把“自己的设备像本地盘一样用”和“陌生设备临时用网页访问”分开处理:前者用 Tailnet + SMB 获得原生体验,后者用 HTTPS + Key 获得低门槛访问。RK 保存数据,公网服务器只做控制和转发,SSD 与 U 盘保持独立,故障边界也比较清晰。

如果要完全复现,建议以 cloud 仓库的部署指南 为准,逐项替换示例值,并在每一层完成本地检查后再继续下一层。

RK3576 私有云盘部署实录:Headscale、Samba、FRP 与单 Key Web 访问
https://blog.huangzy.xyz/posts/rk3576私有云盘部署实录/
Author
纸翼
Published at
2026-09-02
License
CC BY-NC-SA 4.0

Some information may be outdated