作者:scwang18,主要负责技术架构,在容器云方向颇有研究。

前言

KubeSphere 集群默认安装的证书是自签发证书,浏览器访问访问会发出安全提醒。本文记录了利用 let's encrytp 泛域名证书实现 Kubernetes 集群外部服务自动证书配置和证书到期自动更新,支持 HTTPS 访问。我们还部署了证书自动分发组件,实现证书文件自动分发到其他 namespace 。

架构

在 KubeSphere 集群中使用 HTTPS 协议,需要一个证书管理器、一个证书自动签发服务

cert-manager 是一个云原生证书管理开源项目,用于在 KubeSphere 集群中提供 HTTPS 证书并自动续期,支持 Let’s Encrypt, HashiCorp Vault 这些免费证书的签发。在 KubeSphere 集群中,我们可以通过 Kubernetes Ingress 和 Let’s Encrypt 实现外部服务的自动化 HTTPS。

Issuers/ClusterIssuers:定义使用什么证书颁发机构 (CA) 来去颁发证书,Issuers 和 ClusterIssuers 区别是: issuers 是一个名称空间级别的资源,只能用来签发自己所在 namespace 下的证书,ClusterIssuer 是个集群级别的资源 可以签发任意 namespace 下的证书

Certificate:定义所需的 X.509 证书,该证书将更新并保持最新。Certificate 是一个命名空间资源,当 Certificate 被创建时,它会去创建相应的 CertificateRequest 资源来去申请证书。

安装证书管理器

安装证书管理器比较简单,直接执行以下脚本就可以了。

$ kubectl create ns cert-manager
$ helm uninstall cert-manager -n cert-manager $ helm install cert-manager jetstack/cert-manager \
-n cert-manager \
--version v1.8.0 \
--set installCRDs=true \
--set prometheus.enabled=false \
--set 'extraArgs={--dns01-recursive-nameservers-only,--dns01-recursive-nameservers=119.29.29.29:53\,8.8.8.8:53}'

选择证书颁发者

cert-manager 支持以下几种证书颁发者:

  • SelfSigned
  • CA
  • Vault
  • Venafi
  • External
  • ACME

我们选择使用 ACME 来颁发证书。

选择证书校验方式

常用的校验方式有 HTTP-01 、DNS-01 。

DNS-01 校验原理

DNS-01 的校验原理是利用 DNS 提供商的 API Key 拿到 DNS 控制权限, 在 Let’s Encrypt 为 ACME 客户端提供令牌后,ACME 客户端 (cert-manager) 将创建从该令牌和我的帐户密钥派生的 TXT 记录,并将该记录放在 _acme-challenge。 然后 Let’s Encrypt 将向 DNS 系统查询该记录,如果找到匹配项,就可以颁发证书。此方法支持泛域名证书。

HTTP-01 校验原理

HTTP-01 的校验原理是给域名指向的 HTTP 服务增加一个临时 location ,Let’s Encrypt 会发送 HTTP 请求到 http:///.well-known/acme-challenge/,参数中 YOUR_DOMAIN 就是被校验的域名,TOKEN 是 ACME 协议的客户端负责放置的文件,ACME 客户端就是 cert-manager,它通过修改或创建 Ingress 规则来增加这个临时校验路径并指向提供 TOKEN 的服务。Let’s Encrypt 会对比 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.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
email: scwang18@xxx.xxx
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’s Encrypt;
  • spec.acme.privateKeySecretRef: 指示此签发机构的私钥将要存储到哪个 Secret 对象中;
  • spec.acme.solvers: 这里指示签发机构校验方式,有 http01 和 dns01 两种,该字段下配置的 class 和 name 只能同时存在一个, class 指定使用的 Ingress class 名称, name 比较少用,通常用于 Kubernetes 的 Ingress。
$ kubectl apply -f ClusterIssuer.yaml -n cert-manager

执行成功后,会将申请的证书文件放置在 issuer-account-key 这个 Secret 中。

查看证书是否自动创建成功:

$ kubectl -n infra  get certificate

