DukeDuke
主页
项目文档
技术文档
  • 单机版
  • 微服务
  • 代办项目
  • 优鲜项目
项目管理
关于我们
主页
项目文档
技术文档
  • 单机版
  • 微服务
  • 代办项目
  • 优鲜项目
项目管理
关于我们
  • 技术文档

    • 网络原理

      • 交换机
      • 路由器
      • TCP/IP协议
      • HTTP 与 HTTPS
    • 软件架构

      • 什么是软件架构
      • 分层架构
      • 微服务架构
      • 事件驱动架构
      • 领域驱动设计(DDD)
      • 架构图
      • 高并发系统
    • Vue3

      • Vue3简介
      • Vue3响应式系统
      • Vue3组合式API
      • Vue3生命周期
      • Vue3模板语法
      • Vue3组件系统
      • Vue3 路由系统
      • Vue3 状态管理
      • Vue3 性能优化
      • Vue3 TypeScript 支持
      • Vue3 项目实战
      • VUE 面试题大全
      • Node.js 安装
    • JAVA

      • JVM

        • 认识JVM
        • JVM类加载器
        • 运行时数据区
        • 执行引擎
        • 本地方法接口
        • 本地方法库
        • JVM垃圾回收
        • JVM性能监控
        • JVM调优
      • 设计模式
        • 单例模式
        • 工厂模式
        • 策略模式
        • 适配器模式
        • 建造者模式
        • 原型模式
        • 装饰器模式
        • 代理模式
        • 外观模式
        • 享元模式
        • 组合模式
        • 桥接模式
      • Java多线程

        • Java 线程基础详解
        • Java 线程池详解
        • Java ThreadLocal 详解
        • Java volatile 详解
        • Java 线程间通信详解
        • Java 线程安全详解
        • Java 线程调度详解
        • Java 线程优先级详解

        • Java 线程中断详解
        • Java 线程死锁详解
      • Java反射
      • Java 面试题

        • Java 基础概念面试题
        • Java 面向对象编程面试题
        • Java 集合框架面试题
        • Java 多线程与并发面试题
        • JVM 与内存管理面试题
        • Java I/O 与 NIO 面试题
        • Java 异常处理面试题
        • Java 反射与注解面试题
        • Java Spring 框架面试题
        • Java 数据库与 JDBC 面试题
        • Java 性能优化面试题
        • Java 实际项目经验面试题
        • Java 高级特性面试题
        • Java 面试准备建议
    • Python

      • Python简介
      • Python安装
      • Python hello world
      • Python基础语法
      • Python数据类型
      • Python数字
      • Python字符串
      • Python列表
      • Python元组
      • Python字典
      • Python日期时间
      • Python文件操作
      • Python异常处理
      • Python函数
      • Python类
      • Python模块
      • Python包
      • Python多线程
      • Python面向对象
      • Python爬虫
      • Django web框架
      • Python 面试题

        • Python 面试题导航
        • Python 基础概念
        • Python 面向对象编程
        • Python 数据结构
        • Python 高级特性
        • Python 框架
        • Python 性能优化
        • Python 项目经验
    • Spring

      • Spring
      • Springboot
      • Spring Security 安全框架
      • SpringBoot 中的事件详解
      • SpringBoot 中的定时任务详解
      • SpringBoot 自动装配原理与源码解释
    • Mybatis

      • Mybatis
      • Mybatis-Plus
    • 数据库

      • Redis

        • Redis简介
        • Redis(单机)安装
        • Redis配置
        • Redis数据结构
        • RDB、AOF 和混合持久化机制
        • Redis内存管理
        • Redis缓存一致性
        • Redis缓存穿透
        • Redis缓存击穿
        • Redis缓存雪崩
        • Redis Lua脚本
        • Redis主从复制
        • Redis哨兵模式
        • Redis集群
        • Redis数据分片
        • Redis CPU使用率过高
        • Redis面试题
      • MySQL

        • MySQL简介
        • MySQL安装
        • MySQL配置
        • MYSQL日常维护
        • MYSQL优化-慢查询
        • MYSQL优化-索引
        • MYSQL数据库设计规范
    • 消息队列

      • RocketMQ
      • Kafka
      • RabbitMQ
      • 消息队列面试题
    • 微服务

      • SpringCloud 微服务
      • Eureka 注册中心
      • Nacos 注册中心
      • Gateway 网关
      • Feign 服务调用
      • Sentinel 限流 与 熔断
      • Seata 分布式事务
      • CAP 理论
      • Redis 分布式锁
      • 高并发系统设计
    • ELK日志分析系统

      • Elasticsearch 搜索引擎
      • Logstash 数据处理
      • Kibana 可视化
      • ELK 实战
    • 开放API

      • 开放API设计
      • 开放API示例项目
    • 人工智能

      • 人工智能简介
      • 机器学习

      • 深度学习

      • 自然语言处理

      • 计算机视觉

        • CUDA与cuDNN详细安装
        • Conda 安装
        • Pytorch 深度学习框架
        • yolo 目标检测
        • TensorRT 深度学习推理优化引擎
        • TensorFlow 机器学习
        • CVAT 图像标注
        • Windows 下安装 CUDA、cuDNN、TensorRT、TensorRT-YOLO 环境
        • Windows10+CUDA+cuDNN+TensorRT+TensorRT-YOLO 部署高性能YOLO11推理
    • 大数据

      • 大数据简介
      • Hadoop 数据存储
      • Flume 数据采集
      • Sqoop 数据导入导出
      • Hive 数据仓库
      • Spark 数据处理
      • Flink 数据处理
      • Kafka 数据采集
      • HBase 数据存储
      • Elasticsearch 搜索引擎
    • 图像处理

      • 图像处理简介
      • 医学图像web呈现
      • 医学图像处理
      • 切片细胞分离问题
    • 服务器&运维

      • Linux 系统

        • Linux 系统管理
        • Linux 网络管理
        • Linux 文件管理
        • Linux 命令大全
      • Nginx Web 服务器

        • Nginx 安装 与 配置
        • Nginx 负载均衡
        • Nginx SSL证书配置
        • Nginx Keepalived 高可用
      • Docker 容器

        • Docker 简介
        • Docker 安装与配置
        • Docker 命令
        • Docker 部署 Nginx
        • Docker 部署 MySQL
        • Docker 部署 Redis
      • 服务器

        • 塔式服务器
        • 机架式服务器
        • 刀片服务器
      • Git 版本控制
      • Jenkins 持续集成
      • Jmeter 性能测试
      • Let's Encrypt 免费SSL证书
    • 简历

      • 项目经理简历
      • 开发工程师简历

