第77节 管理集群中的 TLS 认证


❤️💕💕新时代拥抱云原生,云原生具有环境统一、按需付费、即开即用、稳定性强特点。Myblog:http://nsddd.topopen in new window


[TOC]

管理集群中的 TLS 认证

Kubernetes 提供 certificates.k8s.io API,可让你配置由你控制的证书颁发机构(CA) 签名的 TLS 证书。 你的工作负载可以使用这些 CA 和证书来建立信任。

certificates.k8s.io API使用的协议类似于 ACME 草案open in new window

集群中的 TLS 信任

信任 Pod 中运行的应用程序所提供的自定义 CAopen in new window 通常需要一些额外的应用程序配置。 你需要将 CA 证书包添加到 TLS 客户端或服务器信任的 CA 证书列表中。 例如,你可以使用 Golang TLS 配置通过解析证书链并将解析的证书添加到 tls.Configopen in new window 结构中的 RootCAs 字段中。

说明:

即使自定义 CA 证书可能包含在文件系统中(在 ConfigMap kube-root-ca.crt 中), 除了验证内部 Kubernetes 端点之外,你不应将该证书颁发机构用于任何目的。 内部 Kubernetes 端点的一个示例是默认命名空间中名为 kubernetes 的服务。

如果你想为你的工作负载使用自定义证书颁发机构,你应该单独生成该 CA, 并使用你的 Pod 有读权限的 ConfigMapopen in new window 分发该 CA 证书。

请求证书

以下部分演示如何为通过 DNS 访问的 Kubernetes 服务创建 TLS 证书。

说明: 本教程使用 CFSSL:Cloudflare's PKI 和 TLS 工具包 点击此处open in new window了解更多信息。

创建证书签名请求

通过运行以下命令生成私钥和证书签名请求(或 CSR):

cat <<EOF | cfssl genkey - | cfssljson -bare server
{
  "hosts": [
    "my-svc.my-namespace.svc.cluster.local",
    "my-pod.my-namespace.pod.cluster.local",
    "192.0.2.24",
    "10.0.34.2"
  ],
  "CN": "my-pod.my-namespace.pod.cluster.local",
  "key": {
    "algo": "ecdsa",
    "size": 256
  }
}
EOF

其中 192.0.2.24 是服务的集群 IP,my-svc.my-namespace.svc.cluster.local 是服务的 DNS 名称,10.0.34.2 是 Pod 的 IP,而 my-pod.my-namespace.pod.cluster.local 是 Pod 的 DNS 名称。 你能看到的输出类似于:

2022/02/01 11:45:32 [INFO] generate received request
2022/02/01 11:45:32 [INFO] received CSR
2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
2022/02/01 11:45:32 [INFO] encoded CSR

此命令生成两个文件;它生成包含 PEM 编码 PKCS#10open in new window 证书请求的 server.csr, 以及 PEM 编码密钥的 server-key.pem,用于待生成的证书。

创建证书签名请求(CSR)对象发送到 Kubernetes API

你可以使用以下命令创建 CSR 清单(YAML 格式),并发送到 API 服务器:

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: my-svc.my-namespace
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  signerName: example.com/serving
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

请注意,在步骤 1 中创建的 server.csr 文件是 base64 编码并存储在 .spec.request 字段中的。你还要求提供 “digital signature(数字签名)”, “密钥加密(key encipherment)” 和 “服务器身份验证(server auth)” 密钥用途, 由 example.com/serving 示例签名程序签名的证书。 你也可以要求使用特定的 signerName。更多信息可参阅 支持的签署者名称open in new window

在 API server 中可以看到这些 CSR 处于 Pending 状态。执行下面的命令你将可以看到:

kubectl describe csr my-svc.my-namespace

批准证书签名请求(CSR)

证书签名请求open in new window 的批准或者是通过自动批准过程完成的,或由集群管理员一次性完成。 如果你被授权批准证书请求,你可以使用 kubectl 来手动完成此操作;例如:

kubectl certificate approve my-svc.my-namespace

判断状态:Status: Approved

