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

发布于

![image.png](http://pic.zhso.org/2026/07/20/b037358c2a83.png)

- 之前开了 GPT Pro 共享车,需要把已经登录的 Web 端共享给车友。直接提供账密码或者分发浏览器资料(比如指纹浏览器),账号凭证和登录状态都不容易控制。
- 后来我改成用 Neko 共享一台已经登录 ChatGPT 的 Chromium 。车友只进入 Neko 房间,不直接接触账号密码;房间口令、控制权和浏览器可访问的网站可以分别限制。下面记录完整的手动部署过程。

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

![image.png](http://pic.zhso.org/2026/07/20/865910e3ec35.png)

- Neko 站点的 HTTPS 、登录接口和 WebSocket 经过 443 ,再由 Nginx 转发到 Neko 的 8080 。
- Neko 页面中浏览器画面和音频通过 WebRTC 传输,本文开放 UDP 52000–52100 ,并保留 TCP 52101 作为回退。
- ChatGPT 账号网络:Chromium 访问网站时使用容器自己的出站网络。给浏览器增加代理,不会替代 WebRTC 的入站端口。

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

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

```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`:

```dotenv
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 展开结果:

```shell
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 直接暴露公网时开启。[完整变量说明](https://neko.m1k1o.net/docs/v3/configuration)可在官方配置页核对。

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

## 反向代理和防火墙
Nginx 配置需要保留 WebSocket Upgrade 头:

```nginx
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 心跳,代理超时时间不能短于会话心跳周期。[官方反向代理示例](https://neko.m1k1o.net/docs/v3/reverse-proxy-setup)给出了相同的 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 账号。需要保留资料时增加卷挂载:

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

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

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

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

### 2.单独设计浏览器出站
- 为了降低 WebRTC 延迟,可以把 Neko 部署在离用户较近的服务器;如果服务器无法直接访问 ChatGPT ,再单独为 Chromium 配置出站代理。
- Neko v3 没有用于设置 Chromium 代理的环境变量。当前 Chromium 镜像会读取 `/etc/chromium/policies/managed/` 下的浏览器策略,因此可以新建 `chromium-proxy.json`:

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

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

```shell
jq empty chromium-proxy.json
```

```yaml
volumes:
- ./chromium-proxy.json:/etc/chromium/policies/managed/20-proxy.json:ro
```

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

```shell
docker compose up -d --force-recreate
```

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

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

- 最后在 Neko 的 Chromium 中打开 [https://api.ipify.org](https://api.ipify.org)。浏览器显示代理出口 IP ,说明 `ProxySettings` 已经生效;
- `docker exec neko curl -fsS https://api.ipify.org` 仍会显示服务器自己的出口 IP ,因为这份策略只作用于 Chromium ,不会改变 Neko 容器的出口 IP 。
- 示例没有加入 `direct://` 回退:代理断开时 Chromium 会直接报错,不会改用服务器出口。Chromium 也不会读取写在代理 URL 中的用户名和密码;需要认证时,优先让上游代理按服务器 IP 放行,或者增加一个只在 Docker 网络内监听的本地转发代理。
- HTTP/SOCKS5 只能代理 Chromium 的 HTTP 、HTTPS 和 WebSocket 请求,但不能转发 UDP 。如果需要让语音等 UDP 流量走代理,应改用 VPN 或网关容器;使用 `network_mode: service:<gateway>` 后,Neko 的 8080 和 WebRTC 端口也要改由网关容器发布。[Chromium 代理文档](https://chromium.googlesource.com/chromium/src/+/main/net/docs/proxy.md)列出了代理协议、WebSocket 选择顺序和认证限制。

### 3.限制用户使用共享浏览器的访问地址
Neko 的 Chromium 镜像已经内置了一份[浏览器策略](https://github.com/m1k1o/neko/blob/master/apps/chromium/policies.json),其中包括禁用开发者工具、下载和访客模式等限制。为了保留这些默认项,先从正在运行的容器复制策略文件,只修改其中的 `URLBlocklist` 和 `URLAllowlist`:

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

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

```shell
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](http://chatgpt.com/) 会同时匹配它的子域名;以点开头的 `.auth.openai.com` 只匹配这个主机名。上面的列表用于 ChatGPT Web 、登录、静态资源和文件访问,域名取自 [OpenAI 当前的网络配置建议](https://help.openai.com/en/articles/9247338-network-recommendations-for-chatgpt-errors-on-web-and-apps)。语音、付款或第三方登录需要使用时,再从官方列表加入对应域名。

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

```shell
jq empty chromium-policies-restricted.json
```

```yaml
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`:

```shell
docker compose up -d --force-recreate
```

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

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

## 排障

| 现象 | 检查 | 处理 |
|------|------|------|
| 域名无法打开 | curl [http://127.0.0.1:8080/health](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/](https://1.1.1.1/) ,再检查 DNS 和 Docker 网段 | 能访问 IP 但不能访问域名时修 DNS ;同时排除 Docker 子网冲突 |

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

```shell
docker compose logs neko | grep nat_ips
```

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

- [博客原文](https://yx.ink/blog/neko-chatgpt)

---

原文链接:[点击查看](https://www.v2ex.com/t/1228623)

评论

暂无评论。