OpenClaw 公网访问:FRP + nginx HTTPS 配置实录
#OpenClaw #FRP #nginx #HTTPS #公网访问
接上篇 《OpenClaw 配置续集:网络修复、邮件插件与 Ollama 接入》,记录第三阶段:为 OpenClaw 配置公网 HTTPS 访问。
一、整体架构
1 | 用户浏览器 |
关键点:
-
MyServer 已运行 frps(端口 6000,token 已配置),所有隧道复用同一个 frps,无需额外部署
-
N100 已有 frpc 运行(管理 SSH、WebDAV 等隧道),只需追加
[openclaw]段 -
nginx 和 frps 在同一台机器,nginx 通过
127.0.0.1:17890访问 frps 的代理端口(不需要对外开放 17890)
二、SSH 与服务器别名说明
1 | ssh MyServer → x.x.x.x:22(直接,真实公网服务器) |
三、N100 frpc 追加 OpenClaw 隧道
编辑 /etc/frp/frpc.ini,在已有 [common](server_addr=x.x.x.x, port=6000)后追加:
1 | [openclaw] |
然后重启 frpc:
1 | sudo systemctl restart frpc |
四、MyServer nginx 配置
证书使用 acme.sh + Cloudflare DNS API 自动签发(域名 NS 已迁移到 Cloudflare)。
申请证书
1 | sudo mkdir -p /etc/nginx/ssl/openclaw |
nginx 站点配置
/etc/nginx/sites-available/openclaw:
1 | server { |
启用站点:
1 | sudo ln -sf /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/openclaw |
Aliyun 安全组
无需额外开放新端口。frps 控制端口 6000 已开放(SSH 隧道在用),nginx 的 80/443 已开放。frps 代理端口 17890 仅供 nginx 本机访问,不需要对外暴露。
五、DNS 配置
在 Cloudflare 添加 A 记录:
| 类型 | 名称 | 内容 | 代理 |
|---|---|---|---|
| A | claw | x.x.x.x | DNS only(灰云) |
acme.sh 使用 DNS API 验证,无需 A 记录提前生效即可申请证书。
六、踩坑记录
1. certbot 系统版本损坏
MyServer 的 /usr/bin/certbot(v1.21.0)因 Python 依赖冲突无法启动(ImportError: cannot import name 'appengine')。改用 acme.sh,已稳定运行。
2. nginx 配置引用不存在的证书导致 nginx -t 失败
最初 nginx 配置引用了 Let’s Encrypt 路径(/etc/letsencrypt/live/...),证书未申请时 nginx 拒绝启动。改用 HTTP-only 临时配置或确保先申请证书再启用站点。
3. OpenClaw 实际监听端口非 18789
OpenClaw 配置 "bind": "lan", "port": 18789 中,port 是元数据(用于 CORS/WebSocket origin 校验),进程实际监听的端口需通过 sudo ss -tlnp 确认。N100 上同时运行了 code-server(127.0.0.1:8080),一度误判为 OpenClaw。
健康检查配置揭示真实端口:
1 | docker inspect openclaw --format '{{json .Config.Healthcheck}}' |
4. OpenClaw ollama.auth: "none" 启动失败
现象:容器反复重启,日志:
1 | models.providers.ollama.auth: Invalid input (allowed: "api-key", "aws-sdk", "oauth", "token") |
原因:OpenClaw 的 doctor --fix 或版本迁移会自动在 ollama provider 写入 "auth": "none",但新版本校验不接受 "none"。
修复:用 Python 原地写入(不能用 sed -i,因为 Docker bind mount 文件不支持 rename):
1 | python3 -c " |
Why sed -i fails on bind mount:sed -i 内部将内容写入临时文件再 rename 替换原文件,Docker bind mount 不允许在挂载点进行 rename 操作,报 Device or resource busy。需要用 open(path, 'w') 直接原地覆写同一 inode。
七、环境变量迁移问题
OpenClaw 更新后会将 openclaw.json 中的敏感字段自动替换为 ${VAR} 环境变量引用,并期望通过 env_file 注入。若 .env 缺少对应变量则启动失败。
需要在 .env 中补充以下变量:
1 | DEEPSEEK_API_KEY=<deepseek api key> |
重要:docker restart 不会重载 env_file,环境变量在容器创建时注入。修改 .env 后必须重建容器。
docker-compose v1.29.2 Bug:执行 docker-compose up -d 时报 KeyError: 'ContainerConfig',这是 docker-compose v1 的已知 bug。改用 docker compose(v2)或手动重建:
1 | docker rm openclaw |
注意:-v 只挂载 data/ 目录,不单独挂载 openclaw.json 文件(原因见九)。
八、EBUSY:OpenClaw CLI 写入失败
现象:安装插件或运行 openclaw config set 时报错:
1 | EBUSY: resource busy or locked, rename '/root/.openclaw/.tmp-openclaw.json' -> '/root/.openclaw/openclaw.json' |
根本原因:OpenClaw CLI 的配置写入使用原子操作(写临时文件 → rename),而 Docker 单文件 bind mount(-v .../openclaw.json:/root/.openclaw/openclaw.json)不允许在挂载点执行 rename,导致所有涉及配置写入的操作失败。
修复:将 openclaw.json 移入 data/ 目录,改为目录挂载:
1 | # 在宿主机上 |
然后从 docker-compose.yml 移除单文件挂载行:
1 | # 修改前(有问题) |
重建容器后 OpenClaw 可正常写入配置。
修改 data/openclaw.json 的权限问题:容器以 root 运行,data/ 目录下的文件属主为 root。在宿主机上直接修改需要 sudo:
1 | sudo python3 -c " |
九、ollama.auth 问题反复出现
除容器初次启动外,OpenClaw 在运行时收到 SIGUSR1(插件安装、doctor --fix 等操作触发内部配置迁移)也会重新写入 "auth": "none",导致下次重启失败。
现象规律:
-
容器重启失败,日志
ollama.auth: Invalid input -
Python 修复后
docker restart,启动成功 -
在容器内执行某些操作(如插件安装)后,再次重启又失败
临时修复流程(每次重启失败后执行):
1 | sudo python3 -c " |
十、插件渠道配置(plugins.allow 白名单)
plugins.allow 是严格白名单,不在列表内的插件(含内置插件)全部对 Agent 不可见。
当前 openclaw.json 配置:
1 | "plugins": { |
修改白名单后执行 docker restart openclaw 即生效(不需要重建容器,因为只是读配置)。
各渠道说明:
| 渠道 | 说明 | 状态 |
|---|---|---|
email |
163 邮箱 IMAP/SMTP | 已配置,运行中 |
gmail |
QQ 邮箱(gmail 插件兼容 QQ SMTP) | 已配置,运行中 |
qqbot |
QQ 机器人(需官方 AppID/AppToken) | 白名单已加,待配置 |
feishu |
飞书机器人 | 白名单已加,待配置 |
imessage |
iMessage(仅 Mac 宿主机可用) | 白名单已加,待配置 |
openclaw-weixin |
微信(需扫码,不支持非交互式) | 白名单已加,待配置 |
QQ Bot 说明:qqbot 插件对接的是 QQ 开放平台 API,不是个人 QQ 账号登录。需前往 q.qq.com 注册机器人,获取 AppID 和 AppToken 后填入配置。
十一、浏览器来源被拒(controlUi.allowedOrigins)
现象:浏览器访问公网 HTTPS 地址后,连接被拒绝,控制台提示 Origin 不被允许。
原因:openclaw.json 中 gateway.controlUi.allowedOrigins 默认只含本地地址。
修复:在 allowedOrigins 数组中加入公网 HTTPS 域名:
1 | "gateway": { |
修改 data/openclaw.json 后 docker restart openclaw 生效。
十二、最终验证
1 | curl -si https://your.domain.example/ --max-time 10 | head -3 |
浏览器访问 https://your.domain.example,输入 .env 中的 OPENCLAW_GATEWAY_TOKEN 值即可登录 OpenClaw。
十三、设备配对认证(devices approve)
现象:首次从新浏览器或新设备访问 Control UI 时,弹出:
需要设备配对 — 此浏览器需要 Gateway 主机的一次性批准后才能使用 Control UI。在 Gateway 主机上运行
openclaw devices list。批准此请求:openclaw devices approve <device-id>
原因:OpenClaw Gateway 在 Token 鉴权之外增加了设备级别的一次性信任机制,防止 Token 泄漏后被任意设备使用。
批准流程(在 N100 上执行):
1 | # 列出待批准设备(含 device-id) |
批准后该浏览器永久记录在 Gateway,无需再次操作。不同浏览器(Chrome/Safari/Firefox)各为独立设备,需分别批准。
十四、硬件架构总览
1 | MacBook Air M1 (192.168.0.110) |
流量路径(公网访问):
1 | 浏览器 HTTPS 443 |
