无 TLS 终止的 Ingress Gateway

安全网关说明了如何为 HTTP 服务配置 HTTPS 访问入口。而本示例将说明如何为 HTTPS 服务配置 HTTPS 访问入口,即配置 Ingress Gateway 以执行 SNI 透传,而不是对传入请求进行 TLS 终止。

本任务中的 HTTPS 示例服务是一个简单的 NGINX 服务。在接下来的步骤中,您首先在 Kubernetes 集群中创建一个 NGINX 服务。接着,通过网关给这个服务配置一个域名是 nginx.example.com 的访问入口。

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

对于此任务,您可以使用自己喜欢的工具来生成证书和密钥。以下命令使用 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. nginx.example.com 创建证书和私钥:

    1. $ openssl req -out nginx.example.com.csr -newkey rsa:2048 -nodes -keyout nginx.example.com.key -subj "/CN=nginx.example.com/O=some organization"
    2. $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in nginx.example.com.csr -out nginx.example.com.crt

部署一个 NGINX 服务

  1. 创建一个 Kubernetes 的 Secret 资源来保存服务的证书:

    1. $ kubectl create secret tls nginx-server-certs --key nginx.example.com.key --cert nginx.example.com.crt
  2. 为 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 nginx.example.com;
    15. ssl_certificate /etc/nginx-server-certs/tls.crt;
    16. ssl_certificate_key /etc/nginx-server-certs/tls.key;
    17. }
    18. }
    19. EOF
  3. 创建一个 Kubernetes 的 ConfigMap 资源来保存 NGINX 服务的配置:

    1. $ kubectl create configmap nginx-configmap --from-file=nginx.conf=./nginx.conf
  4. 部署 NGINX 服务

    1. $ cat <<EOF | istioctl kube-inject -f - | kubectl apply -f -
    2. apiVersion: v1
    3. kind: Service
    4. metadata:
    5. name: my-nginx
    6. labels:
    7. run: my-nginx
    8. spec:
    9. ports:
    10. - port: 443
    11. protocol: TCP
    12. selector:
    13. run: my-nginx
    14. ---
    15. apiVersion: apps/v1
    16. kind: Deployment
    17. metadata:
    18. name: my-nginx
    19. spec:
    20. selector:
    21. matchLabels:
    22. run: my-nginx
    23. replicas: 1
    24. template:
    25. metadata:
    26. labels:
    27. run: my-nginx
    28. spec:
    29. containers:
    30. - name: my-nginx
    31. image: nginx
    32. ports:
    33. - containerPort: 443
    34. volumeMounts:
    35. - name: nginx-config
    36. mountPath: /etc/nginx
    37. readOnly: true
    38. - name: nginx-server-certs
    39. mountPath: /etc/nginx-server-certs
    40. readOnly: true
    41. volumes:
    42. - name: nginx-config
    43. configMap:
    44. name: nginx-configmap
    45. - name: nginx-server-certs
    46. secret:
    47. secretName: nginx-server-certs
    48. EOF
  5. 要测试 NGINX 服务是否已成功部署,需要从其 Sidecar 代理发送请求,并忽略检查服务端的证书(使用 curl-k 选项)。确保正确打印服务端的证书,即 common name (CN) 等于 nginx.example.com

    1. $ kubectl exec "$(kubectl get pod -l run=my-nginx -o jsonpath={.items..metadata.name})" -c istio-proxy -- curl -sS -v -k --resolve nginx.example.com:443:127.0.0.1 https://nginx.example.com
    2. ...
    3. SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
    4. ALPN, server accepted to use http/1.1
    5. Server certificate:
    6. subject: CN=nginx.example.com; O=some organization
    7. start date: May 27 14:18:47 2020 GMT
    8. expire date: May 27 14:18:47 2021 GMT
    9. issuer: O=example Inc.; CN=example.com
    10. SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
    11. > GET / HTTP/1.1
    12. > User-Agent: curl/7.58.0
    13. > Host: nginx.example.com
    14. ...
    15. < HTTP/1.1 200 OK
    16. < Server: nginx/1.17.10
    17. ...
    18. <!DOCTYPE html>
    19. <html>
    20. <head>
    21. <title>Welcome to nginx!</title>
    22. ...

配置 Ingress Gateway

  1. 定义一个 server 部分的端口为 443 的 Gateway。注意,PASSTHROUGH tls TLS 模式,该模式指示 Gateway 以 AS IS 方式传递入口流量,而不终止 TLS。

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: mygateway
    6. spec:
    7. selector:
    8. istio: ingressgateway # use istio default ingress gateway
    9. servers:
    10. - port:
    11. number: 443
    12. name: https
    13. protocol: HTTPS
    14. tls:
    15. mode: PASSTHROUGH
    16. hosts:
    17. - nginx.example.com
    18. EOF
  2. 为通过 Gateway 进入的流量配置路由:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: nginx
    6. spec:
    7. hosts:
    8. - nginx.example.com
    9. gateways:
    10. - mygateway
    11. tls:
    12. - match:
    13. - port: 443
    14. sniHosts:
    15. - nginx.example.com
    16. route:
    17. - destination:
    18. host: my-nginx
    19. port:
    20. number: 443
    21. EOF
  3. 根据确定 Ingress IP 和端口中的指令来定义环境变量 SECURE_INGRESS_PORTINGRESS_HOST

  4. 从集群外访问 NGINX 服务。注意,服务端返回了正确的证书,并且该证书已成功验证(输出了 _SSL certificate verify ok_)。

    1. $ curl -v --resolve "nginx.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" --cacert example.com.crt "https://nginx.example.com:$SECURE_INGRESS_PORT"
    2. Server certificate:
    3. subject: CN=nginx.example.com; O=some organization
    4. start date: Wed, 15 Aug 2018 07:29:07 GMT
    5. expire date: Sun, 25 Aug 2019 07:29:07 GMT
    6. issuer: O=example Inc.; CN=example.com
    7. SSL certificate verify ok.
    8. < HTTP/1.1 200 OK
    9. < Server: nginx/1.15.2
    10. ...
    11. <html>
    12. <head>
    13. <title>Welcome to nginx!</title>

清除

  1. 删除已创建的 Kubernetes 资源:

    1. $ kubectl delete secret nginx-server-certs
    2. $ kubectl delete configmap nginx-configmap
    3. $ kubectl delete service my-nginx
    4. $ kubectl delete deployment my-nginx
    5. $ kubectl delete gateway mygateway
    6. $ kubectl delete virtualservice nginx
  2. 删除证书和密钥:

    1. $ rm example.com.crt example.com.key nginx.example.com.crt nginx.example.com.key nginx.example.com.csr
  3. 删除本示例中生成的配置文件:

    1. $ rm ./nginx.conf