Egress 网关 TLS 连接 发起的过程 (SDS)

TLS 连接流量 Egress的例子展示了如何配置 Istio 来执行TLS origination把流量导入至外部服务,并为通往外部服务的流量进行 TLS 连接。配置 Egress 网关的示例显示了如何配置 Istio 以引导 Egress 流量通过一个专门的 Egress 网关服务。这个例子结合了之前的两个例子描述了如何配置一个 Egress 网关,来执行把 TLS 发起的流量连接到外部服务。

TLS 所需的私钥、服务器证书和 root 证书是通过以下方式配置的Secret Discovery Service (SDS).

开始之前

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

  • 启动sleep样例这将被用作外部调用的测试源。

    如果您已经启用了自动 Sidecar 注入,请执行

    Zip

    1. $ kubectl apply -f @samples/sleep/sleep.yaml@

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

    Zip

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

    注意,任何您可以execcurl的 Pod 都可以。

  • 对于macOS用户,请确认您使用的是1.1或更高版本的openssl

    1. $ openssl version -a | grep OpenSSL
    2. OpenSSL 1.1.1g 2020421

    如果前面的命令输出的是”1.1”或更高版本,如图所示,您的”openssl”命令应该可以按照本任务的说明正常工作。否则,请升级您的openssl或尝试不同的openssl实现,例如在 Linux 机器上

  • 部署 Istio Egress 网关

  • 启用 Envoy 的访问日志

如果您使用基于文件挂载的方法配置了一个egress网关。 而您想把您的 Egress 网关迁移到使用SDS方法,则不需要额外的步骤。

使用SDS在 Egress 网关上执行 TLS 连接

本节描述了如何执行TLS 连接流量 Egress 的例子。只是这次使用了一个 Egress 网关。请注意,在这种情况下,TLS初始化将由 Egress 网关完成。是由 Egress 网关完成的,而不是前一个例子中的挎包。Egress 网关将使用 SDS 而不是文件挂载来提供客户证书。

生成 CA 及证书密钥

对于这项任务,您可以使用您喜欢的工具来生成证书和钥匙。下面的命令使用openssl.

  1. 创建一个根证书和私钥,为您的服务签署证书:

    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创建一个证书和私钥:

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

部署一个简单的 TLS 服务器

为了模拟一个支持简单 TLS 协议的实际外部服务。在您的Kubernetes集群中部署一个NGINX服务器,但在 Istio 服务网外运行,即在没有启用 Istio sidecar 代理注入的命名空间中。

  1. 创建一个命名空间来代表 Istio 网状结构之外的服务,即mesh-external。请注意,Sidecar 代理将不会被自动注入到这个命名空间的 Pod 中,因为自动注入 Sidecar 的功能没有被启用 上。

    1. $ kubectl create namespace mesh-external
  2. 创建 Kubernetes Secrets来保存服务器的和 CA 的证书。

    1. $ 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
    2. $ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt
  3. 为 NGINX 服务器创建一个配置文件:

    1. $ cat <<\EOF > ./nginx.conf
    2. events {
    3. }
    4. http {
    5. log_format main '$remote_addr - $remote_user [$time_local] $status '
    6. '"$request" $body_bytes_sent "$http_referer" '
    7. '"$http_user_agent" "$http_x_forwarded_for"';
    8. access_log /var/log/nginx/access.log main;
    9. error_log /var/log/nginx/error.log;
    10. server {
    11. listen 443 ssl;
    12. root /usr/share/nginx/html;
    13. index index.html;
    14. server_name my-nginx.mesh-external.svc.cluster.local;
    15. ssl_certificate /etc/nginx-server-certs/tls.crt;
    16. ssl_certificate_key /etc/nginx-server-certs/tls.key;
    17. ssl_client_certificate /etc/nginx-ca-certs/example.com.crt;
    18. ssl_verify_client off; # In simple TLS, server doesn't verify client's certificate
    19. }
    20. }
    21. EOF
  4. 创建一个 Kubernetes ConfigMap 来保存 NGINX 服务器的配置。

    1. $ kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
  5. 部署 NGINX 服务器:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: v1
    3. kind: Service
    4. metadata:
    5. name: my-nginx
    6. namespace: mesh-external
    7. labels:
    8. run: my-nginx
    9. spec:
    10. ports:
    11. - port: 443
    12. protocol: TCP
    13. selector:
    14. run: my-nginx
    15. ---
    16. apiVersion: apps/v1
    17. kind: Deployment
    18. metadata:
    19. name: my-nginx
    20. namespace: mesh-external
    21. spec:
    22. selector:
    23. matchLabels:
    24. run: my-nginx
    25. replicas: 1
    26. template:
    27. metadata:
    28. labels:
    29. run: my-nginx
    30. spec:
    31. containers:
    32. - name: my-nginx
    33. image: nginx
    34. ports:
    35. - containerPort: 443
    36. volumeMounts:
    37. - name: nginx-config
    38. mountPath: /etc/nginx
    39. readOnly: true
    40. - name: nginx-server-certs
    41. mountPath: /etc/nginx-server-certs
    42. readOnly: true
    43. - name: nginx-ca-certs
    44. mountPath: /etc/nginx-ca-certs
    45. readOnly: true
    46. volumes:
    47. - name: nginx-config
    48. configMap:
    49. name: nginx-configmap
    50. - name: nginx-server-certs
    51. secret:
    52. secretName: nginx-server-certs
    53. - name: nginx-ca-certs
    54. secret:
    55. secretName: nginx-ca-certs
    56. EOF

为 Egress 流量配置简单的 TLS 连接

  1. 创建一个 Kubernetes Secret 来保存 Egress 网关用来发起 TLS 连接的CA证书。

    1. $ kubectl create secret generic client-credential-cacert --from-file=ca.crt=example.com.crt -n istio-system

    请注意,Istio 的纯 CA 证书的 Secret 名称必须以-cacert结尾,并且秘密必须在与 Istio 相同的命名空间中创建。Secret 必须与 Istio 部署的命名空间相同,本例中为 “istio-system”。

  2. my-nginx.mesh-external.svc.cluster.local创建一个 Egress gateway,端口443,以及目标规则和虚拟服务,以引导流量通过 Egress 网关并从 Egress 网关到外部服务

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: istio-egressgateway
    6. spec:
    7. selector:
    8. istio: egressgateway
    9. servers:
    10. - port:
    11. number: 443
    12. name: https
    13. protocol: HTTPS
    14. hosts:
    15. - my-nginx.mesh-external.svc.cluster.local
    16. tls:
    17. mode: ISTIO_MUTUAL
    18. ---
    19. apiVersion: networking.istio.io/v1alpha3
    20. kind: DestinationRule
    21. metadata:
    22. name: egressgateway-for-nginx
    23. spec:
    24. host: istio-egressgateway.istio-system.svc.cluster.local
    25. subsets:
    26. - name: nginx
    27. trafficPolicy:
    28. loadBalancer:
    29. simple: ROUND_ROBIN
    30. portLevelSettings:
    31. - port:
    32. number: 443
    33. tls:
    34. mode: ISTIO_MUTUAL
    35. sni: my-nginx.mesh-external.svc.cluster.local
    36. EOF
  3. 定义一个VirtualService来引导流量通过 Egress 网关:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: direct-nginx-through-egress-gateway
    6. spec:
    7. hosts:
    8. - my-nginx.mesh-external.svc.cluster.local
    9. gateways:
    10. - istio-egressgateway
    11. - mesh
    12. http:
    13. - match:
    14. - gateways:
    15. - mesh
    16. port: 80
    17. route:
    18. - destination:
    19. host: istio-egressgateway.istio-system.svc.cluster.local
    20. subset: nginx
    21. port:
    22. number: 443
    23. weight: 100
    24. - match:
    25. - gateways:
    26. - istio-egressgateway
    27. port: 443
    28. route:
    29. - destination:
    30. host: my-nginx.mesh-external.svc.cluster.local
    31. port:
    32. number: 443
    33. weight: 100
    34. EOF
  4. 添加一个DestinationRule来执行一个简单的 TLS 连接

    1. $ kubectl apply -n istio-system -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: DestinationRule
    4. metadata:
    5. name: originate-tls-for-nginx
    6. spec:
    7. host: my-nginx.mesh-external.svc.cluster.local
    8. trafficPolicy:
    9. loadBalancer:
    10. simple: ROUND_ROBIN
    11. portLevelSettings:
    12. - port:
    13. number: 443
    14. tls:
    15. mode: SIMPLE
    16. credentialName: client-credential # this must match the secret created earlier without the "-cacert" suffix
    17. sni: my-nginx.mesh-external.svc.cluster.local
    18. EOF
  5. 发送一个 HTTP 请求到http://my-nginx.mesh-external.svc.cluster.local

    1. $ kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
    2. <!DOCTYPE html>
    3. <html>
    4. <head>
    5. <title>Welcome to nginx!</title>
    6. ...
  6. 检查istio-egressgatewayPod 的日志,看看有没有与我们的请求相对应的日志。如果 Istio 被部署在istio-system命名空间,打印日志的命令是:

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

    您应该看到与下面类似的一行:

    1. [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. 删除您创建的 Istio 配置项:

    1. $ kubectl delete destinationrule originate-tls-for-nginx -n istio-system
    2. $ kubectl delete virtualservice direct-nginx-through-egress-gateway
    3. $ kubectl delete destinationrule egressgateway-for-nginx
    4. $ kubectl delete gateway istio-egressgateway
    5. $ kubectl delete secret client-credential-cacert -n istio-system
    6. $ kubectl delete service my-nginx -n mesh-external
    7. $ kubectl delete deployment my-nginx -n mesh-external
    8. $ kubectl delete configmap nginx-configmap -n mesh-external
    9. $ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external
    10. $ kubectl delete namespace mesh-external
  2. 删除证书和私钥:

    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
  3. 删除本例中生成的配置文件。

    1. $ rm ./nginx.conf

通过 Egress 网关发起 双向TLS 连接

与上一节类似,这一节描述了如何配置一个 Egress 网关来执行为外部服务进行 TLS 连接,只是这次使用的是一个需要双向 TLS 连接的服务。

Egress 网关将使用 SDS 而不是文件挂载来提供客户端证书。

生成客户端和服务端的证书和钥匙

对于这项任务,您可以使用您喜欢的工具来生成证书和钥匙。也可以参考下面命令:openssl.

  1. 创建一个 root 证书和私钥,为您的服务端签署证书:

    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创建一个证书和私钥:

    1. $ 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"
    2. $ 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
  3. 生成客户端的证书和私钥:

    1. $ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
    2. $ 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服务器{deploy-a-mutual-TLS-server}

为了模拟一个支持双向 TLS 连接的实际外部服务。在您的 Kubernetes 集群中部署一个NGINX服务器,但在 Istio 服务网外运行,即在没有启用 Istio Sidecar 代理注入的命名空间中运行。

  1. 创建一个命名空间来代表 Istio 网状结构之外的服务,即mesh-external。请注意,Sidecar 代理将不会被自动注入到这个命名空间的 Pod 中,因为自动注入 Sidecar 的功能没有被启用 上。

    1. $ kubectl create namespace mesh-external
  2. 创建 Kubernetes Secrets来保存服务器的证书。

    1. $ 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
    2. $ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt
  3. 为 NGINX 服务器创建一个配置文件:

    1. $ cat <<\EOF > ./nginx.conf
    2. events {
    3. }
    4. http {
    5. log_format main '$remote_addr - $remote_user [$time_local] $status '
    6. '"$request" $body_bytes_sent "$http_referer" '
    7. '"$http_user_agent" "$http_x_forwarded_for"';
    8. access_log /var/log/nginx/access.log main;
    9. error_log /var/log/nginx/error.log;
    10. server {
    11. listen 443 ssl;
    12. root /usr/share/nginx/html;
    13. index index.html;
    14. server_name my-nginx.mesh-external.svc.cluster.local;
    15. ssl_certificate /etc/nginx-server-certs/tls.crt;
    16. ssl_certificate_key /etc/nginx-server-certs/tls.key;
    17. ssl_client_certificate /etc/nginx-ca-certs/example.com.crt;
    18. ssl_verify_client on; # In mutual TLS, server verifies client's certificate
    19. }
    20. }
    21. EOF
  4. 创建一个 Kubernetes ConfigMap 来保存 NGINX 服务器的配置。

    1. $ kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
  5. 部署 NGINX 服务器。

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: v1
    3. kind: Service
    4. metadata:
    5. name: my-nginx
    6. namespace: mesh-external
    7. labels:
    8. run: my-nginx
    9. spec:
    10. ports:
    11. - port: 443
    12. protocol: TCP
    13. selector:
    14. run: my-nginx
    15. ---
    16. apiVersion: apps/v1
    17. kind: Deployment
    18. metadata:
    19. name: my-nginx
    20. namespace: mesh-external
    21. spec:
    22. selector:
    23. matchLabels:
    24. run: my-nginx
    25. replicas: 1
    26. template:
    27. metadata:
    28. labels:
    29. run: my-nginx
    30. spec:
    31. containers:
    32. - name: my-nginx
    33. image: nginx
    34. ports:
    35. - containerPort: 443
    36. volumeMounts:
    37. - name: nginx-config
    38. mountPath: /etc/nginx
    39. readOnly: true
    40. - name: nginx-server-certs
    41. mountPath: /etc/nginx-server-certs
    42. readOnly: true
    43. - name: nginx-ca-certs
    44. mountPath: /etc/nginx-ca-certs
    45. readOnly: true
    46. volumes:
    47. - name: nginx-config
    48. configMap:
    49. name: nginx-configmap
    50. - name: nginx-server-certs
    51. secret:
    52. secretName: nginx-server-certs
    53. - name: nginx-ca-certs
    54. secret:
    55. secretName: nginx-ca-certs
    56. EOF

使用 SDS 给 Egress 流量配置双向 TSL 连接

  1. 创建 Kubernetes Secrets来保存客户的证书。

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

    The secret must be created in the same namespace as Istio is deployed in, istio-system in this case.

    To support integration with various tools, Istio supports a few different Secret formats.

    In this example. a single generic Secret with keys tls.key, tls.crt, and ca.crt is used.

  2. my-nginx.mesh-external.svc.cluster.local创建一个 Egress Gateway,端口443,以及目标规则和虚拟服务,以引导流量通过 Egress 网关并从 Egress 网关到外部服务。

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: istio-egressgateway
    6. spec:
    7. selector:
    8. istio: egressgateway
    9. servers:
    10. - port:
    11. number: 443
    12. name: https
    13. protocol: HTTPS
    14. hosts:
    15. - my-nginx.mesh-external.svc.cluster.local
    16. tls:
    17. mode: ISTIO_MUTUAL
    18. ---
    19. apiVersion: networking.istio.io/v1alpha3
    20. kind: DestinationRule
    21. metadata:
    22. name: egressgateway-for-nginx
    23. spec:
    24. host: istio-egressgateway.istio-system.svc.cluster.local
    25. subsets:
    26. - name: nginx
    27. trafficPolicy:
    28. loadBalancer:
    29. simple: ROUND_ROBIN
    30. portLevelSettings:
    31. - port:
    32. number: 443
    33. tls:
    34. mode: ISTIO_MUTUAL
    35. sni: my-nginx.mesh-external.svc.cluster.local
    36. EOF
  3. 定义一个 VirtualService 来引导流量通过 Egress 网关:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: direct-nginx-through-egress-gateway
    6. spec:
    7. hosts:
    8. - my-nginx.mesh-external.svc.cluster.local
    9. gateways:
    10. - istio-egressgateway
    11. - mesh
    12. http:
    13. - match:
    14. - gateways:
    15. - mesh
    16. port: 80
    17. route:
    18. - destination:
    19. host: istio-egressgateway.istio-system.svc.cluster.local
    20. subset: nginx
    21. port:
    22. number: 443
    23. weight: 100
    24. - match:
    25. - gateways:
    26. - istio-egressgateway
    27. port: 443
    28. route:
    29. - destination:
    30. host: my-nginx.mesh-external.svc.cluster.local
    31. port:
    32. number: 443
    33. weight: 100
    34. EOF
  4. 添加一个 DestinationRule 来执行双向 TLS 连接

    1. $ kubectl apply -n istio-system -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: DestinationRule
    4. metadata:
    5. name: originate-mtls-for-nginx
    6. spec:
    7. host: my-nginx.mesh-external.svc.cluster.local
    8. trafficPolicy:
    9. loadBalancer:
    10. simple: ROUND_ROBIN
    11. portLevelSettings:
    12. - port:
    13. number: 443
    14. tls:
    15. mode: MUTUAL
    16. credentialName: client-credential # this must match the secret created earlier to hold client certs
    17. sni: my-nginx.mesh-external.svc.cluster.local
    18. EOF
  5. 发送一个 HTTP 请求到 http://my-nginx.mesh-external.svc.cluster.local:

    1. $ kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
    2. <!DOCTYPE html>
    3. <html>
    4. <head>
    5. <title>Welcome to nginx!</title>
    6. ...
  6. 检查istio-egressgatewayPod 的日志,看看有没有与我们的请求相对应的日志。如果 Istio 被部署在istio-system命名空间,打印日志的命令是:

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

    您应该看到与下面类似的一行。

    1. [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连接的示例{cleanup-the mutual-TLS-origination-example}

  1. 移除创建的 Kubernetes 资源:

    1. $ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external
    2. $ kubectl delete secret client-credential -n istio-system
    3. $ kubectl delete configmap nginx-configmap -n mesh-external
    4. $ kubectl delete service my-nginx -n mesh-external
    5. $ kubectl delete deployment my-nginx -n mesh-external
    6. $ kubectl delete namespace mesh-external
    7. $ kubectl delete gateway istio-egressgateway
    8. $ kubectl delete virtualservice direct-nginx-through-egress-gateway
    9. $ kubectl delete destinationrule -n istio-system originate-mtls-for-nginx
    10. $ kubectl delete destinationrule egressgateway-for-nginx
  2. 删除证书和私钥:

    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
  3. 删除本例中生成的配置文件:

    1. $ rm ./nginx.conf
    2. $ rm ./gateway-patch.json

清理

删除sleep服务和部署。

  1. $ kubectl delete service sleep
  2. $ kubectl delete deployment sleep