自动获取 Let'sencrypt 的证书

2021 09 3, Fri

TL; DR:

Caddy

不需要做什么,Caddy会自动帮你维护证书

Nginx + Certbot

certbot run --agree-tos --nginx ...

这种会直接通过修改 nginx 配置的方法通过 http01 验证。

也可以用 dns 验证,会麻烦一些,可以通过 manual auth hook 实现

certbot run --agree-tos --preferred-challenges dns --manual -d blog.jeffthecoder.xyz --manual-auth-hook /path/to/your/script

对于已有的证书,也可以修改配置做到在 renew 时使用脚本,一般来说配置文件都会在/etc/letsencrypt/renewal/目录下

[renewalparams]
account = change_it
authenticator = manual
server = https://acme-v02.api.letsencrypt.org/directory
pref_challs = dns-01,
manual_auth_hook = /path/to/your/script
manual_public_ip_logging_ok = True

Cert Manager

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: myemail@mydomain.com
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    server: 'https://acme-v02.api.letsencrypt.org/directory'
    solvers:
      - http01:
          ingress:
            class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: blog-jeffthecoder-xyz-tls
  namespace: blog
spec:
  commonName: blog.jeffthecoder.xyz
  dnsNames:
    - blog.jeffthecoder.xyz
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-prod
  secretName: blog.jeffthecoder.xyz-tls

如果希望用 DNS 来验证所有者,可能需要额外的配置,因为配置 DNS 各家接口不一样。比如说阿里云的需要部署对应的容器和Secret以后,配置 DNS solver 用 Webhook 来处理 DNS 验证的请求。

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # Change to your letsencrypt email
    email: myemail@mydomain.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.yourcompany.com
          solverName: alidns
          config:
            region: ""
            accessKeySecretRef:
              name: alidns-secret
              key: access-key
            secretKeySecretRef:
              name: alidns-secret
              key: secret-key

Let’s Encrypt 和 CA

CA

一个网站的流量,在服务器和客户端之间传输是需要保护的,需要套一层管子保证内容不被偷窥不被篡改。这层管子在现在的场景下叫 TLS。

TLS需要在本地有对授权机构的信任,从而构建一个信任链,我信任 A 机构,A机构信任 B 机构,B 签发了证书,我用本地存储且信任的 A 可以验证 B,B 可以验证网站的证书,证书的信息和网站相符,我就会认为这个网站是机构信任的,他给我的数据就是这个网站所有者要给我的数据。

本地信任的这个机构,我们通常叫 CA 机构,CA 机构会有自己的 CA 证书(下面如果不加解释,CA 通常指的是 CA 机构)。CA 有多种,有 Root CA,有 Intermediate CA,Let’s Encrypt就是一个 CA 机构,专门负责给别人颁发证书。

CA 的职能

但是如果只是颁发证书,那我去申请一个百度的证书,或者所有网站的证书,配置到单位网络里面,那我就可以假扮这些网站,解开网站加密的流量获取别人的密码,甚至换掉内容。这样的行为就叫做 中间人 ( Man in the Middle ) 攻击。

想象一下你用一根管子给朋友传送果汁,但是有个人有了这个证书就可以把管子截开,把果汁倒出来验证成分甚至喝一口或者换成毒药,然后倒回原来的管子里面。换一个例子就是身份证,身份证是政府下发的个人身份证明,相当于是政府给个人发的数字证书,假如派出所出错了,你可以申请到张三李四王五的身份证,那你就可以拿着别人的身份证伪造别人的身份招摇撞骗。这是非常可怕的。

所以对于 CA 来说为了实现信任关系,就需要验证身份的所有者,只给有所有权的人这个证明。

除此之外,其实还会有一些奇奇怪怪的情况,比如说我出国了,身份证要验证身份就需要走海关,比如说身份证遗失了就要吊销身份证。类似的 CA 也需要针对这些情况做一些额外的处理,解决这些问题也是 CA 的职责。

也就是说,CA 保证证书的持有者是所有者本人,其中包括

  • 验证所有者,只给所有者签发证书
  • 通过一定的途径吊销证书
  • 通过一定的途径可以更方便的验证证书
  • ……

Let’s Encrypt

在 Let’s Encrypt 之前,我们通常要购买证书,毕竟签发和验证证书是需要耗费计算力了。除此之外还需要繁复的生成申请的文件,非常麻烦。这一点都不 geek,一点都不自动化。

所以 Let’s Encrypt 出现了,创造了一套 ACME 协议,把申请证书的一整套流程都自动化了。同时整个设施由 Linux Foundation 基金会赞助,不需要付费。

所有者验证

之前提到证书签发需要验证这个域名是你的。这就是所有者验证,可以保证其他人不会申请到你的证书,不会用你的证书构建特殊的网络环境用你的名义骗人。

