feat: 添加 Let's Encrypt SSL 证书管理

- 新增独立 certbot 服务项目
- nginx 配置添加 HTTPS 8443 端口监听
- frpc.toml HTTPS 代理指向 nginx:8443
- 支持自动续签并重载 nginx
- 共享 Docker volume 传递证书
This commit is contained in:
awei
2026-09-08 11:43:34 +00:00
parent b028e6dd73
commit 58d65188a1
13 changed files with 341 additions and 69 deletions

20
certbot/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# Certbot 镜像 - Let's Encrypt 证书管理
FROM certbot/certbot:latest
LABEL maintainer="awei"
LABEL description="Let's Encrypt certificate management service"
# 安装额外工具
RUN apk add --no-cache curl bash
# 创建必要目录
RUN mkdir -p /var/www/certbot /etc/letsencrypt /var/log/letsencrypt
# 设置工作目录
WORKDIR /var/www/certbot
# 暴露端口(用于 ACME 验证)
EXPOSE 80
# 默认命令:启动 web 服务器用于 ACME 验证
CMD ["sh", "-c", "while true; do echo 'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 2\r\n\r\nOK' | nc -l -p 80; done"]

117
certbot/README.md Normal file
View File

@@ -0,0 +1,117 @@
# Let's Encrypt 证书管理服务
独立的 certbot 服务,用于管理 Let's Encrypt SSL证书的申请和自动续签。
## 架构说明
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 用户请求 │───▶│ frps (远程) │───▶│ frpc (本地) │
│ *.stonelan.cn │ │ 80/443端口 │ │ 80/8443端口 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ nginx-proxy 容器 │
│ - 80端口: 处理 ACME 验证请求,转发到 certbot 容器 │
│ - 8443端口: HTTPS 反向代理,使用 Let's Encrypt 证书 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ certbot 容器 │
│ - 处理 ACME HTTP-01 验证 │
│ - 定期续签证书 │
│ - 续签后自动重载 nginx (通过 docker.sock) │
└─────────────────────────────────────────────────────────────────┘
```
## 文件说明
- `Dockerfile` - certbot 镜像定义
- `docker-compose.yml` - certbot 容器配置
- `init-cert.sh` - 初始化申请证书脚本
- `renew-cert.sh` - 自动续签脚本
- `crontab.example` - cron 配置示例
## 使用方法
### 1. 首次申请证书
```bash
cd /home/awei/workspace/common-server-public/certbot
# 为每个域名申请证书
./init-cert.sh fastgpt.stonelan.cn admin@example.com
./init-cert.sh gitea.stonelan.cn admin@example.com
./init-cert.sh image.stonelan.cn admin@example.com
./init-cert.sh registry.stonelan.cn admin@example.com
./init-cert.sh www.stonelan.cn admin@example.com
```
### 2. 配置自动续签
```bash
# 编辑 crontab
crontab -e
# 添加以下行(每天凌晨 2 点检查续签)
0 2 * * * cd /home/awei/workspace/common-server-public/certbot && ./renew-cert.sh >> /var/log/certbot-renew.log 2>&1
```
### 3. 手动续签测试
```bash
cd /home/awei/workspace/common-server-public/certbot
./renew-cert.sh
```
## 证书路径
证书存储在 Docker volume `certbot-certs`nginx 容器通过共享 volume 访问:
- 证书: `/etc/letsencrypt/live/<域名>/fullchain.pem`
- 私钥: `/etc/letsencrypt/live/<域名>/privkey.pem`
## 端口映射
| 服务 | 容器端口 | 宿主机端口 | 说明 |
|------|----------|------------|------|
| nginx | 80 | 8081 | HTTP (ACME验证 + 重定向) |
| nginx | 8443 | 8443 | HTTPS (SSL反向代理) |
| frpc | 80 | 80 (远程) | HTTP 映射 |
| frpc | 8443 | 443 (远程) | HTTPS 映射 |
## 重启 nginx
续签后会自动通过 `docker kill -s HUP nginx-proxy` 重载 nginx。如需手动重启
```bash
docker kill -s HUP nginx-proxy
# 或
docker restart nginx-proxy
```
## 故障排查
### 查看证书状态
```bash
docker compose exec certbot certbot certificates
```
### 查看续签日志
```bash
tail -f /var/log/certbot-renew.log
```
### 检查 ACME 验证是否可达
```bash
curl http://fastgpt.stonelan.cn/.well-known/acme-challenge/test
```
## 注意事项
1. 确保域名 DNS 已正确解析到 frps 服务器 IP
2. 确保 frpc 和 nginx 容器在同一个 Docker 网络 (`trim-default`)
3. 证书有效期 90 天,建议每 60 天续签一次cron 已配置每天检查)
4. 首次申请证书前,确保 nginx 容器已启动并能处理 ACME 验证请求

7
certbot/crontab.example Normal file
View File

@@ -0,0 +1,7 @@
# Let's Encrypt 自动续签 cron 配置
# 每天凌晨 2 点执行续签检查
# 编辑 crontab: crontab -e
# 添加以下行:
0 2 * * * cd /home/awei/workspace/common-server-public/certbot && ./renew-cert.sh >> /var/log/certbot-renew.log 2>&1

View File

@@ -0,0 +1,24 @@
services:
certbot:
build: .
container_name: certbot
restart: unless-stopped
volumes:
- certbot-webroot:/var/www/certbot
- certbot-certs:/etc/letsencrypt
- certbot-logs:/var/log/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- trim-default
volumes:
certbot-webroot:
driver: local
certbot-certs:
driver: local
certbot-logs:
driver: local
networks:
trim-default:
external: true

35
certbot/init-cert.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Let's Encrypt 证书初始化脚本
# 用法: ./init-cert.sh <域名> [邮箱]
set -e
DOMAIN=$1
EMAIL=${2:-"admin@stonelan.cn"}
if [ -z "$DOMAIN" ]; then
echo "用法: $0 <域名> [邮箱]"
echo "示例: $0 fastgpt.stonelan.cn admin@example.com"
exit 1
fi
echo "开始为域名 $DOMAIN 申请证书..."
# 确保 certbot 容器运行
docker compose up -d
# 等待容器启动
sleep 5
# 申请证书(使用 webroot 验证方式)
docker compose exec certbot certbot certonly \
--webroot \
--webroot-path=/var/www/certbot \
--email "$EMAIL" \
--agree-tos \
--no-eff-email \
-d "$DOMAIN" \
--force-renewal
echo "证书申请完成!"
echo "证书路径: /etc/letsencrypt/live/$DOMAIN/"

27
certbot/renew-cert.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
# Let's Encrypt 证书自动续签脚本
# 续签后自动重载 nginx
set -e
echo "开始检查证书续签..."
# 执行续签
docker compose exec certbot certbot renew \
--webroot \
--webroot-path=/var/www/certbot \
--quiet
# 检查是否有证书被续签
if [ $? -eq 0 ]; then
echo "证书续签检查完成"
# 重载 nginx发送 HUP 信号)
echo "重载 nginx 配置..."
docker kill -s HUP nginx-proxy 2>/dev/null || echo "nginx-proxy 容器未运行或不存在"
echo "续签流程完成"
else
echo "证书续签失败"
exit 1
fi