让我们在Kubernetes中使用cert-manager加密SSL证书



在本文中,我将讨论如何使用附加的cert-manager自动化(不仅限于)Kubernetes中的Let's Encrypt的证书排序和更新(不仅如此)。但是,我将首先简要介绍问题的实质。

一点教育计划


HTTP协议是上世纪90年代初期开发的,它紧密地进入了我们的日常生活,很难想象至少一天没有使用它。但是,就其本身而言,当在用户和Web服务器之间交换信息时,它甚至不提供最低级别的安全性。 HTTPS(“ S”-安全)可助您一臂之力:通过使用SSL / TLS中传输的数据打包,该协议已证明可以保护信息免遭拦截,并得到业界的积极推广。

例如,Google 坚持 2014年“无处不在的HTTPS”位置甚至降低了网站的优先级,而没有在搜索结果中显示它。这种“宣传”也不会绕过普通消费者:现代浏览器会警告其用户有关所访问站点的SSL证书的存在和正确性。





个人站点证书的费用从几十美元开始。并非总是购买它是合理且适当的。幸运的是,自2015年底以来,我们提供了免费的替代方案,形式为Let's Encrypt(LE)证书。这个非营利项目是由Mozilla爱好者创建的,目的是通过加密覆盖大多数网站。

证书颁发机构颁发域验证的证书(市场上最简单的)有效期为90天,并且多年来,可以为多个子域颁发所谓的通配符证书。

为了获得证书,该站点使用专门为Let's Encrypt创建自动证书管理环境(ACME)协议中描述的算法使用它时,将通过放置特定的HTTP代码(称为HTTP-01)或安装DNS记录(DNS-01)来通过请求进行域所有权确认-有关它们的更多信息,请参见下文。

认证经理


Cert-manager是Kubernetes的一个特殊项目,它是CA配置(证书颁发机构)和证书直接订购的一组CustomResourceDefinitions(因此,对K8s最低支持版本限制 -v1.12)。在集群中安装CRD是微不足道,归结到使用一个YAML文件:

kubectl create ns cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.13.0/cert-manager.yaml

,还用头盔安装的可能)

要启动顺序过程中,证书颁发机构(CA)的资源必须在集群中声明:Issuer或者ClusterIssuer, -这是用来签署CSR(证书签发请求)。第一种资源和第二种资源的区别在于范围:

  • Issuer 可以在同一个名称空间中使用
  • ClusterIssuer 是全局群集对象。

与证书经理一起练习


1号 自签名证书


让我们从最简单的情况开始-订购自签名证书。例如,对于开发人员的动态测试环境或在使用终止SSL流量的外部平衡器的情况下,此选项非常常见。

该资源Issuer将如下所示:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}

为了颁发证书,有必要描述资源Certificate,指示如何颁发资源(请参阅以下部分issuerRef)以及私钥(字段secretName)所在的位置。之后,Ingress将需要引用此密钥(请参阅tlsc 部分spec):

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: selfsigned-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: selfsigned
  commonName: "yet-another.website"
  dnsNames:
  - "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

将这些资源添加到群集后几秒钟,将颁发证书。您可以在命令的输出中看到相应的报告:

kubectl -n app describe  certificate selfsigned-crt
...
  Normal  GeneratedKey  5s    cert-manager  Generated a new private key
  Normal  Requested     5s    cert-manager  Created new CertificateRequest resource "selfsigned-crt-4198958557"
  Normal  Issued        5s    cert-manager  Certificate issued successfully

如果您查看秘密资源本身,那么它包含:

  • 私钥tls.key
  • 根证书ca.crt
  • 我们的自签名证书tls.crt

openssl例如, 可以使用实用程序查看这些文件的内容,如下所示:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website

