利用Let's Encrypt 为Istio(Envoy)添加TLS 支持

Posted ServiceMesher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用Let's Encrypt 为Istio(Envoy)添加TLS 支持相关的知识,希望对你有一定的参考价值。


原文链接:https://medium.com/@prune998/istio-envoy-cert-manager-lets-encrypt-for-tls-14b6a098f289

译者:殷龙飞

校对:宋净超

更新

感谢 Laurent Demailly 的评论,这里有一些更新。这篇文章已经得到了更新:

  • 现在有一个 Cert-Manager 官方 Helm chart

  • Istio Ingress 也支持基于 HTTP/2 的 GRPC

Istio

Istio 是管理微服务世界中数据流的一种新方式。事实上,这对我来说更是如此。人们不停的谈论微服务与单体应用,说微服务更好开发,易于维护,部署更快。。。呃,他们是对的,但微服务不应该仅仅是小应用程序之间互相通信。微服务应该考虑沉淀为你的基础设施的这种方式。考虑如何决定您的“简单”应用程序公开指标和日志的方式,考虑您如何跟踪状态,考虑如何控制服务之间的流程以及如何管理错误,这些问题应该是做微服务应该考虑的。

那么 Istio 能够在这个微服务世界中增加什么?

Istio 是一个服务网格的实现!

什么?服务网格?我们已经有了 Kubernetes API,我们需要“网格”吗?

那么,是的,你需要服务网格。我不会解释使用它的所有好处,你会在网上找到足够的文档。但是用一句话来说,服务网格就是将您所有的服务提供给其他服务的技术。事实上,它还强制执行所有“微服务”最佳实践,例如添加流量和错误指标,添加对 OpenTracing( Zipkin 和Jaegger)的支持,允许控制重试,金丝雀部署。。。阅读 Istio doc !

所以,回到本话题...

必要条件

  • 建议运行在 Kubernetes1.7 及以上的集群版本

  • 一个或多个 DNS 域名

  • 让 Istio 利用Ingress Controller 在你的集群中工作

  • 将上面的 DNS 域名配置为指向 Istio Ingress IP

SSL

SSL 是安全的(很好),但它通常是软件中实现的最后一件事。为什么?之前它实现起来是“很困难的”,但我现在看不出任何理由。Let's Encrypt 创建一个新的范例,它的 DAMN 很容易使用 API 调用创建 Valide SSL 证书(协议被称为ACME ...)。它为您提供 3 种验证您是域名所有者的方法。使用 DNS,使用 HTTP 或第三种解决方案的“秘密令牌”不再可用,因为它证明是不安全的。 因此,您可以使用 Let's Encrypt 提供给您的特殊 TXT 记录设置您的 DNS,或者将其放入 Web 根路径(如 /.well-known/acme-challenge/xxx)中,然后让我们的加密验证它。这真的很简单,但差不多只能这样。

一些开发者决定直接在应用程序内部实现 ACME 协议。这是来自 Traefik 的人的决定。Caddy 也做了一些类似的“插件”。这很酷,因为您只需定义虚拟主机,应用程序负责收集和更新证书。

可悲的是,Istio(和底层的Envoy代理)没有。这就是这篇博文的要点!

CERT-Manager

许多人认识到,如果不是所有软件都可以实现 ACME 协议,我们仍然需要一个工具来管理(如请求,更新,废弃)SSL 证书。这就是为什么 LEGO 成立的原因。然后 Kubernetes 的 Kube-LEGO ,然后......并且最终,他们几乎都同意将所有内容放入 Cert-Manager !

Cert-Manager 附带 helm chart,所以很容易部署,只需按照文档执行命令即可,就像下面介绍的这样:

更新

现在有一个 Cert-Manager 的官方 Helm 图表,你不需要 git clone ,只需要做 helm install 。

 
   
   
 
  1. git clone https://github.com/jetstack/cert-manager

  2. cd cert-manager

  3. # check out the latest release tag to ensure we use a supported version of cert-manager

  4. git checkout v0.2.3

  5. helm install \

  6. --name cert-manager \

  7. --namespace kube-system \

  8. --set ingressShim.extraArgs='{--default-issuer-name=letsencrypt-prod,--default-issuer-kind=ClusterIssuer}' \

  9. contrib/charts/cert-manager

该命令将启动 kube-system 命名空间中的 Cert-Manager pod。

我使用这一行配置 --default-issuer-kind=ClusterIssuer 所以我只能创建一次我的 Issuer。

什么是 issuer?

