日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

簽名我們了解了位于服務網格內部的應用應如何訪問網格外部的 HTTP 和 HTTPS 服務,我們學習了如何通過 ServiceEntry 對象配置 Istio 以受控的方式訪問外部服務,這種方式實際上是通過 Sidecar 直接調用的外部服務,但是有時候我們可能需要通過專用的 Egress Gateway 服務來調用外部服務,這種方式可以更好的控制對外部服務的訪問。

Istio 使用 Ingress 和 Egress Gateway 配置運行在服務網格邊緣的負載均衡,Ingress Gateway 允許定義網格所有入站流量的入口。 Egress Gateway 是一個與 Ingress Gateway 對稱的概念,它定義了網格的出口。Egress Gateway 允許我們將 Istio 的功能(例如,監視和路由規則)應用于網格的出站流量。

使用場景

比如有一個對安全要求非常嚴格的團隊,要求服務網格所有的出站流量必須經過一組專用節點。專用節點運行在專門的機器上,與集群中運行應用程序的其他節點隔離,這些專用節點用于實施 Egress 流量的策略,并且受到比其余節點更嚴密地監控。

另一個使用場景是集群中的應用節點沒有公有 IP,所以在該節點上運行的網格服務都無法訪問互聯網,那么我們就可以通過定義 Egress gateway,將公有 IP 分配給 Egress Gateway 節點,用它引導所有的出站流量,可以使應用節點以受控的方式訪問外部服務。

接下來我們就來學習下在 Istio 中如何配置使用 Egress Gateway。

準備工作

如果你使用的 demo 這個配置文件安裝 Istio,那么 Egress Gateway 已經默認安裝了,可以通過下面的命令來查看:

$ kubectl get pod -l istio=egressgateway -n istio-system
NAME                                   READY   STATUS    RESTARTS        AGE
istio-egressgateway-556f6f58f4-hkzdd   1/1     Running   0               14d

如果沒有 Pod 返回,可以通過下面的步驟來部署 Istio Egress Gateway。如果你使用 IstioOperator 安裝 Istio,請在配置中添加以下字段:

spec:
  components:
    egressGateways:
      - name: istio-egressgateway
        enabled: true

否則使用如下的 istioctl install 命令來安裝:

$ istioctl install <flags-you-used-to-install-Istio> 
                   --set components.egressGateways[0].name=istio-egressgateway 
                   --set components.egressGateways[0].enabled=true

同樣我們還是使用 sleep 示例做為發送請求的測試源,如果啟用了自動 Sidecar 注入,運行以下命令部署示例應用程序:

kubectl Apply -f samples/sleep/sleep.yaml

否則,在使用以下命令部署 sleep 應用程序之前,手動注入 Sidecar:

kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml)

為了發送請求,您需要創建 SOURCE_POD 環境變量來存儲源 Pod 的名稱:

export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})

用 Egress gateway 發起 HTTP 請求

首先創建一個 ServiceEntry 對象來允許流量直接訪問外部的 edition.cnn.com 服務。

apiVersion:.NETworking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: cnn
spec:
  hosts:
    - edition.cnn.com
  ports:
    - number: 80
      name: http-port
      protocol: HTTP
    - number: 443
      name: https
      protocol: HTTPS
  resolution: DNS

發送 HTTPS 請求到 https://edition.cnn.com/politics 驗證 ServiceEntry 是否已正確應用。

$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
# 輸出如下內
HTTP/1.1 301 Moved Permanently
# ......
location: https://edition.cnn.com/politics
# ......

HTTP/2 200
Content-Type: text/html; charset=utf-8
# ......

然后為 edition.cnn.com 的 80 端口創建一個 egress Gateway,并為指向 Egress Gateway 的流量創建一個 DestinationRule 規則,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway # 匹配 Egress Gateway Pod 的標簽
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - edition.cnn.com # 也支持通配符 * 的形式
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local # 目標規則為 Egress Gateway
  subsets:
    - name: cnn # 定義一個子集 cnn,沒有指定 labels,則 subset 會包含所有符合 host 字段指定的服務的 Pod

