淺析 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 的應用容器中往上游發送請求信息,下面按照圖中的順序介紹如何訪問外部請求。

    1. reviews 服務訪問外部服務,這一步對 reviews 服務來說屬於出口流量,被 iptables 規則攔截轉發至出口流量 OUTPUT 鏈。
    1. OUTPUT 鏈轉發流量至 ISTIO_OUTPUT 鏈。
  1. 3. 在 ISTIO_OUTPUT 鏈中默認有九個規則,決定 reviews 服務訪問外部服務的流量發往何處,在這裏我們可以自定義一個規則 -A ISTIO_OUTPUT -d 36.152.44.0/24 -j RETURN,使訪問 36.152.44.96 這個外部服務的流量跳出當前鏈,調用 POSTROUTING 鏈,直接訪問外部服務;如果使用默認規則,流量被轉發至 ISTIO_REDIRECT 鏈。

    1. ISTIO_REDIRECT 鏈直接重定向至 Envoy 監聽的 15001 出口流量端口。
    1. 外部服務的流量策略並不在 Istio 服務網格中,如果不進行相關配置把外部服務註冊到服務網格內,經過 Envoy 一系列出口流量治理動作後將會返回錯誤信息。因此這裏我們需要配置 ServiceEntry,把外部服務納入服務網格中,然後通過 Envoy 的流量治理後可以繼續發送外部請求,訪問外部請求時又會被 iptables 攔截轉發至出口流量 OUTPUT 鏈。
    1. OUTPUT 鏈轉發流量至 ISTIO_OUTPUT 鏈。
  2. 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"

根據上面配置我們可以在 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 版本,內容可能存在錯誤或不準確的地方,歡迎大家交流指正。

文章推薦

引用鏈接

[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