acme.sh 是一个开源的,实现 ACME 客户端协议的纯 Unix shell 脚本,提供颁发、安装和自动更新证书、邮件通知等功能。 随着作者不断更新,未来将支持更多 CA,目前已经支持 CA 如下:
CA | 有效期/天 | ECC算法 | 最大域名数量 | 通配符证书 | IPv4 | IPv6 | NotAfter | IDN |
---|---|---|---|---|---|---|---|---|
Let's Encrypt | 90 | Yes | 100 | Yes | No | No | No | Yes |
ZeroSSL | 90 | Yes | 100 | Yes | No | No | Yes | Yes |
90 | Yes | 100 | Yes | No | No | Yes | No | |
Buypass | 180 | Yes | 5 | Paid | No | No | No | Yes |
SSL.com | 90 | Yes | 2 | Paid | No | No | No | Yes |
HiCA | 180 | Paid | 10 (1 if Wildcard) | Yes | Paid | Paid | No | Paid |
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(推荐)
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"
注册 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
- 从 https://app.zerossl.com/developer 生成 EAB 凭据
- 执行命令注册您的 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
,就能生成一个随机的ID
、Token
。
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
│ ├── 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 相关的设置,内容如下:
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
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