HTTP(三)基于Acme.sh向Let's Encrypt 进行证书申请

AI 摘要: 本文介绍了如何使用acme.sh工具向Let's Encrypt自动申请证书,并对证书的验证过程进行了介绍。还提供了参考链接以供进一步学习。

1. 引言

之前有两篇文章简单介绍了什么是 CA 以及 Openssl 的简单的操作,本篇介绍如何快速通过 acme.sh 这个开源项目,向 Let’s Encrypt 进行证书申请

协议方面,Let's Encrypt用于自动注册证书颁发机构的质问-响应协议称为自动证书管理环境(ACME),它涉及对证书涵盖的域上的 Web 服务器的各种请求,基于所得到的响应是否与期望相匹配,确保了对域的登记者的控制(域验证)。

Let's Encrypt在签发过程中,这种行为称之为会向签发者的挑战,让签发者证明其为被签发的域名的拥有者,除了本文介绍的在服务器上进行证书挑战,还有基于 DNS 方式的挑战等

2. 下载并安装 acme.sh

安装过程完成了下列内容,参考: https://github.com/Neilpang/acme.sh

  1. 创建和拷贝 acme.sh$HOME目录: ~/.acme.sh/(所有证书也会放这个目录下)
  2. 创建一个 alias: acme.sh=~/.acme.sh/acme.sh
  3. 创建了一个每天定时任务,按需重新签发需要更新的证书
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 直接用git安装
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh

# 指定你的邮箱(通知)
./acme.sh --install -m hold7habits@gmail.com
[Sun Jun 11 15:16:20 CST 2023] It is recommended to install socat first.
[Sun Jun 11 15:16:20 CST 2023] We use socat for standalone server if you use standalone mode.
[Sun Jun 11 15:16:20 CST 2023] If you don't use standalone mode, just ignore this warning.
[Sun Jun 11 15:16:20 CST 2023] Installing to /root/.acme.sh
[Sun Jun 11 15:16:20 CST 2023] Installed to /root/.acme.sh/acme.sh
[Sun Jun 11 15:16:20 CST 2023] Installing alias to '/root/.bashrc'
[Sun Jun 11 15:16:20 CST 2023] OK, Close and reopen your terminal to start using acme.sh
[Sun Jun 11 15:16:20 CST 2023] Installing alias to '/root/.cshrc'
[Sun Jun 11 15:16:20 CST 2023] Installing alias to '/root/.tcshrc'
[Sun Jun 11 15:16:20 CST 2023] Installing cron job
[Sun Jun 11 15:16:20 CST 2023] Good, bash is found, so change the shebang to use bash as preferred.
[Sun Jun 11 15:16:21 CST 2023] OK

# crontab -l
20 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

3. 用 acme.sh 签发阿里云域名 SSL 证书

3.1. 获取域名 KEY 和 SECRET

进入到阿里云官网生产 KEY 和 SECRET: https://ram.console.aliyun.com/users

3.2. 导出 KEY 和 SECRET 到环境变量

1
2
3
# export KEY和SECRET到环境变量中
export Ali_Key="yours key"
export Ali_Secret="yours secret"

可以通过~/.acme.sh/dnsapi/dns_ali.sh 了解更多,后续签发证书后也会把该 KEY 和 SECRET 保存在~/.acme.sh/account.conf 文件中

3.3. 通 acme.sh 的 DNS 模式挑战,签发域名证书

 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
# acme.sh --issue --dns dns_ali -d archstat.com -d *.archstat.com \
    --cert-file /etc/nginx/ssl/archstat.com.cert \
    --key-file /etc/nginx/ssl/archstat.com.key \
    --fullchain-file /etc/nginx/ssl/archstat.com.fullchain \
    --reloadcmd "systemctl restart nginx.service"

# 执行成功后,可以看到信息
...
Sat Mar 25 15:43:18 CST 2023] All success, let's return
[Sat Mar 25 15:43:18 CST 2023] Verifying: archstat.com
[Sat Mar 25 15:43:20 CST 2023] Processing, The CA is processing your order, please just wait. (1/30)
[Sat Mar 25 15:43:25 CST 2023] Success
[Sat Mar 25 15:43:25 CST 2023] Verifying: *.archstat.com
[Sat Mar 25 15:43:28 CST 2023] Processing, The CA is processing your order, please just wait. (1/30)
[Sat Mar 25 15:43:33 CST 2023] Success
[Sat Mar 25 15:43:33 CST 2023] Removing DNS records.
[Sat Mar 25 15:43:33 CST 2023] Removing txt: GTUoo9KJE4sKMS3oEIPMd5_pIrn_51nkHV5JV6BN_mo for domain: _acme-challenge.archstat.com
[Sat Mar 25 15:43:35 CST 2023] Removed: Success
[Sat Mar 25 15:43:35 CST 2023] Removing txt: iAhX2CH3CND4OC68mCSvAbqq5lfd8eWdirunLGrT_eM for domain: _acme-challenge.archstat.com
....
[Sat Mar 25 15:43:59 CST 2023] Your cert is in: /root/.acme.sh/archstat.com_ecc/archstat.com.cer
[Sat Mar 25 15:43:59 CST 2023] Your cert key is in: /root/.acme.sh/archstat.com_ecc/archstat.com.key
[Sat Mar 25 15:43:59 CST 2023] The intermediate CA cert is in: /root/.acme.sh/archstat.com_ecc/ca.cer
[Sat Mar 25 15:43:59 CST 2023] And the full chain certs is there: /root/.acme.sh/archstat.com_ecc/fullchain.cer
[Sat Mar 25 15:43:59 CST 2023] Installing cert to: /etc/nginx/ssl/archstat.com.cert
[Sat Mar 25 15:43:59 CST 2023] Installing key to: /etc/nginx/ssl/archstat.com.key
[Sat Mar 25 15:43:59 CST 2023] Installing full chain to: /etc/nginx/ssl/archstat.com.fullchain
[Sat Mar 25 15:43:59 CST 2023] Run reload cmd: systemctl restart nginx.service

acme.sh 相关参数说明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# acme.sh -h
--issue: 证书签发
--dns [dns_ali]: 使用DNS手动或API模式挑战,https://github.com/acmesh-official/acme.sh/wiki/dnsapi
-d,--domain <domain>: 指定要issue、renew、revoke的域名,支持通配符域名
--cert-file <file>: 在证书签发,重签后,证书Cert文件要拷贝的路径
--key-file <file>:  在证书签发,重签后,证书Key文件要拷贝的路径
--ca-file <file>:   在证书签发,重签后,中间CA证书文件要拷贝的路径
--fullchain-file <file>:  在证书签发,重签后,证书链要拷贝的文件
--days <ndays>: 指定在`--issue`重签间隔天数,默认是60天
--debug: 加入该参数,可以用于调试查看debug信息

签发成功后,后续 cron 定时任务会每天检测证书是否需要重签,重启成功后将重签证书拷贝到对应的 nginx 配置目录,这里是/etc/nginx/ssl目录:

1
2
3
4
5
# tree /etc/nginx/ssl
/etc/nginx/ssl
├── archstat.com.cert
├── archstat.com.fullchain
└── archstat.com.key

4. Nginx 服务的 SSL 配置参考

因为是通配符证书,考虑到证书可能会在多个 server 复用,抽离出来一个ssl_cert.conf文件:

1
2
3
4
# /etc/nginx/ssl_cert.conf
ssl_certificate         ./ssl/archstat.com.fullchain;
ssl_certificate_key     ./ssl/archstat.com.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

nginx 的 server 段部分 ssl 配置项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
    listen 443 ssl;
    server_name archstat.com;

    # ssl config
    include ssl_cert.conf;

    location / {
        return 200 "ok";
    }
}

通过 curl 验证 TLS 是否生效:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# curl -IvL https://archstat.com
* Rebuilt URL to: https://archstat.com/
...
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=archstat.com
*  start date: Mar 25 00:00:00 2023 GMT
*  expire date: Jun 23 23:59:59 2023 GMT
*  subjectAltName: host "archstat.com" matched cert's "archstat.com"
*  issuer: C=AT; O=ZeroSSL; CN=ZeroSSL ECC Domain Secure Site CA
*  SSL certificate verify ok
...

5. 通过 acme.sh 签发证书,验证域名归属(挑战)方式

参考: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert

常见包括 DNS、HTTP 模式:

  • DNS API
  • DNS manual mode:
  • DNS alias mode
  • Nginx mode
  • Apache mode

5.1. Let’s Encrypt 进行 DNS 挑战,这个是操作最简易的,blog 自建的可以优先考虑这类

该 Mode 是基于 DNS API 认证方式(acme.sh 会动态的增加、删除 NX 解析,以证明域名是归你管理)

参考: https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode

具体云厂商 DNS API 认证挑战:https://github.com/acmesh-official/acme.sh/wiki/dnsapi 以阿里云为例:

  1. 进入到阿里云官网,设置访问 key,安全方面基于最小权限设定(建立组、给组分配仅可设定 dns 相关的权限,指定特定的账户的访问 key) https://ram.console.aliyun.com/users

  2. 将有限权限的 dns 账户,获取对应的 key 和秘钥,加入到.bashrc中 export Ali_Key=“yours key” export Ali_Secret=“yours secret”

  3. 签发多个通配符相关的证书、安装到指定目录、并设定重启:

1
2
3
acme.sh --issue --dns dns_ali -d tkstorm.com -d *.tkstorm.com -d archstat.com -d *.archstat.com -d perf.plus -d lupguo.cool \
  --fullchain-file /usr/local/nginx/conf/ssl/tkstorm.com.chained.cert --key-file /usr/local/nginx/conf/ssl/tkstorm.com.key \
  --reloadcmd "service nginx reload"

5.2. Let’s Encrypt 进行 HTTP 接口挑战,GET /.well-known/acme-challenge

该 Mode 是基于 Token GET 请求验证证书归属后,进行证书签发

1
acme.sh  --issue  -d go.archstat.com  -w /data/ssl_challenge --debug
  1. 初始化一个 entry: entry='"type":"http-01","url":"https://acme.zerossl.com/v2/DV90/chall/m4qQXcjcTJd3HiXHYj6ltw","status":"pending","token":"KF8l7T4FX0Rq8rU07No4T6mQ_ZsIBK1zhcDfQqBxez0"'
  2. 确定/.well-known/acme-challenge的 www 地址为/data/lets_challenge
  3. 生成一个 token 文件到 www 目录下
  4. 后 CA 会来检查对应的 token 内容,如果一致则挑战通过,进行域名的证书签发

比如请求: http://go.archstat.com/.well-known/acme-challenge/KF8l7T4FX0Rq8rU07No4T6mQ_ZsIBK1zhcDfQqBxez0'

配置 Nginx 服务器相关信息

  1. 创建一个独有可访问的 URI(location ~ ^/.well-known/acme-challenge/)提供给 Let’s Encrypt 检测,用于证书授权挑战(其他的还有诸如 dns 挑战等)
  2. 创建一个专门用于证书申请的 server(参见下述配置),将挑战文件统一将认证的文件 root 指定到(/data/lets_challenge

5.3. 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
32
33
34
35
36
37
38
39
server {
    listen 80 default_server;
    # 1) 52explore.com (博客)

    server_name 52explore.com www.52explore.com

    # 2) archsatt.com (资料)
    server_name archstat.com www.archstat.com;

    # 3) tkstorm.com (博客)
    server_name tkstorm.com www.tkstorm.com static.tkstorm.com;

    # 4) psr100.cn (静态资源)
    server_name psr100.cn www.psr100.cn;
    server_name static.psr100.cn;
    server_name pac.psr100.cn;
    server_name git.psr100.cn;
    server_name disqus.psr100.cn;

    # 5) psr100.com
    # server_name psr100.com www.psr100.com;

    # 6) ioio.cool
    # server_name ioio.cool www.ioio.cool;

    # 7) heyman.cool
    # server_name heyman.cool www.heyman.cool;

    # let's encrypt域验证,我们把证书生成的内容存储到`/data/lets_challenge`中,让Let'S Encrypt进行查询认证
    location ~ ^/.well-known/acme-challenge/ {
        alias /data/lets_challenge;
        try_files $uri =404;
    }

    # http请求转https
    location / {
        return 301 https://$host$request_uri;
    }
}

针对所有域名 80 端口相关,提供给 Let’ Encrypt 的挑战,这个 nginx 服务,做了以下几个事情:

  • 列出了所有需要进行证书签发的域名,进行统一管理授权和签发
  • 将 acme.sh 把证书生成的内容存储到/data/lets_challenge中,设置一个 locaiton,让 Let’S Encrypt 进行查询认证
  • 为了统一处理,后续请求这些域名的 http 协议统一转发到 https 协议处理(这块当然也可以在各自的 vhost 中设定)

6. 其他

6.1. 通过 acme.sh 代理,进行 ssl 证书签发挑战:(正式申请去掉—test、debug)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 签发支持多个域名的SAN证书
acme.sh --issue \
   -d archstat.com -d www.archstat.com -d docker.archstat.com \
   -d tkstorm.com -d www.tkstorm.com -d static.tkstorm.com \
   -w /data/lets_challenge \
   --test --debug

# 签发通配符证书,存储到/data/lets_challenge 下
acme.sh --issue -d archstat.com -d *.archstat.com \
   -w /data/lets_challenge \
   --test --debug

6.2. domain 认证挑战成功后,进行证书安装,acme.sh 将自动创建一个 cron 任务,并自动签署和更新证书

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
acme.sh --install-cert \
        -d archstat.com -d www.archstat.com -d docker.archstat.com\
        -d tkstorm.com -d www.tkstorm.com -d static.tkstorm.com \
        --cert-file /usr/local/nginx/conf/ssl/arch_tkstorm.cert \
        --key-file /usr/local/nginx/conf/ssl/arch_tkstorm.key \
        --fullchain-file /usr/local/nginx/conf/ssl/arch_tkstorm.fullchain \
        --reloadcmd "systemctl restart nginx.service"

acme.sh --install-cert \
        -d psr100.cn -d www.psr100.cn  -d static.psr100.cn -d pac.psr100.cn -d git.psr100.cn -d disqus.psr100.cn\
        --cert-file /usr/local/nginx/conf/ssl/psr100.cert \
        --key-file /usr/local/nginx/conf/ssl/psr100.key \
        --fullchain-file /usr/local/nginx/conf/ssl/psr100.fullchain \
        --reloadcmd "systemctl restart nginx.service"

# 签发通配符证书
acme.sh --install-cert -d archstat.com -d *.archstat.com \
      --cert-file /etc/nginx/ssl/archstat.com.cer \
      --key-file /etc/nginx/ssl/archstat.com.key \
      --fullchain-file /etc/nginx/ssl/archstat.com.fullchain \
      --reloadcmd "systemctl restart nginx.service"

6.3. 如果定时任务没有开启的话,开启定时任务自动更新签发证书

1
acme.sh --install-cronjob

7. 参考