在上面的對象中我們首先定義了一個 Gateway 對象,不過這里我們定義的是一個 Egress Gateway,通過 istio: egressgateway 匹配 Egress Gateway Pod 的標簽,并在 servers 中定義了 edition.cnn.com 服務的 80 端口。然后定義了一個 DestinationRule 對象,指定了目標規則為 istio-egressgateway.istio-system.svc.cluster.local,并定義了一個子集 cnn。

這里的子集名稱是 cnn,但沒有指定 labels。這意味著,這個 subset 會涵蓋所有屬于 istio-egressgateway.istio-system.svc.cluster.local 服務的 Pod。這種情況下,subset 的作用主要是為了在其他 Istio 配置中提供一個方便的引用名稱,而不是為了區分不同的 Pod 子集。

如何再定義一個 VirtualService 對象將流量從 Sidecar 引導至 Egress Gateway,再從 Egress Gateway 引導至外部服務,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
    - edition.cnn.com
  gateways:
    - istio-egressgateway # Egress Gateway
    - mesh # 網格內部的流量
  http:
    - match:
        - gateways:
            - mesh # 這條規則適用于從服務網格內發出的流量
          port: 80
      route:
        - destination:
            host: istio-egressgateway.istio-system.svc.cluster.local # 流量將被路由到 egress gateway
            subset: cnn
            port:
              number: 80
          weight: 100
    - match:
        - gateways:
            - istio-egressgateway # 這條規則適用于通過 istio-egressgateway 的流量
          port: 80
      route:
        - destination:
            host: edition.cnn.com # 流量將被路由到外部服務
            port:
              number: 80
          weight: 100

在上面的 VirtualService 對象中通過 hosts 指定 edition.cnn.com,表示該虛擬服務用于該服務的請求,gateways 字段中定義了 istio-egressgateway 和 mesh 兩個值,istio-egressgateway 是上面我們定義的 Egress Gateway,mesh 表示該虛擬服務用于網格內部的流量,也就是說這個虛擬服務指定了如何處理來自服務網格內部以及通過 istio-egressgateway 的流量。

mesh 是一個特殊的關鍵字,在 Istio 中表示服務網格內的所有 Sidecar 代理。當使用 mesh 作為網關時,這意味著 VirtualService 中定義的路由規則適用于服務網格內的所有服務,即所有裝有 Istio sidecar 代理的服務。

http 字段中定義了兩個 match,第一個 match 用于匹配 mesh 網關,第二個 match 用于匹配 istio-egressgateway 網關,然后在 route 中定義了兩個 destination,第一個 destination 用于將流量引導至 Egress Gateway 的 cnn 子集,第二個 destination 用于將流量引導至外部服務。

總結來說,這個 VirtualService 的作用是控制服務網格內部到 edition.cnn.com 的流量。當流量起始于服務網格內時,它首先被路由到 istio-egressgateway,然后再路由到 edition.cnn.com,這種配置有助于統一和控制從服務網格內部到外部服務的流量,可以用于流量監控、安全控制或實施特定的流量策略。

應用上面的資源對象后,我們再次向 edition.cnn.com 的 /politics 端點發出 curl 請求:

$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
# ......
HTTP/1.1 301 Moved Permanently

location: https://edition.cnn.com/politics
# ......

HTTP/2 200
Content-Type: text/html; charset=utf-8
# ......

正常和前面的一次測試輸出結果是一致的,但是這次在請求是經過 istio-egressgateway Pod 發出的,我們可以查看日志來驗證:

kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tAIl

正常會看到一行類似于下面這樣的內容:

[2023-11-15T08:48:38.683Z] "GET /politics HTTP/2" 301 - via_upstream - "-" 0 0 204 203 "10.244.1.73" "curl/7.81.0-DEV" "6c2c4550-92d4-955c-b6cb-83bf2b0e06f4" "edition.cnn.com" "151.101.3.5:80" outbound|80||edition.cnn.com 10.244.2.184:46620 10.244.2.184:8080 10.244.1.73:49924 - -

