出口网关与 TLS 发起

The TLS Origination for Egress Traffic example shows how to configure Istio to perform TLS 发起 for traffic to an external service. The Configure an Egress Gateway example shows how to configure Istio to direct egress traffic through a dedicated egress gateway service. This example combines the previous two by describing how to configure an egress gateway to perform TLS origination for traffic to external services.

在您开始之前

  • 按照 安装指南 中的说明设置 Istio。

  • 启动 curl 示例,它将用作外部调用的测试源。

    如果您已启用 自动 sidecar 注入,请执行以下操作

    压缩
    $ kubectl apply -f @samples/curl/curl.yaml@
    

    否则,您必须在部署 curl 应用程序之前手动注入 sidecar。

    压缩
    $ kubectl apply -f <(istioctl kube-inject -f @samples/curl/curl.yaml@)
    

    请注意,任何您可以从其 execcurl 的 pod 都可以。

  • 创建一个 shell 变量来保存用于向外部服务发送请求的源 pod 的名称。如果您使用了 curl 示例,请运行以下命令

    $ export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})
    
  • 对于 macOS 用户,请验证您是否正在使用 openssl 版本 1.1 或更高版本

    $ openssl version -a | grep OpenSSL
    OpenSSL 1.1.1g  21 Apr 2020
    

    如果前面的命令输出版本 1.1 或更高版本(如所示),您的 openssl 命令应与本任务中的说明一起正常工作。否则,请升级您的 openssl 或尝试使用 openssl 的其他实现,例如在 Linux 机器上。

  • 如果尚未启用,请 启用 Envoy 的访问日志记录。例如,使用 istioctl

    $ istioctl install <flags-you-used-to-install-Istio> --set meshConfig.accessLogFile=/dev/stdout
    
  • 如果您没有使用 Gateway API 指令,请确保 部署 Istio 出口网关

使用出口网关执行 TLS 发起

本节描述了如何执行与 出口流量的 TLS 始发 示例中相同的 TLS 始发,只是这次使用的是出口网关。请注意,在这种情况下,TLS 始发将由出口网关执行,而不是由之前示例中的 sidecar 执行。

  1. edition.cnn.com 定义一个 ServiceEntry

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: https
        protocol: HTTPS
      resolution: DNS
    EOF
    
  2. 通过向 http://edition.cnn.com/politics 发送请求来验证您的 ServiceEntry 是否已正确应用。

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

    如果输出中显示“301 Moved Permanently”,则您的 ServiceEntry 已配置正确。

  3. edition.cnn.com(端口 80)创建一个出口 Gateway,以及一个用于将 sidecar 请求定向到出口网关的目标规则。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 80
      name: https-port-for-tls-origination
      protocol: HTTPS
    hosts:
    - edition.cnn.com
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 80
        tls:
          mode: ISTIO_MUTUAL
          sni: edition.cnn.com
EOF
  1. 配置路由规则以通过出口网关定向流量
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - 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
      weight: 100
EOF
  1. 定义一个 DestinationRule 以对 edition.cnn.com 的请求执行 TLS 始发

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    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
    EOF
    
  2. http://edition.cnn.com/politics 发送 HTTP 请求。

    $ kubectl exec "${SOURCE_POD}" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 200 OK
    ...
    

    输出应与 出口流量的 TLS 始发 示例中的输出相同,并且具有 TLS 始发:没有 301 Moved Permanently 消息。

  3. 检查出口网关代理的日志。

如果 Istio 部署在 istio-system 命名空间中,则打印日志的命令为

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

您应该会看到类似于以下内容的行

[2020-06-30T16:17:56.763Z] "GET /politics HTTP/2" 200 - "-" "-" 0 1295938 529 89 "10.244.0.171" "curl/7.64.0" "cf76518d-3209-9ab7-a1d0-e6002728ef5b" "edition.cnn.com" "151.101.129.67:443" outbound|443||edition.cnn.com 10.244.0.170:54280 10.244.0.170:8080 10.244.0.171:35628 - -

清理 TLS 发起示例

删除您创建的 Istio 配置项

$ kubectl delete gw istio-egressgateway
$ kubectl delete serviceentry cnn
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule originate-tls-for-edition-cnn-com
$ kubectl delete destinationrule egressgateway-for-cnn

使用出口网关执行双向 TLS 发起

与上一节类似,本节描述了如何配置出口网关以对外部服务执行 TLS 始发,只是这次使用的是需要双向 TLS 的服务。

本示例更为复杂,因为您需要首先

  1. 生成客户端和服务器证书
  2. 部署支持双向 TLS 协议的外部服务
  3. 使用所需的双向 TLS 证书重新部署出口网关

只有这样,您才能配置外部流量通过出口网关,出口网关将执行 TLS 始发。

生成客户端和服务器证书以及密钥