Let's Encrypt 免费 SSL 证书

概述

Let's Encrypt 是一个免费、自动化和开放的证书颁发机构(CA),提供免费的 SSL/TLS 证书。它通过 ACME(Automated Certificate Management Environment)协议实现证书的自动颁发和续期。

主要特点

  • 完全免费:无需支付任何费用
  • 自动化:支持自动颁发和续期
  • 开放透明:所有证书记录公开可查
  • 安全可靠:符合行业标准的加密算法

验证方式

Let's Encrypt 支持多种域名验证方式:

  1. HTTP-01 挑战:通过 HTTP 验证
  2. DNS-01 挑战:通过 DNS 记录验证
  3. TLS-ALPN-01 挑战:通过 TLS 握手验证

Webroot 方式配置

Webroot 方式是使用 HTTP-01 挑战验证的常用方法,特别适合已经运行 Web 服务器的环境。

前提条件

  • 拥有域名控制权
  • 服务器已安装 Web 服务器(如 Nginx)
  • 服务器可以访问互联网

安装 Certbot

Ubuntu/Debian

sudo apt update
sudo apt install certbot

CentOS/RHEL

sudo yum install epel-release
sudo yum install certbot

配置 Web 服务器

Nginx 配置示例

配置目的: 这个 Nginx 配置是为了支持 Let's Encrypt 的 HTTP-01 挑战验证。具体作用包括:

  1. 提供验证文件访问路径:让 Let's Encrypt 能够通过 HTTP 访问验证文件
  2. 处理验证请求:将 /.well-known/acme-challenge/ 路径的请求正确路由到文件系统
  3. 强制 HTTPS 重定向:将所有其他 HTTP 请求重定向到 HTTPS
  4. 确保验证成功:通过 try_files 指令确保验证文件能被正确访问

在 Nginx 配置文件中添加以下内容:

server {
    listen 80;
    server_name admin.cqduke.com;
	# Webroot 路径配置
    location ^~ /.well-known/acme-challenge/ {
        root /tools/nginx/html;  # 与 --webroot-path 一致
        default_type "text/plain";
        try_files $uri =404;
    }
    #return 301 https://$host$request_uri;
}

配置说明:

  • location ^~ /.well-known/acme-challenge/:匹配验证路径,^~ 表示优先匹配
  • root /tools/nginx/html:指定验证文件的根目录
  • default_type "text/plain":设置默认 MIME 类型为纯文本
  • try_files $uri =404:尝试访问文件,不存在则返回 404
  • return 301 https://$host$request_uri:将其他请求重定向到 HTTPS

创建 Webroot 目录

创建目的

创建 Webroot 目录是为了支持 Let's Encrypt 的 HTTP-01 挑战验证方式。具体目的包括:

  1. 支持 HTTP-01 挑战验证:Let's Encrypt 会向你的域名发送特殊的 HTTP 请求来验证域名控制权
  2. 提供验证文件访问路径:确保 http://your-domain.com/.well-known/acme-challenge/[token] 路径可访问
  3. 确保文件权限正确:让 Web 服务器能够读写验证文件
  4. 与 Nginx 配置配合:为 Nginx 的 location 配置提供对应的文件系统路径
  5. 实现自动化验证:让 Certbot 能够自动创建、访问和清理验证文件

目录创建命令

sudo mkdir -p /tools/nginx/html/.well-known/acme-challenge
sudo chown -R www-data:www-data /tools/nginx/html/.well-known
sudo chmod -R 755 /tools/nginx/html/.well-known

命令说明:

  • mkdir -p:创建完整的目录结构,包括父目录
  • chown:设置目录所有者为 Web 服务器用户(www-data)
  • chmod 755:设置适当的访问权限,确保 Web 服务器可读写

申请证书

申请目的: 使用 webroot 方式申请 Let's Encrypt SSL 证书,通过 HTTP-01 挑战验证域名所有权。

申请过程:

  1. 验证域名控制权:Let's Encrypt 会向你的域名发送验证请求
  2. 生成证书文件:验证成功后自动生成证书和私钥
  3. 存储证书:证书文件存储在 /etc/letsencrypt/live/your-domain.com/ 目录

使用 webroot 方式申请证书:

sudo certbot certonly --webroot \
    --webroot-path=/var/www/html \
    --email your-email@example.com \
    --agree-tos \
    --no-eff-email \
    -d your-domain.com \
    -d www.your-domain.com

命令参数说明:

  • --webroot:使用 webroot 验证方式
  • --webroot-path:指定验证文件的根目录路径
  • --email:指定联系邮箱(用于证书到期通知)
  • --agree-tos:同意 Let's Encrypt 服务条款
  • --no-eff-email:不订阅 EFF 邮件列表
  • -d:指定要申请证书的域名(可指定多个)

配置 SSL

配置目的: 配置 Web 服务器使用 Let's Encrypt 证书,启用 HTTPS 加密访问,提升网站安全性。

SSL 配置要点:

  1. 证书路径:指向 Let's Encrypt 生成的证书文件
  2. 加密协议:使用现代 TLS 协议(TLSv1.2 和 TLSv1.3)
  3. 加密套件:配置强加密算法
  4. 会话缓存:优化 SSL 连接性能
  5. 安全头部:添加安全相关的 HTTP 头部

Nginx SSL 配置

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name admin.cqduke.com;
	# Webroot 路径配置
    location ^~ /.well-known/acme-challenge/ {
        root /tools/nginx/html;  # 与 --webroot-path 一致
        default_type "text/plain";
        try_files $uri =404;
    }
    return 301 https://$host$request_uri;
}

# HTTPS 服务器配置
server {
    listen 443 ssl;
    server_name admin.cqduke.com;

    # SSL 证书配置
    ssl_certificate /etc/letsencrypt/live/admin.cqduke.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/admin.cqduke.com/privkey.pem;

    # 现代加密协议
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # HSTS 安全头(推荐)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # 前端静态文件
    location / {
        root /tools/nginx/html/admin/dist;
        index index.html;
        try_files $uri $uri/ /index.html;

        # 静态资源缓存
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }
    }

    # Webroot 路径配置
    location /.well-known/acme-challenge/ {
        root /tools/nginx/html;
    }

     # API代理到本地后端
    location /api/ {
        proxy_pass http://localhost:9999/;
        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;
    }

    #knife4j文档
    location /doc.html {
        proxy_pass http://localhost:9999;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    access_log /tools/nginx/logs/admin.cqduke.com.access.log main;
    error_log /tools/nginx/logs/admin.cqduke.com.error.log error;

}