因為我們這里只是將 80 端口的流量重定向到 Egress Gateway 了,所以重定向后 443 端口的 HTTPS 流量將直接進入 edition.cnn.com,所以沒有看到 443 端口的日志,但是我們可以通過 SOURCE_POD 的 Sidecar 代理的日志來查看到:
$ kubectl logs "$SOURCE_POD" -c istio-proxy | tail
# ......
[2023-11-15T08:55:55.513Z] "GET /politics HTTP/1.1" 301 - via_upstream - "-" 0 0 191 191 "-" "curl/7.81.0-DEV" "12ce15aa-1247-9b7e-8185-4224f96f5ea0" "edition.cnn.com" "10.244.2.184:8080" outbound|80|cnn|istio-egressgateway.istio-system.svc.cluster.local 10.244.1.73:49926 151.101.195.5:80 10.244.1.73:41576 - -
[2023-11-15T08:55:55.753Z] "- - -" 0 - - - "-" 839 2487786 1750 - "-" "-" "-" "-" "151.101.195.5:443" outbound|443||edition.cnn.com 10.244.1.73:45246 151.101.67.5:443 10.244.1.73:42998 edition.cnn.com -

用 Egress gateway 發起 HTTPS 請求

上面我們已經學習了如何通過 Egress Gateway 發起 HTTP 請求,接下來我們再來學習下如何通過 Egress Gateway 發起 HTTPS 請求。

原理都是一樣的,只是我們需要在相應的 ServiceEntry、Egress Gateway 和 VirtualService 中指定 TLS 協議的端口 443。

首先為 edition.cnn.com 定義 ServiceEntry 服務:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: cnn
spec:
  hosts:
    - edition.cnn.com
  ports:
    - number: 443
      name: tls
      protocol: TLS
  resolution: DNS

應用該資源對象后,發送 HTTPS 請求到 https://edition.cnn.com/politics,驗證該 ServiceEntry 是否已正確生效。

$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
...
HTTP/2 200
Content-Type: text/html; charset=utf-8
...

接下來同樣的方式為 edition.cnn.com 創建一個 Egress Gateway。除此之外還需要創建一個目標規則和一個虛擬服務,用來引導流量通過 Egress Gateway,并通過 Egress Gateway 與外部服務通信。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
    - port:
        number: 443
        name: tls
        protocol: TLS
      hosts:
        - edition.cnn.com
      tls:
        mode: PASSTHROUGH # 透傳
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
    - name: cnn
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
    - edition.cnn.com
  gateways:
    - mesh
    - istio-egressgateway
  tls:
    - match:
        - gateways:
            - mesh
          port: 443
          sniHosts:
            - edition.cnn.com
      route:
        - destination:
            host: istio-egressgateway.istio-system.svc.cluster.local
            subset: cnn
            port:
              number: 443
    - match:
        - gateways:
            - istio-egressgateway
          port: 443
          sniHosts:
            - edition.cnn.com
      route:
        - destination:
            host: edition.cnn.com
            port:
              number: 443
          weight: 100

上面對象中定義的 Gateway 對象和前面的一樣,只是將端口改為了 443,然后在 tls 中指定了 mode: PASSTHROUGH,表示該 Gateway 對象用于 TLS 協議的請求。然后在后面的 VirtualService 對象中就是配置 spec.tls 屬性,用于指定 TLS 協議的請求的路由規則,配置方法和前面 HTTP 方式類似,只是注意要將端口改為 443,并且在 match 中指定 sniHosts 為 edition.cnn.com,表示該虛擬服務用于處理 edition.cnn.com 的 TLS 請求。

應用上面的資源對象后,我們現在發送 HTTPS 請求到 https://edition.cnn.com/politics,輸出結果應該和之前一樣。

$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
...
HTTP/2 200
Content-Type: text/html; charset=utf-8
...

檢查 Egress Gateway 代理的日志,則打印日志的命令是:

kubectl logs -l istio=egressgateway -n istio-system

應該會看到類似于下面的內容:

[2023-11-15T08:59:55.513Z] "- - -" 0 - 627 1879689 44 - "-" "-" "-" "-" "151.101.129.67:443" outbound|443||edition.cnn.com 172.30.109.80:41122 172.30.109.80:443 172.30.109.112:59970 edition.cnn.com

到這里我們就實現了通過 Egress Gateway 發起 HTTPS 請求。最后記得清理上面創建的資源對象:

$ kubectl delete serviceentry cnn
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn

需要注意的是,Istio 無法強制讓所有出站流量都經過 Egress Gateway, Istio 只是通過 Sidecar 代理實現了這種流向。攻擊者只要繞過 Sidecar 代理, 就可以不經 Egress Gateway 直接與網格外的服務進行通信,從而避開了 Istio 的控制和監控。出于安全考慮,集群管理員和云供應商必須確保網格所有的出站流量都要經過 Egress Gateway。這需要通過 Istio 之外的機制來滿足這一要求。例如,集群管理員可以配置防火墻,拒絕 Egress Gateway 以外的所有流量。Kubernetes NetworkPolicy 也能禁止所有不是從 Egress Gateway 發起的出站流量,但是這個需要 CNI 插件的支持。 此外,集群管理員和云供應商還可以對網絡進行限制,讓運行應用的節點只能通過 gateway 來訪問外部網絡。要實現這一限制,可以只給 Gateway Pod 分配公網 IP,并且可以配置 NAT 設備, 丟棄來自 Egress Gateway Pod 之外的所有流量。

Egress TLS Origination

接下來我們將學習如何通過配置 Istio 去實現對發往外部服務的流量的 TLS Origination(TLS 發起)。 若此時原始的流量為 HTTP,則 Istio 會將其轉換為 HTTPS 連接。TLS Origination 的概念前面我們也已經介紹過了。

Istio Egress 出口網關使用

TLS Origination

假設有一個傳統應用正在使用 HTTP 和外部服務進行通信,如果有一天突然有一個新的需求,要求必須對所有外部的流量進行加密。此時,使用 Istio 便可通過修改配置實現此需求,而無需更改應用中的任何代碼。該應用可以發送未加密的 HTTP 請求,由 Istio 為請求進行加密。

從應用源頭發起未加密的 HTTP 請求,并讓 Istio 執行 TLS 升級的另一個好處是可以產生更好的遙測并為未加密的請求提供更多的路由控制。

下面我們就來學習下如何配置 Istio 實現 TLS Origination。

同樣我們這里使用 sleep 示例應用來作為測試源,如果啟用了自動注入 Sidecar,那么可以直接部署 sleep 應用:

kubectl apply -f samples/sleep/sleep.yaml

否則在部署 sleep 應用之前,必須手動注入 Sidecar:

kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml)

創建一個環境變量來保存用于將請求發送到外部服務 Pod 的名稱:

export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsnotallow={.items..metadata.name})

配置對外部服務的訪問

首先使用 ServiceEntry 對象來配置對外部服務 edition.cnn.com 的訪問。這里我們將使用單個 ServiceEntry 來啟用對服務的 HTTP 和 HTTPS 訪問。創建一個如下所示的 ServiceEntry 對象:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: edition-cnn-com
spec:
  hosts:
    - edition.cnn.com
  ports:
    - number: 80
      name: http-port
      protocol: HTTP
    - number: 443
      name: https-port
      protocol: HTTPS
  resolution: DNS

上面的 ServiceEntry 對象中我們指定了 edition.cnn.com 服務的主機名,然后在 ports 中指定了需要暴露的端口及其屬性,表示該 ServiceEntry 對象代表對 edition.cnn.com 的訪問,這里我們定義了 80 和 443 兩個端口,分別對應 http 和 https 服務,resolution: DNS 定義了如何解析指定的 hosts,這里我們使用 DNS 來解析。

直接應用該資源對象,然后向外部的 HTTP 服務發送請求:

$ kubectl exec "${SOURCE_POD}" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
# 輸出如下結果
HTTP/1.1 301 Moved Permanently
# ......
location: https://edition.cnn.com/politics

HTTP/2 200
content-type: text/html; charset=utf-8
# ......

上面我們在使用 curl 命令的時候添加了一個 -L 標志,該標志指示 curl 將遵循重定向。 在這種情況下,服務器將對到 http://edition.cnn.com/politics 的 HTTP 請求進行重定向響應,而重定向響應將指示客戶端使用 HTTPS 向 https://edition.cnn.com/politics 重新發送請求,對于第二個請求,服務器則返回了請求的內容和 200 狀態碼。

盡管 curl 命令簡明地處理了重定向,但是這里有兩個問題。第一個問題是請求冗余,它使獲取 http://edition.cnn.com/politics 內容的延遲加倍,第二個問題是 URL 中的路徑(在本例中為 politics)被以明文的形式發送。如果有人嗅探你的應用與 edition.cnn.com 之間的通信,他將會知曉該應用獲取了此網站中哪些特定的內容。出于隱私的原因,我們可能希望阻止這些內容被嗅探到。通過配置 Istio 執行 TLS 發起,則可以解決這兩個問題。

用于 egress 流量的 TLS 發起

為 edition.cnn.com 創建一個出口網關,端口為 80,接收 HTTP 流量,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
    - port:
        number: 80
        name: tls-origination-port
        protocol: HTTP
      hosts:
        - edition.cnn.com

然后為 istio-egressgateway 創建一個 DestinationRule 對象,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
    - name: cnn

接著我們只需要創建一個 VirtualService 對象,將流量從 Sidecar 引導至 Egress Gateway,再從 Egress Gateway 引導至外部服務,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
    - edition.cnn.com
  gateways:
    - istio-egressgateway # Egress Gateway
    - mesh # 網格內部的流量
  http:
    - match:
        - gateways:
            - mesh
          port: 80
      route:
        - destination:
            host: istio-egressgateway.istio-system.svc.cluster.local
            subset: cnn
            port:
              number: 80
          weight: 100
    - match:
        - gateways:
            - istio-egressgateway
          port: 80
      route:
        - destination:
            host: edition.cnn.com
            port:
              number: 443 # 443 端口
          weight: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: originate-tls-for-edition-cnn-com
spec:
  host: edition.cnn.com
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    portLevelSettings:
      - port:
          number: 443
        tls:
          mode: SIMPLE # initiates HTTPS for connections to edition.cnn.com

需要注意的是上面最后針對 edition.cnn.com 的 DestinationRule 對象,在 trafficPolicy 中指定了 portLevelSettings 用于對不同的端口定義不同的流量策略,這里我們定義了 443 端口的 tls 模式為 SIMPLE,表示當訪問 edition.cnn.com 的 HTTP 請求時執行 TLS 發起。

應用上面的資源對象,然后再次向 http://edition.cnn.com/politics 發送 HTTP 請求:

$ kubectl exec "${SOURCE_POD}" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
# 直接輸出200狀態碼
HTTP/1.1 200 OK
content-length: 2474958
content-type: text/html; charset=utf-8
# ......

這次將會收到唯一的 200 OK 響應,因為 Istio 為 curl 執行了 TLS 發起,原始的 HTTP 被升級為 HTTPS 并轉發到 edition.cnn.com。服務器直接返回內容而無需重定向,這消除了客戶端與服務器之間的請求冗余,使網格保持加密狀態,隱藏了你的應用獲取 edition.cnn.com 中 politics 端點的信息。

如果我們在代碼中有去訪問外部服務,那么我們就可以不用修改代碼了,只需要通過配置 Istio 來獲得 TLS 發起即可,而無需更改一行代碼。

到這里我們就學習了如何通過配置 Istio 實現對外部服務的 TLS 發起。

TLS 與 mTLS 基本概念

前面我們學習了如何通過配置 Istio 實現對外部服務的 TLS 發起,這里其實還有一個 mTLS 的概念呢,由于 TLS 本身就比較復雜,對于雙向 TLS(mTLS)就更復雜了。