你现在应该能看到如下输出:

controlplane $ kubectl get csr
NAME                  AGE     SIGNERNAME                                    REQUESTOR                  REQUESTEDDURATION   CONDITION
csr-46sxj             37d     kubernetes.io/kube-apiserver-client-kubelet   system:bootstrap:z6kr08    <none>              Approved,Issued
csr-6z592             38d     kubernetes.io/kube-apiserver-client-kubelet   system:node:controlplane   <none>              Approved,Issued
my-svc.my-namespace   9m17s   example.com/serving                           kubernetes-admin           <none>              Approved

这意味着证书请求已被批准,并正在等待请求的签名者对其签名。

签名证书签名请求(CSR)

接下来,你将扮演证书签署者的角色,颁发证书并将其上传到 API 服务器。

签名者通常会使用其 signerName 查看对象的 CertificateSigningRequest API, 检查它们是否已被批准,为这些请求签署证书,并使用已颁发的证书更新 API 对象状态。

创建证书颁发机构

你需要授权在新证书上提供数字签名。

首先,通过运行以下命令创建签名证书:

cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
  "CN": "My Example Signer",
  "key": {
    "algo": "rsa",
    "size": 2048
  }
}
EOF

你应该看到类似于以下的输出:

2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
2022/02/01 11:50:39 [INFO] generate received request
2022/02/01 11:50:39 [INFO] received CSR
2022/02/01 11:50:39 [INFO] generating key: rsa-2048
2022/02/01 11:50:39 [INFO] encoded CSR
2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472

这会产生一个证书颁发机构密钥文件(ca-key.pem)和证书(ca.pem)。

下载证书并使用它

现在,作为请求用户,你可以通过运行以下命令下载颁发的证书并将其保存到 server.crt 文件中:

CSR 被签署并获得批准后,你应该看到以下内容:

kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
    | base64 --decode > server.crt

现在你可以将 server.crtserver-key.pem 填充到 Secretopen in new window 中, 稍后你可以将其挂载到 Pod 中(例如,用于提供 HTTPS 的网络服务器)。

kubectl create secret tls server --cert server.crt --key server-key.pem
secret/server created

最后,你可以将 ca.pem 填充到 ConfigMapopen in new window 并将其用作信任根来验证服务证书:

kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem
configmap/example-serving-ca created

批准证书签名请求(CSR)

Kubernetes 管理员(具有适当权限)可以使用 kubectl certificate approvekubectl certificate deny 命令手动批准(或拒绝)证书签名请求(CSR)。 但是,如果你打算大量使用此 API,则可以考虑编写自动化的证书控制器。

注意:

批准证书 CSR 的能力决定了在你的环境中谁信任谁。 不应广泛或轻率地授予批准 CSR 的能力。

在授予 approve 权限之前,你应该确保自己充分了解批准人的验证要求颁发特定证书的后果。

无论上述机器或人使用 kubectl,“批准者”的作用是验证 CSR 满足如下两个要求:

  1. CSR 的 subject 控制用于签署 CSR 的私钥。这解决了伪装成授权主体的第三方的威胁。 在上述示例中,此步骤将验证该 Pod 控制了用于生成 CSR 的私钥。
  2. CSR 的 subject 被授权在请求的上下文中执行。 这点用于处理不期望的主体被加入集群的威胁。 在上述示例中,此步骤将是验证该 Pod 是否被允许加入到所请求的服务中。

当且仅当满足这两个要求时,审批者应该批准 CSR,否则拒绝 CSR。

有关证书批准和访问控制的更多信息, 请阅读证书签名请求open in new window参考页。

手动轮换 CA 证书

注意:

确保备份你的证书目录、配置文件以及其他必要文件。

这里的方法假定 Kubernetes 的控制面通过运行多个 API 服务器以高可用配置模式运行。 另一假定是 API 服务器可体面地终止,因而客户端可以彻底地与一个 API 服务器断开 连接并连接到另一个 API 服务器。