配置说明:

  • listen 443 ssl http2:监听 443 端口,启用 SSL 和 HTTP/2
  • ssl_certificate:指定证书文件路径(包含完整证书链)
  • ssl_certificate_key:指定私钥文件路径
  • ssl_protocols:指定支持的 TLS 协议版本
  • ssl_ciphers:指定加密套件,优先使用前向保密算法
  • ssl_session_cache:启用 SSL 会话缓存,提高性能
  • ssl_session_timeout:设置会话超时时间

自动续期

续期目的: Let's Encrypt 证书有效期为 90 天,需要定期续期以确保持续的 HTTPS 访问。

续期机制:

  1. 自动检测:Certbot 会检查证书到期时间
  2. 智能续期:只在证书即将过期时才执行续期
  3. 无缝切换:续期过程中不影响网站访问
  4. 通知机制:续期失败时发送邮件通知

续期方式:

  • 智能续期:检查证书状态,只在需要时续期
  • 简单续期:直接执行续期命令(传统方式)
  • 手动续期:使用 certbot renew 命令
  • 测试续期:使用 --dry-run 参数测试续期流程

方式一:简单续期(传统方式)

适用场景:希望使用简单直接的续期方式,不关心证书是否即将过期。

创建简单续期脚本:

sudo crontab -e

添加以下内容(每天凌晨 2 点检查续期):

0 2 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

简单续期说明:

  • 0 2 * * *:每天凌晨 2 点执行
  • certbot renew:续期命令
  • --quiet:静默模式,不输出详细信息
  • --post-hook:续期成功后执行的重载命令
  • systemctl reload nginx:重新加载 Nginx 配置

方式二:智能续期脚本(推荐)

适用场景:希望智能检查证书状态,只在证书即将过期时才执行续期,避免不必要的操作。

创建续期脚本:

sudo nano /etc/letsencrypt/renewal-hook.sh

添加以下智能续期脚本内容:

#!/bin/bash

# 智能续期脚本 - 检查证书状态并只在需要时续期
# 作者:系统管理员
# 日期:$(date +%Y-%m-%d)

# 配置变量
DOMAIN="admin.cqduke.com"
CERT_PATH="/etc/letsencrypt/live/${DOMAIN}/cert.pem"
LOG_FILE="/var/log/letsencrypt-renewal.log"
DAYS_BEFORE_EXPIRY=30  # 证书到期前30天开始续期

# 日志函数
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# 检查证书是否存在
check_cert_exists() {
    if [ ! -f "$CERT_PATH" ]; then
        log_message "错误:证书文件不存在 - $CERT_PATH"
        return 1
    fi
    return 0
}

# 检查证书是否即将过期
check_cert_expiry() {
    if ! check_cert_exists; then
        return 1
    fi

    # 获取证书到期时间
    local expiry_date=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2)
    local expiry_timestamp=$(date -d "$expiry_date" +%s)
    local current_timestamp=$(date +%s)
    local days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))

    log_message "证书到期时间:$expiry_date"
    log_message "距离到期还有:$days_until_expiry 天"

    if [ $days_until_expiry -le $DAYS_BEFORE_EXPIRY ]; then
        log_message "证书即将过期(${days_until_expiry}天),需要续期"
        return 0  # 需要续期
    else
        log_message "证书还有${days_until_expiry}天到期,无需续期"
        return 1  # 无需续期
    fi
}

# 执行续期
perform_renewal() {
    log_message "开始执行证书续期..."

    # 检查 Certbot 是否可用
    if ! command -v certbot &> /dev/null; then
        log_message "错误:Certbot 未安装或不可用"
        return 1
    fi

    # 执行续期(使用 webroot 方式)
    if certbot renew --webroot --webroot-path=/tools/nginx/html --quiet; then
        log_message "证书续期成功"

        # 重新加载 Nginx 配置
        if systemctl reload nginx; then
            log_message "Nginx 配置重新加载成功"
            return 0
        else
            log_message "警告:Nginx 重新加载失败"
            return 1
        fi
    else
        log_message "错误:证书续期失败"
        return 1
    fi
}

