前言Kubernetes集群默认安装的证书是自签发证书,浏览器访问会发出安全提醒。
本文记录了利用腾讯云dnspod、cert-manager、let'sencrytp等开源组件,实现泛域名证书的自动生成、续期管理,为现有kubernetes集群应用启动HTTPS,提高系统安全性。
架构在Kubernetes集群中使用HTTPS协议,需要一个证书管理器、一个证书自动签发服务cert-manager是一个云原生证书管理开源项目,用于在Kubernetes集群中提供HTTPS证书并自动续期,支持Let’sEncrypt,HashiCorpVault这些免费证书的签发。
在Kubernetes集群中,我们可以通过KubernetesIngress和Let’sEncrypt实现外部服务的自动化HTTPS。
cert-manager架构图Issuers/ClusterIssuers:定义使用什么证书颁发机构(CA)来去颁发证书,Issuers和ClusterIssuers区别是:issuers是一个名称空间级别的资源,只能用来签发自己所在namespace下的证书,ClusterIssuer是个集群级别的资源可以签发任意namespace下的证书。
Certificate:定义所需的X.509证书,该证书将更新并保持最新。
Certificate是一个命名空间资源,当Certificate被创建时,它会去创建相应的CertificateRequest资源来去申请证书。
安装证书管理器安装证书管理器比较简单,直接执行以下脚本就可以了。
kubectlcreatenscert-manager
helmuninstallcert-manager-ncert-manager
helminstallcert-managerjetstack/cert-manager
-ncert-manager
–versionv1.8.0
–setinstallCRDs=true
–setprometheus.enabled=false
–set'extraArgs={–dns01-recursive-nameservers-only,–dns01-recursive-nameservers=119.29.29.29:53,8.8.8.8:53}'配置证书管理器选择证书颁发者cert-manager支持以下几种证书颁发者SelfSignedCAVaultVenafiExternalACME我们选择使用ACME来颁发证书。
选择证书校验方式常用的校验方式有HTTP-01、DNS-01。
DNS-01校验原理DNS-01的校验原理是利用DNS提供商的APIKey拿到DNS控制权限,在Let’sEncrypt为ACME客户端提供令牌后,ACME客户端(cert-manager)将创建从该令牌和我的帐户密钥派生的TXT记录,并将该记录放在_acme-challenge。
然后Let’sEncrypt将向DNS系统查询该记录,如果找到匹配项,就可以颁发证书。
此方法支持泛域名证书。
HTTP-01校验原理HTTP-01的校验原理是给域名指向的HTTP服务增加一个临时location,Let’sEncrypt会发送http请求到http:///.well-known/acme-challenge/,参数中YOUR_DOMAIN就是被校验的域名,TOKEN是ACME协议的客户端负责放置的文件,ACME客户端就是cert-manager,它通过修改或创建Ingress规则来增加这个临时校验路径并指向提供TOKEN的服务。
Let’sEncrypt会对比TOKEN是否符合预期,校验成功后就会颁发证书。
此方法仅适用于给使用Ingress暴露流量的服务颁发证书,不支持泛域名证书。
优劣对比HTTP-01的校验方式的优点是:配置简单通用,不管使用哪个DNS提供商都可以使用相同的配置方法;缺点是:需要依赖Ingress,如果服务不是通过Ingress暴露的就不适用,而且不支持泛域名证书。
DNS-01的校验方式的优点是没有HTTP-01校验方式缺点,不依赖Ingress,也支持泛域名;缺点就是不同DNS提供商的配置方式不一样,而且只有cert-manager支持的DNS提供商才可以选择这种方式。
Cert-manager支持使用外部webhook的接入DNS提供商,正好公司使用腾讯云的DNSPOD属于支持的行列。
我们可以选择DNS-01。
配置HTTP-01配置示例这个配置示例仅供参考,使用这种方式,有多少的ingress服务,就需要申请多少张证书,比较麻烦,但是配置较为简单,不依赖DNS服务商。
1.创建CA群集证书颁发者证书管理器需要Issuer或ClusterIssuer资源,才能颁发证书。
这两种Kubernetes资源的功能完全相同,区别在于Issuer适用于单一命名空间,而ClusterIssuer适用于所有命名空间。
ClusterIssuer.yamlapiVersion:cert-manager.io/v1
kind:ClusterIssuer
Metadata:
name:letsencrypt
spec:
acme:
email:xxxxx@xyz.cn
server:https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name:issuer-account-key
solvers:
-http01:
ingress:
class:nginx说明:metadata.name:是我们创建的签发机构的名称,后面我们创建证书的时候会引用它spec.acme.email:是你自己的邮箱,证书快过期的时候会有邮件提醒,不过cert-manager会利用acme协议自动给我们重新颁发证书来续期spec.acme.server:是acme协议的服务端,我们使用Let’sEncryptspec.acme.privateKeySecretRef:指示此签发机构的私钥将要存储到哪个Secret对象中spec.acme.solvers:这里指示签发机构校验方式,有http01和dns01两种,该字段下配置的class和name只能同时存在一个,class指定使用的ingressclass名称,name比较少用,通常用于kubernetes的ingress。
kubectlapply-fClusterIssuer.yaml-ncert-manager2.创建ingress服务ingreess-wikijs.yamlapiVersion:networking.k8s.io/v1
kind:Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer:letsencrypt
nginx.ingress.kubernetes.io/proxy-body-size:"0"
name:ingress-wikijs
spec:
ingressClassName:nginx
rules:
-host:wiki.xyz.cn
http:
paths:
-backend:
service:
name:wikijs
port:
number:3000
path:/
pathType:Prefix
TLS:
-hosts:
-wikijs.xyz.cn
secretName:ingress-wikijs-tls注意:在annotations里设置cert-manager.io/cluster-issuer为签名创建的集群证书颁发者letsencrypt使用yaml文件创建ingress后,就可以使用该ingress对外提供https服务了。
#创建ingress
kubectlapply-fingress-wikijs.yaml-ninfra
#查看证书是否自动创建成功
kubectl-ninfragetcertificateDNS01配置示例使用这种方式需要DNS服务商支持通过API创建DNS记录,正好我的DNS服务商是腾讯云dnspod支持,因此在我们的及群里,最终采用了这种方式。
这个方式的配置会比较麻烦,踩了很久的坑,主要是因为我的集群启用了本地DNS服务器,默认cert-manager会通过本地DNS服务器去验证通过API创建的DNStxt记录,会一直检查不到新增的txt记录,造成在challenge阶段就一直pendding。
解决方案附后。
1.在dnspod创建APIID和APIToken参考腾讯云官方文档(https://support.dnspod.cn/Kb/showarticle/tsid/227/)记录下创建的APIID和APIToken。
<APIID>
<APIToken>2.安装cert-manager-webhook-dnspod使用helm安装roc/cert-manager-webhook-dnspod。
helmrepoaddrochttps://charts.imroc.cc
helmuninstallcert-manager-webhook-dnspod-ncert-manager
helminstallcert-manager-webhook-dnspodroc/cert-manager-webhook-dnspod
-ncert-manager
–setclusterIssuer.secretId=<APIID>
–setclusterIssuer.secretKey=<APITOKEN>
–setclusterIssuer.email=xxxx@xyz.cn3.创建泛域名证书xyz-crt.yamlapiVersion:cert-manager.io/v1
kind:Certificate
metadata:
name:xyz-crt
spec:
secretName:xyz-crt
issuerRef:
name:dnspod
kind:ClusterIssuer
group:cert-manager.io
dnsNames:
-"*.xyz.cn"创建集群证书颁发者kapply-fxyz-crt.yaml-ninfra4.验证证书查看证书是否创建成功[root@ks-masterinfra]#kubectlgetCertificate-ncert-manager
NAMEREADYSECRETAGE
cert-manager-webhook-dnspod-caTruecert-manager-webhook-dnspod-ca18m
cert-manager-webhook-dnspod-webhook-tlsTruecert-manager-webhook-dnspod-webhook-tls18m
xyz-crtTruexyz-crt3m12s以上可以看出xyz-crt已经创建成功,READY状态也是True。
查看证书对应的域名[root@ks-masterinfra]#kubectldescribeCertificatexyz-crt-ncert-manager
Name:xyz-crt
Namespace:cert-manager
Labels:<none>
Annotations:<none>
APIVersion:cert-manager.io/v1
Kind:Certificate
Metadata:
CreationTimestamp:2022-05-07T14:19:07Z
Generation:1
ManagedFields:
APIVersion:cert-manager.io/v1
FieldsType:FieldsV1
fieldsV1:
f:status:
.:
f:conditions:
Manager:cert-manager-certificates-trigger
Operation:Update
Subresource:status
Time:2022-05-07T14:19:07Z
APIVersion:cert-manager.io/v1
FieldsType:FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:dnsNames:
f:issuerRef:
.:
f:group:
f:kind:
f:name:
f:secretName:
Manager:kubectl-client-side-apply
Operation:Update
Time:2022-05-07T14:19:07Z
APIVersion:cert-manager.io/v1
FieldsType:FieldsV1
fieldsV1:
f:status:
f:revision:
Manager:cert-manager-certificates-issuing
Operation:Update
Subresource:status
Time:2022-05-07T14:19:14Z
APIVersion:cert-manager.io/v1
FieldsType:FieldsV1
fieldsV1:
f:status:
f:conditions:
k:{"type":"Ready"}:
.:
f:lastTransitionTime:
f:message:
f:observedGeneration:
f:reason:
f:status:
f:type:
f:notAfter:
f:notBefore:
f:renewalTime:
Manager:cert-manager-certificates-readiness
Operation:Update
Subresource:status
Time:2022-05-07T14:19:14Z
ResourceVersion:4784901
UID:f2c9be82-f4cb-4243-b2c3-19f64242cc91
Spec:
DnsNames:
*.xyz.cn
IssuerRef:
Group:cert-manager.io
Kind:ClusterIssuer
Name:dnspod
SecretName:xyz-crt
Status:
Conditions:
LastTransitionTime:2022-05-07T14:19:14Z
Message:Certificateisuptodateandhasnotexpired
ObservedGeneration:1
Reason:Ready
Status:True
Type:Ready
NotAfter:2022-08-05T13:19:11Z
NotBefore:2022-05-07T13:19:12Z
RenewalTime:2022-07-06T13:19:11Z
Revision:1
Events:
TypeReasonAgeFromMessage
————————-
NormalIssuing4m35scert-manager-certificates-triggerIssuingcertificateasSecretdoesnotexist
NormalGenerated4m35scert-manager-certificates-key-managerStorednewprivatekeyintemporarySecretresource"xyz-crt-4ml59"
NormalRequested4m35scert-manager-certificates-request-managerCreatednewCertificateRequestresource"xyz-crt-r76wp"
NormalIssuing4m28scert-manager-certificates-issuingThecertificatehasbeensuccessfullyissued
[root@ks-masterinfra]#从Certificate的描述信息可以看到,这个证书是对应所有*.xyz.cn的泛域名。
所以可以直接用在集群的任何地方。
查看证书内容[root@ks-masterinfra]#kubectldescribesecretxyz-crt-ncert-manager
Name:xyz-crt
Namespace:cert-manager
Labels:<none>
Annotations:cert-manager.io/alt-names:*.xyz.cn
cert-manager.io/certificate-name:xyz-crt
cert-manager.io/common-name:*.xyz.cn
cert-manager.io/ip-sans:
cert-manager.io/issuer-group:cert-manager.io
cert-manager.io/issuer-kind:ClusterIssuer
cert-manager.io/issuer-name:dnspod
cert-manager.io/uri-sans:
Type:kubernetes.io/tls
Data
====
tls.crt:5587bytes
tls.key:1675bytesTLS证书保存在cert-manager命名空间里的xyz-crtsecret。
可以供所有*.xyz.cn的服务使用。
其他这个过程中,最大的坑是,我的集群使用了自建的DNS服务器,默认cert-manager会使用这个集群的自建DNSSERVER进行证书发行的验证,虽然通过调用dnspod的webook在腾讯云DNS服务器上创建的_acme-challenge握手数据,但是在我的自建DNS里是查不到的,所以会一直卡pending状态,[root@ks-masterinfra]#kubectlgetchallenge-A
NAMESPACENAMESTATEDOMAINAGE
cert-managerxyz-crt-f9kp6-381578565-136350475pendingxyz.cn24s查看原因是:WaitingforDNS-01challengepropagation:DNSrecordfor"xyz.cn"notyet[root@ks-masterinfra]#kcmdescribechallengexyz-crt-f9kp6-381578565-136350475
Name:xyz-crt-f9kp6-381578565-136350475
Namespace:cert-manager
Labels:<none>
Annotations:<none>
APIVersion:acme.cert-manager.io/v1
Kind:Challenge
—
中间略
—
Status:
Presented:true
Processing:true
Reason:WaitingforDNS-01challengepropagation:DNSrecordfor"xyz.cn"notyetpropagated
State:pending
Events:
TypeReasonAgeFromMessage
————————-
NormalStarted41scert-manager-challengesChallengescheduledforprocessing
NormalPresented39scert-manager-challengesPresentedchallengeusingDNS-01challengemechanism查了很多资料,在官网上找到解决方案。
办法是让cert-manager强制使用指定的DNS服务器进行握手验证。
我是用的是helm安装cert-manager,所以添加一下set参数–set'extraArgs={–dns01-recursive-nameservers-only,–dns01-recursive-nameservers=119.29.29.29:53,8.8.8.8:53}'参考文档:https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check使用TLS证书配置ingress把我前连天部署的wiki.js拿出来启用http,测试效果。
设置ingresskind:Ingress
apiVersion:networking.k8s.io/v1
metadata:
name:wikijs
namespace:infra
annotations:
nginx.ingress.kubernetes.io/proxy-body-size:'0'
spec:
ingressClassName:nginx
tls:
-hosts:
-wiki.xyz.cn
secretName:xyz-crt
rules:
-host:wiki.xyz.cn
http:
paths:
-path:/
pathType:ImplementationSpecific
backend:
service:
name:wikijs
port:
number:3000测试以上配置完成后,就可以使用https来访问新的wiki.js服务了。
C:UsersAdministrator>curl-Ihttps://wiki.xyz.cn
HTTP/1.1302Found
Date:Sat,07May202214:52:39GMT
Content-Type:text/plain;charset=utf-8
Content-Length:28
Connection:keep-alive
X-Frame-Options:deny
X-XSS-Protection:1;mode=block
X-Content-Type-Options:nosniff
X-UA-Compatible:IE=edge
Referrer-Policy:same-origin
Content-Language:zh
Set-Cookie:loginRedirect=/;Max-Age=900;Path=/;Expires=Sat,07May202215:07:39GMT
Location:/login
Vary:Accept,Accept-Encoding
Strict-Transport-Security:max-age=15724800;includeSubDomains如上所示,就是成功启动了https。
参考https://artifacthub.io/packages/helm/cert-manager/cert-managerhttps://www.1nth.com/post/k8s-cert-manager/https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check