如果集群中只有一个 API 服务器,则在 API 服务器重启期间会经历服务中断期。

  1. 将新的 CA 证书和私钥(例如:ca.crtca.keyfront-proxy-ca.crtfront-proxy-client.key)分发到所有控制面节点,放在其 Kubernetes 证书目录下。

  2. 更新 kube-controller-manageropen in new window--root-ca-file 标志,使之同时包含老的和新的 CA,之后重启 kube-controller-manager。

    自此刻起,所创建的所有ServiceAccountopen in new window 都会获得同时包含老的 CA 和新的 CA 的 Secret。

创建外部负载均衡器

创建服务open in new window时,你可以选择自动创建云网络负载均衡器。 负载均衡器提供外部可访问的 IP 地址,可将流量发送到集群节点上的正确端口上 (假设集群在支持的环境中运行,并配置了正确的云负载均衡器驱动包)。

基于清单文件创建服务

要创建外部负载均衡器,请将以下内容添加到你的 Service 清单文件:

    type: LoadBalancer

你的清单文件可能会如下所示:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
    - port: 8765
      targetPort: 9376
  type: LoadBalancer

使用 kubectl 创建 Service

你也可以使用 kubectl expose 命令及其 --type=LoadBalancer 参数创建服务:

kubectl expose deployment example --port=8765 --target-port=9376 \
        --name=example-service --type=LoadBalancer

此命令通过使用与引用资源(在上面的示例的情况下,名为 exampleDeploymentopen in new window) 相同的选择器来创建一个新的服务。

找到你的 IP 地址

你可以通过 kubectl 获取服务信息,找到为你的服务创建的 IP 地址:

kubectl describe services example-service

这将获得类似如下输出:

Name:                     example-service
Namespace:                default
Labels:                   app=example
Annotations:              <none>
Selector:                 app=example
Type:                     LoadBalancer
IP Families:              <none>
IP:                       10.3.22.96
IPs:                      10.3.22.96
LoadBalancer Ingress:     192.0.2.89
Port:                     <unset>  8765/TCP
TargetPort:               9376/TCP
NodePort:                 <unset>  30593/TCP
Endpoints:                172.17.0.3:9376
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

负载均衡器的 IP 地址列在 LoadBalancer Ingress 旁边。

说明:

如果你在 Minikube 上运行服务,你可以通过以下命令找到分配的 IP 地址和端口:

minikube service example-service --url

保留客户端源 IP

默认情况下,目标容器中看到的源 IP 将不是客户端的原始源 IP。 要启用保留客户端 IP,可以在服务的 .spec 中配置以下字段:

  • .spec.externalTrafficPolicy - 表示此 Service 是否希望将外部流量路由到节点本地或集群范围的端点。 有两个可用选项:Cluster(默认)和 LocalCluster 隐藏了客户端源 IP,可能导致第二跳到另一个节点,但具有良好的整体负载分布。 Local 保留客户端源 IP 并避免 LoadBalancer 和 NodePort 类型服务的第二跳, 但存在潜在的不均衡流量传播风险。

  • .spec.healthCheckNodePort - 指定服务的 healthcheck nodePort(数字端口号)。 如果你未指定 healthCheckNodePort,服务控制器从集群的 NodePort 范围内分配一个端口。 你可以通过设置 API 服务器的命令行选项 --service-node-port-range 来配置上述范围。 在服务 type 设置为 LoadBalancer 并且 externalTrafficPolicy 设置为 Local 时, Service 将会使用用户指定的 healthCheckNodePort 值(如果你指定了它)。

可以通过在服务的清单文件中将 externalTrafficPolicy 设置为 Local 来激活此功能。比如:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
    - port: 8765
      targetPort: 9376
  externalTrafficPolicy: Local
  type: LoadBalancer

一些云服务供应商的负载均衡服务不允许你为每个目标配置不同的权重。

由于每个目标在向节点发送流量方面的权重相同,因此外部流量不会在不同 Pod 之间平均负载。 外部负载均衡器不知道每个节点上用作目标的 Pod 数量。

