使用 Cert-Manager 加密 Kubernetes Ingress

这个例子演示了在 Istio 中使用 Let’s Encrypt 签发 TLS 证书为 Kubernetes Ingress controller 提供安全加固的过程。

您将从全新的 Istio 安装开始,创建示例服务,使用 Kubernetes Ingress 把它开放出去,并使用 cert-manager(与 Istio 捆绑在一起)管理 TLS 证书的签发和续订来确保它的安全,这个证书之后会给 Istio ingress gateway 使用,并根据需要通过 Secrets Discovery Service (SDS) 提供 hot-swapped 功能。

开始之前

  1. $ istioctl manifest apply \
  2. --set values.gateways.istio-ingressgateway.sds.enabled=true \
  3. --set values.global.k8sIngress.enabled=true \
  4. --set values.global.k8sIngress.enableHttps=true \
  5. --set values.global.k8sIngress.gatewayName=ingressgateway

默认情况下, istio-ingressgateway 会以 LoadBalancer 的服务类型开放出来。您可以根据自己的 Kubernetes 环境把 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", "mode": "SIMPLE", "privateKey": "sds", "serverCertificate": "sds"}}]'

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

部署演示应用

接下来使用一个简单的 helloworld 应用来进行演示。下面的命令会为示例应用创建 DeploymentService,并使用 Istio 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: apps/v1
  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: networking.k8s.io/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 的生产和测试 ACME-endpoints)。这个例子中使用测试 ACME-endpoint(将 letsencrypt-staging 替换为 letsencrypt 就能获得浏览器信任的证书)。

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

  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: cert-manager.io/v1alpha2
  3. kind: Certificate
  4. metadata:
  5. name: ingress-cert
  6. namespace: istio-system
  7. spec:
  8. secretName: ingress-cert
  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
  2. -> 状态最终会切换为 'Certificate issued successfully'

这样一来,该服务也应通过 HTTPS 访问了:

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

注意,您需要使用 —insecure 参数,因为测试 ACME-endpoints 签发的证书不受信任。

从测试到生产

现在换成生产 letsencrypt。首先,我们重新申请一下证书。

  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: cert-manager.io/v1alpha2
  3. kind: Certificate
  4. metadata:
  5. name: ingress-cert
  6. namespace: istio-system
  7. spec:
  8. secretName: ingress-cert
  9. issuerRef:
  10. name: letsencrypt
  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
  1. certificate.cert-manager.io/ingress-cert configured

现在删除 secret 来强制 cert-manager 从生产 ACME-endpoints 请求新证书:

  1. $ kubectl delete secret -n istio-system ingress-cert

等等看证书是否成功签发了:

  1. $ watch -n1 kubectl describe cert ingress-cert -n istio-system

您应该能看到如下显示:

  1. Normal CertIssued 13m cert-manager Certificate issued successfully

相关内容

把 Istio 作为外部服务的代理

把 Istio 入口网关配置为外部服务的代理。

无 TLS 终止的 Ingress Gateway

说明了如何为一个 ingress gateway 配置 SNI 透传。

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

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

使用 AWS NLB 配置 Istio Ingress

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

使用外部 Web 服务

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

Ingress Gateway

描述如何配置 Istio gateway,以将服务暴露至服务网格之外。