以下是它的工作原理:

  • 你创建一个 Issuer 配置,它将告诉 Cert-Manager 如何使用 ACME API(你通常只有2个,staging 和 prod )

  • 您创建一个证书定义,告诉哪些域需要 SSL

  • Cert-Manager 为您申请证书

所以,我们来创建 Issuer。在创建 ClusterIssuers 时,我不关心特定的命名空间:

 
   
   
 
  1. apiVersion: certmanager.k8s.io/v1alpha1  

  2. kind: ClusterIssuer  

  3. metadata:  

  4. name: letsencrypt-prod  

  5. namespace: kube-system  

  6. spec:  

  7. acme:

  8.     #The ACME server URL  

  9.     srver: https://acme-v01.api.letsencrypt.org/directory

  10.     email: me@domain.com

  11.     #用于存储ACME帐户私钥的秘密名称  

  12.     privateKeySecretRef:  

  13.       name: letsencrypt-prod  

  14.     #启用HTTP-01质询提供程序  

  15.     http01: {}  

  16. ---  

  17. apiVersion: certmanager.k8s.io/v1alpha1  

  18. kind: ClusterIssuer  

  19. metadata:  

  20. name: letsencrypt -staging  

  21. namespace: kube-system  

  22. spec:  

  23. acme :

  24.     # ACME的服务器URL  

  25.     server: https://acme-staging.api.letsencrypt.org/directory

  26.     email: staging + me@domain.com  

  27.     # 用于存储ACME帐户私钥的密钥的 名称  

  28.     privateKeySecretRef:  

  29.    name: letsencrypt-staging  

  30.     # 启用HTTP-01质询提供程序  

  31.     http01: {}

然后

kubectl apply-f certificate-issuer.yml

现在你应该有一个有效的 Cert-Manager 。您需要为您的域/服务创建配置,以便 Istio Ingress 可以选择正确的证书。

Istio Ingress

Ingress 是您公开服务的前端 Web 代理(这是你的优势......我说 WEB 代理,因为它现在只支持 HTTP/HTTPS)。但让我们假设你知道关于 Ingress 的一切。

更新

这不是一个真正的更新,而是一个更精确的描述,Ingress 也支持 GRPC,当然这是 HTTP/2。

Ingress 的神奇之处在于它在 Kubernetes API 中的实现。您创建一个 Ingress Manifest,并将您的所有流量引导至正确的 Pod!告诉你这种方式就是神奇的魔法(因为你并不知道它如何引导的流量) !

很好,在这种情况下,这就是令人神奇的黑魔法!

例如,Traefik Ingress 绑定端口 80 和 443,管理证书,因此您为 www.mydomain.com 创建入口,并且它正常工作,因为它正在做所有事情。

对于 Istio,当您使用 Cert-Manager 时,还有一些步骤。要快点,在这里他们(截至 2018/01,它可能很快就会改变):

  • 为域 www.mydomain.com 创建证书请求

  • Cert-Manager 将选择这个定义并创建一个 pod,它实际上是一个可以回答 ACME 问题的 Web 服务器(Ingress-Shim) 它还将创建一个服务和一个 HTTP Ingress,以便它可以通过 Lets Encrypt 服务器

  • 以前的观点不适用于您使用 Istio Ingress,因此您必须删除 Service 和 Ingress

  • 创建指向 Pod 的自己的服务

  • 创建您自己的 Istio Ingress,以便可以访问 pod

听起来很疯狂? 那么,现在呢。它甚至是恶梦:

在 Istio 中使用 Cert-Manager 时,您只能拥有一个外部服务证书!所以你必须添加所有公共 DNS 名称到这个证书!

所以我们来实现它...

证书

把这个清单放在一个像 certificate-istio.yml 这样的文件中 :

 
   
   
 
  1. apiVersion: certmanager.k8s.io/v1alpha1  

  2. kind: Certificate  

  3. meteadata:  

  4. name: istio-ingress-certs  

  5. namespace: istio-system  

  6. spec:  

  7. secretName: istio-ingress-certs  

  8. issuerRef:  

  9.     name: letsencrypt-staging  

  10.     kind: ClusterIssuer  

  11. commonName: www.mydomain.com  

  12. dnsNames:  

  13. - www.mydomain.com  

  14. - mobile.mydomain.com  

  15. acme:  

  16.   config:  

  17.   - http01:  

  18.        ingressClass: none

  19.     domains:  

  20.      - www.mydomain.com  

  21.       - mobile.mydomain.com