# 主函数
main() {
    log_message "=== 开始证书续期检查 ==="

    # 检查证书状态
    if check_cert_expiry; then
        # 证书即将过期,执行续期
        if perform_renewal; then
            log_message "证书续期流程完成"
        else
            log_message "证书续期流程失败"
            exit 1
        fi
    else
        # 证书无需续期
        log_message "证书状态正常,无需续期"
    fi

    log_message "=== 证书续期检查完成 ==="
}

# 执行主函数
main "$@"

设置执行权限:

sudo chmod +x /etc/letsencrypt/renewal-hook.sh

配置定时任务

sudo crontab -e

添加以下内容(每天凌晨 2 点检查续期):

# 每天凌晨 2 点执行智能续期检查
0 2 * * * /etc/letsencrypt/renewal-hook.sh

# 每周日凌晨 3 点执行测试续期(可选)
0 3 * * 0 /usr/bin/certbot renew --dry-run --webroot --webroot-path=/tools/nginx/html >> /var/log/letsencrypt-test.log 2>&1

两种续期方式对比

特性简单续期(方式一)智能续期(方式二)
复杂度简单中等
资源消耗每次都会执行续期只在需要时执行续期
日志记录基本日志详细日志记录
错误处理基本完善
适用场景简单环境生产环境推荐
维护成本低中等

智能续期脚本功能说明

智能检查功能:

  1. 证书存在性检查:验证证书文件是否存在
  2. 到期时间检查:计算距离证书到期的天数
  3. 智能续期判断:只在证书到期前 30 天执行续期
  4. 详细日志记录:记录所有操作和状态信息

安全特性:

  • 错误处理和日志记录
  • 证书状态验证
  • Nginx 配置重载确认
  • 失败通知机制

使用方法:

  • 自动运行:通过 crontab 定时执行
  • 手动运行:sudo /etc/letsencrypt/renewal-hook.sh
  • 查看日志:tail -f /var/log/letsencrypt-renewal.log

常见问题解决

1. 权限问题

sudo chown -R www-data:www-data /var/www/html/.well-known
sudo chmod -R 755 /var/www/html/.well-known

2. 防火墙设置

确保端口 80 和 443 开放:

sudo ufw allow 80
sudo ufw allow 443

3. 测试配置

# 测试 Nginx 配置
sudo nginx -t

Standalone 方式配置

Standalone 方式是 Let's Encrypt 的另一种验证方法,特别适合以下场景:

  • 服务器上没有运行 Web 服务器
  • 需要快速获取证书进行测试
  • 在容器化环境中使用
  • 临时获取证书用于开发环境

工作原理

Standalone 方式会临时启动一个内置的 Web 服务器(默认监听 80 端口),用于完成 HTTP-01 挑战验证。验证完成后,内置服务器会自动关闭。

前提条件

  • 拥有域名控制权
  • 服务器可以访问互联网
  • 端口 80 未被占用(申请证书时)
  • 域名已正确解析到服务器 IP

安装 Certbot

安装方法与 Webroot 方式相同:

Ubuntu/Debian

sudo apt update
sudo apt install certbot

CentOS/RHEL

sudo yum install epel-release
sudo yum install certbot

申请证书

使用 standalone 方式申请证书:

sudo certbot certonly --standalone \
    --email your-email@example.com \
    --agree-tos \
    --no-eff-email \
    -d your-domain.com \
    -d www.your-domain.com

高级配置选项

指定端口

如果 80 端口被占用,可以指定其他端口:

sudo certbot certonly --standalone \
    --preferred-challenges http \
    --http-01-port 8080 \
    --email your-email@example.com \
    --agree-tos \
    --no-eff-email \
    -d your-domain.com

指定证书目录

sudo certbot certonly --standalone \
    --cert-path /path/to/custom/cert \
    --key-path /path/to/custom/key \
    --email your-email@example.com \
    --agree-tos \
    --no-eff-email \
    -d your-domain.com

配置 Web 服务器

申请证书后,需要配置 Web 服务器使用新证书:

Nginx 配置示例

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com www.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # SSL 配置优化
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 其他配置...
    location / {
        root /var/www/html;
        index index.html index.htm;
    }
}

自动续期

创建续期脚本

sudo nano /etc/letsencrypt/renewal-hook.sh

添加以下内容:


Docker 环境中的 Let's Encrypt 配置

当您的 Nginx 运行在 Docker 容器中时,配置 Let's Encrypt 证书需要特殊的处理方式。以下是几种适用于 Docker 环境的解决方案。

纯 Docker 命令配置方案

1. 创建证书目录

目的:在宿主机上创建必要的目录结构,用于存储 Let's Encrypt 证书和验证文件。

# 创建 Let's Encrypt 证书存储目录
sudo mkdir -p /etc/letsencrypt

# 创建 Let's Encrypt 数据目录
sudo mkdir -p /var/lib/letsencrypt

# 创建 Certbot 验证文件目录
sudo mkdir -p /var/www/certbot

# 创建 Nginx 配置目录
sudo mkdir -p /opt/nginx/conf.d
sudo mkdir -p /opt/nginx/html
sudo mkdir -p /opt/nginx/logs

2. 创建 Docker 网络

# 创建自定义网络
docker network create nginx-ssl-network

3. 创建 Nginx 配置文件

创建 Nginx 配置文件

创建 /opt/nginx/conf.d/default.conf:

# HTTP 服务器配置(用于验证和重定向)
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    # Let's Encrypt 验证路径
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
        try_files $uri =404;
    }

    # 其他 HTTP 请求重定向到 HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS 服务器配置
server {
    listen 443 ssl http2;
    server_name your-domain.com www.your-domain.com;

    # SSL 证书配置
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # 现代 SSL 配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 安全头部
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    # 网站根目录
    root /usr/share/nginx/html;
    index index.html index.htm;

    # 静态文件处理
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # API 代理(如果需要)
    location /api/ {
        proxy_pass http://backend:8080/;
        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;
    }

    # 日志配置
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
}

4. 启动 Nginx 容器

# 启动 Nginx 容器
docker run -d \
  --name nginx-ssl \
  --network nginx-ssl-network \
  -p 80:80 \
  -p 443:443 \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \
  -v /opt/nginx/html:/usr/share/nginx/html:ro \
  -v /opt/nginx/logs:/var/log/nginx \
  -v /etc/letsencrypt:/etc/letsencrypt:ro \
  -v /var/www/certbot:/var/www/certbot:ro \
  --restart unless-stopped \
  nginx:alpine

5. 申请 SSL 证书

# 申请证书
docker run --rm \
  --network nginx-ssl-network \
  -v /etc/letsencrypt:/etc/letsencrypt \
  -v /var/lib/letsencrypt:/var/lib/letsencrypt \
  -v /var/www/certbot:/var/www/certbot \
  certbot/certbot certonly \
  --webroot \
  --webroot-path=/var/www/certbot \
  --email your-email@example.com \
  --agree-tos \
  --no-eff-email \
  -d your-domain.com \
  -d www.your-domain.com

6. 重新加载 Nginx 配置

# 重新加载 Nginx 配置
docker exec nginx-ssl nginx -s reload

7. 创建自动续期脚本

创建续期脚本

创建 /opt/scripts/renew-certs-docker.sh:

#!/bin/bash

# 纯 Docker 环境中的 Let's Encrypt 自动续期脚本
# 适用于不使用 docker-compose 的环境

# 配置变量
DOMAIN="your-domain.com"
CONTAINER_NAME="nginx-ssl"
NETWORK_NAME="nginx-ssl-network"
LOG_FILE="/var/log/letsencrypt-docker-renewal.log"
DAYS_BEFORE_EXPIRY=30

# 日志函数
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# 检查证书是否存在
check_cert_exists() {
    local cert_path="/etc/letsencrypt/live/${DOMAIN}/cert.pem"
    if [ ! -f "$cert_path" ]; then
        log_message "错误:证书文件不存在 - $cert_path"
        return 1
    fi
    return 0
}

# 检查证书是否即将过期
check_cert_expiry() {
    if ! check_cert_exists; then
        return 1
    fi

    local cert_path="/etc/letsencrypt/live/${DOMAIN}/cert.pem"
    local expiry_date=$(openssl x509 -enddate -noout -in "$cert_path" | cut -d= -f2)
    local expiry_timestamp=$(date -d "$expiry_date" +%s)
    local current_timestamp=$(date +%s)
    local days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))

    log_message "证书到期时间:$expiry_date"
    log_message "距离到期还有:$days_until_expiry 天"

    if [ $days_until_expiry -le $DAYS_BEFORE_EXPIRY ]; then
        log_message "证书即将过期(${days_until_expiry}天),需要续期"
        return 0
    else
        log_message "证书还有${days_until_expiry}天到期,无需续期"
        return 1
    fi
}

# 检查容器是否存在
check_container_exists() {
    if ! docker ps -a --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
        log_message "错误:容器 ${CONTAINER_NAME} 不存在"
        return 1
    fi
    return 0
}

# 检查容器是否运行
check_container_running() {
    if ! docker ps --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
        log_message "错误:容器 ${CONTAINER_NAME} 未运行"
        return 1
    fi
    return 0
}

# 执行续期
perform_renewal() {
    log_message "开始执行证书续期..."

    # 检查容器状态
    if ! check_container_exists; then
        return 1
    fi

    if ! check_container_running; then
        log_message "尝试启动容器 ${CONTAINER_NAME}..."
        docker start ${CONTAINER_NAME}
        sleep 5
        if ! check_container_running; then
            log_message "错误:无法启动容器 ${CONTAINER_NAME}"
            return 1
        fi
    fi

    # 执行续期
    if docker run --rm \
        --network ${NETWORK_NAME} \
        -v /etc/letsencrypt:/etc/letsencrypt \
        -v /var/lib/letsencrypt:/var/lib/letsencrypt \
        -v /var/www/certbot:/var/www/certbot \
        certbot/certbot renew \
        --webroot \
        --webroot-path=/var/www/certbot; then

        log_message "证书续期成功"

        # 重新加载 Nginx 配置
        if docker exec ${CONTAINER_NAME} nginx -s reload; then
            log_message "Nginx 配置重新加载成功"
            return 0
        else
            log_message "警告:Nginx 重新加载失败"
            return 1
        fi
    else
        log_message "错误:证书续期失败"
        return 1
    fi
}

# 主函数
main() {
    log_message "=== 开始纯 Docker 环境证书续期检查 ==="

    # 检查证书状态
    if check_cert_expiry; then
        # 证书即将过期,执行续期
        if perform_renewal; then
            log_message "证书续期流程完成"
        else
            log_message "证书续期流程失败"
            exit 1
        fi
    else
        # 证书无需续期
        log_message "证书状态正常,无需续期"
    fi

    log_message "=== 纯 Docker 环境证书续期检查完成 ==="
}

# 执行主函数
main "$@"

设置执行权限和定时任务

# 设置执行权限
sudo chmod +x /opt/scripts/renew-certs-docker.sh

# 添加到 crontab
sudo crontab -e

添加以下内容:

# 每天凌晨 2 点执行纯 Docker 环境证书续期检查
0 2 * * * /opt/scripts/renew-certs-docker.sh

# 每周日凌晨 3 点执行测试续期
0 3 * * 0 docker run --rm --network nginx-ssl-network -v /etc/letsencrypt:/etc/letsencrypt -v /var/lib/letsencrypt:/var/lib/letsencrypt -v /var/www/certbot:/var/www/certbot certbot/certbot renew --dry-run --webroot --webroot-path=/var/www/certbot >> /var/log/letsencrypt-test.log 2>&1

8. 验证配置

检查证书状态

# 查看证书信息
docker run --rm \
  --network nginx-ssl-network \
  -v /etc/letsencrypt:/etc/letsencrypt \
  -v /var/lib/letsencrypt:/var/lib/letsencrypt \
  certbot/certbot certificates

# 检查证书有效期
openssl x509 -in /etc/letsencrypt/live/your-domain.com/cert.pem -text -noout | grep -A 2 "Validity"

# 测试 SSL 连接
openssl s_client -connect your-domain.com:443 -servername your-domain.com

检查 Nginx 配置

# 检查 Nginx 配置语法
docker exec nginx-ssl nginx -t

