基于acme.sh配置SSL证书

前言

为了提高安全性,在部署网络服务的时候经常需要使用HTTPS,但是域名证书很多都是要收费的,而如果要使用免费的证书,就不可避免受到限制:单域名、时间短、配置麻烦……反复权衡,最终选择用一种麻烦的方式——使用acme.sh从“ZeroSSL”申请并自动更新证书。

ACME简介

自动证书管理环境(英语:Automatic Certificate Management Environment,缩写ACME)是一种通信协议,用于证书颁发机构与其用户的Web服务器之间的自动化交互,允许以极低成本自动化部署公钥基础设施。该协议由互联网安全研究小组(ISRG)为Let’s Encrypt服务设计,已由专门的IETF工作组在RFC 8555中规范为一个互联网标准。

ACME标志

如果要基于ACME实现服务器证书的管理,还需要两个条件:

  1. ACME服务提供商,也就是支持基于ACME的免费或低成本的证书服务,最为知名也是最容易找到资料的也就是前面提到的Let’s Encrypt,此外ZeroSSL、BuyPass等也比较流行,大家可以根据自己的偏好选择。

    功能 LE Buypass ZeroSSL SSL.com Google Public CA
    有效期 90 天 180 天 90 天 90 天 90 天
    多域名 支持 支持,最多 5 个 支持 收费支持 支持
    泛域名 支持 不支持 支持 收费支持 支持
    Rate Limit 收费无 未知
    GUI 管理
    ECC 证书链 未知
    客户支持 社区 收费 收费 收费 收费
  2. ACME客户端,需要安装在我们自己的服务器上,向ACME服务提供商发起请求,对使用ACEME协议的证书进行管理,ACME客户端也有许多,例如Let’s Encrypt比较流行的Certbot等,对于ZeroSSL 可以使用"acme.sh"(其默认采用ZeroSSL)。

    客户端 类型 组织和/或主要赞助商 操作系统 开源 商业使用
    acme.sh shell脚本 Neil Pang,ZeroSSL(apilayer) Linux, macOS
    Caddy 网络服务器 Matt Holt,ArdanLabs,ZeroSSL(apilayer) Linux、macOS、Windows
    Certbot python脚本 互联网安全研究小组 Linux、macOS、Windows
    Certify The Web 图形界面和后台服务 Webprofusion Windows、Linux
    win-acme 命令行 Wouter Tinus Windows

在接下来的内容里,我选择了ZeroSSLacme.sh

acme.sh基本用法

安装acme.sh

acme.sh 是一个集成了 ACME 客户端协议的 Bash 脚本,可以直接网络安装:

1
curl  https://get.acme.sh | sh -s [email protected]

如果网络有问题,可以下载到本地后安装:

1
2
3
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m [email protected]

请注意替换 [email protected] 为你自己的邮箱,避免无法收到上游证书的邮件通知。

安装完成后重新加载 Bash:

1
source ~/.bashrc

然后也可以开启自动更新:

1
acme.sh --upgrade --auto-upgrade

选择默认CA

目前 acme.sh 支持四个正式环境 CA,分别是 Let’s Encrypt、Buypass、ZeroSSL 和 SSL.com,默认使用 ZeroSSL,如果需要更换可以使用如下命令:

1
2
3
4
5
6
7
8
# 切换Let's Encrypt
acme.sh --set-default-ca --server letsencrypt
# 切换Buypass
acme.sh --set-default-ca --server buypass
# 切换ZeroSSL
acme.sh --set-default-ca --server zerossl
# 切换SSL.com
acme.sh --set-default-ca --server ssl.com

添加ZeroSSL 账号

如果已有 ZeroSSL 帐号,可以在后台控制面板拿到 API Key,然后执行如下命令

1
2
apt install jq
curl -s -X POST "https://api.zerossl.com/acme/eab-credentials?access_key=你的API_Key" | jq

终端会输出如下内容

1
2
3
4
5
{
"success": true,
"eab_kid": "kid字符串",
"eab_hmac_key": "hmac_key字符串",
}

然后手工添加帐号

1
2
3
acme.sh --register-account  --server zerossl \
--eab-kid kid字符串 \
--eab-hmac-key hmac_key字符串

DNS验证签发证书

acme.sh 实现了 acme 协议支持的所有验证协议. 一般有两种方式验证: http 和 dns 验证。之所以选择使用dns验证,是因为dns 方式的真正强大之处在于可以使用域名解析商提供的 api 自动添加 txt 记录完成验证。

acme.sh 目前支持 cloudflare, DNSPod, cloudxns, godaddy 以及 ovh 等数十种解析商的自动集成,以DNSPod为例,先登录到DNSPod账号,生成ID和Token:

image-20220424093908271

然后:

1
2
3
4
5
6
7
export DP_Id="前面生成的ID"

export DP_Key="前面生成的Token"

# ZeroSSL支持泛域名证书
acme.sh --issue --dns dns_dp -d *.example.com

证书就会自动生成了. 这里给出的 api id 和 api key 会被自动记录下来, 将来你在使用 dnspod api 的时候, 就不需要再次指定了. 直接生成就好了:

1
acme.sh  --issue    --dns  dns_dp  -d  mydomain2.com

安装证书

Nginx example:

1
2
3
4
acme.sh --install-cert -d example.com \
--key-file /path/to/keyfile/in/nginx/key.pem \
--fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd "service nginx force-reload"

(一个小提醒, 这里用的是 service nginx force-reload, 不是 service nginx reload, 据测试, reload 并不会重新加载证书, 所以用的 force-reload)

Nginx 的配置 ssl_certificate 使用 /etc/nginx/ssl/fullchain.cer ,而非 /etc/nginx/ssl/<domain>.cer ,否则 SSL Labs 的测试会报 Chain issues Incomplete 错误。

--install-cert命令可以携带很多参数, 来指定目标文件. 并且可以指定 reloadcmd, 当证书更新以后, reloadcmd会被自动调用,让服务器生效.

详细参数请参考: https://github.com/Neilpang/acme.sh#3-install-the-issued-cert-to-apachenginx-etc

值得注意的是, 这里指定的所有参数都会被自动记录下来, 并在将来证书自动更新以后, 被再次自动调用。

image-20220424103634822

更新证书

目前证书在 60 天以后会自动更新, 你无需任何操作. 今后有可能会缩短这个时间, 不过都是自动,我们可以查看以下定时任务的具体内容:

1
2
crontab -l
acme.sh --cron

image-20220424103108537

更新 acme.sh

目前由于 acme 协议和 letsencrypt CA 都在频繁的更新, 因此 acme.sh 也经常更新以保持同步.

升级 acme.sh 到最新版 :

1
acme.sh --upgrade

如果你不想手动升级, 可以开启自动升级:

1
acme.sh  --upgrade  --auto-upgrade

之后, acme.sh 就会自动保持更新了.

你也可以随时关闭自动更新:

1
acme.sh --upgrade  --auto-upgrade  0

出错怎么办

如果出错, 请添加 debug log:

1
acme.sh  --issue  .....  --debug 

或者:

1
acme.sh  --issue  .....  --debug  2

番外:Certbot自动签发

网上有很多关于Certbot和Let’s Encrypt的资料,提到Certbot在使用DNS验证的时候,需要手动添加DNS验证记录。实际上Certbot也支持自动的DNS验证,但是需要通过插件来实现。官网列出了一些DNS验证插件,但是没有国内常用的阿里云和DNSPod:

Plugin Auth Inst Notes
haproxy Y Y Integration with the HAProxy load balancer
s3front Y Y Integration with Amazon CloudFront distribution of S3 buckets
gandi Y N Obtain certificates via the Gandi LiveDNS API
varnish Y N Obtain certificates via a Varnish server
external-auth Y Y A plugin for convenient scripting
pritunl N Y Install certificates in pritunl distributed OpenVPN servers
proxmox N Y Install certificates in Proxmox Virtualization servers
dns-standalone Y N Obtain certificates via an integrated DNS server
dns-ispconfig Y N DNS Authentication using ISPConfig as DNS server
dns-clouddns Y N DNS Authentication using CloudDNS API
dns-lightsail Y N DNS Authentication using Amazon Lightsail DNS API
dns-inwx Y Y DNS Authentication for INWX through the XML API
dns-azure Y N DNS Authentication using Azure DNS
dns-godaddy Y N DNS Authentication using Godaddy DNS
njalla Y N DNS Authentication for njalla
DuckDNS Y N DNS Authentication for DuckDNS
Porkbun Y N DNS Authentication for Porkbun
Infomaniak Y N DNS Authentication using Infomaniak Domains API

解决的办法有两个:

  1. 使用第三方DNS验证插件,网上其实有四三方的阿里云和DNSPod插件:certbot-dns-aliyuncertbot-dns-dnpod
  2. 使用Certbot辅助工具,certbot-letencrypt-wildcardcertificates-alydns-au,感兴趣的可以列的参考资料【6】、【7】。

小结

自动化签发、更新SSL证书其实并不是一个多么神秘的事情,只要有合适的服务商和API,我们自己完全也可以造轮子,当然如果有现成的,何乐而不用呢?

参考资料

  1. https://zh.wikipedia.org/wiki/自動憑證更新環境
  2. https://u.sb/acme-sh-ssl/
  3. https://zhuanlan.zhihu.com/p/75032510
  4. https://github.com/acmesh-official/acme.sh/wiki/说明
  5. https://blog.wayneshao.com/posts/46309.html
  6. https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-alydns-au
  7. https://blog.wayneshao.com/posts/46309.html
  8. https://eff-certbot.readthedocs.io/en/stable/using.html#manual
  9. https://absolutecommerce.co.uk/blog/auto-renew-letsencrypt-nginx-certbot