跳到主要内容

使用 acme.sh 申请泛域名证书

本文字数: 2988阅读需 10 分钟
沐晨

acme.sh 是一个开源的,实现 ACME 客户端协议的纯 Unix shell 脚本,提供颁发、安装和自动更新证书、邮件通知等功能。 随着作者不断更新,未来将支持更多 CA,目前已经支持 CA 如下:

CA有效期/天ECC算法最大域名数量通配符证书IPv4IPv6NotAfterIDN
Let's Encrypt90Yes100YesNoNoNoYes
ZeroSSL90Yes100YesNoNoYesYes
Google90Yes100YesNoNoYesNo
Buypass180Yes5PaidNoNoNoYes
SSL.com90Yes2PaidNoNoNoYes
HiCA180Paid10 (1 if Wildcard)YesPaidPaidNoPaid

acme.sh Github Wiki 页面介绍已经很全面了, 本篇文章仅作为导读 & 快速部署使用。

提示

划重点,acme.sh 申请的证书,不管是根域名、子域名、泛域名,通通免费、免费、免费!!! 相比各大云服务商购买的子域名500+、泛域名证书2k/年,还要手动部署,每年手动更新,对个人站点来说使用 acme.sh 更省心。

安装 acme.sh

安装到用户目录

$ curl https://get.acme.sh | sh -s email=[email protected]
提示

安装完成后,必须关闭当前终端并重新打开才能使用 acme.sh -h,或使用 source 命令重新加载配置,如 zsh: source ~/.zshrc,加载的配置文件取决于你使用的 shell

安装到 Docker(推荐)

docker-compose.yml
version: '3.8'
services:
acme.sh:
image: neilpang/acme.sh
container_name: acme.sh
command: daemon
volumes:
- /etc/localtime:/etc/localtime
- ./acmeout:/acme.sh
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=example.com
- DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/ssl/example.com/key.pem
- DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/example.com/cert.pem"
- DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/example.com/ca.pem"
- DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/example.com/fullchain.pem"
- DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="nginx -s reload"

参考 部署到 docker 容器

注册 ZeroSSL

acme.sh 文档 中提到 v3.0 以后,默认的 CA 将使用 ZeroSSL。相比 Let's Encrypt,ZeroSSL API没有速率限制、还提供了 WEB 界面管理证书。

这里可以查看功能比较:ZeroSSL vs Let's Encrypt

注意,如果通过 ZeroSSL 官网申请 SSL 证书, 免费账户是有 3 个 90 天期证书的额度限制,但通过 acme.sh 申请则没有这个限制。

警告

因为使用了 ZeroSSL 作为默认 CA,申请证书之前,必须先在 ZeroSSL 官方网站 注册一个账号,并配置到 acme.sh

  1. https://app.zerossl.com/developer 生成 EAB 凭据
  2. 执行命令注册您的 EAB 凭据:
$ acme.sh --register-account  --server zerossl \
-m [email protected] \
--eab-kid kid \
--eab-hmac-key hmac_key

如果不想使用 ZeroSSL 和注册账号的话可以参考 ACME 服务器文档 切换到其他 CA 提供商

申请证书

官方提供的域名所有权认证方式有两种:

  • HTTP 模式,需要在你的网站根目录下放置一个文件, 来验证你的域名所有权。
  • DNS 模式,手动或使用 DNS 解析服务商提供的 api 添加 txt 解析记录验证域名所有权。

申请证书命令参数示例,根据使用的方式不同还需要添加其它参数才能执行:

# 使用 -d 参数添加需要申请证书的域名,可添加多个。
$ acme.sh --issue -d example.com -d www.example.com

以下所有模式中,证书申请成功后文件会自动存放在安装目录 ~/.acme.sh/example.com,文件夹名称为你添加的域名参数。

提示

泛域名证书的解析目前仅支持 DNS 方式验证,推荐使用 DNS API 模式来验证你的域名所有权

HTTP

HTTP 方式需要在你的网站根目录下放置一个文件, 来验证你的域名所有权。

webroot 模式

如果你已经在服务器上搭建好 Web 服务,acme.sh也安装到了此服务器,可以通过 webroot 的方式来验证申请,命令如下:

$ acme.sh --issue -d example.com -d www.example.com -w /www/example.com

其中 /www/example.com 是网站根目录,要确保能通过 http 正常访问,可以使用 curl 测试如: curl example.com

提示

注意,此方式必须指定域名和域名部署所在的网站根目录; acme.sh在执行过程中会在网站根目录生成一个隐藏文件夹“.well-known”,并在里面自动生成验证文件, 然后自动完成验证,最后会也会自动的删除验证文件, 整个过程没有任何副作用。

standalone 模式

如果服务器上没有 Web 服务,仅安装了acme.sh,则可以使用 standalone 模式申请域名证书, 此时需要将你要申请证书的域名做A记录到这台服务器的IP,acme.sh 有一个内置的独立 Web 服务器, 它可以监听 80 端口以颁发证书。

$ acme.sh --issue -d example.com -d www.example.com --standalone

如服务器未开放 80 端口,可通过参数设置其它端口 --httpport 8080

nginx 模式

如果您的网站正在运行 nginx 服务器,acme.sh 可以使用 nginx 服务器颁发证书。 并且 acme.sh 将在颁发证书后恢复您的 nginx conf。

$ acme.sh --issue -d example.com --nginx

# 还可以指定一个 nginx conf
$ acme.sh --issue -d example.com --nginx /etc/nginx/nginx.conf
提示

如果你使用官方 Docker 镜像 neilpang/acme.sh 安装 acme.sh,请不要使用 nginx 模式申请证书,此处有很多 bug。

DNS

DNS API 模式(推荐,支持泛域名)

这种方式会使用域名服务商提供的 api 自动为你的域名添加一条 txt 解析,验证成功后, 这条解析记录会被删除,大概需要等待 1、2 分钟 等待 DNS 解析。

1、在域名服务商后台申请 Api Token,各域名服务商的配置请参考官方文档

这里以腾讯云为例,登录 DNSPod 后台

  • 点击页面右上角头像、点击 API 密钥 菜单、选择 DNSPod Token
  • 点击 创建密钥 取一个名称如 acme.sh,就能生成一个随机的 IDToken
警告

Token 值只会在创建时显示一次,请记得备份。

建议开启 IP 白名单,防止 Token 泄露后产生损失。

2、执行以下命令申请证书:

# Dnspod.cn Domain Api Token
export DP_Id="1234"
export DP_Key="sADDsdasdgdsf"

# 申请证书,执行后参数将保存至 ~/.acme.sh/account.conf 在自动续期时使用。
$ acme.sh --issue --dns dns_dp -d example.com -d *.example.com

# 如果是使用 docker 安装的 acme.sh 则需要在传递环境变量时需使用 -e 参数,如:
$ docker exec \
-e DP_Id=1234 \
-e DP_Key=sADDsdasdgdsf \
container_name --issue --dns dns_dp -d example.com -d *.example.com

打印日志出现 Cert success 表示成功。

DNS 手动模式

这种方式不需要申请 DNS API Token,但需要你登录到域名服务商后台,手动增加 txt 记录以验证域名所有权。 手动模式不支持自动续期,证书过期后还需要手动添加 txt 纪录,不建议使用。

1、生成随机验证 txt

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

将输出:

Add the following txt record:
Domain:_acme-challenge.www.example.com
Txt value: 9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Please add those txt records to the domains. Waiting for the dns to take effect.

2、登录 DNS 后台,使用 Txt value 新增一条 txt 解析纪录

3、重新执行申请证书,使用 renew 参数:

$ acme.sh --renew -d example.com

安装证书

生成的证书默认都放在安装目录下: ~/.acme.sh, 请不要直接使用此目录下的证书文件, 它们仅供内部使用,文件夹结构将来可能会更改。 例如: 不要直接在 nginx 的配置文件引用这个目录的证书文件,

~/.acme.sh
.
├── .acme.sh
│   ├── example.com
│   │ ├── ca.cer
│   │ ├── fullchain.cer
│   │ ├── example.com.cer
│   │ ├── example.com.key

1、手动拷贝证书到 web 服务器目录,如 nginx:

$ cp ~/.acme.sh/example.com/example.com.key /etc/nginx/ssl/example.com/key.pem;
$ cp ~/.acme.sh/example.com/fullchain.cer /etc/nginx/ssl/example.com/fullchain.pem;

2、使用 --install-cert 命令 (推荐)

# 使用本机命令安装
$ acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com/key.pem \
--fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
--reloadcmd "nginx -s reload"

# 使用 docker 安装

#!/bin/bash
cid=`docker ps | grep acme | awk '{print $1}'`
docker exec ${cid} acme.sh --deploy -d "example.com" --deploy-hook docker

执行命令后,证书文件会被copy到相应的位置, 随后命令中的参数将保存在 ~/.acme.sh/example.com 目录下的 example.com.conf 文件里, 定时任务在自动更新证书的时候会使用这里的参数部署新的证书和 reload 服务器。

提示
  • reloadcmd 参数必须提供正确的重新加载命令,若命令失败,即时更新了证书,从 web 访问是不会生效的。
  • web 服务器使用 cert.pem 会导致 curl 无法通过认证,建议使用 fullchain.pem,具体查看 Nginx SSL configuration - fullchain.pem vs cert.pem

配置服务器

在 /etc/nginx/snippets 目录下创建代码片段文件 ssl-params.conf 专门拿来配置 SSL 相关的设置,内容如下:

/etc/nginx/snippets/ssl-params.conf

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 60m;

ssl_session_tickets on;

ssl_stapling on;
ssl_stapling_verify on;

resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
ssl_prefer_server_ciphers on;

# 证书文件绝对路径
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

# 以下配置来自 [提高安全性的最佳 Nginx 配置](https://godruoyi.com/posts/best-nginx-configuration-for-improved-security),建议参考。
server_tokens off;
add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
add_header X-Frame-Options deny;
add_header X-Content-Type-Options nosniff;
add_header x-xss-protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https:; connect-src 'self' https:; img-src 'self' data: https: blob:; style-src 'unsafe-inline' https:; font-src https:";

接下来配置 server,通过 include 命令引入 ssl-params.conf

Nginx Config
server {
listen 80;
server_name example.com www.example.com;

return 301 https://$server_name$request_uri;
}

server
{
listen 443 ssl http2 fastopen=3 reuseport;
server_name example.com www.example.com;

# 引入 ssl 配置,如果申请的是泛域名证书,使用 include 就可以很方便地管理多个站点
include snippets/ssl-params.conf;

access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;

#root /www/example.com;
#index index.html index.htm;

location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8080;
}
}
# 测试配置是否成功
$ nginx -t

# 重启 web 服务
$ nginx -s reload

证书自动续期

acme.sh 安装时会使用 cron 创建定时任务, 默认的计划任务是每天 0:00 点检查一次是否需要续期。 定时任务可以通过 crontab -l 命令查看,还可以手动配置:

$ crontab -e
# 输入以下任务
0 0 * * * "~/.acme.sh"/acme.sh --cron --home "~/.acme.sh" > /dev/null

crontab文件中,每一行都代表一项任务,每行的每个字段代表一项设置,未设置使用 * 占位,它的格式如下:

minute hour day month week  command

具体命令使用说明参考 crontab

通知

acme.sh 可以在定时任务执行时发送通知。例如在每天晚上的检查任务运行时,您可能会收到基于 notify-level 和 notify-mode 设置的通知。

查看文档 acme.sh/notify

通知可以是电子邮件或任何其他支持的方式,例如请求 webhook 等。也可以自己实现 hook

其他操作

1、查看已签发证书的域名:

$ acme.sh --list

2、移除不需要再次签发证书的域名:

$ acme.sh --remove -d example.com

3、强制刷新证书

$ acme.sh --renew -d example.com --force

4、查看已安装证书信息

$ acme.sh --info -d example.com
# 会输出如下内容:
DOMAIN_CONF=/root/.acme.sh/example.com/example.com.conf
Le_Domain=example.com
Le_Alt=no
Le_Webroot=dns_ali

5、卸载

$ acme.sh --uninstall

参考

acme.sh

Let's Encrypt 泛域名证书申请及配置

Loading Comments...