# 用acme.sh实现ssl证书部署及自动续签

> wandoubaba / 2024-11-04

## 介绍

`acme.sh`是一个实现了 `acme` 协议的客户端工作，使用它可以申请`Let's Encrpypt`、`TrustAsia`等机构的免费SSL证书并可以实现自动部署与自动续签。

项目地址：<https://github.com/acmesh-official/acme.sh>

本文主要介绍如何使用 `acme.sh` 为域名申请免费的90天证书，并实现Nginx自动部署和自动续签。

> 本文的操作环境是 Debian 11/12 系统，域名从腾讯云注册（DNSPod提供解析）。

## 安装acme.sh客户端

使用发下命令安装，email可以换成自己的：

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

如果上面的命令安装失败，可以换下面这种方式：

```sh
git clone https://gitee.com/neilpang/acme.sh.git
cd acme.sh
./acme.sh --install -m my@example.com
. ~/.bashrc
```

整个安装过程只会把工具安装到`~/.acme.sh/`目录下，只是在`~/.bashrc`中添加了一句`. "/root/.acme.sh/acme.sh.env"`，看一眼`acme.sh.env`内容，是这样的：

```sh
cat /root/.acme.sh/acme.sh.env
# 结果
export LE_WORKING_DIR="/root/.acme.sh"
alias acme.sh="/root/.acme.sh/acme.sh"
```

建议再安装个`socat`（它本身功能也十分强大，是linux网络运维的瑞士军刀），可以用于以`standalone mode`申请证书时临时“伪装”一个服务器出来。

```sh
apt-get install socat
```

## 确认证书申请方式

在`acme.sh`的官方文档中可以看到分发证书的方式有很多种，比如文件验证、独立模式、手动DNS、自动DNS等等，我们为了实现全自动化和未来的自动续签，所以我们选择“自动DNS解析方式”。

我的域名注册商是腾讯云，解析服务由DNSPod提供，幸运的是`acme.sh`支持DNSPod平台的API密钥，所以我先到DNSPod平台上去创建一组密钥：

- 登录网址：<https://www.dnspod.cn/>
- 登录控制台
- 我的账号 - API密钥
- 切换到`DNSPod Token`（不要用腾讯云API密钥）
- 记录`ID`和`Token`（页面关闭后就再也看不见了）

执行下面命令：

```sh
export DP_Id=<Id>
export DP_Key=<Token>
```

## 申请证书

```sh
acme.sh --issue --dns dns_dp -d example.com
```

如果执行成功，最后可以看到类似下面的结果，说明已经将证书文件安装到了本地：

```sh
[Mon Nov  4 01:12:52 PM CST 2024] Your cert is in: /root/.acme.sh/example.com/example.com.cer
[Mon Nov  4 01:12:52 PM CST 2024] Your cert key is in: /root/.acme.sh/example.com/example.com.key
[Mon Nov  4 01:12:52 PM CST 2024] The intermediate CA cert is in: /root/.acme.sh/example.com/ca.cer
[Mon Nov  4 01:12:52 PM CST 2024] And the full chain certs is there: /root/.acme.sh/example.com/fullchain.cer
[Mon Nov  4 01:12:52 PM CST 2024] _on_issue_success
```

## 部署证书

下面分别是在apache上和在nginx上部署证书的示例，需要替换的内容是`example.com`和相`关文件的绝对路径`，以及服务`重新加载配置的命令`。

### Apache示例:

```sh
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"
```

### Nginx示例:

```sh
acme.sh --install-cert -d example.com \
--key-file       /path/to/keyfile/in/nginx/key.pem  \
--fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd     "service nginx force-reload"
```

### 修改配置文件

acme.sh只会重新部署证书文件，不会去更改任何其他配置文件（为了安全考虑），所以，要让web服务器应用证书，还需要手动去修改相关的配置文件。

我们以nginx为例，提供一个web站点加载ssl证书并提供https服务（开443端口）的示例：

```nginx
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    # ssl
    listen       443 ssl;
    server_name  localhost;

    ssl_certificate /etc/nginx/certs/cert.pem; # 证书文件路径
    ssl_certificate_key /etc/nginx/certs/key.pem; # 私钥文件路径
 
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;



    #access_log  /var/log/nginx/host.access.log  main;

    location / {
         root   /usr/share/nginx/html;
         index  index.html index.htm;
    }

    error_page  404              /404.html;
    location = /404.html {
        root   /usr/share/nginx/html;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

如果我们希望所有的http请求（80）都可以自动转到https（443）上，可以对上面的配置文件做一些修改：

```nginx
server {  
    listen       80;  
    listen       [::]:80;  
    server_name  localhost;  

    # 重定向所有 HTTP 请求到 HTTPS  
    return 301 https://$host$request_uri;  
}  

server {  
    listen       443 ssl;  
    listen       [::]:443 ssl;  
    server_name  localhost;  

    ssl_certificate /etc/nginx/certs/cert.pem; # 证书文件路径  
    ssl_certificate_key /etc/nginx/certs/key.pem; # 私钥文件路径  

    ssl_session_cache shared:SSL:1m;  
    ssl_session_timeout  10m;  
    ssl_ciphers HIGH:!aNULL:!MD5;  
    ssl_prefer_server_ciphers on;  

    #access_log  /var/log/nginx/host.access.log  main;  

    location / {  
        root   /usr/share/nginx/html;  
        index  index.html index.htm;  
    }  

    error_page  404              /404.html;  
    location = /404.html {  
        root   /usr/share/nginx/html;  
    }  

    # redirect server error pages to the static page /50x.html  
    error_page   500 502 503 504  /50x.html;  
    location = /50x.html {  
        root   /usr/share/nginx/html;  
    }  
}
```

修改完配置文件后，再要重新启动服务或者重新加载一下配置，才会生效。

## 验证自动续签机制

```sh
crontab -l
```

如果看到有下面这样的定时任务，说明acme.sh的自动续签机制是生效的

```sh
43 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
```
