手里吃灰的域名越来越多,偶尔还是会拿来测试测试,每次手动配置证书就很麻烦,刚好最近Let's Encrypt Authority也由X3更新为了R3版本,使用了新的OCSP地址,解决了污染问题。(此处想说点什么,但还是算了QAQ)因此又用回acme.sh自动签发更新证书,官方有很详细的使用文档,这里只做简单记录。

安装

curl  https://get.acme.sh | sh

若后面出现command not found,则需要手动执行以下命令:

source ~/.bashrc

生成证书

HTTP认证方式

该方式acme.sh会在你的指定的网站根目录下自动生成一个文件,来验证域名所有权,然后自动完成验证并签发证书:

acme.sh  --issue  -d mydomain.com -d www.mydomain.com  --webroot  /home/wwwroot/mydomain.com/

完成后会自动删除验证文件,无需多余操作。

DNS认证方式(推荐)

这里为了方便,选择通过DNS认证方式,这种方式不需要服务器和公网IP,只需要DNS的解析记录即可完成验证,一般主流域名服务商都提供API接口,acme.sh目前支持包括主流的CloudFlare、DNSPod、Aliyun、Amazon Route53在内的多达131个的域名API,你这可以在这里查看到详细的支持列表。我的DNS服务器一般用的是Cloudflare,因此这里以Cloudflare的API为例验证,获取Cloudflare API,可以自己选择全局api还是单域api,我这里选择了单域api,获取到api后注意保存,然后导入:

export CF_Token="xxxxxxxxxxxxxxxxxxxxxxxx"
export CF_Account_ID="xxxxxxxxxxxxxxxxxxxxxxxx"
export CF_Zone_ID="xxxxxxxxxxxxxxxxxxxxxxxx"

其中CF_Token为你获取到的API密钥,CF_Account_ID和CF_Zone_ID可以在你域名概述页面的API分栏看到。

生成证书:

acme.sh --issue --dns dns_cf -d mydomain.com -d www.mydomain.com

若要生成通配符证书,则:

acme.sh --issue --dns dns_cf -d mydomain.com -d *.mydomain.com

2021年6月29日更新

今天准备签发一张证书,结果发现提示错误:

acme.sh is using ZeroSSL as default CA now.
Please update your account with an email address first.

然后去 Github 上项目看了下,发现一篇公告,说是从8月1日起,默认 CA 将换成 ZeroSSL,不过这不是还没到8月1日么...虽然 ZeroSSL 以前了解过也不错,同样支持免费签发 90 天期限的证书,包括通配符和 ECC 证书,甚至还支持 IP 证书,不过需要注册账号才行。至于为什么变成了 ZeroSSL,从这篇公告来看,应该是 acme.sh 被 apilayer 收购了。不过我还是习惯了 Let's Encrypt 的方便,想要继续使用 letsencryp 证书,公告里也给出了两种解决方案。

第一种是签发证书时指定 CA:

acme.sh --issue --dns dns_cf -d mydomain.com --server letsencrypt

另一种是直接更改默认 CA:

acme.sh --set-default-ca --server letsencrypt

如果设置了默认的 CA,以后就算版本升级也将一直默认使用指定的 CA。


大概30s左右就能成功签发证书,证书生成后会将你前面提供的api信息自动记录下来,将来在使用的时候就不需要再次指定了,直接生成:

acme.sh --issue -d mydomain2.com --dns dns_cf

安装证书

默认生成的证书都放在安装目录下:~/.acme.sh/,建议使用--install-cert命令指定目标位置,将证书文件复制到相应的位置,这里用Nginx示例:

acme.sh --install-cert -d mydomain.com \
--key-file       /path/to/ssl/private.key  \
--fullchain-file /path/to/ssl/fullchain.pem \
--reloadcmd     "service nginx force-reload"

这里指定的所有参数都会被自动记录下来,并在将来证书自动更新以后,被再次自动调用,目前证书在60天以后会自动更新,无需任何操作。

如果要撤销一个证书,使用:

acme.sh --list
acme.sh --revoke -d mydomain.com

如果要删除一个证书,使用:

acme.sh --list
acme.sh --remove -d mydomain.com

更新acme.sh

升级acme.sh到最新版:

acme.sh --upgrade

开启自动升级:

acme.sh  --upgrade  --auto-upgrade

关闭自动更新:

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

关于自动更新失败

可能会有多种情况导致更新失败,一是如果时国内服务器自动更新可能会遇到各种玄学网络问题,二是前面提到的 acme.sh 更换了默认的 CA,如果你手动签发一次会发现相关的错误信息,解决办法已在前文提及,而我主要遇到的时另一种情况。

其实以前就发现过自动更新证书失败的情况,但服务器和域名比较多,有的成功有的失败就没太在意。因为我基本上都用的 Cloudflare API 进行签发,最近发现有部分域名 DNS 设置里面出现了几十条用于认证的 TXT 记录(吐槽一下,Cloudflare不能多选删除,一条一条的删累死了...),于是准备研究一下问题出在哪里。

通过前面大量的 TXT 记录可以推断出 API 是调用成功了的,但却签发失败了,于是直接打开 .acme.sh/account.conf 文件,发现里面记录的 API Token 居然只有一个域名的,然后在 Github 上的一条 issue 中发现了问题所在,acme.sh 默认只会保留最新的域名 Token 信息,如果你在一台服务器上设置了多个域名,那么新设置的域名 Token 就会直接覆盖掉前面的,导致前面的域名更新证书时调用的是新域名的 Token,自然就会认证失败,而且由于失败后多次尝试,也会在最新域名的 DNS 里留下大量的 TXT 记录。

简单来说就是同一个 DNS 服务商通过 API 签发证书,只能保存一个域名的 Token 信息。而解决方法有两种:

1. 签发证书时指定配置文件,通过添加 --accountconf 参数实现(推荐):

acme.sh --issue --dns dns_cf -d mydomain.com --accountconf /root/.acme.sh/account-custom.conf

此时你的 Cron 自动任务也要做出对应的修改,新增一个任务,并且时间最好和默认的 acme.sh 任务错开:

# 默认任务
20 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
# 指定配置
30 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" --accountconf "/root/.acme.sh/account-custom.conf" > /dev/null

此时需要额外注意的是,如果你前面修改了 acme.sh 默认的 CA,那么指定配置文件也需要切换:

acme.sh --set-default-ca --server letsencrypt --accountconf /root/.acme.sh/account-custom.conf

2. 签发时指定配置文件路径,通过添加 --config-home 参数实现:

acme.sh --issue --dns dns_cf -d mydomain.com --config-home /root/acmeconfig/ 

同样,Crontab 自动任务也要做出对应的修改,新增一个任务:

59 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" --config-home "/root/acmeconfig/" > /dev/null

Tags: SSL

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.