NumServicePods << _NumNodesNumServicePods >> NumNodes 时, 即使没有权重,也会看到接近相等的分布。

内部 Pod 到 Pod 的流量应该与 ClusterIP 服务类似,所有 Pod 的概率相同。

回收负载均衡器

特性状态: Kubernetes v1.17 [stable]

在通常情况下,应在删除 LoadBalancer 类型 Service 后立即清除云服务供应商中的相关负载均衡器资源。 但是,众所周知,在删除关联的服务后,云资源被孤立的情况很多。 引入了针对服务负载均衡器的终结器保护,以防止这种情况发生。 通过使用终结器,在删除相关的负载均衡器资源之前,也不会删除服务资源。

具体来说,如果服务具有 type LoadBalancer,则服务控制器将附加一个名为 service.kubernetes.io/load-balancer-cleanup 的终结器。 仅在清除负载均衡器资源后才能删除终结器。 即使在诸如服务控制器崩溃之类的极端情况下,这也可以防止负载均衡器资源悬空。

外部负载均衡器供应商

请务必注意,此功能的数据路径由 Kubernetes 集群外部的负载均衡器提供。

当服务 type 设置为 LoadBalancer 时,Kubernetes 向集群中的 Pod 提供的功能等同于 type 设置为 ClusterIP,并通过使用托管了相关 Kubernetes Pod 的节点作为条目对负载均衡器 (从外部到 Kubernetes)进行编程来扩展它。 Kubernetes 控制平面自动创建外部负载均衡器、健康检查(如果需要)和包过滤规则(如果需要)。 一旦云服务供应商为负载均衡器分配了 IP 地址,控制平面就会查找该外部 IP 地址并将其填充到 Service 对象中。

Kubernetes Ingress 配置泛域名 TLS 证书

让 Ingress 高可用可以用 LB, 当然也有 Local 的方法,

这里录了利用 let's encrytp 泛域名证书实现 Kubernetes 集群外部服务自动证书配置和证书到期自动更新,支持 HTTPS 访问。我们还部署了证书自动分发组件,实现证书文件自动分发到其他 namespace 。

  • 创建 Ingress 选用 HTTPS 监听协议时,选用合适的服务器证书能够确保访问安全。
  • 为所有的 HTTPS 域名绑定同一个证书,简化配置 Ingress 下所有 HTTPS 规则的证书,使更新操作更加便捷。
  • 为不同的域名绑定不同的证书,改善服务器与客户端 SSL/TLS。

利用 dnspod 、 cert-manager 、let’s encrytp 等开源组件,实现泛域名证书的自动生成、续期管理,为现有kubernetes 集群应用启动 HTTPS,提高系统安全性。

架构:

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

cert-manager 是一个云原生证书管理开源项目,用于在 Kubernetes 集群中提供 HTTPS 证书并自动续期,支持 Let’s Encrypt, HashiCorp Vault 这些免费证书的签发。在Kubernetes集群中,我们可以通过 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 请求,参数中 YOUR_DOMAIN 就是被校验的域名,TOKEN 是 ACME 协议的客户端负责放置的文件,ACME 客户端就是 cert-manager,它通过修改或创建 Ingressopen in new window 规则来增加这个临时校验路径并指向提供 TOKEN 的服务。Let’s Encrypt 会对比 TOKEN 是否符合预期,校验成功后就会颁发证书。此方法仅适用于给使用 Ingress 暴露流量的服务颁发证书,不支持泛域名证书。

配置

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: xxxx@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’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
2. 创建 ingress 服务
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.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
kubectl apply -f ingress-wikijs.yaml -n infra
# 查看证书是否自动创建成功
kubectl -n infra  get certificate

DNS01配置示例

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

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

1. 在 dnspod 创建 API ID 和 API Token

参考dnspod 云官方文档 记录下创建的 API ID 和 API Token 。

<API ID >
<API Token>
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=<API ID > \
    --set clusterIssuer.secretKey=<API Token> \
    --set clusterIssuer.email=xxxx@xyz.cn
3. 创建泛域名证书
xyz-crt.yaml
apiVersion: 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"
4. 验证证书

