为什么需要这个方案

树莓派通常部署在家庭局域网内,没有公网 IP,外部无法直接访问。即使通过端口映射暴露服务,HTTP 明文传输也存在安全风险。

理想的方案需要同时解决两个问题:

  1. 内网穿透——让外部流量能到达树莓派
  2. HTTPS 加密——保护传输数据,避免中间人攻击

本文使用 frp 解决穿透问题,Caddy 解决 HTTPS 问题,部署在一台阿里云轻量应用服务器上作为中转节点。

架构概览

Caddy + frp 架构图

流量路径:浏览器 → Caddy (HTTPS) → frps (vhost HTTP) → frpc → 树莓派本地服务

环境说明

组件环境
树莓派Raspberry Pi 4B, Raspberry Pi OS (Debian 13 Trixie), arm64
阿里云服务器轻量应用服务器, Ubuntu 24.04 LTS
frpv0.70.0(从 GitHub Releases 下载)
Caddyv2.11.4(通过 apt 安装)

第一步:部署 frps(阿里云服务器)

下载安装

# 下载 frp v0.70.0
wget https://github.com/fatedier/frp/releases/download/v0.70.0/frp_0.70.0_linux_amd64.tar.gz
tar xzf frp_0.70.0_linux_amd64.tar.gz
sudo cp frp_0.70.0_linux_amd64/frps /usr/local/bin/

编辑配置文件

创建 /etc/frp/frps.toml

bindPort = 7000
vhostHTTPPort = 8080

transport.tls.force = true

subDomainHost = "example.com"  # 替换为你的域名

auth.method = "token"
auth.token = "你的frp认证token"  # 建议使用长随机字符串

# Server Dashboard(可选,仅本地访问)
webServer.addr = "127.0.0.1"
webServer.port = 27500
webServer.user = "admin"
webServer.password = "你的dashboard密码"

关键配置说明

  • bindPort = 7000:frp 客户端连接端口
  • vhostHTTPPort = 8080:HTTP 子域名路由端口,Caddy 会将匹配子域名的请求转发到这里
  • transport.tls.force = true:强制 TLS 加密 frp 隧道,防止中间人攻击
  • subDomainHost:你的主域名,frpc 配置的子域名会拼接在这个域名下
  • Dashboard 绑定 127.0.0.1,仅允许本地访问,外部通过 Caddy 反向代理访问

设置 systemd 服务

sudo tee /etc/systemd/system/frps.service << 'EOF'
[Unit]
Description=frp server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now frps

验证服务状态:

sudo systemctl status frps

第二步:部署 frpc(树莓派)

下载安装

# 下载 frp v0.70.0(arm64 版本)
wget https://github.com/fatedier/frp/releases/download/v0.70.0/frp_0.70.0_linux_arm64.tar.gz
tar xzf frp_0.70.0_linux_arm64.tar.gz
sudo cp frp_0.70.0_linux_arm64/frpc /usr/local/bin/

编辑配置文件

创建 ~/.config/frpc.toml(或 /etc/frp/frpc.toml):

serverAddr = "x.x.x.x"  # 替换为阿里云服务器公网 IP
serverPort = 7000

auth.method = "token"
auth.token = "你的frp认证token"  # 必须与 frps 一致

transport.tls.enable = true

# 客户端 Dashboard(可选,仅本地访问)
webServer.addr = "127.0.0.1"
webServer.port = 27400
webServer.user = "admin"
webServer.password = "你的dashboard密码"

# 代理:SSH 访问
[[proxies]]
name = "raspberrypi-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 20022
transport.useCompression = true

# 代理:Web 服务(通过子域名访问)
[[proxies]]
name = "raspberrypi-web"
type = "http"
localIP = "127.0.0.1"
localPort = 8080  # 树莓派上实际运行的服务端口
subdomain = "pi"  # 访问 pi.example.com 时转发到此服务
transport.useCompression = true

关键配置说明

  • transport.tls.enable = true:启用 TLS 加密连接 frps
  • SSH 代理使用 TCP 类型,映射到服务器的 20022 端口
  • Web 服务使用 HTTP 类型 + subdomain,通过子域名访问
  • transport.useCompression = true:启用压缩,节省带宽

设置 systemd 服务

