在反向 proxy 後面執行中繼
反向 proxy 是你自己的一台網頁伺服器,它接收來自瀏覽器的連線,再把連線交給中繼。以下情況需要它:你想為區域網路或伺服器上的中繼加上 HTTPS;中繼要放在你已經在經營的網站的子路徑下(https://);或瀏覽器和中繼之間隔著 tunnel 的邊緣節點(例如 Cloudflare)。中繼本機上的 proxy 不需要任何設定就能運作。另一台機器上的 proxy 需要兩到三項設定,見下方。有五個 config. key 會告訴中繼有關 proxy 的事,每一個都解決一個特定的問題;之後會逐一說明。
Proxy 必須做的事 #
中繼的頁面、API 和即時連線都使用同一個連接埠(除非你改了 port,否則是 3333)。proxy 必須:
- 傳遞 WebSocket 升級。 App 會對中繼保持一條開啟的 Socket.IO 連線,路徑為
/(有前綴時為socket. io/ <prefix>/)。如果升級無法通過,頁面會載入,但永遠不會顯示即時更新。socket. io/ - 不要緩衝長時間的連線,也不要讓它們逾時。 只要頁面開著,這個 socket 就會保持開啟,回合的內容會在發生時即時串流過去。請使用較長的讀取和閒置逾時,並且不要緩衝回應。
- 傳遞
Host標頭,或傳送X-。中繼會用它組出分享連結的位址,而且 0.9.11 新增 只接受來自該位址上頁面的即時連線(見Forwarded- Host allowed)。Origins X-和Forwarded- Host X-只有來自中繼信任的 proxy 時才算數。Forwarded- Proto - 在 proxy 終止 TLS 時傳送
X-,讓中繼知道瀏覽器用的是 HTTPS(見 Cookie 與 HTTPS),並傳送Forwarded- Proto X-,讓它知道用戶端的位址。Forwarded- For - 保持路徑原樣。 不要改寫它。如果你在前綴下提供中繼,中繼會自己移除前綴(見
remote)。Path
你不需要快取規則。中繼會為它的頁面、service worker、資訊清單和 API 傳送 Cache-,也會傳送 CDN- 和 Cloudflare-。不要新增快取 / 的規則:分享的檢視會輪詢新內容,而被快取的回應會讓觀看者看到舊的快照。
以下是適用於同一台機器上中繼的最簡 Caddy 範例。Caddy 會自行傳遞 WebSocket 升級,並加上 X-:
relay.example.com {
reverse_proxy 127.0.0.1:3333
}
如果是 nginx 或其他 proxy,請對照上面的清單,查看它自己的文件,了解如何傳遞升級和設定逾時。關於 tunnel,見 SSH tunnel 和 Cloudflare Tunnel;關於存取 token,見存取。
位於另一台機器上的 proxy 0.9.11 新增 #
內建的 tunnel 和中繼本機上的 proxy 都是透過 127. 連到中繼,預設的 trust 會相信它們轉送的標頭。位於另一台主機、另一個容器或 VM 中的 proxy,則是不同的對端。在你信任它之前,中繼會忽略它的 X-、X- 和 X-,因此:
- 每個訪客的位址都是 proxy 的位址:所有人共用同一個登入鎖定,每筆存取紀錄項目中也都是同一個位址;
- 新的分享連結會以
http://和 proxy 送來的主機開頭; - 登入 cookie 不會標上
Secure。
在設定檔中設定三個 key,然後重新啟動中繼:
{
"localhostOnly": false,
"trustProxy": "192.0.2.10",
"allowedOrigins": ["https://relay.example.com"]
}
localhost讓中繼在迴路位址以外也監聽。除了 proxy 之外,請對其他一切關閉這個連接埠。Only: false trust是 proxy 的位址、它的子網路,或中繼前面的 proxy 數量(一個時為Proxy 1)。見下方。allowed只有在 proxy 改寫Origins Host標頭且沒有傳送X-時才需要。見下方。Forwarded- Host
中繼本機上的 proxy 既不需要 trust,也不需要 localhost,只有在它改寫 Host 時才需要 allowed。在網址前綴下,也要設定 remote。
告訴中繼有關 proxy 的 key #
這五個 key 都列在 config.json 的表格中。變更後請重新啟動中繼。
trustProxy #
預設為 "loopback"。它決定中繼相信誰的 X- 標頭。使用預設值時,只有從同一台機器連線的 proxy 會被相信,這對在中繼旁邊執行的 Caddy、nginx 或 cloudflared 來說正好合適。
中繼會從受信任 proxy 的標頭取得:
- 請求是否透過 HTTPS 而來,這會決定登入 cookie 是否帶有
Secure旗標,以及新分享連結的協定; - 瀏覽器所連的主機(
X-),用於新的分享連結,以及即時連線的來源檢查;Forwarded- Host - 用戶端的位址,用於登入鎖定、分享檢視的線上狀態限制,以及分享連結的存取紀錄。該紀錄中的國家(
cf-)也只會從受信任的 proxy 讀取。ipcountry
如果你的 proxy 在另一台機器上執行,中繼會看到一個非本機的對端,並忽略這些標頭。請給 trust 一個 Express 的 trust proxy 所接受的值:跳數(1)、位址或子網路("10.)、由它們組成的清單,或 "loopback"。
只在前面有 proxy 時
只有在每一條連到中繼的連線真的都經過你的 proxy 時,才把 trust 設得比 "loopback" 更寬。否則任何能連到這個連接埠的人,都能送出偽造的 X-。
0.9.11 新增 每次使用轉送標頭都會依循 trust。在 0.9.10 中,分享檢視的線上狀態限制和新分享連結的位址,會從任何用戶端讀取這些標頭。
allowedOrigins 0.9.11 新增 #
預設為 []。即時連線(Socket.IO)只接受來自中繼自身來源的瀏覽器交握:瀏覽器送出的 Origin 必須帶有瀏覽器所連的主機;當受信任的 proxy 送出 X- 時就是它,否則是 Host。其他網站上的頁面無法用你的登入 cookie 開啟即時連線。不送出 Origin 的用戶端(例如腳本)不受影響。
會把 Host 標頭改寫成中繼內部位址、且沒有送出 X- 的 proxy,會破壞這個比對。這時請列出公開的來源,完全照瀏覽器顯示的樣子,包含協定、主機,以及預設以外的任何連接埠:
"allowedOrigins": ["https://relay.example.com"]
沒有它的話,頁面會載入,你也能登入,但沒有任何東西會即時更新,而且中繼會針對每個來源記錄一次:
[socket] Refused a handshake from origin https://relay.example.com (host 127.0.0.1:3333); list it in allowedOrigins if a proxy rewrites the Host header
remotePath #
預設為 ""。當 proxy 在子路徑下提供中繼時,把它設為該前綴,例如 "/。開頭的斜線會自動補上,結尾的斜線會被移除。
設定之後,中繼會:
- 從每個進來的請求拿掉前綴,所以 proxy 傳遞路徑時要帶著前綴,不要移除它;
- 把前綴放進登入 cookie 的路徑、socket 路徑(
/),以及檔案和分享連結的位址。oar/ socket. io/
沒有設定時,位於前綴下的中繼找不到自己的檔案和 socket,因為頁面會在網站的根目錄要求 / 和 /。開啟中繼時請加上結尾的斜線(https://)。
已安裝的 App 屬於它安裝時所用的位址,所以位於前綴下的中繼是一個獨立的 App。見安裝成 App。
publicHostnames #
預設為 []。列出連到中繼本身的主機名稱,例如 ["relay.。中繼只在一個地方使用它們:預覽通道會拒絕位於這些名稱(或 localhost、127.)上的 previews.,這樣被預覽的 App 就永遠不會和中繼共用登入 cookie。如果你把中繼放在使用新名稱的 proxy 後面,並且使用預覽,請把那個名稱加到這裡。
tunnelMarkerHeaders #
預設為 []。你的 proxy 或 tunnel 的邊緣節點會加到它轉送的每個請求上的標頭名稱。中繼會把它們用於下方的 session worker 路徑保護。Cloudflare 加上的 cf- 一律在清單上。值可以是陣列,也可以是以逗號分隔的字串。環境變數 COPILOT_(以逗號分隔)會加入更多名稱;0.9.11 新增 兩份清單會合併,所以 tunnel 不再會關閉這個變數。見環境變數。
狀態點
tunnel 不會讓狀態點變成琥珀色。只有在中繼自己受管理的 Cloudflare tunnel 已連線時,狀態點才會是琥珀色(見對話選單)。你自己執行的 proxy 永遠不會讓它變成琥珀色。
Session worker 路徑保護 #
每個 agent session 都在中繼所在機器上的一個 worker 中執行,worker 透過 /(有前綴時為 <prefix>/)上的 WebSocket 和中繼溝通。proxy 或 tunnel 會把它主機名稱下的每個路徑都轉送給中繼,所以如果沒有保護,這個路徑就能從網際網路連到。只有本機的 worker 應該使用它。
0.9.11 新增 只有當請求來自中繼本機、且沒有經過任何轉送時,保護才會讓對該路徑的請求通過:
- 連線的對端是迴路位址(
127.或0. 0. 1 ::);1 - 它沒有帶任何標記標頭:
cf-,或ray tunnel中的某一個;Marker Headers - 它沒有帶
X-、Forwarded- For Forwarded或X-。Real- IP
其他一切都會在檢查登入之前就被拒絕:
- 一般請求會得到
403,內容為{"error":"Session worker endpoints are reachable from this machine only"}; - WebSocket 升級會得到
403 Forbidden,連線也會被關閉。
每次拒絕都會記錄為 [tunnel-,並指出是什麼讓這個請求露了餡。本機的 worker 直接連到 127.,不帶這些標頭,因此不受影響。分享連結透過 tunnel 仍能正常運作:保護只針對 worker 路徑,而不是針對匿名的請求。
Cloudflare 會加上 cf-,Caddy 也會自行加上 X-,所以保護不需任何設定就能搭配它們運作;使用 nginx 時,請用 proxy_ 傳送 X-。完全不加任何東西的轉送器,例如 SSH 反向 tunnel 或單純的 TCP proxy,看起來就像本機的處理程序;這時該路徑只受存取 token 保護。使用這類轉送器時,請讓它加上一個帶有固定非空值的標頭,例如 X-,並列出它的名稱:"tunnel。或者在中繼前面就拒絕這個路徑。
只有中繼所在機器能使用的端點 #
0.9.11 新增 有兩樣東西只回應來自中繼本機、且沒有經過任何 proxy 轉送的請求,規則與路徑保護相同:
- 上面提到的 session worker socket;
- 執行影像操作(
POST /),由中繼自己的 worker 呼叫。其他一切都會得到api/ image- operations/ …/ execute 403Image operation execution is reachable from this machine only(影像操作執行僅能從本機存取)。
其他所有東西都只需要存取 token,不論請求來自何處。這包括 🌄 Restart web relay(重新啟動網頁中繼)、用橫幅上的 Cancel(取消)按鈕撤回排入佇列的重新啟動,以及 🚮 Empty queue(清空佇列):任何已登入的瀏覽器都能使用它們,透過區域網路、任何 proxy 或 tunnel 也一樣。
到 0.9.10 為止,這三項原本只應供中繼所在的機器使用,但檢查會讓經由 tunnel 或同一台機器上 proxy 的一切通過,卻拒絕直接的區域網路連線,同時頁面還回報成功。如果遠端使用者不應該能重新啟動中繼或清空它的佇列,請讓 proxy 拒絕 POST /、POST / 和 POST /。在中繼所在的機器上,oar restart 一律有效。
Cookie 與 HTTPS #
登入 cookie 是 Http 和 Same,有效期 30 天,路徑為 /(或 remote 前綴)。當中繼認為請求是安全的,cookie 就會帶有 Secure。
- proxy 在同一台機器上終止 TLS。 proxy 會送出
X-,預設的Forwarded- Proto: https trust會相信它,cookie 就會是Proxy Secure。 - proxy 在另一台機器上終止 TLS。 如上所述設定
trust。在你設定之前,cookie 沒有Proxy Secure旗標,但仍然可以運作。 - 混合的設定。 如果你也在區域網路上直接以純
http開啟中繼,那個請求不是安全的,所以它的 cookie 沒有Secure旗標;經由 proxy 的 HTTPS 請求則會得到這個旗標。瀏覽器會依位址分別保存 cookie。
檢查設定 #
- 在瀏覽器中開啟經由 proxy 的位址。登入畫面或對話清單會出現。
- 查看狀態點:它會變成綠色,而且當你開始一個回合時,頁面會顯示即時更新。如果一直處於重新連線的狀態,表示 WebSocket 無法通過。
- 在前綴下,用結尾的斜線開啟位址。頁面、它的樣式和即時連線都會載入,而且 cookie 的路徑就是該前綴(在瀏覽器的開發人員工具中查看)。
- 建立一個分享連結(分享)。它的位址使用公開的主機名稱和
https,如果你設定了前綴,也會包含前綴。 - 從外部測試保護:請求
https://。你應該會看到relay. example. com/ api/ session- worker/ ws 403,中繼的記錄中也會有一行[tunnel-。如果你得到其他回應,表示你的 proxy 既沒有加上標記標頭,也沒有加上guard] X-。Forwarded- For - 在另一台機器上的 proxy 後面:從兩台裝置登入,並在 Settings → Sharing(設定 → 分享)中檢查分享連結的存取紀錄顯示的是觀看者的位址,而不是 proxy 的位址。如果顯示的是 proxy 的位址,表示
trust與 proxy 不相符。Proxy
無法正常運作時 #
| 你看到的情況 | 可能的原因 |
|---|---|
| 頁面載入了,但一直在重新連線,或從不更新 | proxy 沒有在 / 上傳遞 WebSocket 升級,或會關閉閒置的連線。 |
頁面載入了,但沒有任何東西即時更新,而且記錄中出現 Refused a handshake from origin |
proxy 改寫了 Host 標頭。請在 allowed 中列出公開的來源,或讓 proxy 原樣傳遞 Host。 |
| 登入時所有人同時看到 Too many failed sign-in attempts(登入失敗次數過多) | 中繼不信任另一台機器上的 proxy,所以所有訪客共用它的位址和它的鎖定。請設定 trust。 |
| 在前綴下,頁面空白或缺少檔案 | 沒有設定 remote,或 proxy 移除了前綴。proxy 必須傳遞前綴。 |
| 在前綴下,一再被要求重新登入 | cookie 的路徑是 remote 前綴,所以瀏覽器只會在該前綴下送出它。請在該前綴下開啟中繼,並加上結尾的斜線。 |
| 分享連結或檔案連結的位址錯誤,或沒有前綴 | 沒有設定 remote,或 proxy 沒有送出 Host 或 X- / X-,或中繼不信任該 proxy。 |
在另一台機器上的 proxy 後面,分享連結以 http:// 開頭,或 cookie 沒有 Secure 旗標 |
trust 仍然是 "loopback"。 |
透過你的 proxy 時,worker 路徑的回應不是 403 |
proxy 既沒有加上標記標頭,也沒有加上 X-。見路徑保護。 |
| 狀態點從不變成琥珀色 | 這是預期的行為:只有中繼自己受管理的 Cloudflare tunnel 才會讓它變色。 |
| 預覽拒絕在新名稱上啟動 | 把中繼的主機名稱加到 public。 |
關於首次登入和存取 token,見首次執行。