# lego 用 DNS-01 申请通配符证书（lego v5 教程）

> 用 lego v5 通过阿里云 DNS 或腾讯云 DNSPod 的 API 申请 Let's Encrypt 通配符证书：安装、lego run、部署钩子和每日续期定时器。

Source: https://ssl.sh/zh/guides/lego-dns-01/
Last updated: 2026-11-10

lego 用一条 `lego run` 命令调用 DNS 服务商 API 完成 DNS-01 验证，为 `example.com` 和 `*.example.com` 签发 Let's Encrypt 通配符证书。部署钩子负责安装证书和重载 Web 服务，systemd 定时器每天运行一次负责续期。

适用于 Linux 上的 lego v5（2026 年 5 月发布，最新版本以 GitHub releases 为准）。示例用阿里云 DNS，也给出腾讯云 DNSPod 的写法。

## 前提条件

DNS-01 只要求能调用 DNS API，不需要开放入站端口，80 端口被封或未备案也能用。

- 带 systemd 的 Linux 服务器，有 sudo 权限。
- 域名解析托管在 lego 支持的服务商（`lego dnshelp` 可以列出全部）。
- 阿里云：创建 RAM 子账号，只授予云解析 DNS 的管理权限（如 `AliyunDNSFullAccess`），生成 AccessKey。腾讯云：在访问管理里创建子用户并授予 DNSPod 权限，生成 SecretId/SecretKey。
- 服务器能访问 Let's Encrypt 和 DNS 服务商的 API。

## 第一步：安装 lego

从 lego 的 GitHub releases 页面下载对应平台的压缩包，然后安装：

```bash
tar -xzf lego_*_linux_amd64.tar.gz lego
sudo install -m 755 lego /usr/local/bin/lego
lego --version
```

文档列出的其他方式：`go install github.com/go-acme/lego/v5@latest`（需要 Go 1.25+）、Docker 镜像 `goacme/lego`、Homebrew 和发行版软件包。从 v4 升级的，先对原数据目录运行 `lego migrate`。

## 第二步：保存 DNS 密钥

把密钥写进只有 root 能读的环境变量文件，避免留在命令历史里。阿里云 DNS：

```bash
sudo install -d -m 700 /etc/lego
sudo tee /etc/lego/env > /dev/null <<'EOF'
ALICLOUD_ACCESS_KEY=your-access-key-id
ALICLOUD_SECRET_KEY=your-access-key-secret
EOF
sudo chmod 600 /etc/lego/env
```

腾讯云 DNSPod 改用 `TENCENTCLOUD_SECRET_ID` 和 `TENCENTCLOUD_SECRET_KEY`，后面的 `--dns alidns` 换成 `--dns tencentcloud`。

## 第三步：签发证书

一条 `lego run` 完成签发，`--dns alidns` 表示通过阿里云 DNS API 做 DNS-01 验证：

```bash
sudo bash -c 'set -a; . /etc/lego/env; set +a; \
  lego run --accept-tos --path /etc/lego \
    --dns alidns -d example.com -d "*.example.com"'
```

lego 会自动添加 `_acme-challenge` TXT 记录、等待生效、完成验证并清理记录。想用 Let's Encrypt 的 45 天证书，加 `--profile tlsserver`（见 [Let's Encrypt 证书配置文件](https://ssl.sh/zh/guides/lets-encrypt-certificate-profiles/)）；调试时加 `--server https://acme-staging-v02.api.letsencrypt.org/directory` 使用测试环境。

## 第四步：添加部署钩子

`--deploy-hook` 在证书创建或续期后运行，lego 通过 `LEGO_HOOK_CERT_PATH` 和 `LEGO_HOOK_CERT_KEY_PATH` 传入文件位置：

```bash
sudo tee /etc/lego/deploy.sh > /dev/null <<'EOF'
#!/bin/sh
set -e
install -d -m 700 /etc/nginx/ssl
install -m 644 "$LEGO_HOOK_CERT_PATH"     /etc/nginx/ssl/example.com.crt
install -m 600 "$LEGO_HOOK_CERT_KEY_PATH" /etc/nginx/ssl/example.com.key
systemctl reload nginx
EOF
sudo chmod 755 /etc/lego/deploy.sh
```

Nginx 引用 `/etc/nginx/ssl/example.com.crt`（lego 默认把签发者证书打包进 `.crt`，就是完整证书链）和 `/etc/nginx/ssl/example.com.key`。首次部署用 `lego certificates list --path /etc/lego` 找到文件，手动复制一次。

## 验证

配置正确时，`example.com` 和 `www.example.com` 返回同一张 Let's Encrypt 证书，SAN 里有 `example.com` 和 `*.example.com`。用 [SSL 证书检查](https://ssl.sh/zh/tools/ssl-checker/) 检查，或在终端运行：

```bash
curl ssl.sh/example.com
curl ssl.sh/www.example.com
```

在服务器上：

```bash
sudo lego certificates list --path /etc/lego
openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -ext subjectAltName -enddate
```

## 自动续期

lego 没有常驻进程，每天运行一次 `lego run`，没到期就什么都不做。`--renew-days 0`（默认）时按证书有效期自动计算续期时间，并按 Let's Encrypt 的 ARI 窗口续期（除非加了 `--ari-disable`）。创建服务和定时器：

```ini
# /etc/systemd/system/lego.service
[Unit]
Description=lego certificate renewal

[Service]
Type=oneshot
EnvironmentFile=/etc/lego/env
ExecStart=/usr/local/bin/lego run --accept-tos --path /etc/lego --dns alidns -d example.com -d *.example.com --deploy-hook /etc/lego/deploy.sh
```

```ini
# /etc/systemd/system/lego.timer
[Unit]
Description=Daily lego renewal check

[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
```

```bash
sudo systemctl daemon-reload && sudo systemctl enable --now lego.timer
sudo systemctl start lego.service && sudo journalctl -u lego -n 20 --no-pager
```

续期前 lego 默认会随机等待一段时间，手动运行时可加 `--no-random-sleep`。每天检查足以应对 2027 年 2 月 10 日的 64 天证书和 2028 年 2 月 16 日的 45 天证书。建议把域名加到 [ssl.sh 监控](https://ssl.sh/zh/monitor/)（开发中）。

## 常见问题排查

- **unknown command "renew" 或 flag provided but not defined**：在 lego v5 上用了 v4 的写法，见上面的常见问题和迁移文档。
- **alidns: ... Forbidden 或 InvalidAccessKeyId**：AccessKey 错误或 RAM 子账号没有云解析权限。
- **propagation: time limit exceeded**：加大传播等待时间（`--dns.propagation.wait`，见 `lego run --help`），或确认域名的权威 DNS 确实在你配置的服务商。
- **CAA record prevents issuance**：见 [CAA 记录阻止签发](https://ssl.sh/zh/errors/caa-record-prevents-issuance/)。
- **触发频率限制**：生产环境测试次数太多，改用测试环境，见 [Let's Encrypt 频率限制](https://ssl.sh/zh/errors/lets-encrypt-rate-limit/)。
- **Web 服务还在用旧证书**：部署钩子没运行或失败，查看 `journalctl -u lego`。