值得注意的是,在一般情况下,使用此证书颁发的证书将不会被Issuer连接的客户端信任原因很简单:它没有CA (请参阅项目网站上的注释为避免这种情况,您需要Certificate在秘密文件路径中指定其所在位置ca.crt这可以是公司的CA组织-用已用于其他服务器服务/信息系统需求的密钥来签署为Ingress颁发的证书。

2号 让我们使用HTTP验证加密证书


如前所述,要颁发LE证书,可以使用两种类型的域所有权确认:HTTP-01和DNS-01。

第一种方法(HTTP-01)启动具有单独部署的小型Web服务器,该服务器将通过链接<YOUR_DOMAIN > / 发送到Internet 因此,此方法意味着可以从端口80外部访问Ingress,并且将域的DNS记录解析为公共IP地址。

验证已颁发证书(DNS-01)的第二个选项来自从拥有API到托管域记录的DNS服务器。发行者使用指示的令牌在域上创建TXT记录,然后ACME服务器在确认期间将其接收。正式受支持的DNS提供商包括CloudFlare,AWS Route53,Google CloudDNS和其他提供商,包括其自己的实现(acme-dns)。

注意:“让我们加密” 对ACME服务器的请求有相当严格的限制。为了避免长时间使用,建议使用letsencrypt-staging证书类型进行调试(区别仅在于ACME服务器)。

因此,我们描述资源:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - yet-another.website

请注意,登台服务器地址指定servery acme(c Issuer稍后将它替换为战斗。 应用此配置,我们跟踪整个订单路径:



  1. 创作Certificate催生了新资源CertificateRequest

    kubectl -n app describe certificate le-crt
    ...
    Created new CertificateRequest resource "le-crt-1127528680"
  2. 在他的描述中-创作的标记Order

    kubectl -n app describe certificaterequests le-crt-1127528680
    Created Order resource app/le-crt-1127528680-1805948596
  3. Order描述了测试通过的参数以及当前的状态。此类验证由资源执行Challenge

    kubectl -n app describe order le-crt-1127528680-1805948596
    Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
  4. 最后,此资源的详细信息包含有关扫描本身状态的信息:

    kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
    ...
      Reason:      Successfully authorized domain                                                                                                                                                                      
    ...
      Normal  Started         2m45s  cert-manager  Challenge scheduled for processing
      Normal  Presented       2m45s  cert-manager  Presented challenge using http-01 challenge mechanism
      Normal  DomainVerified  2m22s  cert-manager  Domain "yet-another.website" verified with "http-01" validation

如果满足所有条件(即,可以从外部访问该域,则LE没有禁令...)-在不到一分钟的时间内,将颁发证书。如果成功,describe certificate le-crt记录将出现在输出中Certificate issued successfully

现在,您可以安全地更改服务器地址以进行战斗(https://acme-v02.api.letsencrypt.org/directory)并重新排序已签名not Fake LE Intermediate X1而是的真实证书Let's Encrypt Authority X3

为此,您首先需要删除资源Certificate:否则,不会激活任何订购过程,因为证书已经存在并且相关。删除机密将导致其立即返回并显示以下消息describe certificate

  Normal  PrivateKeyLost  44s                   cert-manager  Lost private key for CertificateRequest "le-crt-613810377", deleting old resource

仍需对Issuer上述已经使用过的“战斗”宣言Certificate(它没有改变):

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx

收到消息后Certificate issued successfullydescribe检查它:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

3号 带DNS验证的通配符LE


通过将证书立即写到站点的所有子域,然后使用DNS检查(通过CloudFlare),我们将使任务复杂化。

首先,在CloudFlare控制面板中获取令牌以通过API工作:

  1. 配置文件→API令牌→创建令牌。
  2. 设置权限如下:
    • 权限:
      • 区域-DNS-编辑
      • 区域-区域-读取
    • 区域资源:
      • 包括-所有区域
  3. 复制保存后收到的令牌(例如:)y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M

创建一个将存储此令牌的密钥,并在发行者中引用它:

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token
type: Opaque
stringData:
  api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - "yet-another.website"
  - "*.yet-another.website"

(如果您正在尝试,请不要忘记使用暂存!)

让我们完成确认域所有权的过程:

kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
  State:       pending
Events:
  Type    Reason     Age   From          Message
  ----    ------     ----  ----          -------
  Normal  Started    54s   cert-manager  Challenge scheduled for processing
  Normal  Presented  53s   cert-manager  Presented challenge using dns-01 challenge mechanism

TXT记录将出现在面板中:



...,不久后状态将变为:

Domain "yet-another.website" verified with "dns-01" validation

确保证书对任何子域均有效:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
          DNS:*.yet-another.website, DNS:yet-another.website

通常,通过DNS进行验证不会很快进行,因为大多数DNS供应商都有一个数据更新周期,该数据更新周期显示从DNS记录更改到所有供应商DNS服务器的实际更新之间经过了多少时间。但是,ACME标准还提供了两个验证选项的组合,可用于加快接收主域证书的速度。在这种情况下,说明Issuer如下:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - selector:
        dnsNames:
        - "*.yet-another.website"
      dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token            
    - selector:
        dnsNames:
        - "yet-another.website"
      http01:
        ingress:
          class: nginx

如果应用此配置,将创建两个资源Challenge

kubectl -n app describe orders le-crt-613810377-1285319347
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"                 
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"

4号 使用入口特殊注释


除了在cert-manager中创建证书的直接路径外,还可以使用一个名为ingress-shim的组件,而不必显式创建资源Certificate。想法是,借助特殊的Ingress注释,将使用其中指示的证书自动对证书进行排序Issuer。结果大致是以下Ingress资源:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

为了进行正确的操作,此处仅存在Issuer就足够了,也就是说,创建一个更少的实体。

此外,还有一个过时的注释kube-lego - kubernetes.io/tls-acme: "true",- Issuer在使用Helm参数(或在管理器容器启动选项中)安装cert-manager时,需要默认作业

由于用于订购SSL证书的方法的不透明性(同时解决了出现的各种问题),我们公司不使用这些选项,也无法提供建议,但仍决定在本文中进行介绍以获取更完整的信息。

而不是结论


通过CRD的简单操作,我们从Let's Encrypt项目中了解了如何为Kubernetes集群中Ingress的一部分启动的域站点编写可自动更新,自签名和免费的SSL证书。

本文提供了解决我们实践中最常见问题的示例。但是,证书管理器功能不限于上述功能。在实用程序网站上,您可以找到使用其他服务的示例-例如,与Vault捆绑在一起使用外部发行中心(发行方)的示例

聚苯乙烯


另请参阅我们的博客:


All Articles