TLS 是一個連接層協議,旨在為 TCP 連接提供安全保障。TLS 在連接層工作,可以與任何使用 TCP 的應用層協議結合使用。例如,HTTPS 是 HTTP 與 TLS 的結合(HTTPS 中的 S 指的是 SSL,即 TLS 的前身),TLS 認證的流程大致如下所示:

  • 首先向第三方機構 CA 提交組織信息、個人信息(域名)等信息并申請認證。
  • CA 通過多種手段驗證申請者提供信息的真實性,如組織是否存在、企業是否合法,是否擁有域名的所有權等。如信息審核通過,CA 會向申請者簽發認證文件-證書。
  • 證書包含以下信息:申請者公鑰、申請者的組織信息和個人信息、簽發機構 CA 的信息、有效時間、證書序列號等信息的明文,同時包含一個簽名。其中簽名的產生算法:首先,使用散列函數計算公開的明文信息的信息摘要,然后,采用 CA 的私鑰對信息摘要進行加密,密文即簽名。
  • 客戶端向服務端發出請求時,服務端返回證書文件。
  • 客戶端讀取證書中的相關的明文信息,采用相同的散列函數計算得到信息摘要,然后,利用對應 CA 的公鑰解密簽名數據,對比證書的信息摘要,如果一致,則可以確認證書的合法性。
  • 客戶端還會驗證證書相關的域名信息、有效時間等信息; 客戶端會內置信任 CA 的證書信息(包含公鑰),比如瀏覽器基本上都帶有知名公共 CA 機構的證書,如 Verisign、Digicert 等,這些證書在發布時被打包在一起,當我們下載瀏覽器時,就經把正確的證書放進了瀏覽器,如果 CA 不被信任,則找不到對應 CA 的證書,證書也會被判定非法。

Istio Egress 出口網關使用

認證過程

當然 HTTPS 的工作流程和這個過程基本就一致了:

  • 客戶端發起一個 HTTPS 請求。
  • 服務端把配置好的證書返回給客戶端。
  • 客戶端驗證證書:比如是否在有效期內,證書的用途是不是匹配 Client 請求的站點,是不是在 CRL 吊銷列表里面,它的上一級證書是否有效等。
  • 客戶端使用偽隨機數生成對稱密鑰,并通過證書里服務器的公鑰進行加密,后續使用該對稱密鑰進行傳輸信息。
  • 服務端使用自己的私鑰解密這個消息,得到對稱密鑰。至此,客戶端和服務端都持有了相同的對稱密鑰。
  • 服務端使用對稱密鑰加密明文內容 A,發送給客戶端。
  • 客戶端使用對稱密鑰解密響應的密文,得到明文內容 A。
  • 客戶端再次發起 HTTPS 的請求,使用對稱密鑰加密請求的明文內容 B,然后服務器使用對稱密鑰解密密文,得到明文內容 B。

Istio Egress 出口網關使用

HTTPS 工作流程

當然雙向 TLS 就更為復雜了,Mutual TLS(雙向 TLS),或稱 mTLS,對于常規的 TLS,只需要服務端認證,mTLS 相對來說有一個額外的規定:客戶端也要經過認證。在 mTLS 中,客戶端和服務器都有一個證書,并且雙方都使用它們的公鑰/私鑰對進行身份驗證。

TLS 保證了真實性,但默認情況下,這只發生在一個方向上:客戶端對服務器進行認證,但服務器并不對客戶端進行認證。為什么 TLS 的默認只在一個方向進行認證?因為客戶端的身份往往是不相關的。例如我們在訪問優點知識的時候,你的瀏覽器已經驗證了要訪問的網站服務端的身份,但服務端并沒有驗證你的瀏覽器的身份,它實際上并不關心你的瀏覽器的身份,這對于互聯網上的 Web 項目來說足夠了。但是在某些情況下,服務器確實需要驗證客戶端的身份,例如,當客戶端需要訪問某些敏感數據時,服務器可能需要驗證客戶端的身份,以確??蛻舳擞袡嘣L問這些數據,這就是 mTLS 的用武之地,mTLS 是保證微服務之間跨服務通信安全的好方法。

  • 首先,你想要安全的通信。當我們把我們的應用程序拆分為多個服務時,我們最終會在這些服務之間的網絡上發送敏感數據。任何能夠進入網絡的人都有可能讀取這些敏感數據并偽造請求。
  • 其次,你關心客戶端的身份。首先,你要確保你能知道調用是什么時候發生的,以便進行診斷,并正確記錄指標等事項。此外,你可能想對這些身份進行授權(允許 A 調用 B 嗎)。當然授權是另外的話題了。