我们在这里看到的是:

  • 我们想要一个证书

  • 它将支持2个域名 www.mydomain.com 和 mobile.mydomain.com

  • 此证书请求与 Istio Ingress(istio-system)位于同一个命名空间中,

  • 它将使用 HTTP-01 回答 ACME 的问题

  • Istio Ingress(Envoy代理)期望该证书将被复制到一个名为 istio-ingress-certs 的 K8s Secret 中(这是超级重要,最好不要修改这个名字)。

然后 :

kubectl apply-f certificate-istio.yml

完成之后,您通过 cert-manager pod 将可以看到 Istio Ingress 的日志情况,例如:

 
   
   
 
  1. istio-ingress-7f8468bb7b-pxl94 istio-ingress [2018-01-23T21:01:53.341Z] "GET /.well-known/acme-challenge/xxxxxxx HTTP/1.1" 503 UH 0 19 0 - "10.20.5.1" "Go-http-client/1.1" "xxx" "www.domain.com" "-"

  2. istio-ingress-7f8468bb7b-pxl94 istio-ingress [2018-01-23T21:01:58.287Z] "GET /.well-known/acme-challenge/xxxxxx HTTP/1.1" 503 UH 0 19 0 - "10.20.5.1" "Go-http-client/1.1" "xxxx" "mobile.domain.com" "-"

这是因为 Let's Encrypt 服务器正在轮询验证令牌,并且您的设置尚未运行。截至目前你的设置看起来像这样:


现在是删除由 Cert-Manager 创建的不需要的东西的时候了。使用您最擅长的 K8s 工具,如仪表板或 kubectl,并从 istio-system 命名空间中删除 Service 和 Ingress。它们将被命名为 cm-istio-ingress-certs-xxxx。 如果您的证书申请中有许多域名,你应该删除多余的域名。

另外,不要删 pod !(如果有错误,它们将被重新创建)