查看证书是否创建成功

[root@ks-master infra]# 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
xyz-crt                             True    xyz-crt                             3m12s

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

查看证书对应的域名

[root@ks-master infra]# kubectl describe Certificate xyz-crt -n cert-manager
Name:         xyz-crt
Namespace:    cert-manager
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2022-05-07T14:19:07Z
  Generation:          1
  Managed Fields:
    API Version:  cert-manager.io/v1
    Fields Type:  FieldsV1
    fieldsV1:
      f:status:
        .:
        f:conditions:
    Manager:      cert-manager-certificates-trigger
    Operation:    Update
    Subresource:  status
    Time:         2022-05-07T14:19:07Z
    API Version:  cert-manager.io/v1
    Fields Type:  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
    API Version:  cert-manager.io/v1
    Fields Type:  FieldsV1
    fieldsV1:
      f:status:
        f:revision:
    Manager:      cert-manager-certificates-issuing
    Operation:    Update
    Subresource:  status
    Time:         2022-05-07T14:19:14Z
    API Version:  cert-manager.io/v1
    Fields Type:  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
  Resource Version:  4784901
  UID:               f2c9be82-f4cb-4243-b2c3-19f64242cc91
Spec:
  Dns Names:
    *.xyz.cn
  Issuer Ref:
    Group:      cert-manager.io
    Kind:       ClusterIssuer
    Name:       dnspod
  Secret Name:  xyz-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 "xyz-crt-4ml59"
  Normal  Requested  4m35s  cert-manager-certificates-request-manager  Created new CertificateRequest resource "xyz-crt-r76wp"
  Normal  Issuing    4m28s  cert-manager-certificates-issuing          The certificate has been successfully issued
[root@ks-master infra]# 

从 Certificate 的描述信息可以看到,这个证书是对应所有 *.xyz.cn 的泛域名。所以可以直接用在集群的任何地方。

查看证书内容

[root@ks-master infra]# kubectl describe secret xyz-crt -n cert-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:  5587 bytes
tls.key:  1675 bytes

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

其他

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

[root@ks-master infra]# kubectl get challenge -A
NAMESPACE      NAME                                       STATE     DOMAIN         AGE
cert-manager   xyz-crt-f9kp6-381578565-136350475    pending   xyz.cn   24s

查看原因是: Waiting for DNS-01 challenge propagation: DNS record for "xyz.cn" not yet

[root@ks-master infra]# kcm describe challenge xyz-crt-f9kp6-381578565-136350475
Name:         xyz-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 "xyz.cn" 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}'    

使用 TLS 证书配置 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.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 服务了。

Let’s Encrypt

为了在您的网站上启用 HTTPS,您需要从证书颁发机构(CA)获取证书(一种文件)。 Let’s Encrypt 正是其中一家证书颁发机构。 要从 Let’s Encrypt 获取您网站域名的证书,您必须证明您对域名的实际控制权。 这一过程通常由 Web 主机上运行的 ACME 协议open in new window客户端完成。

申请证书的最佳方式取决于您是否具备服务器的命令行访问权限open in new window(也称为 SSH 权限)。 如果您仅使用控制面板(例如 cPanelopen in new windowPleskopen in new windowWordPressopen in new window)管理您的网站,您很有可能没有命令行访问权限。 您可以联系您的托管服务提供商确认。

拥有命令行访问权限

我们建议大多数具有命令行访问权限的人使用 Certbotopen in new window ACME 客户端。 它可以在不下线您的服务器的前提下自动执行证书颁发和安装。 对于不需要自动配置的用户,Certbot 还提供专家模式。 它易于使用,适用于许多操作系统,并且具有出色的(注:英文)文档。 前往 Certbot 官网open in new window即可获取针对各类操作系统与服务器软件的使用说明。

如果 Certbotopen in new window 不能满足您的需求,或者您想尝试别的客户端,还有更多 ACME 客户端open in new window可供选择。 选定 ACME 客户端软件后,请参阅该客户端的文档。

END 链接