Operation

使用 acme.sh 自动签发续期 Let’s encrypt Free 证书

使用 acme.sh 自动签发续期 Let's encrypt Free 证书

前言

之前看过一些教程快速撸一个免费HTTPS证书的文章,是通过 Certbot 来管理 Let's Encrypt 的证书,使用前需要安装一堆库,觉得不太友好。所谓条条大路通罗马,肯定还有其他方法可以做这个事情。

通过 Let’s encrypt 可以获得 90 天免费且可续期的 SSL 证书,而利用 acme.sh 这个库可以自动生成和更新,它是用Shell脚本编写的,不需要安装其他东西,比较纯净值得一试,如下就以参考官方及民间文档整合了一下具体的配置过程。

1. 准备

  • 一个已备案已解析的域名(以用来支持 http 访问)
  • 开启服务器的 80/443 端口防火墙。

2. 安装 acme.sh

curl -L https://get.acme.sh | sh
  • 执行如上命令,会做这些操作:

    • 从 GitHub 上下载 sh 脚本并执行;
    • 解压文件到 ${HOME}/.acme.sh 目录下;
    • 给命令行设置一个 acme.sh 的 alias 别名;
    • 最后注册一个 cron 定时任务来自动更新证书(可 crontab -l 查看);
    • 安装完成后要自行重启命令行,或者重新加载一下 . ~/.bashrc 文件;
  • 检查生效

acme.sh -h
https://github.com/acmesh-official/acme.sh
v3.0.5
Usage: acme.sh <command> ... [parameters ...]
Commands:
  -h, --help               Show this help message.
  -v, --version            Show version info.
  --install                Install acme.sh to your system.
  --uninstall              Uninstall acme.sh, and uninstall the cron job.
...

3. 签发证书

签发 SSL 证书需要证明这个域名是属于你的,即域名所有权,一般有两种方式验证:HTTP 和 DNS 验证。

通过 acme.sh 可以签发单域名、多域名、泛域名证书,还可以签发 ECC 证书。为了简单起见,这里以单域名证书为例,后面再拓展一下好了。

采用如下任意一种方式只要签发安装成功就行!

3.1 HTTP 验证

这种方式 acme.sh 会自动在你的网站根目录下放置一个文件,来验证你的域名所有权,验证之后就签发证书,最后会自动删除验证文件。

前提是要绑定的域名已经绑定到了所在服务器上,且可以通过公网进行访问!

  • 3.1.1 Webroot mode

    假设服务器在运行着的,网站域名为 example.com,根目录为 /home/wwwroot/example.com。那么只需要执行下面这条语句就行。

acme.sh  --issue  -d example.com  -w /home/wwwroot/example.com
  • 3.1.2 Apache / Nginx mode

    如果用的是 Apache 或者 Nginx 服务器,可以自动寻找配置文件来进行签发。

acme.sh  --issue  -d example.com  --apache --debug  2  # Apache
acme.sh  --issue  -d example.com  --nginx --debug  2   # Nginx

如果找不到配置文件的话可以自行配置。

acme.sh  --issue  -d example.com  --nginx /etc/nginx/nginx.conf --debug  2  # 指定nginx的conf
acme.sh  --issue  -d example.com  --nginx /etc/nginx/conf.d/example.com.conf --debug  2  # 指定网站的conf
# 监听 http server
acme.sh  --issue  -d example.com  --standalone --debug  2

如果用了反代理后不是 80 端口,也可手动指定。

acme.sh  --issue  -d example.com  --standalone --httpport 88 --debug  2

当然它还支持 tls 模式,非 443 端口的话也可以自行指定。

acme.sh  --issue  -d example.com  --alpn
acme.sh  --issue  -d example.com  --alpn --tlsport 8443  # 自行指定tls端口

3.2 DNS 验证

这种方式下,不需要任何服务器及任何公网 ip,只需要 DNS 的解析记录即可完成验证,
比如当服务器不能直接公网访问,以及某些 VPS 直接绑定域名没备案的话是上不去的,就需要采用这种方案了,
当然,手里有域名只是想生成一个证书而已也可以这么用。

  • 3.2.1 DNS API mode

    这种方式贼强大,直接可以利用域名服务商提供的 API 就可以自动帮你添加 TXT 记录完成验证和证书签发。而且60天后还可以自动完成续期,可以爽歪歪。

    比如说 CloudFlare 的,在这里获取你的API Key。可以用全局 API Key,将参数导入到命令行。

export CF_Token="xxxxx"
export CF_Account_ID="xxx"

为了限制权限,可以新建一个区域的 API Key. 这里只需要 Zone.DNS 的编辑权限(restrict the API Token only for write access to Zone.DNS for a single domain)就行。

export CF_Token="xxxxx"
export CF_Account_ID="xxx"
export CF_Zone_ID="xxx"

Account_ID 和 Zone_ID 在域名的管理页面右下方可以得到,而后再签发证书。

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

之后这些配置信息会保存到 ~/.acme.sh/account.conf 这个文件里,在证书续期或者其他利用 CF 进行验证的时候会自动调用。

当然,比如国内一般用的是 DNSPod / AliDNS 等也提供了 API,类似配置就好了,如下以 AliDNS 为例,注:如果是RAM子用户,则一定要设置权限如 AliyunDNSFullAccess

export Ali_Key="xxx"
export Ali_Secret="xxxx"
acme.sh --issue --dns dns_ali -d example.com -d www.example.com --debug  2

大约几分钟之后看到如下日志,就表示签发成功了。
(当然此过程中你也能在 https://dns.console.aliyun.com/#/dns/setting/example.com 看到,被创建了2条TXT记录,之后又被自动删除)

....
[Thu Jun 23 19:29:01 CST 2022] Your cert is in: /root/.acme.sh/example.com/example.com.cer
[Thu Jun 23 19:29:01 CST 2022] Your cert key is in: /root/.acme.sh/example.com/example.com.key
[Thu Jun 23 19:29:01 CST 2022] The intermediate CA cert is in: /root/.acme.sh/example.com/ca.cer
[Thu Jun 23 19:29:01 CST 2022] And the full chain certs is there: /root/.acme.sh/example.com/fullchain.cer
[Thu Jun 23 19:29:01 CST 2022] _on_issue_success
acme.sh  --issue  -d example.com  --dns  -d www.example.com --debug  2
  • 3.2.3 DNS Alias mode

    如果域名服务商没有提供 API,或者是一个挺重要的域名,为了安全不希望或者不方便直接配置这个域名的解析记录,可以通过另一个没那么重要的域名(可能是专门用来签发证书的)间接进行配置。
    实际上还是需要有一个域名来验证所有权。

3.3 多域名配置

多个域名签发同一张证书。只需要在验证方式之后添加多个 -d <you_domain> 参数即可。

acme.sh --issue -d example.com -w /home/wwwroot/example.com -d www.example.com --debug  2
acme.sh  --issue  -d example.com  --standalone  -d www.example.com --debug  2
acme.sh  --issue  -d example.com  --dns  -d www.example.com --debug  2

也可以多个域名指定不同的验证方式,例如

acme.sh  --issue  \
-d aa.com  -w /home/wwwroot/aa.com \
-d bb.com  --dns dns_cf \
-d cc.com  --apache \
-d dd.com  -w /home/wwwroot/dd.com --debug  2

3.4 泛域名配置

Wildcard certificates,同理,只需要加个 * 就好。但似乎只适用于 DNS 验证的方式。

acme.sh  --issue -d example.com  -d '*.example.com'  --dns dns_cf --debug  2

3.5 签发 ECC 证书

  • https://github.com/acmesh-official/acme.sh#single-domain-ecc-certificate

    默认签发的都是基于 RSA 密钥加密的证书,而 ECC (Elliptic Curve Cryptography, 椭圆曲线密码) 密钥的保密性比 RSA 更好,密钥长度更短,更能对抗量子解密等,目前现代的操作系统和浏览器都支持 ECC 证书了(Windows XP 及其之前的就算了)。

    Let's Encrypt 提供了 ECDSA 证书的签发,且 acme.sh 也支持。
    其实只需要加上一个以 ec- 为前缀的 --keylength (-k) 参数即可。理论上上面的各种验证方式都适用。

  • 单域名
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 --debug  2
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 --debug  2
  • 多域名

    支持以下长度的证书,一般就用 ec-256 就行了。

ec-256 (prime256v1, “ECDSA P-256”)
ec-384 (secp384r1, “ECDSA P-384”)
ec-521 (secp521r1, “ECDSA P-521”, which is not supported by Let’s Encrypt yet.)

3.6 安装(copy)证书

签发证书成功后,需要把证书安装或者复制到真正需要的地方,如 nginx / apache 的目录下。

官方说必须用下面的命令来安装证书,不能直接用 ~/.acme.sh/ 目录下的证书文件,因为那只能内部使用,且未来目录结构可能会更改。

我们只需要使用 --install-cert 命令,指定目标位置,然后证书文件就会被 copy 到相应的位置了。

其中域名是必须的,其他参数是可选的。

  • 3.6.1 Nginx

    比如你可以在 nginx 的目录下新建一个 cert.d 目录,然后把证书安装 copy 过去。

acme.sh --install-cert -d example.com \
--key-file   /etc/nginx/cert.d/example.com.key \
--fullchain-file /etc/nginx/cert.d/example.com.fullchain.cer \
--reloadcmd  "service nginx force-reload" --debug  2

这里用的是 nginx force-reload, 不是 nginx reload, 据测试发现因为后者并不会重新加载证书。
Nginx 配置 ssl_certificate 使用 /etc/nginx/cert.d/fullchain.cer,而非 /etc/nginx/cert.d/example.cer,否则 SSL Labs 的测试会报 Chain issues Incomplete 错误。

  • 2.6.2 Apache
acme.sh --install-cert -d example.com \
--cert-file      /path/to/certfile/in/apache/cert.pem  \
--key-file       /path/to/keyfile/in/apache/key.pem  \
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd     "service apache2 force-reload" --debug  2

在命令中的 reloadcmd 参数很重要! 这个用来指定证书更新(Renew)后执行的命令,从而使续期后的证书生效。

默认 60 天就会续期一次,上面这些参数会记录下来并自动执行。非常方便

4. 生成 dhparam.pem 文件(可选)

openssl dhparam -out /etc/nginx/cert.d/dhparam.pem 2048

这一步是为了增强 SSL 的安全性。这里生成一个更强壮的 DHE 参数。

前向安全性(Forward Secrecy)的概念很简单:客户端和服务器协商一个永不重用的密钥,并在会话结束时销毁它。服务器上的 RSA 私钥用于客户端和服务器之间的 Diffie-Hellman 密钥交换签名。从 Diffie-Hellman 握手中获取的预主密钥会用于之后的编码。因为预主密钥是特定于客户端和服务器之间建立的某个连接,并且只用在一个限定的时间内,所以称作短暂模式(Ephemeral)。

使用了前向安全性,如果一个攻击者取得了一个服务器的私钥,他是不能解码之前的通讯信息的。这个私钥仅用于 Diffie Hellman 握手签名,并不会泄露预主密钥。Diffie Hellman 算法会确保预主密钥绝不会离开客户端和服务器,而且不能被中间人攻击所拦截。

nginx 依赖于 OpenSSL 给 Diffie-Hellman (DH)的输入参数。不幸的是,这意味着 Diffie-Hellman Ephemeral(DHE)将使用 OpenSSL 的默认设置,包括一个用于密钥交换的1024位密钥。因为我们正在使用2048位证书,DHE 客户端就会使用一个要比非 DHE 客户端更弱的密钥交换。
更多参考这里 Guide to Deploying Diffie-Hellman for TLS 吧。

5. 配置 Nginx

修改网站的 conf 配置文件,加入 SSL 的相关配置。

server {
  server_name example.com;
  listen       443 ssl http2 default_server;
  listen       [::]:443 ssl http2 default_server;

  ssl_certificate /etc/nginx/cert.d/example.com.fullchain.cer;
  ssl_certificate_key /etc/nginx/cert.d/example.com.key;
  ssl_dhparam /etc/nginx/cert.d/dhparam.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256:ECDHE+3DES:RSA+3DES';
  ssl_prefer_server_ciphers on;
  # ...
}

ssl_dhparam /etc/nginx/cert.d/dhparam.pem; 是在 #4.生成 dhparam.pem 文件 步骤中生成的,可选。

ssl_ciphers用于指定加密套件,这里采用的是 CloudFlare 家的,具体也不是很清楚 emmm。可以参考一下 Mozilla 的 Wiki:Security/Server Side TLS

  • TLSv1.3(可选)
    现在很多网站都上 TLSv1.3 了,证书检测的网站对于 TLSv1、TLSv1.1 都认为不安全了,Firefox 自 74.0 版本开始也完全放弃对加密协议 TLS 1.0 和 TLS 1.1 的支持了。

  • 对于 TLSv1.3 的配置,需要安装最新版的 openssl(OpenSSL 1.1.1 built with TLSv1.3或更高),而后重新编译 nginx,是有点麻烦这里懒得弄了,后面需要再折腾吧。

  • 开启 HSTS(可选)
    当然,为了更加安全,可以选择开启 HSTS(HTTP Strict Transport Security,HTTP严格传输安全协议),强制浏览器通过 https 进行访问。需要在 location 下的设置中加入一个 header。

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";

接下来的一年(即31536000秒)中,浏览器看到 header 中包含 Strict-Transport-Security 的话就会自动切换到 https。

但是在首次访问网站时如果被劫持了,浏览器还是可能会通过 HTTP 明文传递信息。为此,Chrome 维护了一个 HSTS preload list,内置在浏览器中,对于 Chrome, Firefox, Opera, Safari, IE 11 and Edge 等主流浏览器也适用。可以在这里提交你的域名到这个列表里。(不过提交之前要考虑好,全站上 https 噢

如果 TLS 证书无效或不可信,用户不能忽略浏览器警告继续访问网站。这就是前几天访问 GitHub (Pages) 等网站被拦下来的原因了。

上面的配置完成后检查一下配置是否正确,而后重启 nginx。

nginx -t
systemctl restart nginx

之后即可尝试一下能否通过 https 来访问网站了。

6. 更新证书

证书的有效期为 90 天,acme.sh 默认会 60 天更新(Renew)一次。

在安装 acme.sh 的时候就自动配置了一条 cron 任务了,会每天检查证书的情况。当然可以到 crontab 里看一下。

crontab -l
50 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
# 可修改为
50 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" >>/mnt/disk1/log/acme.sh/acme.out 2>&1

也可以试着用上面这条命令执行看一下相关的配置是否正确。

  • 强制续签
acme.sh --renew -d example.com --force
acme.sh --renew -d example.com --force --ecc  # 如果用的是ECC证书
  • 停止续签证书

  • 查看证书

acme.sh --list
  • 停止 Renew
acme.sh --remove -d example.com [--ecc]

之后手动把目录下的证书移除就行。

0x08 升级 acme.sh

acme.sh --upgrade    # 手动升级
acme.sh --upgrade --auto-upgrade    # 自动升级
acme.sh --upgrade --auto-upgrade 0  # 停止自动升级
  • 小结

    除了上面这些配置之外,acme.sh 还提供了通知提醒,可以调用其他 API 来推送提醒,具体参考官方Wiki:notify。

    总之这个工具还是很实用的,大大降低了 SSL 配置的门槛,至于整个操作过程的安全性,只能说世界上的绝对安全是不可能的,永远只有相对或更的区别,都是想方设法尽可能做到安全。

    由于时效性可能导致部分内容不适用,可参考官网:https://github.com/acmesh-official/acme.sh

7. FAQ

  • -

8. 相关

留言

您的电子邮箱地址不会被公开。