在本文中,我将讨论如何使用附加的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将需要引用此密钥(请参阅tls
c 部分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
请注意,登台服务器的地址指定为server
y acme
(c Issuer
)。稍后将它替换为战斗。
应用此配置,我们跟踪整个订单路径:- 创作
Certificate
催生了新资源CertificateRequest
:
kubectl -n app describe certificate le-crt
...
Created new CertificateRequest resource "le-crt-1127528680"
- 在他的描述中-创作的标记
Order
:
kubectl -n app describe certificaterequests le-crt-1127528680
…
Created Order resource app/le-crt-1127528680-1805948596
- 它
Order
描述了测试通过的参数以及当前的状态。此类验证由资源执行Challenge
:
kubectl -n app describe order le-crt-1127528680-1805948596
…
Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
- 最后,此资源的详细信息包含有关扫描本身状态的信息:
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 successfully
,describe
检查它: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工作:- 配置文件→API令牌→创建令牌。
- 设置权限如下:
- 复制保存后收到的令牌(例如:)
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:
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捆绑在一起或使用外部发行中心(发行方)的示例。聚苯乙烯
另请参阅我们的博客: