使用acme.sh和阿里云dns来自动签发ssl证书

使用acme.sh和阿里云dns来自动签发ssl证书

前言

自己建过站的小伙伴都知道,为了网站的安全性、网站被搜索引擎收录等等目的,将网站从http升级到https都是一个非常必要的工作。

而这个过程中,最有名、实用的工具就是acme.sh了,它是一个免费为域名生成ssl证书的程序,本文主要内容是关于acme的使用,以及如何将其搭配DNS以更好的使用,nginx配置。

acme.sh项目的地址:https://github.com/acmesh-official/acme.sh

acme.sh已经提供了非常容易阅读、容易理解的文档,推荐遇到问题先看文档:https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E

安装ACME.SH

官方的一键安装代码:

1
curl https://get.acme.sh | sh -s email=my@example.com

其中需要提供一个email,根据官网文档的说明,这个email是用于在Let’s Encrypt上注册账号用的,如果证书快过期了,会收到提醒邮件(我是从来没有收到过的)。

现在的版本如果没有提供email会报错,所以还是需要提供一个的。

但是官网的一键安装会将工具自动安装到目前用户的家目录下,如果你是root用户就会安装到/root/.acme.sh/文件夹下面,生成的证书也是在这个文件夹,如果想换一个位置安装,那么需要这样操作:

1
2
git clone https://github.com/acmesh-official/acme.sh.git && cd acme.sh
./acme.sh --install --home "软件安装目录" --cert-home "证书存放目录" --accountemail "my@example.com"

第二行中提供了三个参数,**--home指定了软件安装到哪里,--cert-home指定了软件生成的证书放在哪里**。

例如,我想把软件安装到/usr/local/acme.sh文件夹,证书存放在/usr/local/nginx/conf/ssl,那么就执行:

1
./acme.sh --install --home /usr/local/acme.sh --cert-home /usr/local/nginx/conf/ssl --accountemail  "my@example.com"