2. 在 Ingress 中使用上步申请到的 SSL 证书

# ingreess-wikijs.yaml

apiVersion: 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.xxx.xxx
http:
paths:
- backend:
service:
name: wikijs
port:
number: 3000
path: /
pathType: Prefix
tls:
- hosts:
- wikijs.xxx.xxx
secretName: ingress-wikijs-tls

注意:在 annotations 里 设置 cert-manager.io/cluster-issuer 为签名创建的集群证书颁发者 letsencrypt

使用 yaml 文件创建 ingress 后,就可以使用该 Ingress 对外提供 HTTPS 服务了。

# 执行创建 ingress
kubectl apply -f ingress-wikijs.yaml -n infra

DNS01 配置示例

使用这种方式需要 DNS 服务商支持通过 API 创建 DNS 记录,正好我的 DNS 服务商是腾讯云 dnspod 支持,因此在我们的及群里,最终采用了这种方式。

这个方式的配置会比较麻烦,踩了很久的坑,主要是因为我的集群启用了本地 DNS 服务器,默认 cert-manager 会通过本地 DNS 服务器去验证通过 API 创建的 DNS txt 记录,会一直检查不到新增的 txt 记录,造成在 challenge 阶段就一直 pendding。解决方案附后。

1. 在 dnspod 创建 API ID 和 API Token