接下來我們就來測試下如何通過 egress 網關發起雙向 TLS 連接。

通過 egress 網關發起雙向 TLS 連接

首先使用 openssl 命令生成客戶端和服務器的證書與密鑰,為你的服務簽名證書創建根證書和私鑰:

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
# 生成 CA 證書和私鑰

為 my-Nginx.mesh-external.svc.cluster.local 創建證書和私鑰:

# 為 my-nginx.mesh-external.svc.cluster.local 創建私鑰和證書簽名請求
$ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:2048 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization"
# 使用 CA 公鑰和私鑰以及證書簽名請求為 my-nginx.mesh-external.svc.cluster.local 創建證書
$ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in my-nginx.mesh-external.svc.cluster.local.csr -out my-nginx.mesh-external.svc.cluster.local.crt

然后生成客戶端證書和私鑰:

# 為 client.example.com 創建私鑰和證書簽名請求
$ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
# 使用相同的 CA 公鑰和私鑰以及證書簽名請求為 client.example.com 創建證書
$ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.example.com.csr -out client.example.com.crt

接著我們來部署一個雙向 TLS 服務器,為了模擬一個真實的支持雙向 TLS 協議的外部服務,我們在 Kubernetes 集群中部署一個 NGINX 服務,該服務運行在 Istio 服務網格之外,比如運行在一個沒有開啟 Istio Sidecar proxy 注入的命名空間中。

創建一個命名空間 mesh-external 表示 Istio 網格之外的服務,注意在這個命名空間中,Sidecar 自動注入是沒有開啟的,不會在 Pod 中自動注入 Sidecar proxy。

kubectl create namespace mesh-external

然后創建 Kubernetes Secret,保存服務器和 CA 的證書。

$ kubectl create -n mesh-external secret tls nginx-server-certs --key my-nginx.mesh-external.svc.cluster.local.key --cert my-nginx.mesh-external.svc.cluster.local.crt
$ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt

生成 NGINX 服務器的配置文件:

$ cat <<EOF > ./nginx.conf
events {
}

http {
  log_format main '$remote_addr - $remote_user [$time_local]  $status '
  '"$request" $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;
  error_log  /var/log/nginx/error.log;

  server {
    listen 443 ssl;

    root /usr/share/nginx/html;
    index index.html;

    server_name my-nginx.mesh-external.svc.cluster.local;
    ssl_certificate /etc/nginx-server-certs/tls.crt;
    ssl_certificate_key /etc/nginx-server-certs/tls.key;
    ssl_client_certificate /etc/nginx-ca-certs/example.com.crt;
    ssl_verify_client on;
  }
}
EOF

生成 Kubernetes ConfigMap 保存 NGINX 服務器的配置文件:

kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.cnotallow=./nginx.conf

然后就可以部署 NGINX 服務了:

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  namespace: mesh-external
  labels:
    run: my-nginx
spec:
  ports:
  - port: 443
    protocol: TCP
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  namespace: mesh-external
spec:
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 443
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx
          readOnly: true
        - name: nginx-server-certs
          mountPath: /etc/nginx-server-certs
          readOnly: true
        - name: nginx-ca-certs
          mountPath: /etc/nginx-ca-certs
          readOnly: true
      volumes:
      - name: nginx-config
        configMap:
          name: nginx-configmap
      - name: nginx-server-certs
        secret:
          secretName: nginx-server-certs
      - name: nginx-ca-certs
        secret:
          secretName: nginx-ca-certs
EOF

現在如果我們在網格內部去直接訪問這個 my-nginx 服務,是無法訪問的,第一是沒有內置 CA 證書,另外是 my-nginx 服務開啟了 mTLS,需要客戶端證書才能訪問,現在我們的網格中是沒有對應的客戶端證書的,會出現 400 錯誤。

