K8s 中使用 cert-manager 申请免费 Https 证书

2022年1月21日 374点热度 0人点赞 0条评论

K8s 中使用 cert-manager 申请免费 Https 证书

Intro

最近在尝试将自己的应用从自己用 kind 部署的一个 k8s 集群迁移到 Azure 的 AKS 上,其中一个问题就是 https 证书,原来的 k8s 集群是放在 nginx 后端的并没有直接管理 https 证书,https 证书是放在 nginx 层面的,完全运行在 AKS 上的 k8s 集群中就要解决 https 证书的问题,我们可以使用 cert-manager 来申请由 Let's encrypt 提供的免费 https 证书,因为我的域名是在阿里云上的,并不在官方的 DNS 服务商支持范围内,所以需要使用第三方的实现的 webhook 支持,而网上的一些第三方的很多 API version 都不对了,而且使用起来感觉也不太方便,所以自己基于别人的魔改了一版,完善了 helm 的支持,基本可以一个命令创建所有需要的资源

Cert-manager

随着 HTTPS 不断普及,越来越多的网站都在从 HTTP 升级到 HTTPS,现在如果不使用 https Chrome 浏览器都会提示不安全,而且有些服务可能会要求必须是 https 才可以,而 Let's encrypt 为大家提供了免费的 https 证书,免费证书的时间是三个月,我们可以在过期之前重新申请,对于个人来说是个不错的选择。

cert-manager 是 Kubernetes 上的全能证书管理工具,如果对安全级别和证书功能要求不高,可以利用 cert-manager 基于 ACME 协议与 Let's Encrypt 来签发免费证书并自动续期,实现永久免费使用证书。

下面这张图是一个 cert-manager 的总体架构图

图片

High level overview diagram explaining cert-manager architecture

Issuer 是指证书的来源,它可以从各种受支持的来源颁发证书,包括 Let's Encrypt、HashiCorp Vault 和 Venafi 以及私有 PKI。

Certificates 就是指的证书,证书是由指定的 issuer 来签发的

Kubernetes secrets 一般是指保存 https 证书的 secret,使用 https 的时候会用到这种 kubernetes.io/tls 类型的 secret

  • Issuer/ClusterIssuer: 用于指示 cert-manager 用什么方式签发证书,本文主要讲解签发免费证书的 ACME 方式。ClusterIssuer 与 Issuer 的唯一区别就是 Issuer 只能用来签发自己所在 namespace 下的证书,ClusterIssuer 可以签发任意 namespace 下的证书。
  • Certificate: 用于告诉 cert-manager 我们想要什么域名的证书以及签发证书所需要的一些配置,包括对 Issuer/ClusterIssuer 的引用

Let’s Encrypt 利用 ACME 协议来校验域名是否真的属于你,校验成功后就可以自动颁发免费证书,证书有效期只有 90 天,在到期前需要再校验一次来实现续期, cert-manager 可以自动续期,这样就可以基本使用永久免费的证书了。如何校验这个域名是否属于你呢?主要有两种校验方式是 HTTP-01DNS-01

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 暴露流量的服务颁发证书,并且不支持泛域名证书。

DNS-01 校验原理

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

HTTP-01 的校验方式配置简单通用,不管使用哪个 DNS 提供商都可以使用相同的配置方法;缺点是:需要依赖 Ingress,如果你的服务不是用 Ingress 暴露流量的就不适用,而且不支持泛域名证书。

DNS-01 的校验方式不依赖 Ingress,也支持泛域名;缺点就是不同 DNS 提供商的配置方式不一样,而且 DNS 提供商有很多,cert-manager 的 Issuer 不可能每个都去支持,不过有一些可以通过部署实现了 cert-manager 的 Webhook 的服务来扩展 Issuer 进行支持,比如 DNSPod 和 阿里 DNS,详细 Webhook 列表请参考: https://cert-manager.io/docs/configuration/acme/dns01/#webhook

Sample

这里假定以后安装好了 helm3,如果没有安装可以参考:https://helm.sh/docs/intro/install/ 先安装 helm

首先我们需要安装 cert-manager,你可以执行下面的命令来通过 helm 安装 cert-manager

helm repo add jetstack https://charts.jetstack.io

helm repo update

helm install cert-manager jetstack/cert-manager -n cert-manager --create-namespace --set installCRDs=true --version v1.6.1

安装完 cert-manager 之后,就可以配置 issuer 和 证书了

HTTP-01 比较简单,基本按照文档就可以走通了,这里不多介绍了 https://cert-manager.io/docs/tutorials/acme/http-validation/

如果你的 DNS 服务商官方就支持了也是跟着文档配置就可以了 https://cert-manager.io/docs/tutorials/acme/dns-validation/

而阿里云目前并非官方支持的,你需要使用第三方的,觉得不好用的,可以像我一样基于别人的配置改造一下,下面我们就以阿里云为例来介绍使用 cert-manager 来配置阿里云的 DNS,以我改造的 AliDNS webhook 为例

首先克隆项目 https://github.com/WeihanLi/certmanager-webhook-alidns 到本地,然后在项目根目录下执行下面的命令来配置证书

# 切换目录到 helm chart 包目录
cd deploy/certmanager-webhook-alidns

# render template,只展示渲染后的 yaml 文件,不安装
helm template certmanager-webhook-alidns . --set issuer.create=true --set issuer.host=weihanli.top --set [email protected] --set issuer.secret.accessKeyId=AliAccessKeyId --set issuer.secret.accessKeySecret=AliAccessKeySecret -n cert-manager

