使用 cert-manager 加密 Kubernetes Ingress

这个例子演示了在 Istio 中使用 Let’s Encrypt 获取 TLS 证书为 Kubernetes Ingress controller 提供安全加固的过程。虽然 Istio 提供了更强大的功能,例如 GatewayVirtual service,它们可以用于更加高级的流量管理功能,而可选的 Kubernetes Ingress 控制器支持则可以简单的把传统应用和第三方解决方案集成到服务网格之中,并由此获得 Istio 提供的遥测和跟踪能力。

首先要从一个全新安装的 Istio 入手,创建一个示例应用,并利用 Kubernetes Ingress 资源将服务开放出去,Istio 可以为这一过程提供加密服务,它调用自带的 cert-manager 管理 TLS 证书的签发和续期,然后把证书分发给 Istio 的 Ingress gateway,并在必要时使用 SDS 进行证书的热交换。

开始之前

安装 Istio 并确认已经启用 Ingress Gateway 的 Kubernetes Ingress 支持、SDS 以及 cert-manager。下面的例子展示了使用 Helm template 完成设置这些依赖项目的方法:

  1. $ helm template $HOME/istio-fetch/istio \
  2. --namespace=istio-system \
  3. --set gateways.istio-ingressgateway.sds.enabled=true \
  4. --set global.k8sIngress.enabled=true \
  5. --set global.k8sIngress.enableHttps=true \
  6. --set global.k8sIngress.gatewayName=ingressgateway \
  7. --set certmanager.enabled=true \
  8. --set certmanager.email=mailbox@donotuseexample.com \
  9. > $HOME/istio-fetch/istio.yaml

缺省情况下, istio-ingressgateway 会以 LoadBalancer 的服务类型开放出来。根据本地环境条件,可以考虑把 gateways.istio-ingressgateway.type 参数设置为 NodePort

配置 DNS 名称和 Gateway

记录一下 istio-ingressgateway 服务的外部 IP 地址:

  1. $ kubectl -n istio-system get service istio-ingressgateway

对你的 DNS 进行设置,给 istio-ingressgateway 服务的外部 IP 地址分配一个合适的域名。为了能让例子正常执行,需要一个真正的域名,用于签署 TLS 证书。可以把域名保存为环境变量,便于后面的使用:

  1. $ INGRESS_DOMAIN=mysubdomain.mydomain.edu

Istio 安装中包含了一个自动生成的 Gateway,用于给 Kubernetes Ingress 资源提供路由服务。缺省情况下,它不会使用 SDS,所以需要对其进行修改,让 SDS 来为 istio-ingressgateway 分发 TLS 证书:

  1. $ kubectl -n istio-system edit gateway

然后修改 https-default 端口对应的 tls 内容:

  1. $ kubectl -n istio-system \
  2. patch gateway istio-autogenerated-k8s-ingress --type=json \
  3. -p='[{"op": "replace", "path": "/spec/servers/1/tls", "value": {"credentialName": "ingress-cert-staging", "mode": "SIMPLE", "privateKey": "sds", "serverCertificate": "sds"}}]'

现在就可以部署一个演示应用了。

部署演示应用

接下来使用一个简单的 helloworld 应用来进行演示。下面的命令会为示例应用创建 DeploymentService 对象,并使用 istio-ingressgateway 所支持的 Ingress 资源开放服务。

  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: helloworld
  6. labels:
  7. app: helloworld
  8. spec:
  9. ports:
  10. - port: 5000
  11. name: http
  12. selector:
  13. app: helloworld
  14. ---
  15. apiVersion: extensions/v1beta1
  16. kind: Deployment
  17. metadata:
  18. name: helloworld
  19. spec:
  20. template:
  21. metadata:
  22. labels:
  23. app: helloworld
  24. spec:
  25. containers:
  26. - name: helloworld
  27. image: istio/examples-helloworld-v1
  28. resources:
  29. requests:
  30. cpu: "100m"
  31. imagePullPolicy: IfNotPresent
  32. ports:
  33. - containerPort: 5000
  34. ---
  35. apiVersion: extensions/v1beta1
  36. kind: Ingress
  37. metadata:
  38. annotations:
  39. kubernetes.io/ingress.class: istio
  40. name: helloworld-ingress
  41. spec:
  42. rules:
  43. - host: "$INGRESS_DOMAIN"
  44. http:
  45. paths:
  46. - path: /hello
  47. backend:
  48. serviceName: helloworld
  49. servicePort: 5000
  50. ---
  51. EOF

注意,这里用了前面定义的 INGRESS_DOMAIN 变量。

现在就可以用 HTTP 来访问演示应用了:

  1. $ curl http://$INGRESS_DOMAIN/hello
  2. Hello version: v1, instance: helloworld-5d498979b6-jp2mf

因为没有配置任何的 TLS 证书,所以 HTTPS 访问还未能启用,下面就开始进行配置。

使用 cert-manager 获取 Let’s Encrypt 证书

目前的 Istio 中应该已经启动了 cert-manager,并带有两个 ClusterIssuer 对象(分别对应 Let’s Encrypt 的生产和演练环境)。这个例子中可以使用演练环境(letsencrypt-staging 替换为 letsencrypt 就能获得受浏览器信任的证书),

为了用 cert-manager 进行证书的签发和管理,需要创建一个 Certificate 资源:

  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: certmanager.k8s.io/v1alpha1
  3. kind: Certificate
  4. metadata:
  5. name: ingress-cert-staging
  6. namespace: istio-system
  7. spec:
  8. secretName: ingress-cert-staging
  9. issuerRef:
  10. name: letsencrypt-staging
  11. kind: ClusterIssuer
  12. commonName: $INGRESS_DOMAIN
  13. dnsNames:
  14. - $INGRESS_DOMAIN
  15. acme:
  16. config:
  17. - http01:
  18. ingressClass: istio
  19. domains:
  20. - $INGRESS_DOMAIN
  21. ---
  22. EOF

注意这里的 secretName 要匹配前面配置 Gateway 资源时其中的 credentialName 字段值。Certificate 对象会被 cert-manager 处理,最终会签发新证书。可以通过对 Certificate 对象状态的查询来得知整个过程的进展:

  1. $ kubectl -n istio-system describe certificate ingress-cert-staging
  2. -> 状态最终会切换为 'Certificate issued successfully'

这样一来就可以使用 HTTPS 进行访问了:

  1. $ curl --insecure https://$INGRESS_DOMAIN/hello
  2. Hello version: v1, instance: helloworld-5d498979b6-jp2mf

注意,因为演练环境签发的证书不受信任,这里用了 —insecure 参数。

相关内容

没有 TLS 的 Ingress gateway

介绍如何为入口网关配置 SNI 直通。

使用 Cert-Manager 部署一个自定义 Ingress 网关

如何使用 cert-manager 手工部署一个自定义 Ingress 网关。

使用AWS NLB 配置 Istio Ingress

描述如何在AWS上使用网络负载均衡器配置 Istio Ingress。

使用外部 Web 服务

描述基于 Istio Bookinfo 示例的简单场景。

使用 SDS 为 Gateway 提供 HTTPS 加密支持

如何配置 Istio,借助 SDS(Secret 发现服务),使用 TLS 或双向 TLS 将服务开放到网格之外。

控制 Ingress 流量

介绍在服务网格 Istio 中如何配置外部公开服务。