(作为提醒: kubectl-n istio-systemdeletecm-istio-ingress-certs-xxxx

服务

既然您的设置很干净,您可以继续并重新创建所需的 Service 和 ingress 。

您需要尽可能多的 Service ,因为您拥有不同的域名。在我们的例子中,2.这是清单:

 
   
   
 
  1. apiVersion: v1

  2. kind: Service

  3. metadata:

  4.  name: cert-manager-ingress-www

  5.  namespace: istio-system

  6.  annotations:

  7.    auth.istio.io/8089: NONE

  8. spec:

  9.  ports:

  10.  - port: 8089

  11.    name: http-certingr

  12.  selector:

  13.    certmanager.k8s.io/domain: www.mydomain.com

  14. ---

  15. apiVersion: v1

  16. kind: Service

  17. metadata:

  18.  name: cert-manager-ingress-mobile

  19.  namespace: istio-system

  20.  annotations:

  21.    auth.istio.io/8089: NONE

  22. spec:

  23.  ports:

  24.  - port: 8089

  25.    name: http-certingr

  26.  selector:

  27.    certmanager.k8s.io/domain: mobile.mydomain.com

然后

kubectl apply-f certificate-services.yml

然后你可以检查你的 Service。每个 Service 都应该有一个指定的目标 pod。

请注意,Service 名称无关紧要。这取决于你给出一个特定的名称,所以你不会混淆你所有的域名。

Ingress

现在是创建 Ingress 的时候了,因此您的 “ ACME Token Pods ” 可以从外部访问。

 
   
   
 
  1. apiVersion: extensions/v1beta1

  2. kind: Ingress

  3. metadata:

  4.  annotations:

  5.    kubernetes.io/ingress.class: istio

  6.    certmanager.k8s.io/acme-challenge-type: http01

  7.    certmanager.k8s.io/cluster-issuer: letsencrypt-staging

  8.  name: istio-ingress-certs-mgr

  9.  namespace: istio-system

  10. spec:

  11.  rules:

  12.  - http:

  13.      paths:

  14.      - path: /.well-known/acme-challenge/.*

  15.        backend:

  16.          serviceName: cert-manager-ingress-www

  17.          servicePort: http-certingr

  18.    host: www.mydomain.com

  19.  - http:

  20.      paths:

  21.      - path: /.well-known/acme-challenge/.*

  22.        backend:

  23.          serviceName: cert-manager-ingress-mobile

  24.          servicePort: http-certingr

  25.    host: mobile.mydomain.com

再次,我们在这里需要注意一些事情:

  • 证书, Service 和 Ingress 需要在同一个命名空间中 ingress class 是Istio(显然)

  • 我们正在使用 staging Issuer(记住我们第一步创建的 Issuer )。 您必须根据创建的 IssuerClusterIssuer使用正确的 annotation。文档位于 Ingress-Shim 项目中

  • 我们必须为每个域创建一个 HTTP 规则

  • backend/srvice 必须我们在上一步中创建的服务,以及域名匹配,所以:用 www.mydomain.com →serviceName cert-manager-ingress-www→pod cm-istio-ingress-certs-xxx,其中label certmanager.k8s.io/domain = www.mydomain.com

再次:

kubectl apply-f certificate-ingress.yml

就是这样!

检查 Istio-Ingress 日志,您应该看到几个“GET /.well-known/acme-challenge/xxx HTTP / 1.1”200

示例应用程序

我使用了一个示例应用程序来验证我的设置正在工作:

 
   
   
 
  1. apiVersion: v1

  2. kind: Service

  3. metadata:

  4.  name: helloworld-v1

  5.  labels:

  6.    app: helloworld

  7.    version: v1

  8. spec:

  9.  ports:

  10.  - name: http

  11.    port: 8080

  12.  selector:

  13.    app: helloworld

  14.    version: v1

  15. ---

  16. apiVersion: v1

  17. kind: Service

  18. metadata:

  19.  name: helloworld-v2

  20.  labels:

  21.    app: helloworld

  22.    version: v2

  23. spec:

  24.  ports:

  25.  - name: http

  26.    port: 8080

  27.  selector:

  28.    app: helloworld

  29.    version: v2

  30. ---

  31. apiVersion: extensions/v1beta1

  32. kind: Ingress

  33. metadata:

  34.  annotations:

  35.    kubernetes.io/ingress.class: istio

  36.    kubernetes.io/ingress.allow-http: "false"

  37.  name: istio-ingress-https

  38. spec:

  39.  tls:

  40.    - secretName: istio-ingress-certs

  41.  rules:

  42.  - http:

  43.      paths:

  44.      - path: /.*

  45.        backend:

  46.          serviceName: helloworld-v1

  47.          servicePort: 8080

  48.    host: www.mydomain.com

  49.  - http:

  50.      paths:

  51.      - path: /.*

  52.        backend:

  53.          serviceName: helloworld-v2

  54.          servicePort: 8080

  55.    host: mobile.mydomain.com

  56. ---

  57. apiVersion: extensions/v1beta1

  58. kind: Ingress

  59. metadata:

  60.  annotations:

  61.    kubernetes.io/ingress.class: istio

  62.  name: istio-ingress-http

  63. spec:

  64.  rules:

  65.  - http:

  66.      paths:

  67.      - path: /.*

  68.        backend:

  69.          serviceName: helloworld-v1

  70.          servicePort: 8080

  71.    host: www.mydomain.com

  72.  - http:

  73.      paths:

  74.      - path: /.*

  75.        backend:

  76.          serviceName: helloworld-v2

  77.          servicePort: 8080

  78.    host: mobile.mydomain.com

  79. ---

  80. apiVersion: v1

  81. kind: ReplicationController

  82. metadata:

  83.  labels:

  84.    app: helloworld

  85.    version: v1

  86.  name: helloworld-v1

  87. spec:

  88.  replicas: 1

  89.  template:

  90.    metadata:

  91.      labels:

  92.        app: helloworld

  93.        version: v1

  94.    spec:

  95.      containers:

  96.        - image: "kelseyhightower/helloworld:v1"

  97.          name: helloworld

  98.          ports:

  99.            - containerPort: 8080

  100.              name: http

  101. ---

  102. apiVersion: v1

  103. kind: ReplicationController

  104. metadata:

  105.  labels:

  106.    app: helloworld

  107.    version: v2

  108.  name: helloworld-v2

  109. spec:

  110.  replicas: 1

  111.  template:

  112.    metadata:

  113.      labels:

  114.        app: helloworld

  115.        version: v2

  116.    spec:

  117.      containers:

  118.        - image: "kelseyhightower/helloworld:v2"

  119.          name: helloworld

  120.          ports:

  121.            - containerPort: 8080

  122.              name: http

我们必须再次感谢 Kelsey Hightower 是他提供的 HelloWorld 示例应用程序

以上是关于利用Let's Encrypt 为Istio(Envoy)添加TLS 支持的主要内容,如果未能解决你的问题,请参考以下文章

免费证书申请——Let's Encrypt的申请与应用(IIS,Tomcat)

Let's Encrypt,站点加密之旅

HTTP免费升级HTTPS详细步骤 Let's Encrypt

let's encrypt申请

IIS - 自动申请部署Let's Encrypt的免费SSL证书

Let's Encrypt 证书申请及配置