使用 Neko 搭建 ChatGPT 拼车共享浏览器

7 月 20 日
 mirror

网络结构

先总览一下网络拓扑,帮助大家理解 Neko + 共享浏览器的网络链路:

域名页面能打开,只能证明 443 和 8080 正常,不能证明 WebRTC 已经连通。Neko WebRTC 文档 也明确要求媒体端口直接可达,或者通过 TURN 中继。(部署时就踩了这个坑)

Docker Compose 部署

准备一台带公网 IP 的 VPS 、一个已解析到该 IP 的域名,以及 Docker Engine 和 Compose 插件。新建目录后写入 compose.yaml:

services:
  neko:
    image: ghcr.io/m1k1o/neko/chromium:latest
    container_name: neko
    restart: unless-stopped
    shm_size: "2gb"
    ports:
      - "127.0.0.1:8080:8080"
      - "52000-52100:52000-52100/udp"
      - "52101:52101/tcp"
    environment:
      NEKO_MEMBER_PROVIDER: "multiuser"
      NEKO_MEMBER_MULTIUSER_USER_PASSWORD: "${NEKO_USER_PASSWORD:?required}"
      NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: "${NEKO_ADMIN_PASSWORD:?required}"

      NEKO_SERVER_BIND: "0.0.0.0:8080"
      NEKO_SERVER_PROXY: "true"
      NEKO_DESKTOP_SCREEN: "1920x1080@30"

      NEKO_SESSION_IMPLICIT_HOSTING: "false"
      NEKO_SESSION_CONTROL_PROTECTION: "true"
      NEKO_FILETRANSFER_ENABLED: "false"
      NEKO_DESKTOP_UPLOAD_DROP: "false"

      NEKO_WEBRTC_EPR: "52000-52100"
      NEKO_WEBRTC_NAT1TO1: "${NEKO_PUBLIC_IP:?required}"
      NEKO_WEBRTC_ICELITE: "true"
      NEKO_WEBRTC_TCPMUX: "52101"

同目录创建 .env:

NEKO_PUBLIC_IP=203.0.113.10
NEKO_USER_PASSWORD=replace-with-a-long-random-value
NEKO_ADMIN_PASSWORD=replace-with-another-long-random-value

普通用户和管理员使用不同口令。当前配置关闭了点击画面自动取得控制权,并要求管理员在房间内时普通用户才能取得控制权;如果需要无人值守的远程控制,应按使用场景调整这两个选项。

把 .env 权限收紧,并在启动前检查 Compose 展开结果:

chmod 600 .env
docker compose config --quiet
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:8080/health

NEKO_SERVER_BIND=0.0.0.0:8080 让 Docker 能访问容器内服务;宿主机的端口映射仍限定在 127.0.0.1,外部访问只能经过反向代理。NEKO_SERVER_PROXY=true 用于信任反向代理传入的客户端地址头,不能在 Neko 直接暴露公网时开启。完整变量说明可在官方配置页核对。

官方示例使用 latest。完成测试后可以固定到验证过的镜像版本或摘要,避免重新拉取镜像时引入未验证变更。

反向代理和防火墙

Nginx 配置需要保留 WebSocket Upgrade 头:

server {
    listen 443 ssl http2;
    server_name neko.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

修改后执行 nginx -t,确认无误再 reload 。Neko 会定期发送 WebSocket 心跳,代理超时时间不能短于会话心跳周期。官方反向代理示例给出了相同的 Upgrade 配置。

需要开放的端口如下:

端口 协议 用途
443 TCP HTTPS 、登录接口和 WebSocket
80 TCP 可选;用于 ACME HTTP-01 签发或跳转 HTTPS
52000–52100 UDP WebRTC 媒体端口,必须与 NEKO_WEBRTC_EPR 完全一致
52101 TCP 可选的 WebRTC TCP mux 回退

不要把 8080 开放到公网。云防火墙和系统防火墙都要检查;只配置其中一层仍可能导致媒体连接超时。

关键配置

1.持久化 Chromium 资料

默认情况下,重建容器会丢失 Chromium 的 Cookie 、扩展和设置,为了避免每次重启容器需要重新登录 chatgpt 账号。需要保留资料时增加卷挂载:

    volumes:
      - ./chromium-data:/home/neko/.config/chromium

挂载后先检查目录属主。浏览器无法启动、只有黑屏时,可以在容器内确认目录是否属于 neko 用户:

docker exec -it neko ls -la /home/neko/.config/chromium
docker exec -it neko chown -R neko:neko /home/neko/.config/chromium

持久化目录会同时保存网站登录态。允许多人进入同一个房间时,应把整个 profile 视为共享数据,并单独决定备份和销毁方式。

把 WebRTC 收敛到单端口

如果不想开放一段 UDP 端口,可以改用 UDP/TCP mux 。删除 NEKO_WEBRTC_EPR 及其端口映射,改成:

    ports:
      - "127.0.0.1:8080:8080"
      - "52000:52000/udp"
      - "52000:52000/tcp"
    environment:
      NEKO_WEBRTC_UDPMUX: "52000"
      NEKO_WEBRTC_TCPMUX: "52000"

UDP 延迟通常更低,TCP 适合作为受限网络中的回退。端口不能在 Docker 层改映射,例如不能把宿主机 52000 转到容器 59000 。

2.单独设计浏览器出站

{
  "ProxySettings": {
    "ProxyMode": "fixed_servers",
    "ProxyServer": "socks5://proxy.example.com:1080",
    "ProxyBypassList": "localhost,127.0.0.1"
  }
}

HTTP 代理可以把 ProxyServer 改成 http://proxy.example.com:3128。先检查 JSON ,再把文件挂载到 Chromium 的 managed policy 目录:

jq empty chromium-proxy.json
    volumes:
      - ./chromium-proxy.json:/etc/chromium/policies/managed/20-proxy.json:ro

如果前面已经配置了 Chromium 资料持久化,把两个挂载项放在同一个 volumes 列表中。重新创建容器使策略生效:

docker compose up -d --force-recreate

先从容器测试代理是否可达。下面以 SOCKS5 为例,HTTP 代理将参数改为 --proxy http://proxy.example.com:3128:

docker exec neko curl -fsS --proxy socks5h://proxy.example.com:1080 https://api.ipify.org

3.限制用户使用共享浏览器的访问地址

Neko 的 Chromium 镜像已经内置了一份浏览器策略,其中包括禁用开发者工具、下载和访客模式等限制。为了保留这些默认项,先从正在运行的容器复制策略文件,只修改其中的 URLBlocklist 和 URLAllowlist:

docker cp neko:/etc/chromium/policies/managed/policies.json ./chromium-policies.json

用 jq 只替换这两个字段,输出一份新的策略文件:

jq '
  .URLBlocklist = ["*", "file://*"] |
  .URLAllowlist = [
    "chatgpt.com",
    "chat.openai.com",
    ".auth.openai.com",
    ".auth0.openai.com",
    ".setup.auth.openai.com",
    "oaistatic.com",
    "oaiusercontent.com",
    "oaistatsig.com",
    ".cdn.openaimerge.com",
    ".challenges.cloudflare.com",
    ".cdn.workos.com",
    ".forwarder.workos.com",
    ".setup.workos.com",
    ".images.workoscdn.com",
    ".workos.imgix.net",
    "chrome://policy"
  ]
' chromium-policies.json > chromium-policies-restricted.json

Chromium 的策略语法中,chatgpt.com 会同时匹配它的子域名;以点开头的 .auth.openai.com 只匹配这个主机名。上面的列表用于 ChatGPT Web 、登录、静态资源和文件访问,域名取自 OpenAI 当前的网络配置建议。语音、付款或第三方登录需要使用时,再从官方列表加入对应域名。

先检查 JSON 格式,然后把文件挂载回原路径:

jq empty chromium-policies-restricted.json
    volumes:
      - ./chromium-data:/home/neko/.config/chromium
      - ./chromium-policies-restricted.json:/etc/chromium/policies/managed/policies.json:ro

如果不需要持久化 Chromium 资料,删除第一行卷挂载即可。重新创建容器后,在 Chromium 中打开 chrome://policy,点击 Reload policies (重新加载策略),确认 URLBlocklist 和 URLAllowlist 的状态为 OK:

docker compose up -d --force-recreate

最后分别打开 https://chatgpt.com 和一个未放行的网站,确认前者可用、后者显示被管理员阻止。验证完成后可以从白名单中删除 chrome://policy,再重新创建容器。

URLBlocklist 限制的是 Chromium 中的地址访问,不是容器级防火墙;chatgpt.com 内部的会话、设置等功能也仍在放行范围内。需要限制容器内其他进程的出站连接时,还要在出口代理或防火墙上单独配置白名单。

排障

现象 检查 处理
域名无法打开 curl http://127.0.0.1:8080/health 、Nginx 错误日志 先确认容器健康,再检查反代目标、证书和 443
登录页正常,画面超时 EPR 、Docker UDP 映射、两层防火墙、nat_ips 日志 让端口范围和协议完全一致;修正 NEKO_WEBRTC_NAT1TO1
有光标但 Chromium 黑屏 shm_size 、profile 路径和属主 保持至少 2 GB /dev/shm ;修复或暂时移除持久化目录
外网正常,内网无法连接 路由器是否支持 NAT loopback 使用公网地址回环、VPN 或 TURN
Chromium 没有网络 先访问 https://1.1.1.1 ,再检查 DNS 和 Docker 网段 能访问 IP 但不能访问域名时修 DNS ;同时排除 Docker 子网冲突

服务端可以先看 Neko 识别到的公网地址:

docker compose logs neko | grep nat_ips

客户端使用 Chromium 时打开 chrome://webrtc-internals。如果 candidate 中只有不可达的内网地址,继续检查 NAT1TO1 、端口映射或 TURN ,不必反复修改 Nginx 。Neko Troubleshooting 也采用这个排查顺序。

2001 次点击
所在节点    分享发现
2 条回复
morkerlin
7 月 22 日
摄像头和麦克风没法用
mirror
7 月 23 日
@morkerlin webrtc 音视频相关的端口确认一下?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/1228623

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX