N100 网络重构与服务修复记录
#Docker #WebDAV #frp #Samba #openclaw #服务器运维
背景
路由器设备更换,局域网网段从 192.168.0.0/24 整体迁移至 192.168.2.0/24。N100 重启后各服务需要恢复,同时处理 openclaw 权限修复遗留的路径问题、WebDAV 公网访问故障及 LLM 连接问题。
本文涉及的 openclaw 权限修复遗留问题,源自 《N100 安全事件响应:OpenClaw SSH 暴力破解与 fail2ban 加固》。
一、磁盘检查与挂载恢复
btrfs RAID1 健康验证
1 | # 只读检查(不修改数据),双盘 RAID1 只需指定其中一块 |
两块盘共享同一 fsid(RAID1 特性),num_devices: 2。btrfs check 会自动找到另一块盘,无需手动指定两个设备。
开机挂载验证
重启后确认两个挂载点均正常:
1 | /dev/sdc1 on /nfs type btrfs (rw,noatime,compress=zstd:3,...) |
Docker 启动顺序修复
原问题:Docker 容器在磁盘挂载完成前启动,导致依赖 /nfs、/smb 的容器失败。
在 /etc/systemd/system/docker.service.d/wait-mounts.conf 添加:
1 | [Unit] |
使 Docker 守护进程等待两个挂载点就绪后再启动。
二、openclaw 路径修复
问题:/root/.openclaw 权限拒绝
openclaw 以 user: 1000:1000(node 用户)运行,但 openclaw.json 中所有 agent 的 workspace 和 agentDir 路径仍指向 /root/.openclaw/,导致:
1 | Error: EACCES: permission denied, mkdir '/root/.openclaw/workspace-download' |
修复
将 openclaw.json 中所有路径从 /root/.openclaw 改为 /home/node/.openclaw:
1 | { |
exec-approvals socket 路径修复
同样原因,exec-approvals.json 的 socket 路径需指向 node 用户家目录:
1 | { |
三、WebDAV 公网访问恢复
架构说明
WebDAV 公网访问链路:
1 | 外部客户端 |
关键区分:
-
4455:对外的 nginx HTTPS 端口(Alibaba Cloud 安全组开放)
-
4456:frp 内部隧道端口(frpc
remote_port),VPS 内部使用,安全组无需对外开放
/etc/frp/frpc.ini 配置:
1 | [webdav_tcp] |
VPS /etc/nginx/sites-available/port_4455.conf 核心配置:
1 | server { |
故障排查过程
| 检查项 | 结果 | 结论 |
|---|---|---|
N100 本地 curl http://127.0.0.1:4455/milin/ |
401 | Caddy 正常 |
frpc 日志 webdav_tcp start proxy success |
✅ | 隧道建立 |
VPS ss -tlnp | grep 4456 |
LISTEN | frps 监听正常 |
外网 curl https://[VPS_IP]:4455/milin/ |
503/超时 | 安全组未生效 |
根本原因:Alibaba Cloud 安全组的 4455 规则虽已存在(创建于 2025-07-09),但实际未生效。解决方案:在控制台删除旧规则并重新添加,强制规则刷新后立即恢复。
验证
1 | # LAN 直接访问 |
401 表示认证请求被正确处理,WebDAV 服务链路完整。
注意:本地
curl使用 v2raya HTTP 代理(http_proxy=http://127.0.0.1:1087),测试公网访问必须加--noproxy '*'绕过本地代理,否则 503 是代理超时而非实际服务返回。
目录结构
| WebDAV 路径 | 实际目录 | 内容 |
|---|---|---|
/milin/ |
/smb(exfat 3.6T) |
影视、照片、备份等 |
/joplin/ |
/nfs/joplin(btrfs 1.8T) |
Joplin 笔记同步 |
四、SMB 服务
现状
1 | systemctl is-active smbd nmbd # active active |
挂载点 /smb/Movies 目录结构正常(电影、电视剧、动画)。
已知问题:Samba 密码数据库为空
Samba 维护独立于 Linux 系统账号的密码数据库(/var/lib/samba/private/passdb.tdb)。即使 Linux 用户存在,若未通过 smbpasswd 注册,认证会失败,map to guest = Bad User 会将其映射为 guest,而 valid users = @smbfilm 的共享 guest 无权访问。
修复方法(需在 N100 本地执行,须交互式输入密码):
1 | sudo smbpasswd -a milin |
SMB 共享配置
1 | [电影] path=/smb/Movies valid users=@smbfilm read only=no |
smbfilm 组成员:filmplayer、nll2025、milin
五、局域网网段迁移(192.168.0.x → 192.168.2.x)
路由器更换后,所有设备 IP 变更:
| 设备 | 旧 IP | 新 IP |
|---|---|---|
| N100(MyUbuntu) | 192.168.0.155 | 192.168.2.14 |
| milin_desktop | 192.168.0.111 | 192.168.2.10 |
| milin_win11 | 192.168.0.195 | 192.168.2.12 |
涉及更新的配置文件
| 文件 | 变更内容 |
|---|---|
openclaw/openclaw.json |
Ollama baseUrl 更新 |
openclaw/.env |
OPENCLAW_LLM_API_URL 更新 |
openclaw/init-ssh.sh |
SSH HostName 更新 |
openclaw/data/.ssh/config |
SSH HostName 更新 |
openclaw/data/workspace-download/tools/qbit_shell.sh |
QB_HOST 更新 |
openclaw/data/workspace-download/AGENTS.md |
qBittorrent URL 更新 |
~/.ssh/config(Mac) |
已在迁移前更新,无需修改 |
批量替换命令示例:
1 | sed -i 's|192\.168\.0\.155|192.168.2.14|g' <文件列表> |
六、openclaw LLM 连接问题(已解决)
错误现象
1 | All models failed (2): |
根因分析
DeepSeek(云端 API):
N100 宿主机本身可正常访问外网(curl https://api.deepseek.com 返回 401),但 openclaw Docker 容器完全无法出网(连 1.1.1.1 也超时)。
根本原因是 v2raya nftables 透明代理拦截了 Docker 容器流量:
v2raya 使用 table inet v2raya 中的 tp_rule 链,在 PREROUTING 阶段(优先级 dstnat-5,即 -105)对所有 TCP 流量做判断:
1 | chain tp_rule { |
关键点:白名单判断的是目标地址(daddr),而非源地址。172.16.0.0/12 在白名单中表示"目标是 Docker 子网则直连",而不是"来自 Docker 子网则放行"。因此 Docker 容器(源 IP 172.20.0.x)访问外网 IP 时,目标地址不在白名单,流量被 redirect to :52345——目标改写为 127.0.0.1:52345 后进入 INPUT 链,无法再转发,最终丢失。
诊断确认:
1 | # 容器内测试 |
Ollama(本地):
milin_desktop(192.168.2.10)当前不在线,EHOSTUNREACH 属正常情况,机器开机后可恢复。
修复过程
第一步:v2raya nftables 添加源地址放行规则
在 tp_rule 链最前面插入一条基于源地址的 return 规则,使来自 Docker 子网的流量直接跳过重定向:
1 | sudo nft insert rule inet v2raya tp_rule ip saddr 172.16.0.0/12 return |
验证插入结果:
1 | sudo nft list chain inet v2raya tp_rule |
第二步:发现并移除错误的 HTTP_PROXY 环境变量
添加 nft 规则后 DeepSeek 仍然 000。排查发现:前期排查时在 docker-compose.yml 中加入了 HTTP_PROXY=http://172.20.0.1:20175,但对应的转发服务已不存在,导致容器内所有 HTTPS 请求先尝试连接死掉的代理端口,直接超时。
临时绕过验证:
1 | docker exec -e HTTPS_PROXY="" -e HTTP_PROXY="" openclaw \ |
修复:删除 docker-compose.yml 中的三行 proxy 环境变量并重建容器:
1 | sed -i '/HTTP_PROXY\|HTTPS_PROXY\|NO_PROXY/d' docker-compose.yml |
第三步:持久化 nft 规则
v2raya 重启时会重建 table inet v2raya,手动插入的规则会丢失。通过 systemd drop-in 在 v2raya 启动后自动补规则:
1 | sudo mkdir -p /etc/systemd/system/v2raya.service.d |
访问模式说明
修复后 Docker 容器的网络访问方式:
| 场景 | 访问路径 |
|---|---|
| DeepSeek、其他直连服务 | 直连出网(Docker MASQUERADE → 路由器 → 互联网) |
| GFW 屏蔽的服务(搜索 API 等) | HTTP_PROXY → Python 转发代理 → v2raya(20171)→ 出网(见第七节) |
| Ollama(局域网) | 直接 LAN 路由 |
七、权限审批与代理补充修复
7.1 openclaw exec-approvals 设置
openclaw 的 exec-approvals 默认为 allowlist 模式。问题:download agent 执行的命令含 shell 重定向(2>/dev/null),allowlist 模式不支持对重定向符做 glob 匹配,导致相关命令永远无法加入白名单,UI 的"始终允许"按钮灰显。
修复:将 download agent 单独配置为 security: "full" + ask: "off",其他 agent 保持 allowlist 模式不变:
1 | python3 -c " |
7.2 fail2ban 误封 Mac LAN IP
现象:Mac(192.168.2.13)无法 SSH 到 N100,fail2ban 将其封禁。
原因:Mac ~/.ssh/config 中裸 IP 192.168.2.14 未匹配任何 Host 条目,SSH 使用 Mac 本地用户名 lin(而非 milin)登录,连续认证失败触发 fail2ban。
修复:
1 | # 解封 Mac IP |
根本预防:Mac 的 ~/.ssh/config 中 MyUbuntuLocal 条目同时匹配 Host 名和裸 IP,确保裸 IP 也使用正确的用户名:
1 | Host MyUbuntuLocal 192.168.2.14 |
7.3 Docker 容器 HTTP 代理(web_search 修复)
背景:第六节的 nft saddr bypass 规则让 Docker 容器可以直连出网,但无法访问 GFW 屏蔽的站点。openclaw 的 web_search 工具需要通过 v2raya 代理才能工作。
解决方案:在宿主机 Docker bridge 网关(172.20.0.1)上运行 Python TCP 转发代理,将容器发来的 HTTP 代理请求转发到 v2raya 的 HTTP 代理端口。
v2raya 代理端口:20170 = SOCKS5,20171 = HTTP proxy(curl 验证 DuckDuckGo 返回 200)
脚本保存至 /home/milin/v2raya-docker-proxy.py:
1 | import socket, threading |
UFW 放行规则(按 Docker 子网范围放行,不依赖具体 bridge 名称):
1 | sudo ufw allow from 172.16.0.0/12 to any port 20170 proto tcp |
注意:
docker compose up --force-recreate会重建容器网络,导致 bridge 接口名称(如br-ceefd7058599)变更。UFW 若绑定具体 bridge 名称则失效,必须改用源 IP 子网匹配。
openclaw .env 追加代理配置:
1 | HTTP_PROXY=http://172.20.0.1:20170 |
NO_PROXY 范围:需涵盖整个 LAN 子网(
192.168.2.0/24),而非单个 IP。否则同网段的 Ollama(192.168.2.10)、qBittorrent(192.168.2.14:9090)等服务请求会被错误地发往 HTTP 代理,导致 ETIMEDOUT。
持久化:更新 v2raya systemd drop-in,在 nft 规则之后同步启动 Python 代理:
1 | # /etc/systemd/system/v2raya.service.d/docker-bypass.conf |
验证:
1 | docker exec openclaw curl -s --max-time 15 'https://duckduckgo.com/' -o /dev/null -w 'duckduckgo: %{http_code}\n' |
八、N100 重启后的恢复 checklist
1 | # 1. 确认磁盘挂载 |
遗留问题
| 问题 | 状态 | 说明 |
|---|---|---|
| Samba 用户密码注册 | ⏳ 待执行 | 需在 N100 本地运行 sudo smbpasswd -a <user> |
| v2raya 透明代理绕过 Docker | ✅ 已解决 | nft 源地址规则 + 移除 HTTP_PROXY 环境变量 + systemd drop-in 持久化 |
| Docker 容器访问 GFW 屏蔽站点 | ✅ 已解决 | Python TCP 代理(172.20.0.1:20170 → 127.0.0.1:20171)+ HTTP_PROXY 环境变量 |
| UFW bridge 名称失效(force-recreate 后) | ✅ 已解决 | UFW 规则改为按 172.16.0.0/12 子网匹配,不依赖 bridge 名称 |
| NO_PROXY 未覆盖全 LAN 导致 Ollama ETIMEDOUT | ✅ 已解决 | NO_PROXY 改为 192.168.2.0/24,覆盖所有 LAN 服务 |
| openclaw exec-approvals 过严 | ✅ 已解决 | download agent 设为 security: full + ask: off |
| fail2ban 误封 Mac LAN IP | ✅ 已解决 | ignoreip 添加 192.168.2.0/24 + SSH config 修复裸 IP 匹配 |
| milin_desktop Ollama 连通 | ⏳ 待验证 | IP 已更新为 192.168.2.10,待机器上线后验证 |
| milin_win11 Ollama 连通 | ⏳ 待验证 | IP 已更新为 192.168.2.12,待机器上线后验证 |
| Mac SSH 连接 N100 已知密钥更新 | ✅ 已处理 | ssh-keygen -R 192.168.2.14 && ssh -o StrictHostKeyChecking=accept-new MyUbuntuLocal |