# 查看 Nginx 日志
docker logs nginx-ssl

# 检查容器状态
docker ps -a --filter name=nginx-ssl

9. 管理命令

常用管理命令

# 查看容器状态
docker ps -a --filter name=nginx-ssl

# 查看容器日志
docker logs nginx-ssl

# 进入容器
docker exec -it nginx-ssl sh

# 重启容器
docker restart nginx-ssl

# 停止容器
docker stop nginx-ssl

# 启动容器
docker start nginx-ssl

# 删除容器
docker rm nginx-ssl

# 查看网络
docker network ls
docker network inspect nginx-ssl-network

10. 故障排除

常见问题及解决方案

  1. 证书申请失败
# 检查域名解析
nslookup your-domain.com

# 检查端口是否开放
sudo netstat -tlnp | grep :80
sudo netstat -tlnp | grep :443

# 查看 Certbot 详细日志
docker run --rm --network nginx-ssl-network -v /etc/letsencrypt:/etc/letsencrypt -v /var/lib/letsencrypt:/var/lib/letsencrypt -v /var/www/certbot:/var/www/certbot certbot/certbot certificates --verbose
  1. Nginx 无法访问证书文件
# 检查证书文件权限
ls -la /etc/letsencrypt/live/your-domain.com/

# 修复权限
sudo chown -R root:root /etc/letsencrypt
sudo chmod -R 755 /etc/letsencrypt

# 检查容器挂载
docker inspect nginx-ssl | grep -A 10 "Mounts"
  1. 容器网络问题
# 检查容器网络
docker network ls
docker network inspect nginx-ssl-network

# 重新创建网络
docker network rm nginx-ssl-network
docker network create nginx-ssl-network

# 重新启动容器
docker stop nginx-ssl
docker rm nginx-ssl
# 然后重新运行启动命令
  1. 续期失败
# 手动测试续期
docker run --rm --network nginx-ssl-network -v /etc/letsencrypt:/etc/letsencrypt -v /var/lib/letsencrypt:/var/lib/letsencrypt -v /var/www/certbot:/var/www/certbot certbot/certbot renew --dry-run --webroot --webroot-path=/var/www/certbot

# 检查证书状态
docker run --rm --network nginx-ssl-network -v /etc/letsencrypt:/etc/letsencrypt -v /var/lib/letsencrypt:/var/lib/letsencrypt certbot/certbot certificates

# 查看续期日志
tail -f /var/log/letsencrypt-docker-renewal.log

11. 安全建议

  1. 定期备份证书:
# 备份证书目录
sudo tar -czf /backup/letsencrypt-$(date +%Y%m%d).tar.gz /etc/letsencrypt

# 创建备份脚本
cat > /opt/scripts/backup-certs.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
tar -czf ${BACKUP_DIR}/letsencrypt-${DATE}.tar.gz /etc/letsencrypt
find ${BACKUP_DIR} -name "letsencrypt-*.tar.gz" -mtime +30 -delete
EOF
chmod +x /opt/scripts/backup-certs.sh
  1. 监控证书状态:
# 创建监控脚本
cat > /opt/scripts/monitor-certs.sh << 'EOF'
#!/bin/bash
DOMAIN="your-domain.com"
LOG_FILE="/var/log/cert-monitor.log"
CERT_PATH="/etc/letsencrypt/live/${DOMAIN}/cert.pem"

if [ -f "$CERT_PATH" ]; then
    EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2)
    echo "$(date) - 证书到期时间: $EXPIRY" >> $LOG_FILE
else
    echo "$(date) - 错误: 证书文件不存在" >> $LOG_FILE
fi
EOF
chmod +x /opt/scripts/monitor-certs.sh
  1. 设置告警:
# 在续期脚本中添加邮件通知
if [ $? -ne 0 ]; then
    echo "证书续期失败 - $(date)" | mail -s "Let's Encrypt 续期失败" admin@your-domain.com
fi

12. 方案对比

特性Docker Compose 方案纯 Docker 命令方案
复杂度中等简单
依赖需要 docker-compose只需要 Docker
配置管理集中化分散化
学习成本中等低
维护性好中等
适用场景复杂项目简单项目
最近更新:: 2025/8/14 09:20
Contributors: Duke
Prev
Jmeter 性能测试