参考腾讯云官方文档(https://support.dnspod.cn/Kb/showarticle/tsid/227/)

记录下创建的 API ID 和 API Token (加码处理,需要自行获取自己的 API ID 和 API Token)。

AKIDVt3z4uVss11xjIdmddgMmHXXssssHp9D2buxrWR8
SekbG2gqdflQs5xxxviGagX8TYO

2. 安装 cert-manager-webhook-dnspod

使用 helm 安装 roc/cert-manager-webhook-dnspod。

$ helm repo add roc https://charts.imroc.cc

$ helm uninstall cert-manager-webhook-dnspod -n cert-manager
$ helm install cert-manager-webhook-dnspod roc/cert-manager-webhook-dnspod \
-n cert-manager \
--set clusterIssuer.secretId=AKIDVt3z4uVss11xjIdmddgMmHXXssssHp9D2buxrWR8 \
--set clusterIssuer.secretKey=SekbG2gqdflQs5xxxviGagX8TYO \
--set clusterIssuer.email=xxx@xxx.xxx

3. 创建泛域名证书

# ipincloud-crt.yaml

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ipincloud-crt
spec:
secretName: ipincloud-crt
issuerRef:
name: dnspod
kind: ClusterIssuer
group: cert-manager.io
dnsNames:
- "*.xxx.xxx"

创建集群证书颁发者:

$ kubectl apply -f ipincloud-crt.yaml -n infra

4. 验证证书

查看证书是否创建成功:

$ kubectl get Certificate -n cert-manager
NAME READY SECRET AGE
cert-manager-webhook-dnspod-ca True cert-manager-webhook-dnspod-ca 18m
cert-manager-webhook-dnspod-webhook-tls True cert-manager-webhook-dnspod-webhook-tls 18m
ipincloud-crt True ipincloud-crt 3m12s

以上可以看出 ipincloud-crt 已经创建成功, READY 状态也是 True。

查看证书对应的域名:

$ kubectl describe Certificate ipincloud-crt -n cert-manager
Name: ipincloud-crt
Namespace: cert-manager
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: 2022-05-07T14:19:07Z
...
Spec:
Dns Names:
*.xxx.xxx
Issuer Ref:
Group: cert-manager.io
Kind: ClusterIssuer
Name: dnspod
Secret Name: ipincloud-crt
Status:
Conditions:
Last Transition Time: 2022-05-07T14:19:14Z
Message: Certificate is up to date and has not expired
Observed Generation: 1
Reason: Ready
Status: True
Type: Ready
Not After: 2022-08-05T13:19:11Z
Not Before: 2022-05-07T13:19:12Z
Renewal Time: 2022-07-06T13:19:11Z
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 4m35s cert-manager-certificates-trigger Issuing certificate as Secret does not exist
Normal Generated 4m35s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "ipincloud-crt-4ml59"
Normal Requested 4m35s cert-manager-certificates-request-manager Created new CertificateRequest resource "ipincloud-crt-r76wp"
Normal Issuing 4m28s cert-manager-certificates-issuing The certificate has been successfully issued

从 Certificate 的描述信息可以看到,这个证书是对应所有 *.xxx.xxx 的泛域名。

查看证书内容:

$ kubectl describe secret ipincloud-crt -n cert-manager
Name: ipincloud-crt
Namespace: cert-manager
Labels: <none>
Annotations: cert-manager.io/alt-names: *.xxx.xxx
cert-manager.io/certificate-name: ipincloud-crt
cert-manager.io/common-name: *.xxx.xxx
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: 5587 bytes
tls.key: 1675 bytes

TLS 证书保存在 cert-manager 命名空间里的 ipincloud-crt secret。可以供所有 ``*.xxx.xxx` 的服务使用。

其他

这个过程中,遇到最大的坑是:我的集群使用了自建的 DNS 服务器,默认 cert-manager 会使用这个集群的自建 DNS SERVER 进行证书发行的验证,虽然通过调用 dnspod 的 webook 在 腾讯云 DNS 服务器上创建的 _acme-challenge 握手数据,但是在我的自建 DNS 里是查不到的,所以会一直卡 pending 状态。

$ kubectl get challenge -A
NAMESPACE NAME STATE DOMAIN AGE
cert-manager ipincloud-crt-f9kp6-381578565-136350475 pending xxx.xxx 24s

查看原因是:

Waiting for DNS-01 challenge propagation: DNS record for "xxx.xxx" not yet

$ kubectl -n cert-manager describe challenge ipincloud-crt-f9kp6-381578565-136350475
Name: ipincloud-crt-f9kp6-381578565-136350475
Namespace: cert-manager
Labels: <none>
Annotations: <none>
API Version: acme.cert-manager.io/v1
Kind: Challenge
---
中间略
---
Status:
Presented: true
Processing: true
Reason: Waiting for DNS-01 challenge propagation: DNS record for "xxx.xxx" not yet propagated
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 41s cert-manager-challenges Challenge scheduled for processing
Normal Presented 39s cert-manager-challenges Presented challenge using DNS-01 challenge mechanism

查了很多资料,在官网上找到解决方案。办法是让 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

配置证书复制到其他 namespace

安装 kubed

$ helm repo add appscode https://charts.appscode.com/stable/
$ helm repo update
$ helm install kubed appscode/kubed \
--version v0.13.2 \
--namespace cert-manager

修改 Certificated 文件

为 certificated 对象设置 secretTemplate, 设置需要同步到哪些 namespace。

# ipincloud-crt.yaml

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ipincloud-crt
spec:
secretName: ipincloud-crt
issuerRef:
name: dnspod
kind: ClusterIssuer
group: cert-manager.io
dnsNames:
- "*.xxx.xxx"
secretTemplate:
annotations:
kubed.appscode.com/sync: "cert-manager-tls=ipincloud-crt"

给需要同步的目标 namespace 打 label

上一步的 secretTemplate 里指定了同步的目标 namespace 的 label 过滤条件 cert-manager-tls=ipincloud-crt , 因此,我们需要对接收同步 secret 的 namespace 打上相应的 label。

$ kubectl label ns default cert-manager-tls=ipincloud-crt
$ kubectl label ns app cert-manager-tls=ipincloud-crt
$ kubectl label ns dev-app cert-manager-tls=ipincloud-crt
$ kubectl label ns dev-infra cert-manager-tls=ipincloud-crt
$ kubectl label ns dev-wly cert-manager-tls=ipincloud-crt
$ kubectl label ns infra cert-manager-tls=ipincloud-crt
$ kubectl label ns istio-system cert-manager-tls=ipincloud-crt
$ kubectl label ns uat-app cert-manager-tls=ipincloud-crt
$ kubectl label ns uat-wly cert-manager-tls=ipincloud-crt
$ kubectl label ns wly cert-manager-tls=ipincloud-crt
$ kubectl label ns kubesphere-controls-system cert-manager-tls=ipincloud-crt

查看是否复制成功

查看目标 namespace 是否复制 secret 成功。

$ kubectl get secret ipincloud-crt
NAME TYPE DATA AGE
ipincloud-crt kubernetes.io/tls 2 18m

查看复制的 secret ,可以看到 label 信息中记录了证书来源信息。

$ kubectl describe secret ipincloud-crt
Name: ipincloud-crt
Namespace: default
Labels: kubed.appscode.com/origin.cluster=unicorn
kubed.appscode.com/origin.name=ipincloud-crt
kubed.appscode.com/origin.namespace=cert-manager
Annotations: cert-manager.io/alt-names: *.xxx.xxx
cert-manager.io/certificate-name: ipincloud-crt
cert-manager.io/common-name: *.xxx.xxx
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:
kubed.appscode.com/origin:
{"namespace":"cert-manager","name":"ipincloud-crt","uid":"b4713633-731e-4151-844f-0f6d9cf6352c","resourceVersion":"12531075"} Type: kubernetes.io/tls Data
====
tls.crt: 5587 bytes
tls.key: 1675 bytes

使用 TLS 证书配置 Ingress

设置 Ingress

kind: 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.xxx.xxx
secretName: ipincloud-crt
rules:
- host: wiki.xxx.xxx
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: wikijs
port:
number: 3000

测试

以上配置完成后,就可以使用 HTTPS 来访问新的 wiki.js 服务了。

$ curl -I https://wiki.xxx.xxx
HTTP/1.1 302 Found
Date: Sat, 07 May 2022 14:52:39 GMT
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=%2F; Max-Age=900; Path=/; Expires=Sat, 07 May 2022 15:07:39 GMT
Location: /login
Vary: Accept, Accept-Encoding
Strict-Transport-Security: max-age=15724800; includeSubDomains

如上所示,就是成功启动了 HTTPS 。

参考

本文由博客一文多发平台 OpenWrite 发布!

为 KubeSphere 集群启用免费的泛域名 SSL 证书并实现证书自动更新和分发的更多相关文章

  1. Let'sEncrypt 免费通配符/泛域名SSL证书添加使用教程

    Let'sEncrypt 免费通配符/泛域名SSL证书添加使用教程 通配符证书一般还是比较贵的一般最便宜的通配符证书5.60美元一年,只不过Let'sEncrypt的有效期是3个月,对于一般用户来说基 ...

  2. [细节版]Let'sEncrypt 免费通配符/泛域名SSL证书添加使用教程

    参考网址:https://lnmp.org/faq/letsencrypt-wildcard-ssl.html 使用的dns服务商:阿里云 , 更多服务商地址可见参考网址. 遇见的问题一. [Sat ...

  3. kubesphere集群搭建(多节点)

    kubesphere官网:https://kubesphere.io/docs/advanced-v2.0/zh-CN/introduction/intro/ 一.准备环境 1.准备服务器 maste ...

  4. 分布式存储系统之Ceph集群启用Dashboard及使用Prometheus监控Ceph

    前文我们了解了Ceph之上的RadosGW基础使用相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16768998.html:今天我们来聊一聊Ceph启 ...

  5. 新开源HTML5单文件网页版ACME客户端,可在线申请Let's Encrypt、ZeroSSL免费HTTPS多域名通配符泛域名SSL/TLS证书(RSA/ECC/ECDSA)

    目录 开源项目的起源 项目地址 使用方法 第一步:选择Let's Encrypt.ZeroSSL或其他证书颁发机构 第二步:证书配置,填写域名 第三步:完成域名所有权的验证 第四步:下载保存证书PEM ...

  6. kubesphere集群节点扩容

    原有的节点是 : master[123] , node[1234] 新加的节点node5 一.修改配置文件hosts.ini [root@master0 ~]# /conf/hosts.ini [al ...

  7. Let’s Encrypt Wildcard 免费泛域名SSL证书获取安装

    2018 年 1 月Let’s Encrypt CA 宣布免费提供通配符证书(Wildcard certificate).通配符证书是一种可被多个子域使用的公钥证书.这意味着,单个证书可用于提供多台服 ...

  8. Let's Encrypt泛域名SSL证书申请

    操作系统:CentOS 7 github:https://github.com/Neilpang/acme.sh 有中文说明: https://github.com/Neilpang/acme.sh ...

  9. 在 Linux 部署多节点 Kubernetes 集群与 KubeSphere 容器平台

    KubeSphere 是在 Kubernetes 之上构建的以应用为中心的企业级容器平台,所有供为用户提供简单易用的操作界面以及向导式操作方式.同时,KubeSphere Installer 提供了 ...

  10. Kubernetes 多集群在开源项目 KubeSphere 的应用

    Kubernetes 多集群使用场景 随着容器的普及和 Kubernetes 的日渐成熟,企业内部运行多个 Kubernetes 集群已变得颇为常见.概括起来,多个集群的使用场景主要有以下几种. 多集 ...

随机推荐

  1. mini_imagenet 数据集生成工具

    最近在看小样本方面的论文,发现这个mini_imagenet这个数据集比较常用,但是却不好找,找了半天也没有找到,最后在找到了这样的答案: 小样本学习(Few shot learning)标准数据集( ...

  2. NVIDIA公司的半成品项目cule——GPU端运行的Atari2600游戏环境——已经废弃的项目

    官网介绍地址: https://developer.nvidia.com/blog/new-open-source-gpu-accelerated-atari-emulator-for-reinfor ...

  3. 自己实现一个自动检测网卡状态,并设置ip地址,源码见文章底部

    阅读本文前,请先学习下面几篇文章 <搞懂进程组.会话.控制终端关系,才能明白守护进程干嘛的?> <简简单单教你如何用C语言列举当前所有网口!> <Linux下C语言操作网 ...

  4. 关于Protobuf在使用中的一些注意点

    Protobuf是谷歌旗下的一款二进制序列化协议 协议的编写 在项目中新建一个xxx.proto文件 文件的格式 第一行写protobuf的版本 syntax = "proto3" ...

  5. SpringBoot静态文件映射问题

    如果遇到这种情况,检查静态文件(js/css/img)是不是在默认的static路径下,在查看application配置中的 static-path-pattern: 是否和前端映射路径完全相同,要是 ...

  6. Redis实战之session共享

    当线上集群时候,会出现session共享问题. 虽然Tomcat提供了session copy的功能,但是缺点比较明显: 1:当Tomcat多的时候,session需要大量同步到多台集群上,占用内网宽 ...

  7. python pyqt6 QMenu 设定圆角边框

    本来这个没有必要写,但是因为写的过程中,按照网上的写法运行,不知道为什么QMenu的右下角有圆角边框与直角背景颜色会覆盖显示 所以还是有必要写一下 menu = QMenu(self.tool_but ...

  8. 神经网络之卷积篇:详解池化层(Pooling layers)

    详解池化层 除了卷积层,卷积网络也经常使用池化层来缩减模型的大小,提高计算速度,同时提高所提取特征的鲁棒性,来看一下. 先举一个池化层的例子,然后再讨论池化层的必要性.假如输入是一个4×4矩阵,用到的 ...

  9. Dell存储备份告警:

    创建时间 修改日期 对象名称 消息 类型 告警状态 已确认 告警定义 类型 23-3-12 11:59:26 23-3-12 11:59:37 copyMirrorswap 2 CMs Operati ...

  10. 使用js闭包实现可取消的axios请求

    在平常开发中,经常会遇到重复请求的情况,也许是因为网络问题,也许是因为接口问题等等,传统做法是客户端采用防抖来限制用户发送接口的频率,一般出个loading转圈圈的形式, 但是很少使用取消请求的做法, ...