# install without creating ClusterIssuer,安装 webhook,但是不创建证书 issuer 和申请证书
helm install certmanager-webhook-alidns . -n cert-manager

# install with creating ClusterIssuer,安装 webhook 并创建证书 issuer 和申请 https 证书
helm install certmanager-webhook-alidns . --set issuer.create=true --set issuer.host=weihanli.top --set [email protected] --set issuer.secret.accessKeyId=AliAccessKeyId --set issuer.secret.accessKeySecret=AliAccessKeySecret -n cert-manager

可以使用 helm template 来测试自己要安装的资源情况

默认只安装 webhook 不会创建 ClusterIssuer 以及证书,如果要创建需要提供更多参数,需要指定 issuer.createtrue,然后通过 issuer.email 来指定申请证书的邮箱,并且需要提供阿里云的 accessKeyId 和 accessKeySecret 来修改 DNS 解析记录以实现自动地 DNS 验证进而自动签发证书,默认地创建 ClusterIssuer 的时候也会创建一个证书,如果不需要创建证书则配置 issuer.createCertfalse,如果要创建需要指定一个域名,比如 weihanli.top

上面最后一个命令会自动创建证书,安装完成后,我们就可以去检查我们的证书了,执行kubectl get cert -n cert-manager 来查看 cert-manager 命名空间下的证书,上面命令创建的证书名称是 weihanli-top-tls-cert,会是 host 名字把 . 替换成 - 然后加上 -tls-cert,证书签发完成 kubectl get secret -n cert-manager 会看到和证书同名的一个 secret

类似下面这样

NAME TYPE DATA AGE
weihanli-top-tls-cert kubernetes.io/tls 2 4d

上面的命令会创建一个 ClusterIssuer 和 Certificate,yaml 定义如下:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
  labels:
    app: webhook-alidns
    chart: webhook-alidns-0.1.0
    release: certmanager-webhook-alidns
    heritage: Helm
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: "[email protected]"
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        webhook:
          config:
            accessKeyId: AliAccessKeyId
            accessKeySecretRef:
              key: accessKeySecret
              name: ali-credential
              regionId: "cn-shanghai"
            ttl: 600
          groupName: certmanager.webhook.alidns
          solverName: alidns
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: weihanli-top-tls-cert
  labels:
    app: webhook-alidns
    chart: webhook-alidns-0.1.0
    release: certmanager-webhook-alidns
    heritage: Helm
spec:
  commonName: "weihanli.top"
  dnsNames:
  - "weihanli.top"
  - '*.weihanli.top'
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt
  secretName: weihanli-top-tls-cert

同时还会有一个保存阿里云 accessKeyId 和 accessKeySecret 的 secret 生成

apiVersion: v1
kind: Secret
metadata:
  labels:
    app: webhook-alidns
    chart: webhook-alidns-0.1.0
    release: certmanager-webhook-alidns
    heritage: Helm
  name: ali-credential
type: Opaque
data:
  accessKeyId: "QWxpQWNjZXNzS2V5SWQ="
  accessKeySecret: "QWxpQWNjZXNzS2V5U2VjcmV0"

有了证书之后我们就可以配置成 Ingress 默认的 https 证书,这样就不需要每个 ingress 都指定证书 secret 了,我们使用基于 nginx 的 ingress-nginx 来配置,通过 helm 来安装配置 ingress-nginx

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --set controller.extraArgs.default-ssl-certificate=cert-manager/weihanli-top-tls-cert --create-namespace

controller.extraArgs.default-ssl-certificate 配置为我们的证书对应的 secret,格式为:<namespace>/<secret-name>

然后我们就可以在 ingress 中配置 https 了,下面是一个配置示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
    kubernetes.io/ingress.class: nginx
  name: homepage
  namespace: default
spec:
  rules:
  - host: weihanli.top
    http:
      paths:
      - backend:
          service:
            name: homepage
            port:
              number: 80
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - weihanli.top

这里我们不需要指定证书的 secret,因为我们已经设置了默认的证书配置,再看一个示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$1
  name: sparktodo-api-ingress
spec:
  rules:
  - host: "sparktodo-api.weihanli.top"
    http:
      paths:
      - pathType: Prefix
        path: "/?(.*)"
        backend:
          service:
            name: sparktodo-api
            port:
              number: 80
      - pathType: Prefix
        path: "/monitor/?(.*)"
        backend:
          service:
            name: sparktodo-api
            port:
              number: 52323
  tls:
  - hosts:
    - sparktodo-api.weihanli.top

这个示例稍微复杂一些,主要是 /monitor/ 开头的请求转给 52323 端口,其他的请求转给 80 端口

More

按照上面的 helm chart 来配置自我感觉还是比较简单的,更多配置选项可以参考 helm chart 包的定义

https://github.com/WeihanLi/certmanager-webhook-alidns,

如果想自己玩一下,可能会遇到一些问题,cert-manager 的文档可能会帮到你 https://cert-manager.io/docs/faq/acme/,你也可以基于我魔改的再魔改一下哈

另外 cert-manager 文档上有一张非常详细的图来介绍证书的申请、验证、颁发整个过程,值得一看

图片

Life of a Certificate

References

  • https://www.cnblogs.com/tencent-cloud-native/p/13883790.html
  • https://github.com/WeihanLi/certmanager-webhook-alidns
  • https://cert-manager.io/docs/installation/helm/
  • https://cert-manager.io/docs/faq/acme/
  • https://helm.sh/docs/intro/install/

18560K8s 中使用 cert-manager 申请免费 Https 证书

root

这个人很懒,什么都没留下

文章评论