HTTP 升級
Envoy 升級支援主要用於 WebSocket 和 CONNECT
支援,但也可用於任意升級。
升級會將 HTTP 標頭和升級酬載都傳遞到 HTTP 過濾器鏈。
可以設定 upgrade_configs,無論是否使用自訂過濾器鏈。
如果僅指定了 upgrade_type,則升級標頭、任何請求和回應主體,以及 HTTP 資料酬載都將通過預設的 HTTP 過濾器鏈。
為了避免為升級酬載使用僅限 HTTP 的過濾器,可以為給定的升級類型設定自訂 filters,包括僅使用路由器過濾器將 HTTP 資料發送到上游。
提示
緩衝通常與升級不相容,因此如果在預設 HTTP 過濾器鏈中設定了 Buffer 過濾器,則應使用 升級過濾器 並將緩衝過濾器排除在該清單之外,來排除升級。
可以在 每個路由 的基礎上啟用或停用升級。
任何每個路由的啟用/停用都會自動覆寫如下所述的 HttpConnectionManager 設定,但自訂過濾器鏈只能在每個 HttpConnectionManager 的基礎上設定。
HCM 升級已啟用 |
路由升級已啟用 |
升級已啟用 |
---|---|---|
真 (預設) |
真 (預設) |
真 |
真 (預設) |
假 |
假 |
假 |
真 (預設) |
真 |
假 |
假 |
假 |
提示
升級的統計資訊全部捆綁在一起,因此 WebSocket 和其他升級的 統計資訊 會通過諸如 downstream_cx_upgrades_total
和 downstream_cx_upgrades_active
等統計資訊進行追蹤。
HTTP/2 或 HTTP/3 跳躍上的 WebSocket
雖然預設情況下會關閉 HTTP/2 和 HTTP/3 對 WebSockets 的支援,但 Envoy 確實支援通過 HTTP/2 及以上協定通道傳輸 WebSockets,以便在整個過程中首選統一的 HTTP/2+ 網格部署;例如,這會啟用以下形式的部署
[用戶端
] —-> HTTP/1.1
>—- [前端 Envoy
] —-> HTTP/2
>—- [Sidecar Envoy
—-> HTTP/1
>—- 應用程式
]
在這種情況下,如果用戶端正在使用 WebSocket,我們希望 WebSocket 功能完整地到達上游伺服器,這意味著它需要遍歷 HTTP/2+ 跳躍。
對於 HTTP/2,這是通過 Extended CONNECT (RFC 8441) 支援來實現的,通過在第二層 Envoy 上將 allow_connect 設定為 true
來啟用。
對於 HTTP/3,通過 alpha 選項 allow_extended_connect 設定了平行支援,因為目前還沒有正式的 RFC。
WebSocket 請求將轉換為 HTTP/2+ CONNECT
資料流,其中 :protocol
標頭指示原始升級,遍歷 HTTP/2+ 跳躍,並降級回 HTTP/1 WebSocket 升級。
相同的升級-CONNECT
-升級轉換將在任何 HTTP/2+ 跳躍上執行,但有文檔中記錄的缺陷,即 HTTP/1.1 方法始終假定為 GET
。
非 WebSocket 升級允許使用任何有效的 HTTP 方法(即 POST
),並且當前的升級/降級機制將捨棄原始方法,並將升級請求轉換為最終 Envoy-上游跳躍上的 GET
方法。
注意
HTTP/2+ 升級路徑具有非常嚴格的 HTTP/1.1 相容性,因此不會代理帶有主體的 WebSocket 升級請求或回應。
CONNECT
支援
預設情況下,Envoy CONNECT
支援已關閉(Envoy 會回應 CONNECT
請求而內部產生 403 錯誤)。
可以通過上述升級選項啟用 CONNECT
支援,將升級值設定為特殊關鍵字 CONNECT
。
雖然對於 HTTP/2 及以上版本,CONNECT
請求可能具有路徑,但通常對於 HTTP/1.1 CONNECT
請求沒有路徑,只能使用 connect_matcher 來匹配。
注意
在為 CONNECT
請求執行非萬用字元網域匹配時,將匹配 CONNECT
目標,而不是 Host
/Authority
標頭。您可能需要包含埠(例如,hostname:port
)才能成功匹配。
Envoy 可以通過兩種方式處理 CONNECT
,一種是像對待任何其他請求一樣代理 CONNECT
標頭,並讓上游終止 CONNECT
請求,另一種是終止 CONNECT
請求,並將酬載作為原始 TCP 資料轉發。
設定 CONNECT
升級設定時,預設行為是代理 CONNECT
請求,使用升級路徑像對待任何其他請求一樣對待它。
如果需要終止,可以通過設定 connect_config 來完成。
如果此訊息存在於 CONNECT
請求中,則路由器過濾器將剝離請求標頭,並將 HTTP 酬載轉發到上游。收到來自上游的初始 TCP 資料後,路由器將合成 200 回應標頭,然後將 TCP 資料作為 HTTP 回應主體轉發。
警告
如果未正確設定,此 CONNECT
支援模式可能會產生重大安全漏洞,因為如果上游位於主體酬載中,則會轉發未經清理的標頭。
請謹慎使用!
提示
有關代理 connect 的範例,請參閱 configs/proxy_connect.yaml
有關終止 connect 的範例,請參閱 configs/terminate_http1_connect.yaml 和 configs/terminate_http2_connect.yaml
注意
對於 CONNECT
-over-TLS,目前無法將 Envoy 設定為在一個跳躍中以明文方式執行 CONNECT
請求並加密先前未加密的酬載。
要以純文字傳送 CONNECT
並加密酬載,必須先通過「上游」TLS 回送連線轉發 HTTP 酬載以對其進行加密,然後讓 TCP 監聽器接收加密的酬載並將 CONNECT
上傳送上游。
通過 HTTP 通道傳輸 TCP
Envoy 還支援通過 HTTP CONNECT
或 HTTP POST
請求通道傳輸原始 TCP。請在下面找到一些使用案例。
HTTP/2+ CONNECT
可用於通過預先預熱的安全連線代理多路復用的 TCP,並攤銷任何 TLS 交握的成本。
代理 SMTP 的設定範例如下所示
[SMTP 上游
] —> 原始 SMTP
>— [L2 Envoy
] —> 透過 HTTP/2 CONNECT 通道傳輸的 SMTP
>— [L1 Envoy
] —> 原始 SMTP
>— [用戶端
]
HTTP/1.1 CONNECT 可用於讓 TCP 用戶端連線到自己的目的地,並通過 HTTP 代理伺服器(例如:不支援 HTTP/2 的公司代理)傳輸。
[HTTP 伺服器
] —> 原始 HTTP
>— [L2 Envoy
] —> 透過 HTTP/1.1 CONNECT 通道傳輸的 HTTP
>— [L1 Envoy
] —> 原始 HTTP
>— [HTTP 用戶端
]
注意
當使用 HTTP/1 CONNECT
時,您最終會為每個 TCP 用戶端連線在 L1 和 L2 Envoy 之間建立一個 TCP 連線,當您可以選擇時,最好使用 HTTP/2 或更高版本。
當中間代理不支援 CONNECT
時,HTTP POST
也可用於代理多工 TCP。
一個代理 HTTP 的範例設定如下所示:
[TCP 伺服器
] —> 原始 TCP
>— [L2 Envoy
] —> 透過 HTTP/2 或 HTTP/1.1 POST 通道傳輸的 TCP
>— [中間代理
] —> HTTP/2 或 HTTP/1.1 POST
>— [L1 Envoy
] —> 原始 TCP
>— [TCP 用戶端
]
提示
此類設定的範例可以在 Envoy 範例組態目錄中找到。
對於 HTTP/1.1 CONNECT
,請嘗試以下任一方法:
$ envoy -c configs/encapsulate_in_http1_connect.yaml --base-id 1
$ envoy -c configs/terminate_http1_connect.yaml --base-id 1
對於 HTTP/2 CONNECT
,請嘗試以下任一方法:
$ envoy -c configs/encapsulate_in_http2_connect.yaml --base-id 1
$ envoy -c configs/terminate_http2_connect.yaml --base-id 1
對於 HTTP/2 POST
,請嘗試以下任一方法:
$ envoy -c configs/encapsulate_in_http2_post.yaml --base-id 1
$ envoy -c configs/terminate_http2_post.yaml --base-id 1
在所有情況下,您都將執行第一個 Envoy,監聽 10000 埠上的 TCP 流量,並將其封裝在 HTTP CONNECT
或 HTTP POST
請求中,以及第二個監聽 10001 埠的 Envoy,該 Envoy 會剝離 CONNECT
標頭(對於 POST
請求則不需要),並將原始 TCP 轉發到上游,在此案例中為 google.com。
Envoy 會等待 HTTP 通道建立(即收到 CONNECT
請求的成功回應),然後才開始將下游 TCP 資料串流至上游。
如果您想要解封裝 CONNECT
請求,並對解封裝的酬載執行 HTTP 處理,最簡單的方法是使用內部監聽器。
CONNECT-UDP
支援
注意
CONNECT-UDP
處於 alpha 狀態,可能不夠穩定,不適合在生產環境中使用。我們建議謹慎使用此功能。
CONNECT-UDP
(RFC 9298) 允許 HTTP 用戶端通過 HTTP 代理伺服器建立 UDP 通道。與僅限於通道傳輸 TCP 的 CONNECT
不同,CONNECT-UDP
可用於代理基於 UDP 的協定,例如 HTTP/3。
Envoy 中預設停用 CONNECT-UDP
支援。與 CONNECT
類似,可以透過將值設定為特殊關鍵字 CONNECT-UDP
,透過upgrade_configs啟用它。與 CONNECT
類似,CONNECT-UDP
請求預設會轉發到上游。connect_config必須設定為終止請求,並將酬載作為 UDP 資料包轉發到目標。
範例設定
以下範例設定會讓 Envoy 將 CONNECT-UDP
請求轉發到上游。請注意,upgrade_configs 設定為 CONNECT-UDP
。
27 filters:
28 - name: envoy.filters.network.http_connection_manager
29 typed_config:
30 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
31 codec_type: HTTP3
32 stat_prefix: ingress_http
33 route_config:
34 name: local_route
35 virtual_hosts:
36 - name: local_service
37 domains:
38 - "*"
39 routes:
40 - match:
41 connect_matcher:
42 {}
43 route:
44 cluster: cluster_0
45 http_filters:
46 - name: envoy.filters.http.router
47 typed_config:
48 "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
49 http3_protocol_options:
50 allow_extended_connect: true
51 upgrade_configs:
52 - upgrade_type: CONNECT-UDP
53 clusters:
54 - name: cluster_0
以下範例設定會讓 Envoy 終止 CONNECT-UDP
請求,並將 UDP 酬載傳送到目標。如此範例中所示,connect_config 必須設定為終止 CONNECT-UDP
請求。
26 filters:
27 - name: envoy.filters.network.http_connection_manager
28 typed_config:
29 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
30 codec_type: HTTP3
31 stat_prefix: ingress_http
32 route_config:
33 name: local_route
34 virtual_hosts:
35 - name: local_service
36 domains:
37 - "*"
38 routes:
39 - match:
40 connect_matcher:
41 {}
42 route:
43 cluster: service_google
44 upgrade_configs:
45 - upgrade_type: CONNECT-UDP
46 connect_config:
47 {}
48 http_filters:
49 - name: envoy.filters.http.router
50 typed_config:
51 "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
52 http3_protocol_options:
53 allow_extended_connect: true
54 upgrade_configs:
55 - upgrade_type: CONNECT-UDP
56 clusters:
57 - name: service_google
透過 HTTP 建立 UDP 通道
注意
原始 UDP 通道傳輸處於 alpha 狀態,可能不夠穩定,不適合在生產環境中使用。我們建議謹慎使用此功能。
除了上述章節中說明的 CONNECT-UDP
終止之外,Envoy 也支援透過 HTTP CONNECT
或 HTTP POST
請求建立原始 UDP 通道,方法是利用 UDP Proxy 監聽器篩選器。預設情況下,會停用 UDP 通道傳輸,並且可以透過設定 tunneling_config 的組態來啟用。
注意
目前,Envoy 僅支援透過 HTTP/2 串流建立 UDP 通道。
預設情況下,tunneling_config
會根據HTTP RFC 中的 Proxying UDP,升級連線以針對每個 UDP 會話(UDP 會話由資料包 5 元組識別)建立 HTTP/2 串流。由於此升級協定需要一個封裝機制來保留原始資料包的界限,因此需要套用HTTP Capsule 會話篩選器。HTTP/2 串流將在向上游連線上多工。
與 TCP 通道傳輸不同,後者可透過交替停用從連線插槽讀取來套用下游流量控制,對於 UDP 資料包,不支援此機制。因此,當建立 UDP 通道且從下游接收到新的資料包時,如果上游已就緒,則會將其串流傳輸到上游,或由 UDP Proxy 停止。如果上游未就緒(例如:在等待 HTTP 回應標頭時),則可以捨棄資料包或將其緩衝,直到上游就緒。在這種情況下,預設會捨棄下游資料包,除非 buffer_options 由 tunneling_config
設定。預設緩衝區限制很小,旨在嘗試防止大量不需要的緩衝記憶體,但應根據所需的使用案例進行調整。當上游準備就緒時,UDP Proxy 將首先清除所有先前緩衝的資料包。
注意
如果設定了 POST
,則上游串流不符合 connect-udp RFC,而是 POST 請求。標頭中使用的路徑將從 post_path 欄位設定,並且標頭將不包含目標主機和目標埠,這是 connect-udp 協定所要求的。應謹慎使用此選項。
範例設定
以下範例設定讓 Envoy 將原始 UDP 資料包透過升級後的 CONNECT-UDP
請求,隧道傳輸至上游。
32 session_filters:
33 - name: envoy.filters.udp.session.http_capsule
34 typed_config:
35 '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.session.http_capsule.v3.FilterConfig
36 tunneling_config:
37 # note: proxy_host supports string substitution, for example setting "%FILTER_STATE(proxy.host.key:PLAIN)%"
38 # will take the target host value from the session's filter state.
39 proxy_host: proxy.host.com
40 # note: target_host supports string substitution, for example setting "%FILTER_STATE(target.host.key:PLAIN)%"
41 # will take the target host value from the session's filter state.
42 target_host: target.host.com
43 # note: The target port value can be overridden per-session by setting the required port value for
44 # the filter state key ``udp.connect.target_port``.
45 default_target_port: 443
46 retry_options:
47 max_connect_attempts: 2
48 buffer_options:
49 max_buffered_datagrams: 1024
50 max_buffered_bytes: 16384
51 headers_to_add:
52 - header:
53 key: original_dst_port