Istio Egress 出口網關使用

開啟了雙向認證

為 egress 流量配置雙向 TLS

創建 Kubernetes Secret 保存客戶端證書:

kubectl create secret -n istio-system generic client-credential --from-file=tls.key=client.example.com.key 
  --from-file=tls.crt=client.example.com.crt --from-file=ca.crt=example.com.crt

Secret 所在的命名空間必須與出口網關部署的位置一致,我們這里是 istio-system 命名空間。

然后為 my-nginx.mesh-external.svc.cluster.local 創建一個端口為 443 的 Egress Gateway,以及目標規則和虛擬服務來引導流量流經 egress 網關并從 egress 網關流向外部服務。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
    - my-nginx.mesh-external.svc.cluster.local
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: egressgateway-for-nginx
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: nginx
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 443
        tls:
          mode: ISTIO_MUTUAL
          sni: my-nginx.mesh-external.svc.cluster.local
EOF

上面我們定義的 Gateway 對象和前面的一樣,只是將端口改為了 443,然后在 tls 中指定了 mode: ISTIO_MUTUAL,表示該 Gateway 對象用于 TLS 雙向認證協議的請求。

然后同樣在后面的 DestinationRule 對象中配置了通過 istio-egressgateway 的流量的規則,這里我們定義了 443 端口的 tls 模式為 ISTIO_MUTUAL,表示當訪問 my-nginx.mesh-external.svc.cluster.local 的 TLS 請求時執行 TLS 雙向認證。

最后我們定義一個 VirtualService 對象來引導流量流經 egress 網關:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-nginx-through-egress-gateway
spec:
  hosts:
  - my-nginx.mesh-external.svc.cluster.local
  gateways:
  - istio-egressgateway
  - mesh  # 網格內部的流量
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: nginx
        port:
          number: 443
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 443
    route:
    - destination:
        host: my-nginx.mesh-external.svc.cluster.local
        port:
          number: 443
      weight: 100
EOF

上面的 VirtualService 對象定義網格內部對 my-nginx.mesh-external.svc.cluster.local 服務的訪問引導至 istio-egressgateway,然后再由 istio-egressgateway 引導流量流向外部服務。

添加 DestinationRule 執行雙向 TLS:

$ kubectl apply -n istio-system -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: originate-mtls-for-nginx
spec:
  host: my-nginx.mesh-external.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    portLevelSettings:
    - port:
        number: 443
      tls:
        mode: MUTUAL
        credentialName: client-credential # 這必須與之前創建的用于保存客戶端證書的 Secret 相匹配
        sni: my-nginx.mesh-external.svc.cluster.local
EOF

發送一個 HTTP 請求至 http://my-nginx.mesh-external.svc.cluster.local:

$ kubectl exec "$(kubectl get pod -l app=sleep -o jsnotallow={.items..metadata.name})" -c sleep -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

檢查 istio-egressgateway Pod 日志,有一行與請求相關的日志記錄。如果 Istio 部署在命名空間 istio-system 中,打印日志的命令為:

kubectl logs -l istio=egressgateway -n istio-system | grep 'my-nginx.mesh-external.svc.cluster.local' | grep HTTP

將顯示類似如下的一行:

[2023-11-17T08:23:51.203Z] "GET / HTTP/1.1" 200 - via_upstream - "-" 0 615 17 16 "10.244.1.100" "curl/7.81.0-DEV" "434b5755-54da-9924-9e2a-a204b5a2124c" "my-nginx.mesh-external.svc.cluster.local" "10.244.1.106:443" outbound|443||my-nginx.mesh-external.svc.cluster.local 10.244.2.239:35198 10.244.2.239:8443 10.244.1.100:56448 my-nginx.mesh-external.svc.cluster.local -

Istio Egress 出口網關使用

雙向認證

即使我們直接在網格中訪問的是 HTTP 的服務,但是通過配置 Istio,我們也可以實現對外部服務的雙向 TLS 認證。

參考文檔:https://istio.io/latest/docs/tasks/traffic-management/egress/。

分享到:
標簽:網關
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定