Skip to content

Nginx 使用 acme.sh 生成和自动续签证书

安装 acme.sh

Github:https://github.com/acmesh-official/acme.sh

中文文档:https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E

shell
[root@localhost ~]# curl https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1032    0  1032    0     0    602      0 --:--:--  0:00:01 --:--:--   603
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  219k  100  219k    0     0   4130      0  0:00:54  0:00:54 --:--:-- 30572
[Mon Jul 21 04:35:41 PM CST 2024] Installing from online archive.
[Mon Jul 21 04:35:41 PM CST 2024] Downloading https://github.com/acmesh-official/acme.sh/archive/master.tar.gz
[Mon Jul 21 04:35:43 PM CST 2024] Extracting master.tar.gz
[Mon Jul 21 04:35:43 PM CST 2024] It is recommended to install socat first.
[Mon Jul 21 04:35:43 PM CST 2024] We use socat for the standalone server, which is used for standalone mode.
[Mon Jul 21 04:35:43 PM CST 2024] If you don't want to use standalone mode, you may ignore this warning.
[Mon Jul 21 04:35:43 PM CST 2024] Installing to /root/.acme.sh
[Mon Jul 21 04:35:43 PM CST 2024] Installed to /root/.acme.sh/acme.sh
[Mon Jul 21 04:35:43 PM CST 2024] Installing alias to '/root/.bashrc'
[Mon Jul 21 04:35:43 PM CST 2024] Close and reopen your terminal to start using acme.sh
[Mon Jul 21 04:35:43 PM CST 2024] Installing alias to '/root/.cshrc'
[Mon Jul 21 04:35:43 PM CST 2024] Installing alias to '/root/.tcshrc'
[Mon Jul 21 04:35:43 PM CST 2024] Installing cron job
no crontab for root
no crontab for root
[Mon Jul 21 04:35:43 PM CST 2024] bash has been found. Changing the shebang to use bash as preferred.
[Mon Jul 21 04:35:44 PM CST 2024] OK
[Mon Jul 21 04:35:44 PM CST 2024] Install success!

通过安装日志,可以知道,安装到了 /root/.acme.sh/acme.sh 目录下。

验证域名所有权

shell
[root@localhost ~]# cd /root/.acme.sh/
# acme.sh脚本默认ca服务器是zerossl,经常出错,
# 会导致获取证书的时候一直出现:Pending, The CA is processing your order, please just wait.
# 只需要把ca服务器改成letsencrypt 即可,虽然更改以后还是有概率出现pending,但基本2-3次即可成功
[root@localhost ~]# ./acme.sh --set-default-ca --server letsencrypt

# 域名验证(其中:\*.aday.fun 的配置为泛域名,* 即通配符。)
# 注意:-d 参数可以配置多个,比如下面命令。
# ./acme.sh --issue --dns -d aday.fun -d \*.aday.fun --yes-I-know-dns-manual-mode-enough-go-ahead-please
# 注意:如果命令参数中:-d aday.fun -d \*.aday.fun 指定了一个二级主域名,和一个三级泛域名。
# 因此,日志中也会打印两组验证解析配置,所以在阿里域名解析中需要配置两条 TXT 类型的域名解析记录.

# 这里仅演示泛域名。
[root@localhost ~]# ./acme.sh --issue --dns -d *.aday.fun --yes-I-know-dns-manual-mode-enough-go-ahead-please
[Mon Jul 21 04:55:29 PM CST 2024] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Mon Jul 21 04:55:30 PM CST 2024] Account key creation OK.
[Mon Jul 21 04:55:30 PM CST 2024] Registering account: https://acme-v02.api.letsencrypt.org/directory
[Mon Jul 21 04:55:31 PM CST 2024] Registered
[Mon Jul 21 04:55:31 PM CST 2024] ACCOUNT_THUMBPRINT='2f45bdd2dd644a12bbb99de5b83d0ef3'
[Mon Jul 21 04:55:31 PM CST 2024] Creating domain key
[Mon Jul 21 04:55:31 PM CST 2024] The domain key is here: /root/.acme.sh/aday.fun_ecc/aday.fun.key
[Mon Jul 21 04:55:31 PM CST 2024] Multi domain='DNS:aday.fun,DNS:*.aday.fun'
[Mon Jul 21 04:55:36 PM CST 2024] Getting webroot for domain='*.aday.fun'
[Mon Jul 21 04:55:36 PM CST 2024] Add the following TXT record:
[Mon Jul 21 04:55:36 PM CST 2024] Domain: '_acme-challenge.aday.fun'
[Mon Jul 21 04:55:36 PM CST 2024] TXT value: '0547550dd65741fbb4af31ffec98a4de'
[Mon Jul 21 04:55:36 PM CST 2024] Please make sure to prepend '_acme-challenge.' to your domain
[Mon Jul 21 04:55:36 PM CST 2024] so that the resulting subdomain is: _acme-challenge.aday.fun
[Mon Jul 21 04:55:36 PM CST 2024] Please add the TXT records to the domains, and re-run with --renew.
[Mon Jul 21 04:55:36 PM CST 2024] Please add '--debug' or '--log' to see more information.
[Mon Jul 21 04:55:36 PM CST 2024] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh

# 在你的域名管理面板中添加这条 txt 记录即可
# 阿里的就去阿里域名控制台中,域名解析服务额外配置一条 TXT 类型的域名解析记录(参考上面打印出来的日志):
Domain: '_acme-challenge.aday.fun'
TXT value: '0547550dd65741fbb4af31ffec98a4de'

# 配置完保存后,等待解析完成之后, 重新生成证书。

生成证书

shell
# 注意,和域名验证时命令不一样,一个是:--issue,另一个是:--renew
[root@localhost ~]# ./acme.sh --renew -d *.aday.fun --yes-I-know-dns-manual-mode-enough-go-ahead-please
[Mon Jul 21 05:24:25 PM CST 2024] The domain '*.aday.fun' seems to already have an ECC cert, let's use it.
[Mon Jul 21 05:24:25 PM CST 2024] Renewing: '*.aday.fun'
[Mon Jul 21 05:24:25 PM CST 2024] Renewing using Le_API=https://acme-v02.api.letsencrypt.org/directory
[Mon Jul 21 05:24:25 PM CST 2024] Skipping. Next renewal time is: 2024-09-19T09:23:25Z
[Mon Jul 21 05:24:25 PM CST 2024] Add '--force' to force renewal.
[root@hecs1366183 .acme.sh]# ./acme.sh --renew -d *.aday.fun --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
[Mon Jul 21 05:24:29 PM CST 2024] The domain '*.aday.fun' seems to already have an ECC cert, let's use it.
[Mon Jul 21 05:24:29 PM CST 2024] Renewing: '*.aday.fun'
[Mon Jul 21 05:24:29 PM CST 2024] Renewing using Le_API=https://acme-v02.api.letsencrypt.org/directory
[Mon Jul 21 05:24:30 PM CST 2024] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Mon Jul 21 05:24:30 PM CST 2024] Single domain='*.aday.fun'
[Mon Jul 21 05:24:32 PM CST 2024] Getting webroot for domain='*.aday.fun'
[Mon Jul 21 05:24:33 PM CST 2024] *.aday.fun is already verified, skipping dns-01.
[Mon Jul 21 05:24:33 PM CST 2024] Verification finished, beginning signing.
[Mon Jul 21 05:24:33 PM CST 2024] Let's finalize the order.
[Mon Jul 21 05:24:33 PM CST 2024] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/1850490947/289530579467'
[Mon Jul 21 05:24:33 PM CST 2024] Downloading cert.
[Mon Jul 21 05:24:33 PM CST 2024] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/03ce3950097da09e5d0b0de1da485daf2be2'
[Mon Jul 21 05:24:34 PM CST 2024] Cert success.
-----BEGIN1CERTIFICATE-----
CzAJBgNVBA1TAlVTMRYwFAYDVQQKEw1MZXBFbmNyeXB0MQswCQYDVQQDEwJF
+MQoMAoGCC1GSM49BAMDA2gAMGUCMDBynbgUqytcfp9RIM/gPOs4HDfAlmwL
kaBY4z+/nz1bBeEMTJZKwtp3FZBWigIxALn56RRk+HcEYGclRLkfIU5Y2DkS
owQEme4T9a1pWBojr2pKmtvHQ==
-----END C1RTIFICATE-----
[Mon Jul 21 05:24:34 PM CST 2024] Your cert is in: /root/.acme.sh/*.aday.fun_ecc/*.aday.fun.cer
[Mon Jul 21 05:24:34 PM CST 2024] Your cert key is in: /root/.acme.sh/*.aday.fun_ecc/*.aday.fun.key
[Mon Jul 21 05:24:34 PM CST 2024] The intermediate CA cert is in: /root/.acme.sh/*.aday.fun_ecc/ca.cer
[Mon Jul 21 05:24:34 PM CST 2024] And the full-chain cert is in: /root/.acme.sh/*.aday.fun_ecc/fullchain.cer

使用 openssl 转换 fullchain.cer 为 fullchain.crt 证书

如果不需要 .crt 证书,则跳过该章节(nginx 可以不需要,只使用 fullchain.cer 和 *.aday.fun.key 即可。)。

但有些时候我们需要其他格式的证书,比如: FRP 内网穿透工具启用 https 时,需要 .crt 证书。

FRP 文档参考:FRP:为本地 HTTP 服务启用 HTTPS

shell
cd /root/.acme.sh/*.aday.fun_ecc
# cer 转 crt
$ openssl x509 -inform PEM -in fullchain.cer -out fullchain.crt

更多转换参考:Linux 使用 openssl 转换证书格式

copy/安装证书

前面证书生成以后, 接下来需要把证书 copy 到真正需要用它的地方.

注意, 默认生成的证书都放在安装目录下: ~/.acme.sh/, 请不要直接使用此目录下的文件, 例如: 不要直接让 nginx 的配置文件使用,这里面的文件都是内部使用,而且目录结构可能会变化。

正确的使用方法是使用 --install-cert 命令,并指定目标位置, 然后证书文件会被 copy 到相应的位置, 例如:

Nginx example:

shell
./acme.sh --install-cert -d *.aday.fun \
--key-file       /etc/nginx/ssl/*.aday.fun.key  \
--fullchain-file /etc/nginx/ssl/fullchain.cer \
--reloadcmd     "service nginx force-reload"

Nginx 配置参考:Nginx 开启 https 访问

一个小提醒, 这里用的是 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

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

查看已安装证书

shell
./acme.sh --info -d *.aday.fun
# 会输出如下内容:
The domain '*.aday.fun' seems to already have an ECC cert, let's use it.
DOMAIN_CONF=/root/.acme.sh/*.aday.fun_ecc/*.aday.fun.conf
Le_Domain=*.aday.fun
Le_Alt=no
Le_Webroot=dns
Le_PreHook=
Le_PostHook=
Le_RenewHook=
Le_API=https://acme-v02.api.letsencrypt.org/directory
Le_Keylength=ec-256
Le_OrderFinalize=https://acme-v02.api.letsencrypt.org/acme/finalize/1850490947/289530579467
Le_LinkOrder=https://acme-v02.api.letsencrypt.org/acme/order/1850490947/289530579467
Le_LinkCert=https://acme-v02.api.letsencrypt.org/acme/cert/03ce3950097da09e5d0b0de1da485daf2be2
Le_CertCreateTime=1721640274
Le_CertCreateTimeStr=2024-07-21T09:24:34Z
Le_NextRenewTimeStr=2024-09-19T09:24:34Z
Le_NextRenewTime=1726737874

更新证书

目前证书在 60 天以后会自动更新, 你无需任何操作. 今后有可能会缩短这个时间, 不过都是自动的, 你不用关心.

请确保 cronjob 正确安装, 看起来是类似这样的:

shell
[root@localhost ~]# crontab -l
56 * * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null