对于此任务,您可以使用您喜欢的工具生成证书和密钥。以下命令使用 openssl

  1. 创建一个根证书和私钥来签署您服务证书。

    $ 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
    
  2. 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"
    $ 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
    

    您也可以选择向证书中添加 SubjectAltNames,如果您希望为目标启用 SAN 验证。例如

    $ cat > san.conf <<EOF
    [req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    countryName = US
    [v3_req]
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth, clientAuth
    basicConstraints = critical, CA:FALSE
    subjectAltName = critical, @alt_names
    [alt_names]
    DNS = my-nginx.mesh-external.svc.cluster.local
    EOF
    $
    $ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:4096 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization" -config san.conf
    $ 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 -extfile san.conf -extensions v3_req
    
  3. 生成客户端证书和私钥

    $ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
    $ 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 代理注入的命名空间中。

  1. 创建一个命名空间来表示 Istio 网格之外的服务,即 mesh-external。请注意,由于未在该命名空间上 启用 自动 sidecar 注入,因此 sidecar 代理不会自动注入到该命名空间中的 pod 中。

    $ kubectl create namespace mesh-external
    
  2. 创建 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
    
  3. 为 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
    
  4. 创建 Kubernetes ConfigMap 来保存 NGINX 服务器的配置

    $ kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
    
  5. 部署 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
      replicas: 1
      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
    

为出口流量配置双向 TLS 发起

  1. 在出口网关部署的 相同命名空间 中创建一个 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

为了支持与各种工具的集成,Istio 支持几种不同的 Secret 格式。在本示例中,使用了带有密钥 tls.keytls.crtca.crt 的单个通用 Secret。

  1. my-nginx.mesh-external.svc.cluster.local(端口 443)创建一个出口 Gateway,以及一个用于将 sidecar 请求定向到出口网关的目标规则。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
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/v1
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
  1. 配置路由规则以通过出口网关定向流量
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
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
  1. 添加一个 DestinationRule 以执行双向 TLS 始发
$ kubectl apply -n istio-system -f - <<EOF
apiVersion: networking.istio.io/v1
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 # this must match the secret created earlier to hold client certs
        sni: my-nginx.mesh-external.svc.cluster.local
        # subjectAltNames: # can be enabled if the certificate was generated with SAN as specified in previous section
        # - my-nginx.mesh-external.svc.cluster.local
EOF
  1. 验证凭据是否已提供给出口网关并处于活动状态
$ istioctl -n istio-system proxy-config secret deploy/istio-egressgateway | grep client-credential
kubernetes://client-credential            Cert Chain     ACTIVE     true           1                                          2024-06-04T12:46:28Z     2023-06-05T12:46:28Z
kubernetes://client-credential-cacert     Cert Chain     ACTIVE     true           16491643791048004260                       2024-06-04T12:46:28Z     2023-06-05T12:46:28Z
  1. http://my-nginx.mesh-external.svc.cluster.local 发送 HTTP 请求

    $ kubectl exec "$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})" -c curl -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    ...
    
  2. 检查出口网关代理的日志

如果 Istio 部署在 istio-system 命名空间中,则打印日志的命令为

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

您应该会看到类似于以下内容的行

[2018-08-19T18:20:40.096Z] "GET / HTTP/1.1" 200 - 0 612 7 5 "172.30.146.114" "curl/7.35.0" "b942b587-fac2-9756-8ec6-303561356204" "my-nginx.mesh-external.svc.cluster.local" "172.21.72.197:443"

清理双向 TLS 发起示例

  1. 删除 NGINX 双向 TLS 服务器资源

    $ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external
    $ kubectl delete configmap nginx-configmap -n mesh-external
    $ kubectl delete service my-nginx -n mesh-external
    $ kubectl delete deployment my-nginx -n mesh-external
    $ kubectl delete namespace mesh-external
    
  2. 删除网关配置资源

$ kubectl delete secret client-credential -n istio-system
$ kubectl delete gw istio-egressgateway
$ kubectl delete virtualservice direct-nginx-through-egress-gateway
$ kubectl delete destinationrule -n istio-system originate-mtls-for-nginx
$ kubectl delete destinationrule egressgateway-for-nginx
  1. 删除证书和私钥

    $ rm example.com.crt example.com.key my-nginx.mesh-external.svc.cluster.local.crt my-nginx.mesh-external.svc.cluster.local.key my-nginx.mesh-external.svc.cluster.local.csr client.example.com.crt client.example.com.csr client.example.com.key
    
  2. 删除本示例中使用的生成配置文件

    $ rm ./nginx.conf
    

清理

删除 curl 服务和部署

压缩
$ kubectl delete -f @samples/curl/curl.yaml@
这些信息是否有用?
您是否有任何改进建议?

感谢您的反馈!