sudo tee /etc/systemd/system/frpc.service << 'EOF'
[Unit]
Description=frp client
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/frpc -c /home/pi/.config/frpc.toml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now frpc

验证服务状态:

sudo systemctl status frpc

第三步:部署 Caddy(阿里云服务器)

Caddy 的最大优势是自动 HTTPS——它会自动为你的域名申请和续期 Let’s Encrypt 证书,无需手动管理。

安装 Caddy

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

配置 Caddyfile

编辑 /etc/caddy/Caddyfile

# frp Server Dashboard
pi-admin.example.com {
    reverse_proxy 127.0.0.1:27500
}

# 树莓派 Web 服务(通过 frp 子域名代理)
pi.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up Host {host}
    }
}

配置说明

  • pi-admin.example.com:访问 frps 的 Dashboard 界面
  • pi.example.com:访问树莓派上运行的 Web 服务
  • header_up Host {host}:将原始 Host 头传递给 frps,确保子域名路由正确

验证配置并启动

修改 Caddyfile 后,先验证配置格式是否正确:

# 检查 Caddyfile 语法
sudo caddy validate --config /etc/caddy/Caddyfile

# 格式化 Caddyfile
sudo caddy fmt --overwrite /etc/caddy/Caddyfile

确认无误后启动/重载 Caddy:

# 首次启动
sudo systemctl enable --now caddy

# 后续修改配置后重载(无需重启,零停机)
sudo systemctl reload caddy

# 查看状态
sudo systemctl status caddy

提示:Caddy 支持 reload 热重载,修改配置后不需要重启服务,连接不会中断。

Caddy 启动后会自动:

  1. 检测配置的域名
  2. 向 Let’s Encrypt 申请证书
  3. 配置 HTTPS
  4. 自动续期证书

验证证书是否生效:

curl -I https://pi.example.com

响应头中应包含 server: Caddystrict-transport-security 等 HTTPS 相关头。

第四步:验证完整链路

测试 HTTPS 访问

在浏览器中访问:

  • https://pi.example.com → 树莓派 Web 服务
  • https://pi-admin.example.com → frps Dashboard

两个地址都应显示有效的 HTTPS 证书(Let’s Encrypt)。

测试 SSH 访问

ssh -p 20022 user@你的阿里云服务器IP

安全建议

  1. frp token:使用 openssl rand -hex 32 生成高强度 token,不要使用弱密码
  2. Dashboard 访问:frps 和 frpc 的 Dashboard 都绑定 127.0.0.1,仅通过 Caddy 反向代理对外暴露,建议为 Dashboard 添加认证
  3. TLS 强制加密:frps 配置 transport.tls.force = true,frpc 配置 transport.tls.enable = true,确保隧道全程加密
  4. 端口最小化:仅暴露必要的服务端口,SSH 可限制来源 IP
  5. 定期更新:保持 frp、Caddy 和系统处于最新版本

常见问题

Caddy 证书申请失败

确保域名的 DNS A 记录已正确指向阿里云服务器的公网 IP,且 80 端口未被其他服务占用(Caddy 需要 80 端口进行 HTTP-01 验证)。

子域名访问 404

检查 frpc 的 subdomain 配置是否与 Caddy 的域名匹配。frps 会将 pi.example.com 的请求路由到 frpc 注册的 pi 子域名代理。

frp 连接断开

检查 frpc 和 frps 的 token 是否一致,TLS 配置是否匹配。查看日志排查:

# frps 日志
sudo journalctl -u frps -f

# frpc 日志(如果使用 systemd)
journalctl -u frpc -f

总结

这套方案的几个实在好处:

  • 证书不用管:Caddy 自动搞定 Let’s Encrypt 证书的申请和续期,到期自动续,不用操心
  • 全程加密:frp 走 TLS 隧道,外面再套一层 Caddy HTTPS,双重保险
  • 成本低:就一台阿里云轻量应用服务器,树莓派那边连公网 IP 都不需要
  • 省心:三个服务都走 systemd,崩了自动拉起来,开机自启

树莓派上跑的博客、HomeAssistant、文件服务之类的,都可以用这套方式安全地暴露到外网。


环境版本参考:frp v0.70.0, Caddy v2.11.4, Raspberry Pi OS (Debian 13 Trixie), Ubuntu 24.04 LTS