所有者验证通常有两种,对于通配符证书来说,因为没法某个特定的URL放置一个文件,所以需要一个特殊的域名解析记录叫做 TXT 记录,这类验证叫 DNS01 验证。设置好 TXT 记录,等待 CA 机构认证,通过以后你就可以拿到证书了。

而另一种主流的验证方法叫适用于固定的域名,也就是放置文件的 HTTP01 验证。这种方法需要让特定的 URL 上的 GET 请求返回一串定义好的字符串。比如说在 Webserver的特定目录下放置一个文件就可以完成验证,相对比较简单一些。

验证证书

证书签发以后本地需要知道这个证书是可信的,就需要知道有一些 CA 是可信的且 CA 签发了这个证书。所以大部分系统都会内置一系列公认可信的 CA 并持续更新。

Let’s Encrypt的 CA 证书在2015前后就被各个系统和浏览器默认作为可信 CA 内置,所以稍微新一点的系统应该都可以正常使用 Let’s Encrypt签发的证书。

吊销证书

有时候我们需要吊销一个证书,比如说证书不再需要了,或者证书被其他人滥用了,那么我们需要吊销证书。电子证书是很难吊销的,怎么办呢?各家 CA 设计了 在线证书状态协议 ( Online Certificate Status Protocol )

OCSP 协议就是另外建一个数据库,每个证书都有自己的特征,如果要吊销这个证书就在数据库里面记一笔。浏览器在验证证书的时候会同时找 CA 机构的服务器通过 OCSP 协议询问:这个证书还能使吗?服务器查了查这个吊销数据库,没发现吊销的记录,说:能用,那这个证书就没问题了。

ACME & Certbot & Cert Manager

刚才提到 Let’s Encrypt 要自动化整个流程,所以设计了 ACME 协议,同时自己有一套符合 ACME 标准的自动化证书签发服务。除了 Let’s Encrypt 以外,也有一些其他的 ACME 服务提供证书签发的服务,比如说 ZeroSSL,有兴趣就自己读文档,我这种博客更新总是不及时的。

要通过这些自动化的机构签发证书,就需要用各种 ACME 的客户端。我自己常用的就是 Certbot 和 Cert Manager,也有很多其他很优秀的工具,比如说 acme.sh、Caddy这些。

用起来都很简单,就是大概说说。

Certbot

Certbot 是一个基于 Python 的命令行工具,通过执行各种命令就可以签发、续签或者吊销证书。同时也有不少插件,用来集成 NginX 等其他的工具,方便日常任务。

比如说为 NginX 内的所有 80 上的 http server 都通过 HTTP01 签发一个 ssl 证书,签发完成以后自动更新 NginX 配置:

certbot run --nginx --agree-tos --email "xxxx@xxxxx.com" -n 

为 NginX 内某些域名签发一个 ssl 证书,签发完成以后自动更新 NginX 配置:

certbot run --nginx --agree-tos --email "xxxx@xxxxx.com" -d xxx.com -d yyy.com -n

把某个目录作为网站的根目录,用 HTTP01 签发证书:

certbot certonly --webroot --webroot-path /path/to/webroot -d xxx.com --agree-tos --email "xxxx@xxxxx.com"

手动完成 DNS01 验证:

certbot certonly --manual --agree-tos --email "xxxx@xxxxx.com" --preferred-challenges dns

手动完成 HTTP01 验证:

certbot certonly --manual --agree-tos --email "xxxx@xxxxx.com" --preferred-challenges http

通过脚本自动完成验证(验证和清理):

certbot certonly --manual --agree-tos --email "xxxx@xxxxx.com" --preferred-challenges dns --manual-auth-hook /path/to/auth/hook --manual-cleanup-hook /path/to/cleanup/hook

续签域名:

certbot renew -d xxx.com

Certbot

Certbot 主要是和 Ingress Controller 相结合,自动化完成 HTTP01 验证、签发证书并配置 Ingress Controller。同时也可以通过 Webhook,也就是外部的 API 调用完成验证过程。更新比较快,建议直接看文档。

比如说这样:

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: myemail@mydomain.com
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    server: 'https://acme-v02.api.letsencrypt.org/directory'
    solvers:
      - http01:
          ingress:
            class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: blog-jeffthecoder-xyz-tls
  namespace: blog
spec:
  commonName: blog.jeffthecoder.xyz
  dnsNames:
    - blog.jeffthecoder.xyz
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-prod
  secretName: blog.jeffthecoder.xyz-tls

正确配置以后,Cert Manager就会签发一个证书并且保存在 Secret 里面,到时候直接在 Ingress 定义中引用就行。

Webhook

CertManager 的 Webhook 实际上很像 certbot 的 manual auth hook 和 manual cleanup hook。可以自己写一个试试看,官方给出了一个参考实现:https://github.com/cert-manager/webhook-example