淺析 Istio:如何從網格中訪問外部服務
作者:李運田,中國移動雲能力中心軟件開發工程師,專注於雲原生、Istio、微服務、Spring Cloud 等領域。
本文是中國移動雲能力中心的「淺析 Istio」系列文章之一,之前發佈的文章見:
在生產環境使用 Istio 的時候,可能最需要考慮的問題一個是安全問題一個是性能問題,在這裏和大家一起探討下一個安全問題,如何在 Istio 網格中訪問外部服務。Istio 提供了兩種模式來配置對外部請求的訪問策略,並通過配置項 outboundTrafficPolicy.mode 來指定。默認的模式是 ALLOW_ANY,也就是允許在網格內請求所有外部的未知服務;另外一個模式是 REGISTRY_ONLY,表示只允許請求註冊到服務網格註冊表中的服務。默認的 ALLOW_ANY 模式雖然使用方便,但是存在一定的安全隱患,建議的做法是切換到 REGISTRY_ONLY 模式。那麼在 REGISTRY_ONLY 模式下如何訪問外部服務?實現機制是什麼呢?在這裏針對這兩個問題和大家一起探討下。
方案調研
目前我們安裝部署 Istio 使用的是 helm,可以在安裝中添加相應的配置 --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY
修改 outboundTrafficPolicy.mode 的值;如果 Istio 已經安裝好,通過 kubectl edit cm istio -n istio-system
可以動態修改此值。
apiVersion: v1
data:
mesh: |-
defaultConfig:
discoveryAddress: istiod.istio-system.svc:15012
proxyMetadata:
DNS_AGENT: ""
tracing:
zipkin:
address: zipkin.istio-system:9411
outboundTrafficPolicy:
mode: REGISTRY_ONLY
配置完 REGISTRY_ONLY 後,我們在 pod 中無法訪問外部服務
Istio 默認訪問外部服務
那麼在 REGISTRY_ONLY 模式下,如何才能訪問外部服務呢?首先我們通過下圖 [1] 看下 Istio 在什麼時機可以訪問外部服務,在這種時機下怎麼修改配置才能訪問到外部服務?
Istio 中的透明流量劫持
圖中描述了 product 服務訪問 review 服務,這裏我們假設 review 服務會繼續調用 36.152.44.96 這個外部服務。
Istio 流量流轉
當 reviews 應用需要訪問 36.152.44.96 這個外部服務時,會在 reviews 的應用容器中往上游發送請求信息,下面按照圖中的順序介紹如何訪問外部請求。
-
- reviews 服務訪問外部服務,這一步對 reviews 服務來說屬於出口流量,被 iptables 規則攔截轉發至出口流量 OUTPUT 鏈。
-
- OUTPUT 鏈轉發流量至 ISTIO_OUTPUT 鏈。
-
3. 在 ISTIO_OUTPUT 鏈中默認有九個規則,決定 reviews 服務訪問外部服務的流量發往何處,在這裏我們可以自定義一個規則
-A ISTIO_OUTPUT -d 36.152.44.0/24 -j RETURN
,使訪問 36.152.44.96 這個外部服務的流量跳出當前鏈,調用POSTROUTING 鏈
,直接訪問外部服務;如果使用默認規則,流量被轉發至 ISTIO_REDIRECT 鏈。 -
- ISTIO_REDIRECT 鏈直接重定向至 Envoy 監聽的 15001 出口流量端口。
-
- 外部服務的流量策略並不在 Istio 服務網格中,如果不進行相關配置把外部服務註冊到服務網格內,經過 Envoy 一系列出口流量治理動作後將會返回錯誤信息。因此這裏我們需要配置
ServiceEntry
,把外部服務納入服務網格中,然後通過 Envoy 的流量治理後可以繼續發送外部請求,訪問外部請求時又會被 iptables 攔截轉發至出口流量 OUTPUT 鏈。
- 外部服務的流量策略並不在 Istio 服務網格中,如果不進行相關配置把外部服務註冊到服務網格內,經過 Envoy 一系列出口流量治理動作後將會返回錯誤信息。因此這裏我們需要配置
-
- OUTPUT 鏈轉發流量至 ISTIO_OUTPUT 鏈。
-
7. 在這裏會匹配到 ISTIO_OUTPUT 鏈的第四條規則
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
,流量直接 RETURN 到下一個鏈POSTROUTING 鏈
,經 POSTROUTING 鏈流出訪問外部服務。
我們看到通過第 11 步、第 13 步兩種方式修改配置都可以使 Istio 內部的服務訪問外部服務,具體應該怎麼做呢?
通過 iptables 規則訪問
如果想在第 11 步直接跳出 ISTIO_OUT 鏈調用 POSTROUTING 鏈,需要添加一個自定義的 iptables 規則 -A ISTIO_OUTPUT -d 36.152.44.0/24 -j RETURN
,我們知道 iptables 規則是在 istio-init 裏定義的,istio-init 啓動時執行的是 istio-iptables 命令,我們在 manifests 的 injection-template.yaml
文件裏看到 istio-iptables 默認的部分配置。
- "-i"
- "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}"
- "-x"
- "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}"
- "-b"
- "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}"
- "-d"
{{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}
- "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}"
{{- else }}
- "15090,15021,15020"
-
•
-i
默認爲*
,所有出站流量都會被重定向到 Envoy 代理 -
•
-x
默認爲空,當 - i 參數爲*
時,用來指明哪些地址不用重定向 Envoy 代理,直接進行轉發 -
•
-b
默認爲*
,逗號分隔的端口,指定端口的流量將重定向到 Envoy -
•
-d
默認爲 15090,15021,15020,逗號分隔的端口,指定哪些端口的流量不用重定向到 Envoy
根據上面配置我們可以在 Istio 服務中通過 global.proxy.*
進行全局配置,還可以在服務的 deployment 裏通過 traffic.sidecar.istio.io/*
配置相應的參數。我們現在的目的是需要把訪問 36.152.44.96 的請求不重定向到 Envoy 代理,直接進行轉發,因此我們在 deployment 裏添加 traffic.sidecar.istio.io/excludeOutboundIPRanges: 36.152.44.0/24
,執行完後我們 describe pod 的詳細信息,獲取到 pod 中的部分配置如下,可以看到設置生效,36.152.44.0/24 不會重定向到 Envoy 中
-i
*
-x
36.152.44.0/24
-b
*
-d
15090,15021,15020
繼續查看 pod 中的 iptables 規則,可以看到在原來的 ISTIO_OUTPUT 九條 iptables 規則中插入了一個自定義的規則 A ISTIO_OUTPUT -d 36.152.44.0/24 -j RETURN
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -d 36.152.44.0/24 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
通過 ServiceEntry 訪問
Istio 在 15001 端口使用 VirtualOutboundListener 處理出向請求,Iptable 將 Envoy 所在 Pod 的對外請求攔截後發向本地的 15001 端口,該監聽器接收後並不進行業務處理,而是根據請求的目的端口分發給其他獨立的監聽器處理。我們訪問的外部服務爲 36.152.44.96:80,因此 Envoy 根據目的端口匹配到 0.0.0.0_80
這個 Outbound listener,並轉交給該 listener。
Envoy_listener 配置
當 0.0.0.0_80 接收到出向請求後,並不會直接發送到目的 cluster,其實通過查看 0.0.0.0_80 的 listener 的信息,我們也找不到目的 cluster 或 endpoint,在這個 listener 中配置了一個路由規則 80,在該路由規則中會根據不同的請求目的地進行路由匹配處理。
Envoy_listener 配置
通過 name 爲 80 的路由規則我們沒找到符合 36.152.44.96 的請求,因此會被 listener 裏的 default_filter_match
處理,進入到 BlackHoleCluster 集羣裏,請求被丟棄。這裏我們簡單介紹下 Envoy 中的兩個特殊 cluster:BlackHoleCluster
和 PassthroughCluster
, BlackHoleCluster
中沒有配置任何處理請求的 host。請求進入該 cluster 後將被丟棄掉,而不是發向一個 host,如果 outboundTrafficPolicy.mode=REGISTRY_ONLY
,默認情況下請求的外部服務都會直接進入 BlackHoleCluster 中丟掉。PassthroughCluster
的 type 被設置爲 ORIGINAL_DST
,表明任何發向該 cluster 的請求都會被直接發送到其請求中的原始目的地,如果 outboundTrafficPolicy.mode=ALLOW_ANY
,Envoy 不會對請求進行重新路由直接發送到原始目的地。
在 outboundTrafficPolicy.mode=REGISTRY_ONLY
模式下,爲了流量不進入 BlackHoleCluster
中,我們需要添加 ServiceEntry,把外部請求註冊到服務網格中,以便 Envoy 可以找到外部服務的 route 進行流量處理。
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: baidu-ip
spec:
hosts:
- www.baidu.com
addresses:
- 36.152.44.96
location: MESH_EXTERNAL
ports:
- number: 80
name: baidu-http
protocol: HTTP
resolution: NONE
配置完 ServiceEntry 後我們再次查詢 review 的 route 信息,可以看到 36.152.44.96 已經被加入 route 中了,直接在 pod 中訪問外部服務 36.152.44.96 可以得到正確的請求。
添加完 Envoy
總結
通過對 Istio 訪問外部服務的方案進行調研,我們學習了 Istio 是如何通過 iptables 處理進入網格中的流量,也大體瞭解了 Envoy 處理 Outbound 流量的流程。以上的調研基於 Istio 1.8 版本,內容可能存在錯誤或不準確的地方,歡迎大家交流指正。
文章推薦
-
• 淺析 Istio—— 服務治理之概念和原理 [2]
-
• 一種靈活注入 Istio Sidecar 的方案探索 [3]
-
• 淺析 Istio—— 流量治理之路由管理 [4]
-
• NFV 走向雲原生時代:Network Service Mesh 項目介紹 [5]
-
• 淺析 Istio—— 可觀測性 [6]
引用鏈接
[1]
下圖: https://jimmysong.io/blog/sidecar-injection-iptables-and-traffic-routing/
[2]
淺析 Istio—— 服務治理之概念和原理: https://cloudnative.to/blog/istio-traffic-management-series-service-management-concept-theory/
[3]
一種靈活注入 Istio Sidecar 的方案探索: https://cloudnative.to/blog/istio-sidecar-injection-method/
[4]
淺析 Istio—— 流量治理之路由管理: https://cloudnative.to/blog/istio-traffic-management-series-route-management/
[5]
NFV 走向雲原生時代:Network Service Mesh 項目介紹: https://cloudnative.to/blog/202002-network-service-mesh/
[6]
淺析 Istio—— 可觀測性: https://cloudnative.to/blog/istio-observability/
雲原生社區動態 雲原生社區是國內最大獨立第三方雲原生終端用戶社區。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Kl66kuM2mOh6mTPgh5V2VQ