docs(architecture): WS tunnel 網路/防火牆需求文件
整理 WS tunnel 要通需要的網路設定(正式環境交接用): - 核心:邊界反代開 WebSocket upgrade 轉發(或 L4 TCP passthrough), 現在剝 WS upgrade header → tunnel 400 - 配套:idle timeout≥90s、wss TLS、DNS、上線前移除 demo 繞法 - Port 清單:唯一對公網開 :9527,:3800/:3801 絕不對外 - 含 HAProxy/ALB/Cloudflare/F5 等效設定 + 驗證方法 + 回傳碼對照 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
58313169c2
commit
dbe5d6a78e
332
docs/autoflow/04-architecture/tunnel-ws-network-requirements.md
Normal file
332
docs/autoflow/04-architecture/tunnel-ws-network-requirements.md
Normal file
@ -0,0 +1,332 @@
|
||||
# Tunnel WebSocket 網路 / 防火牆 / 反代設定需求
|
||||
|
||||
> **文件目的**:讓「公網使用者的 visionA Agent 能建立 tunnel WebSocket 連線」,需要在網路層、防火牆、反向代理上做哪些設定。
|
||||
> **適用對象**:網管 / 維運 / 部署交接人員(不需先懂 tunnel 內部協定)。
|
||||
> **本文件與 `tunnel.md` 的分工**:`tunnel.md` 講 tunnel 內部協定(yamux / session / 訊息格式);本文件只講「WS 連線要能穿過網路各層並成功握手,每一層要開/設什麼」。
|
||||
> **最後更新**:2026-07(整段連線排查確認後整理,供正式上線 + 交接用)
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR(給趕時間的人)
|
||||
|
||||
正式環境要讓 tunnel 通,**最關鍵的一件事**:
|
||||
|
||||
> **公網入口那台「邊界反向代理」必須顯式轉發 WebSocket upgrade(`Connection: Upgrade` + `Upgrade: websocket`),並用 HTTP/1.1。**
|
||||
> 目前它沒設,會剝掉 upgrade header,導致後端收到殘缺握手回 **400 bad handshake**。這是 tunnel 建不起來的唯一根因。
|
||||
|
||||
其餘四件事:
|
||||
1. **防火牆**:放行 agent 出站 → 公網入口 `:9527`(wss/TLS)。
|
||||
2. **Idle timeout**:邊界反代 / LB 的 idle timeout 拉長到 **≥ 90s**(tunnel 是長連線,靠 10s 心跳維持)。
|
||||
3. **TLS**:agent 走 `wss://`,公網入口憑證需有效(Let's Encrypt 已有)。
|
||||
4. **DNS**:`stage-9527.innovedus.com` 解析到公網入口 `1.34.63.223`。
|
||||
|
||||
---
|
||||
|
||||
## 1. WebSocket tunnel 的網路本質(先建立共識)
|
||||
|
||||
### 1.1 為什麼 tunnel 用 WebSocket
|
||||
|
||||
Agent 跑在使用者的內網電腦、沒有公網 IP,雲端無法「主動連進去」。所以改由 **agent 主動撥一條長連線出來**,之後雲端所有要送給 agent 的請求都走這條反向通道。這條長連線用 WebSocket 承載(底層再跑 yamux 多工),因為 WebSocket:
|
||||
- 走標準 `443`/HTTP(S) port,穿透企業防火牆的成功率最高(比自訂 TCP port 好穿);
|
||||
- 是雙向全雙工,雲端能主動往 agent 推資料;
|
||||
- 能被大多數反代 / LB 理解。
|
||||
|
||||
### 1.2 為什麼 WebSocket 對反代設定「特別敏感」
|
||||
|
||||
WebSocket 連線是從一個**普通 HTTP 請求「升級(upgrade)」**而來的。這個升級靠兩個 HTTP header:
|
||||
|
||||
```
|
||||
Connection: Upgrade
|
||||
Upgrade: websocket
|
||||
```
|
||||
|
||||
這兩個是 **hop-by-hop header**——依 HTTP 規範,它們只對「當前這一跳」有效,**每一層反向代理都必須「主動、顯式」把它們再往下轉發**。反代若沒特別設定,預設行為就是**把它們吃掉不轉**。
|
||||
|
||||
結果:
|
||||
|
||||
```
|
||||
agent 送:Connection: Upgrade / Upgrade: websocket
|
||||
→ 邊界反代沒設 WS 轉發 → 把這兩個 header 剝掉
|
||||
→ 後端 remote-proxy 收到一個「沒有 upgrade 意圖」的普通 HTTP 請求
|
||||
→ 無法完成 WebSocket 握手 → 回 400 Bad Request(bad handshake)
|
||||
```
|
||||
|
||||
**這就是本專案排查到的根本卡點**。實測鐵證:同一個 WS 握手,agent 直連內網 `192.168.0.130:9527` 成功回 **101 Switching Protocols**;一旦改走公網邊界反代,就變 **400**。差別只在「中間那層有沒有轉發 upgrade header」。
|
||||
|
||||
> 白話總結:**tunnel 建不起來 99% 是某一層反代把 upgrade header 吃掉了。** 每多一層反代,就要多確認一次那層有沒有開 WS 轉發。
|
||||
|
||||
### 1.3 為什麼要強制 HTTP/1.1(不能 HTTP/2)
|
||||
|
||||
`Connection` 這個 header 在 **HTTP/2 是被禁止的**(h2 用不同的多工機制,沒有 hop-by-hop header 概念)。如果 agent 跟反代之間 TLS 協商(ALPN)談成了 h2,那 `Connection: Upgrade` 這套 RFC 6455 的 WebSocket 握手方式根本無法運作。
|
||||
|
||||
因此:
|
||||
- **Agent 端已修正**:TLS 握手強制 ALPN 只提供 `http/1.1`,避免被協商成 h2。
|
||||
- **反代端要求**:面向 backend 轉發時用 `proxy_http_version 1.1`(nginx)或等效設定,不要用 h2 轉發 WS。
|
||||
|
||||
> 注意:這裡指的是「承載 WebSocket 那一跳」要用 h1.1。TLS 本身照常(wss = WebSocket over TLS)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 完整連線鏈路與每一跳的要求
|
||||
|
||||
### 2.1 架構拓樸
|
||||
|
||||
```
|
||||
┌─────────────┐ wss (TLS, h1.1) ┌──────────────────────────┐ http (內網) ┌────────────────────────┐
|
||||
│ visionA │ WS upgrade │ 邊界反向代理(公網入口) │ 轉發 │ container 內 nginx │
|
||||
│ Agent │ ──────────────────► │ stage-9527.innovedus.com │ ──────────────► │ 192.168.0.130:9527 │
|
||||
│ (桌面程式) │ :9527 │ 1.34.63.223:9527 │ │ /tunnel/connect │
|
||||
│ gorilla/ws │ │ · LE HTTPS termination │ │ (Host 白名單 + WS 轉發) │
|
||||
└─────────────┘ │ · NAT → 內網 130 │ └───────────┬────────────┘
|
||||
│ ⚠ 目前剝掉 WS upgrade │ │ http (loopback)
|
||||
└──────────────────────────┘ ▼
|
||||
↑ 本專案碰不到、非 repo 內 ┌────────────────────────┐
|
||||
↑ 需網管在此開 WS 轉發 │ remote-proxy :3800 │
|
||||
│ (tunnel WS server, │
|
||||
│ yamux, 10s keepalive) │
|
||||
└────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 逐跳要求
|
||||
|
||||
| 跳 | 從 → 到 | 協定 | 這一跳要能過,需要什麼 |
|
||||
|----|---------|------|----------------------|
|
||||
| ① | Agent → 邊界反代 | wss (TLS+h1.1) | 防火牆放行 agent 出站到 `:9527`;DNS 解析正確;憑證有效 |
|
||||
| ② | **邊界反代 → container nginx** | http(NAT/內網) | **⚠ 最關鍵:邊界反代必須顯式轉發 `Upgrade`/`Connection` header + 用 h1.1 + 長 read timeout。目前缺這段 → 400。** |
|
||||
| ③ | container nginx → remote-proxy | http(loopback) | 已正確設定(`/tunnel/connect` location 已含 WS 轉發,見 §3.2)。Host 需命中白名單。 |
|
||||
| ④ | remote-proxy 內部 | yamux over WS | 已就緒(10s 心跳 / 30s 判定掉線)。 |
|
||||
|
||||
**每一跳都要「WS 通」,只要任何一層剝掉 upgrade header,整條就斷。** ② 是目前唯一未達標的一跳。
|
||||
|
||||
---
|
||||
|
||||
## 3. 正式環境要做什麼(重點,給網管 / 交接)
|
||||
|
||||
### 3.1 邊界反向代理(公網入口那台,**最關鍵**)
|
||||
|
||||
這台做 Let's Encrypt HTTPS termination + NAT 轉發到內網 `192.168.0.130`。它**目前是 L7 HTTP proxy 且沒設 WS 轉發**,所以剝掉 upgrade header。要修,二選一:
|
||||
|
||||
#### 選項 A(推薦):在邊界反代加 WebSocket upgrade 轉發
|
||||
|
||||
如果這台是 **nginx**,針對 `/tunnel/connect`(或整個 domain)加:
|
||||
|
||||
```nginx
|
||||
# http context(server block 外)先定義 upgrade map
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9527 ssl;
|
||||
server_name stage-9527.innovedus.com;
|
||||
# ... LE 憑證設定 ...
|
||||
|
||||
location /tunnel/connect {
|
||||
proxy_pass http://192.168.0.130:9527; # NAT 到內網 container nginx
|
||||
|
||||
# ── WebSocket 轉發三要素(缺一不可)──
|
||||
proxy_http_version 1.1; # ① 必須 h1.1(h2 不支援 hop-by-hop header)
|
||||
proxy_set_header Upgrade $http_upgrade; # ② 顯式轉發 upgrade header
|
||||
proxy_set_header Connection $connection_upgrade; # ③ 顯式轉發 connection header
|
||||
|
||||
# ── 長連線 timeout(tunnel 是長連線,靠心跳維持)──
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
|
||||
# ── 不要 buffer WS 訊框 ──
|
||||
proxy_buffering off;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **驗收**:改完後,從外部對 `wss://stage-9527.innovedus.com:9527/tunnel/connect` 發 WS 握手應回 **101**(見 §5)。
|
||||
|
||||
#### 選項 B:如果那台不是 nginx(可能是防火牆 / LB / WAF)
|
||||
|
||||
不同設備的等效設定名稱:
|
||||
|
||||
| 設備類型 | 要開的等效設定 | 備註 |
|
||||
|----------|--------------|------|
|
||||
| **HAProxy** | `mode http` + 預設即支援 WS(h1.1);或直接 `mode tcp` 做 L4 passthrough | 確認 `timeout tunnel` 拉長(如 `timeout tunnel 1h`) |
|
||||
| **AWS ALB / GCP HTTPS LB** | 原生支援 WebSocket,但要確認 **idle timeout** 拉長(ALB 預設 60s) | Target group 用 HTTP/1.1 |
|
||||
| **Cloudflare / CDN** | 開啟 WebSockets(帳號設定內有開關);避免走會剝 header 的 transform rule | 免費/付費方案都支援 WS |
|
||||
| **F5 / Nginx Plus / 商用 WAF** | 需開「WebSocket profile / passthrough」;部分 WAF 預設攔 upgrade | 若 WAF 檢測 body,長連線可能被誤殺 |
|
||||
| **通用防火牆 / NAT** | 若只是 L4 轉發(TCP passthrough),**天生支援 WS**(它不看 HTTP header),只要把 `:9527` TCP 轉到內網即可 | 最單純、最不會出錯的做法 |
|
||||
|
||||
> **最保險做法**:若邊界那台能做 **L4 / TCP passthrough**(純轉 TCP、不解 HTTP),WebSocket 天生就通,因為它根本不碰 HTTP header。代價是 HTTPS termination 要往內移(改由 container 那層或另設)。是否採用取決於現有架構,需與網管確認。
|
||||
|
||||
### 3.2 Container 內 nginx(已就緒,僅供交接核對)
|
||||
|
||||
Repo 內 `docker/nginx.stage.conf` 的 `/tunnel/connect` **已正確設定**,交接時只需確認未被改壞:
|
||||
|
||||
```nginx
|
||||
map $http_upgrade $connection_upgrade { # http context
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
location /tunnel/connect {
|
||||
proxy_pass http://visiona_tunnel; # → 127.0.0.1:3800 (remote-proxy)
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_buffering off;
|
||||
proxy_set_header Host $host;
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
**Host 白名單注意**:此 nginx 只接受 `Host: stage-9527.innovedus.com`;不符的 Host(含直接打 IP)一律 `444`(直接關連線)。這是 trust boundary 防護。若邊界反代轉發時改寫或遺漏 Host,會被打 444——邊界反代必須帶正確的 `Host` header 下來。
|
||||
|
||||
### 3.3 防火牆 / Port 放行清單
|
||||
|
||||
| Port | 協定 | 方向 | 開在哪 | 用途 | 必要性 |
|
||||
|------|------|------|--------|------|--------|
|
||||
| **9527** | TCP / TLS(wss) | agent 出站 → 公網入口入站 | 邊界防火牆(公網側) | agent 建 tunnel 的唯一對外入口 | **必須** |
|
||||
| 9527 | TCP | 邊界反代 → 內網 130 | 內網防火牆 | NAT 轉發到 container nginx | **必須**(內部) |
|
||||
| 3800 | TCP | container nginx → remote-proxy | container loopback | tunnel WS server(面向 agent 側) | 內部(同 container,無需對外) |
|
||||
| 3801 | TCP | api-server → remote-proxy | 內部 | remote-proxy internal HTTP(面向 api-server 側) | 內部(**絕不可對外**) |
|
||||
| 3721 | TCP | nginx → api-server | container loopback | api-server | 內部 |
|
||||
| 3000 | TCP | nginx → frontend | container loopback | Next.js frontend | 內部 |
|
||||
|
||||
**關鍵放行只有一條**:讓使用者端 agent 能出站連到 **公網入口 `:9527`**(TCP/wss)。其餘都在雲端內部 / container loopback,不對公網開放。
|
||||
|
||||
> ⚠️ **安全提醒**:`:3800`(tunnel WS server)與 `:3801`(remote-proxy internal,無認證的 HTTP 轉發端點)**絕對不能直接暴露到公網**。它們只該從 container loopback / 內網受信任來源存取。對外一律只走 `:9527` 經 nginx。
|
||||
|
||||
### 3.4 Idle timeout(長連線存活)
|
||||
|
||||
Tunnel 是長連線,靠 **yamux 每 10s 送一次心跳**維持(連續 3 次未回 = 30s 判定掉線)。中間任何一層若 idle timeout 太短,會在心跳間隔間把連線掐斷。
|
||||
|
||||
| 層 | 建議 idle / read timeout | 理由 |
|
||||
|----|------------------------|------|
|
||||
| 邊界反代 / LB | **≥ 90s**(建議直接設 3600s 或 86400s) | 要大於心跳間隔且留安全邊際;雲端 LB 常見預設 60s 偏短 |
|
||||
| container nginx | 86400s(已設) | 長連線;心跳由 yamux 處理,nginx 不該中途斷 |
|
||||
| remote-proxy session idle | `VISIONA_TUNNEL_IDLE_TIMEOUT`,預設 **30s** | 這是「判定對端失聯」的時間(= 3 次心跳未回)。demo 曾調 24h 避免誤斷,但正式環境**建議保留 30s 預設**(配合 10s 心跳,這是正確的掉線判定值),不要盲目拉長,否則殭屍 session 不會被清 |
|
||||
|
||||
> 釐清:`VISIONA_TUNNEL_IDLE_TIMEOUT=30s` 是「多久沒心跳就判死」,不是「多久沒 API 請求就斷 tunnel」。tunnel 只要心跳正常就永遠在線。demo 當時調成 24h 是為了繞過另一個問題(心跳未被中間層正確轉發時的誤斷),**正式環境把中間層 WS 轉發修對後,應回到 30s 預設**。
|
||||
|
||||
### 3.5 HTTPS / TLS
|
||||
|
||||
- Agent 走 `wss://`(WebSocket over TLS)。公網入口的憑證(Let's Encrypt)必須有效、未過期、CN/SAN 涵蓋 `stage-9527.innovedus.com`。
|
||||
- Agent 端**不應**在正式環境用 `InsecureSkipVerify`(略過憑證驗證)——那只允許 dev 自簽憑證時使用。
|
||||
- TLS termination 在公網入口那台(LE 憑證);container nginx 收到的是純 HTTP(內網),這是正常的(`X-Forwarded-Proto: https` 已由 nginx 補回給 backend)。
|
||||
|
||||
### 3.6 DNS
|
||||
|
||||
`stage-9527.innovedus.com` 必須解析到公網入口 `1.34.63.223`。agent 的連線 URL(tunnel endpoint)指向此 domain。若走內網繞法(見附錄)則直接用 IP,不經 DNS。
|
||||
|
||||
---
|
||||
|
||||
## 4. 各層設定 Checklist(交接核對表)
|
||||
|
||||
| 層 | 要做什麼 | 為什麼 | 誰負責 | 狀態 |
|
||||
|----|---------|--------|--------|------|
|
||||
| **邊界反向代理**(公網入口) | 針對 `/tunnel/connect`(或整 domain)開 **WebSocket upgrade 轉發**:h1.1 + `Upgrade`/`Connection` header + read timeout ≥ 90s + buffering off;或改 L4 TCP passthrough | 這層目前剝掉 upgrade header → 400;是 tunnel 建不起來的唯一根因 | **網管**(此設備非 repo 內) | ❌ **待做(最優先)** |
|
||||
| **防火牆 / Port** | 放行 agent 出站 → 公網入口 `:9527`(TCP/wss);確認邊界反代 → 內網 `130:9527` 內部轉發通;`:3800`/`:3801` 不對外 | agent 唯一對外入口;內部端點防護 | 網管 | 需確認 |
|
||||
| **Idle timeout** | 邊界反代 / LB idle timeout 拉長到 ≥ 90s(建議 3600s+) | tunnel 長連線靠 10s 心跳維持,太短會誤斷 | 網管 | 需確認 |
|
||||
| **HTTPS / TLS** | 公網入口 LE 憑證有效、涵蓋 domain;agent 走 wss、正式不 skip verify | wss 安全;憑證錯 agent 連不上 | 網管 / 部署 | LE 已有,需核對 |
|
||||
| **DNS** | `stage-9527.innovedus.com` → `1.34.63.223` | agent 找得到入口 | 網管 | 需確認 |
|
||||
| **container nginx** | 確認 `/tunnel/connect` 的 WS 轉發設定未被改壞;Host 白名單接受 `stage-9527...`;正式上線移除 demo 用的 IP 直連 server block(或加 `allow 內網網段; deny all;`) | 這層已就緒;demo 繞法不可留在正式 | 部署 | ✅ 已就緒(demo block 待移除) |
|
||||
| **remote-proxy** | 確認 `:3800` tunnel port、`VISIONA_TUNNEL_IDLE_TIMEOUT` 回到 30s 預設 | 心跳/掉線判定正確 | 部署 | ✅(idle timeout 需從 demo 的 24h 改回) |
|
||||
| **agent 端** | 強制 ALPN `http/1.1`(已修);連線 URL 指向公網 domain;正式憑證不 skip verify | 避免被協商成 h2 導致 WS 握手失敗 | 已修(code) | ✅ 已修 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 驗證方法(怎麼確認每一層 WS 通)
|
||||
|
||||
WebSocket 握手成功的標誌是 HTTP **101 Switching Protocols**。用以下方式逐層測,快速定位是哪一跳斷的。
|
||||
|
||||
### 5.1 用 curl 測 WS 握手(看回傳 code)
|
||||
|
||||
```bash
|
||||
# 對「內網 container nginx」直測(繞過邊界反代)— 預期 101
|
||||
curl -i -N \
|
||||
-H "Connection: Upgrade" \
|
||||
-H "Upgrade: websocket" \
|
||||
-H "Sec-WebSocket-Version: 13" \
|
||||
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
|
||||
-H "Host: stage-9527.innovedus.com" \
|
||||
http://192.168.0.130:9527/tunnel/connect
|
||||
|
||||
# 對「公網邊界反代」測(正式路徑)— 修好前是 400,修好後應為 101
|
||||
curl -i -N \
|
||||
-H "Connection: Upgrade" \
|
||||
-H "Upgrade: websocket" \
|
||||
-H "Sec-WebSocket-Version: 13" \
|
||||
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
|
||||
https://stage-9527.innovedus.com:9527/tunnel/connect
|
||||
```
|
||||
|
||||
### 5.2 回傳 code 對照表(快速定位斷點)
|
||||
|
||||
| 收到 | 意義 | 該查哪一層 |
|
||||
|------|------|-----------|
|
||||
| **101** Switching Protocols | ✅ 握手成功,WS 通 | 這一層 OK |
|
||||
| **400** Bad Request(bad handshake) | upgrade header 被中間層剝掉 → 後端收到殘缺握手 | **邊界反代沒開 WS 轉發**(本專案根因);或中間某層吃掉 header |
|
||||
| **444**(連線直接被關、無回應) | Host 白名單擋掉 | 反代沒帶正確 `Host: stage-9527.innovedus.com` 下來,命中 container nginx 的 `default_server` → 444 |
|
||||
| **502 / 504** | 到達 nginx 但上游 remote-proxy 不通 / timeout | remote-proxy(`:3800`)沒起,或 read timeout 太短 |
|
||||
| **連線逾時 / 拒絕** | 網路層不通 | 防火牆沒放行 `:9527`,或 DNS 解錯 |
|
||||
|
||||
### 5.3 用 wscat 做完整 WS 連線測試(更真實)
|
||||
|
||||
```bash
|
||||
# npm i -g wscat
|
||||
wscat -c "wss://stage-9527.innovedus.com:9527/tunnel/connect?token=<測試 token>"
|
||||
# 成功:連上不立即斷;失敗:印出 400 / handshake 錯誤
|
||||
```
|
||||
|
||||
### 5.4 用 openssl 檢查 TLS / ALPN(排 h2 誤協商)
|
||||
|
||||
```bash
|
||||
openssl s_client -connect stage-9527.innovedus.com:9527 -alpn http/1.1 -servername stage-9527.innovedus.com </dev/null 2>/dev/null | grep -i 'ALPN\|Verify'
|
||||
# 確認 ALPN 談成 http/1.1、憑證 Verify return code: 0 (ok)
|
||||
```
|
||||
|
||||
### 5.5 逐層排查建議順序
|
||||
|
||||
1. 先測 §5.1 **內網直連**(130)→ 若 101,證明 container nginx + remote-proxy 都 OK,問題純在邊界反代。
|
||||
2. 再測 §5.1 **公網邊界反代** → 若 400,就是邊界反代沒轉 WS(照 §3.1 修)。
|
||||
3. 若 444 → 檢查邊界反代有沒有把 `Host` 改成 / 帶成 `stage-9527.innovedus.com`。
|
||||
4. 若逾時 → 回頭查防火牆 §3.3 與 DNS §3.6。
|
||||
|
||||
---
|
||||
|
||||
## 附錄 A:demo 環境的臨時繞法(**正式不可用**)
|
||||
|
||||
排查期間為了讓 tunnel 先 online 做 demo,採用「**agent 直連內網、繞過邊界反代**」的臨時 hack。記錄於此供理解,但**正式上線一律不可沿用**。
|
||||
|
||||
### A.1 demo 怎麼繞通的
|
||||
|
||||
| 繞法 | 做了什麼 | 為什麼能通 |
|
||||
|------|---------|-----------|
|
||||
| agent 直連內網 | agent 連 `ws://192.168.0.130:9527`(不走公網 `:9527` 邊界反代) | 直接打 container nginx,跳過「會剝 header 的邊界反代」 |
|
||||
| nginx 加 IP server block | `docker/nginx.stage.conf` 加 `server_name 192.168.0.130` 的 block(含完整 WS 轉發) | agent 用 IP 當 Host 時能過白名單(否則打 444) |
|
||||
| `VISIONA_RELAY_PUBLIC_URL` 改內網 | pairing exchange 回給 agent 的 relay URL 改成內網 `ws://192.168.0.130:9527/...` | 讓 agent 拿到內網 URL 去連 |
|
||||
| `VISIONA_TUNNEL_IDLE_TIMEOUT` 改 24h | 拉長掉線判定 | 繞過 demo 當時中間層心跳未正確轉發導致的誤斷 |
|
||||
|
||||
### A.2 為什麼正式不能用
|
||||
|
||||
- **只在同一內網可行**:`192.168.0.130` 是私有 IP,公網使用者的 agent 根本連不到。
|
||||
- **明文 http(無 TLS)**:demo 走 `ws://`(非 wss),tunnel 流量未加密,正式環境不可接受。
|
||||
- **繞過 trust boundary**:IP 直連 server block 是刻意開的後門,正式上線前**必須移除**,或改成 `allow <內網網段>; deny all;` 限制來源。
|
||||
- **idle timeout 24h 掩蓋問題**:正式環境把中間層 WS 轉發修對後,應回到 30s 預設,讓殭屍 session 能被正確清理。
|
||||
|
||||
> **正式上線正解**:修好 §3.1 的邊界反代 WS 轉發,讓公網使用者的 agent 能經 `wss://stage-9527.innovedus.com:9527/tunnel/connect` 正常建立 tunnel,然後移除本附錄的所有 demo 繞法。
|
||||
|
||||
---
|
||||
|
||||
## 附錄 B:相關檔案 / 設定索引
|
||||
|
||||
| 項目 | 位置 |
|
||||
|------|------|
|
||||
| container nginx 設定(含 `/tunnel/connect` WS 轉發 + demo IP block) | `docker/nginx.stage.conf` |
|
||||
| tunnel 內部協定 / session 管理 / yamux 心跳參數 | `docs/autoflow/04-architecture/tunnel.md` |
|
||||
| tunnel port / idle timeout 等環境變數 | `visionA-backend/internal/config/config.go`、`load.go`(`VISIONA_TUNNEL_PORT=3800`、`VISIONA_PROXY_INTERNAL_PORT=3801`、`VISIONA_TUNNEL_IDLE_TIMEOUT=30s`、`VISIONA_RELAY_PUBLIC_URL`) |
|
||||
| stage 部署設定 | `docs/autoflow/04-architecture/stage-deployment.md`、`docs/autoflow/07-delivery/stage-deployment-setup.md` |
|
||||
Loading…
x
Reference in New Issue
Block a user