需要注意的是,如果文件夹不存在那么会自动创建,安装过程会直接根据设定来安装,不会再在下面创建一个acme.sh文件夹,所以**不能将参数写成--home /usr/local/**。

进一步设置

安装好acme.sh文件夹后,进入安装目录,会发现有一个文件叫做account.conf,内容就是这样的:

1
2
3
4
5
6
7
8
9
10
11
#LOG_FILE="/usr/local/acme.sh/acme.sh.log"
#LOG_LEVEL=1

#AUTO_UPGRADE="1"

#NO_TIMESTAMP=1


CERT_HOME='/usr/local/nginx/conf/ssl'
ACCOUNT_EMAIL='my@example.com'
UPGRADE_HASH='7221d488e54dfc6bcb30ca562f6d6e38ec5bf6ce'

可以看到其实acme的设置就是放在这里的,我们可以直接修改这个conf文件来修改设置。比如修改email,修改证书存放目录就修改CERT_HOME=XXX。

利用NDS生成证书的设置

acme.sh有两种验证的方式,一种是http,这种要求acme必须安装在部署网站的服务器上,例如为域名 haoran.co 创建证书,acme验证的时候,是会在域名文件夹下面生成验证文件,然后通过http访问来读取这个验证文件是否跟生成的一致,如果一致,那么就说明进行证书申请操作的是域名的拥有者。

虽然acme还会自动读取nginx配置来获取域名文件夹(部署的时候添加–nginx参数),但是有的时候这种方法是不适用的,比如域名是proxy到了后台运行的端口上等等,或者我是在其他的服务器上申请证书,那么就需要用到另一种验证方式:dns验证。

dns验证的原理是,如果你是域名的拥有者,那么意味着你可以为域名设置解析,比如通过阿里云或者CloudFlare等域名管理者设置dns解析,那么acme就是调用阿里云或其他域名管理者的api,自动添加一条域名解析信息,通常是txt类型,主机记录是[_acme-challenge-域名前缀]的记录,然后再验证实际获得的记录值跟设置的是不是相等,如果相等,那么就说明操作的人可以拿到阿里云的api密钥,拥有域名了。

综上,利用DNS生成证书,最重要要告诉acme两个信息,一是你用的是哪家的api,是阿里云还是cloudflare, dnspod, cloudxns, godaddy等等,acme支持的范围很广,二是api密钥

进行设置有两种方式:

方式一,通过环境变量

1
2
export Ali_Key="xxxxxxxx"
export Ali_Secret="xxxxxxxx"

方式二,直接在account.conf添加两行

1
2
SAVED_Ali_Key='XXXXXX'
SAVED_Ali_Secret='XXXXXXX'

方式一的设置,acme也会自动记录到account.conf文件中,所以这两种方式是完全一致的。

获取阿里云key和secret的过程就不说了,建议在阿里云上单独添加一个子用户(链接在此),添加权限时搜索”dns”,如图为其添加权限:

阿里云子账号添加权限

如果你使用的不是阿里云,那么可以在这里找到支持的dns列表:https://github.com/acmesh-official/acme.sh/wiki/dnsapi

设置默认服务

除了以上的设置,还有一个非常重要的设置:设置acme证书生成时使用的服务

目前acme可以使用的服务有这些(官网链接):

Short Name ACME server URL Usage Wiki
letsencrypt https://acme-v02.api.letsencrypt.org/directory N/A
letsencrypt_test https://acme-staging-v02.api.letsencrypt.org/directory N/A
buypass https://api.buypass.com/acme/directory BuyPass.com CA
buypass_test https://api.test4.buypass.no/acme/directory BuyPass.com CA
zerossl https://acme.zerossl.com/v2/DV90 ZeroSSL.com CA
sslcom https://acme.ssl.com/sslcom-dv-rsa, https://acme.ssl.com/sslcom-dv-ecc SSL.com CA
google https://dv.acme-v02.api.pki.goog/directory Google Public CA
googletest https://dv.acme-v02.test-api.pki.goog/directory Google Public CA

这些服务有的是大陆可以很好访问的,比如letsencrypt,但也有些访问经常失败。

在我过去申请证书遇到的最多的错误,就是连接不上服务,最后证书申请失败。

为了一劳永逸的解决这个问题,在account.conf文件中添加这样一行:

1
DEFAULT_ACME_SERVER='https://acme-v02.api.letsencrypt.org/directory'

这样acme就会默认使用这个服务来申请证书了。

当然,每次申请证书的时候,也可以添加上--server letsencrypt参数来指定服务

申请证书

前面各种都设置好了之后(总共也就是添加了三行而已),后面申请证书就非常简单了:

1
2
cd acme安装目录
./acme.sh --issue --dns dns_ali -d 你的域名

其中,--dns dns_ali必须要指定,这样acme才知道是使用阿里云dns来验证。

-d后面跟的是域名。现在acme支持泛域名证书申请,例如为your_domain域名申请泛域名证书,那么就这样写:

1
./acme.sh --issue --dns dns_ali -d your_domain -d *.your_domain

泛域名证书可以为所有的下一级子域名生效,比如your_domain的泛域名证书,可以为test1.your_domain提供验证,可以为haha.your_domain提供验证,但是无法为haha.haha.your_domain域名提供验证。

更新证书

在安装acme的时候,acme会自动添加一条crontab规则,可以运行 crontab -l查看:

1
24 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

每天夜间0点24分的时候,acme都会尝试自动更新所有快要过期的证书。

手动更新也不是不可以:

1
cd acme安装目录 && ./acme.sh --cron

还可以添加–force参数来强制更新,但是没必要对吧。

nginx配置

nginx配置ssl证书也没有什么难度,这里有一个模板:

记得替换里面的your_domain为你的域名

另外有两个地方需要重点注意:

  1. ssl_certificate。ssl_certificate这个是提供证书文件的路径,通常是cer,pem格式,acme证书一般会生成两个cer文件,一个叫做fullchain.cer,一个是your_domain.cer,fullchain.cer里面包含了ca证书链,推荐使用fullchain.cer,如果使用your_domain.cer,有的情况下域名验证会失败(比如python requests),配置了域名证书,但证书却验证失败是一个非常让人头大的问题,很有可能就是这里配置有错误!

  2. ssl_dhparam。这是在进行ssl证书时执行Diffie-Hellman (DH) 密钥交换相关的设置,涉及到ssl证书验证的一个环节——服务器SSL/TLS的瞬时Diffie-Hellman公共密钥,如果没有指定,会使用1024位的,安全套接层(Secure Sockets Layer,SSL)公共密钥小于1024可能导致存在可以恢复纯文本信息的风险。为了安全期间,还是建议生成一个2048位或4096位的ssl_dhparam文件,只需运行代码:

    1
    2
    3
    openssl dhparam -out dhparam.pem 2048
    # 生成4096位的:
    openssl dhparam -out dhparam.pem 4096

    然后设置ssl_dhparam的路径即可。注意,该文件的名称必须是dhparam.pem,否则nginx可能会报错!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server {
listen 80;
server_name your_domain *.your_domain; # 泛域名设置:*.
return 301 https://$host$request_uri;
access_log /home/wwwlogs/your_domain.log;
}

server {
listen 443 ssl http2;
server_name *.your_domain your_domain;
index index.html index.htm index.php default.html default.htm default.php;
root /home/wwwroot/your_domain; # 替换为真实路径

ssl_certificate /usr/local/nginx/conf/ssl/fullchain.cer; # 替换为真实路径
ssl_certificate_key /usr/local/nginx/conf/ssl/your_domain.key; # 替换为真实路径
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES;
#ssl_dhparam /usr/local/nginx/conf/ssl/dhparam.pem; # 替换为真实路径

location ~ /.well-known {
allow all;
}

location ~ /\. {
deny all;
}

access_log /home/logs/your_domain.log;
}

如果是需要进行后端代理,那么可以用这个模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
upstream your_backend {
server your_ip:port; # 替换为真实后端路径
keepalive 64;
}

server {
listen 80;
server_name your_domain;
access_log /home/logs/your_domain.log;
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name your_domain;

ssl_certificate /usr/local/nginx/conf/ssl/fullchain.cer; # 替换为真实路径
ssl_certificate_key /usr/local/nginx/conf/ssl/your_domain.key; # 替换为真实路径
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 20m;
server_tokens off;
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_verify_depth 10;
#ssl_dhparam /usr/local/nginx/conf/ssl/dhparam.pem; # 替换为真实路径

location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://your_backend;
}

location ~ /\. {
deny all;
}

access_log /home/logs/your_domain.log;
error_log /home/logs/error.your_domain.log error;
}

其他可选项:

ssl_ciphers配置:

1
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256: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";

密码套件的顺序非常重要,因为它决定在优先级算法将被选中。上面的建议重视算法提供完美的向前保密。

确保你已经添加了以下几行:

1
2
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

ssl_prefer_server_ciphers:在SSLv3或这是TLSv1握手时选择一个密码,通常是使用客户端的偏好。如果这个指令是启用的,那么服务器反而是使用服务器的偏好。

作者

Haoran

发布于

2022-08-30

更新于

2022-08-30

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×