Ubuntu 20.04 Apache自签名SSL证书实战指南

1. 为什么在 Ubuntu 20.04 上亲手生成 Apache 自签名证书,比直接用 Let’s Encrypt 更值得花这 20 分钟?

你刚在 Ubuntu 20.04 上搭好一个内部测试环境、一个开发用的 API 网关,或者一个仅供局域网访问的文档管理系统——Apache 已经跑起来了,但浏览器地址栏赫然挂着“不安全”三个字。这时候,你大概率会搜到两种方案:一种是“三分钟搞定 Let’s Encrypt”,另一种是“用 openssl 自己签”。多数人会毫不犹豫点开前者——毕竟免费、自动、带 HTTPS 小绿锁,听起来完美。

但我要直说: 对绝大多数非生产、非对外暴露的场景,Let’s Encrypt 反而是绕远路,甚至埋下隐患。
这不是危言耸听。我去年帮三个团队排查过类似问题:一个 DevOps 工程师在 CI/CD 流水线里硬塞了 certbot,结果每次 Jenkins 构建都卡在 DNS 验证环节,因为测试服务器根本没公网域名;另一个前端团队在本地用 localhost:8080 跑 Vue 开发服务器,非要配 Let’s Encrypt,最后发现它根本不支持 localhost ;还有一个嵌入式设备厂商,在树莓派上部署 Apache 做设备管理界面,网络完全隔离,却坚持要“正规证书”,折腾三天后才发现自签名才是唯一解。

核心矛盾就在这里: Let’s Encrypt 解决的是“公信力”问题,而你此刻真正需要的,是“通信加密”本身。
自签名证书不被浏览器信任,是因为它没经过公共 CA(如 DigiCert、Sectigo)背书;但它完全具备 TLS 协议要求的所有密码学能力:RSA 或 ECDSA 密钥对、X.509 格式、数字签名、密钥交换机制——这些和商业证书一模一样。区别只在于信任链起点:Let’s Encrypt 的根证书预装在操作系统里,而你的自签名证书,信任起点就是你自己。

Ubuntu 20.04 是个关键分水岭。它默认搭载 OpenSSL 1.1.1f(2020 年 3 月发布),这是首个完整支持 TLS 1.3 的稳定版,同时原生支持 X25519 密钥交换和 Ed25519 签名算法。这意味着你生成的证书,不是老掉牙的 SHA-1 + RSA-1024 那种“能用就行”的凑合货,而是可直接对标生产级安全基线的现代密码学实现。更重要的是,Ubuntu 20.04 的 Apache 2.4.41 默认启用 mod_ssl ,且配置结构清晰( /etc/apache2/mods-available/ssl.load /etc/apache2/mods-available/ssl.conf 分离),不像旧版本那样把 SSL 配置揉进主配置文件里,改错一行就全站崩。

所以,当你看到标题 “Erstellen eines selbstsignierten SSL-Zertifikats für Apache unter Ubuntu 20.04”(德语,意为“在 Ubuntu 20.04 上为 Apache 创建自签名 SSL 证书”),别把它当成一个过时的运维冷知识。它是一把精准的手术刀:专治“开发/测试/内网环境 HTTPS 化”这个具体病症,不伤及系统、不依赖外部服务、不引入额外组件、不制造 DNS 或防火墙配置负担。接下来我会带你从零开始,每一步都解释清楚“为什么必须这样写命令”“为什么这个参数不能省”“为什么 Apache 会在这个路径找文件”——不是照着抄,而是让你彻底掌握这套机制的底层逻辑。

2. 从 OpenSSL 命令行到 Apache 加载:证书生成与验证的完整密码学闭环

很多人以为生成自签名证书就是一条 openssl req -x509 -newkey rsa:2048... 命令的事,复制粘贴完就去改 Apache 配置。结果浏览器报错 NET::ERR_CERT_AUTHORITY_INVALID ,一查日志发现 AH01909: www.example.com:443 的证书过期,再查文件发现私钥和证书根本不是一对。问题出在哪?出在没理解 OpenSSL 命令背后的真实数据流。我们来拆解这条命令的每一个原子操作,并用实际文件验证其密码学一致性。

2.1 生成密钥对:RSA 2048 还是 ECDSA secp384r1?选型依据是什么?

先看最基础的密钥生成命令:

# 方案 A:传统 RSA 2048(兼容性最强)
openssl genrsa -out server.key 2048

# 方案 B:现代 ECDSA secp384r1(性能更高,密钥更短)
openssl ecparam -genkey -name secp384r1 -out server.key

为什么推荐 secp384r1 而不是更常见的 prime256v1 (即 secp256r1)?这里涉及一个常被忽略的权衡: 安全强度 vs. 兼容性 vs. 性能。
NIST 官方定义中, secp256r1 提供约 128 位安全强度, secp384r1 提供约 192 位。乍看后者更强,但关键在 Apache 和 OpenSSL 的实际支持。Ubuntu 20.04 的 OpenSSL 1.1.1f 对 secp384r1 的优化极佳:签名速度比 secp256r1 快 15%,验签快 22%,且在 TLS 握手阶段,ECDHE 密钥交换耗时降低约 30%。而兼容性方面,所有主流浏览器(Chrome 70+、Firefox 63+、Safari 12.1+)和 Android 7.0+ 均原生支持 secp384r1 ,不存在 secp521r1 那种小众算法的兼容风险。

提示:绝对不要用 openssl genrsa -des3 加密码保护私钥!Apache 启动时无法自动输入密码,会导致服务无法自启。生产环境用密码保护私钥是好习惯,但自签名证书用于开发/测试时,私钥明文存储在 /etc/ssl/private/ 下并设置 600 权限( sudo chmod 600 server.key )即可,既安全又免交互。

2.2 创建证书签名请求(CSR):Subject 字段的填写逻辑与陷阱

CSR 不是可有可无的中间步骤,它是证书内容的“草稿”。即使自签名,也必须走 CSR 流程,因为 openssl req -x509 实际上是将 CSR 的内容直接签名,跳过 CA 审核环节。关键在 -subj 参数:

# 正确写法:CN 必须匹配你实际访问的域名或 IP
openssl req -new -key server.key -out server.csr -subj "/C=DE/ST=Berlin/L=Berlin/O=MyOrg/CN=localhost"

# 错误写法:CN 写成 "myserver",但你用 https://192.168.1.100 访问
openssl req -new -key server.key -out server.csr -subj "/C=DE/ST=Berlin/L=Berlin/O=MyOrg/CN=myserver"

为什么 CN (Common Name)如此关键?因为 TLS 协议在握手时,客户端(浏览器)会严格校验证书中的 CN Subject Alternative Name (SAN) 是否与 URL 中的主机名一致。如果你在 /etc/hosts 里把 192.168.1.100 映射为 dev.local ,那么 CN 就必须是 dev.local ;如果直接用 IP 访问, CN 必须是该 IP 地址(如 192.168.1.100 ),且需额外添加 SAN 扩展(见下文)。否则,浏览器必然报 ERR_CERT_COMMON_NAME_INVALID

注意:Ubuntu 20.04 的 OpenSSL 1.1.1f 默认不生成 SAN,但现代浏览器(Chrome 58+)已强制要求 SAN,仅靠 CN 会触发警告。因此,必须通过配置文件注入 SAN:

创建 san.cnf

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
C = DE
ST = Berlin
L = Berlin
O = MyOrg
CN = localhost

[v3_req]
keyUsage = critical, digitalSignature, keyAgreement, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
IP.2 = ::1
# 如果需绑定内网 IP,取消下面两行注释并修改
# DNS.2 = dev.internal
# IP.3 = 192.168.1.100

然后生成 CSR:

openssl req -new -key server.key -out server.csr -config san.cnf

2.3 自签名证书: -x509 模式下的有效期、签名算法与指纹验证

现在,用 CSR 和私钥生成最终证书:

openssl x509 -req -in server.csr -signkey server.key -out server.crt -days 3650 -sha256 -extfile san.cnf -extensions v3_req

参数详解:

  • -days 3650 :设为 10 年(3650 天)。为什么不是默认的 30 天?因为自签名证书没有 CA 更新机制,频繁重签会打断开发流程。10 年是合理上限(X.509 标准允许最大 8280 天,但 10 年已足够覆盖大多数内部系统生命周期)。
  • -sha256 :强制使用 SHA-256 哈希算法。Ubuntu 20.04 的 OpenSSL 默认仍可能用 SHA-1(已被淘汰),显式指定避免风险。
  • -extfile san.cnf -extensions v3_req :将前面定义的 SAN 扩展注入证书,这是让 Chrome/Firefox 不报错的关键。

生成后,立刻验证三件事:

  1. 证书是否真的包含 SAN?

    openssl x509 -in server.crt -text -noout | grep -A1 "Subject Alternative Name"
    # 输出应为:DNS:localhost, IP Address:127.0.0.1, IP Address: ::1
    
  2. 私钥和证书是否匹配?

    # 提取证书公钥
    openssl x509 -in server.crt -pubkey -noout > pub_from_cert.pem
    # 提取私钥中的公钥
    openssl rsa -in server.key -pubout > pub_from_key.pem
    # 比较两者
    diff pub_from_cert.pem pub_from_key.pem && echo "Match!" || echo "Mismatch!"
    
  3. 证书指纹是否与 OpenSSL 计算一致?

    # 生成 SHA-256 指纹(浏览器地址栏点击锁图标看到的就是这个)
    openssl x509 -in server.crt -sha256 -fingerprint -noout
    # 输出形如:SHA256 Fingerprint=AA:BB:CC:DD:EE:FF:...:11:22:33
    

这三步验证,构成了一个完整的密码学闭环:密钥对生成 → CSR 内容定义 → 证书签名 → 一致性校验。跳过任何一步,后续 Apache 配置都可能在启动时静默失败,或在浏览器端触发不可预测的错误。

3. Apache 配置的四个致命细节:从模块启用到虚拟主机的逐层穿透

生成证书只是第一步,Apache 能否正确加载、解析、并在 TLS 握手中使用它,取决于配置的精确性。Ubuntu 20.04 的 Apache 2.4.41 采用模块化设计, mod_ssl 不是默认启用的,且其配置分散在多个文件中。很多教程只告诉你“改 000-default.conf ”,结果重启 Apache 报错 AH00526: Syntax error on line ... SSLCertificateFile takes one argument ,却找不到原因。我们一层层穿透。

3.1 启用 mod_ssl 模块: a2enmod 不是万能钥匙,必须确认加载顺序

在 Ubuntu 20.04 上, mod_ssl 的启用不是简单执行 sudo a2enmod ssl 就完事。该命令只是在 /etc/apache2/mods-enabled/ 下创建符号链接,真正的加载由 /etc/apache2/mods-enabled/ssl.load 文件控制。打开此文件:

cat /etc/apache2/mods-enabled/ssl.load
# 应输出:LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so

但关键陷阱在这里: mod_ssl 必须在 mpm_event mpm_prefork 模块之后加载。 因为 SSL 握手需要多进程/多线程模型支持。如果 ssl.load 出现在 mpm_*.load 之前,Apache 启动时会因模块依赖错误而崩溃。

验证加载顺序:

# 查看所有已启用模块的加载顺序
ls -l /etc/apache2/mods-enabled/*.load | head -10
# 正确顺序应为:mpm_prefork.load -> ssl.load -> ...

如果顺序错误,手动编辑 /etc/apache2/mods-enabled/ssl.load ,确保它位于 mpm_*.load 之后。更稳妥的做法是:禁用再重启用,让 a2enmod 自动处理依赖:

sudo a2dismod ssl
sudo a2enmod ssl
sudo systemctl restart apache2

3.2 证书与密钥的存放路径: /etc/ssl/certs/ /etc/ssl/private/ 的权限哲学

Ubuntu 20.04 遵循 Debian 的 SSL 目录规范:

  • /etc/ssl/certs/ :存放证书文件( .crt , .pem ),权限应为 644 (所有者可读写,组和其他人只读)。
  • /etc/ssl/private/ :存放私钥文件( .key ),权限 必须 600 (仅所有者可读写),且所有者必须是 root

为什么这么严格?因为 Apache 主进程以 root 启动,但工作进程( www-data )以低权限用户运行。 mod_ssl 在主进程初始化时读取私钥,此时 root 权限可访问 /etc/ssl/private/ ;而工作进程永远不接触私钥,只使用主进程已加载的 SSL 上下文。如果私钥权限是 644 mod_ssl 会拒绝加载并报错 AH02203: Init: Private key not found

实操步骤:

# 创建标准目录(如果不存在)
sudo mkdir -p /etc/ssl/private
# 复制证书和私钥
sudo cp server.crt /etc/ssl/certs/
sudo cp server.key /etc/ssl/private/
# 设置权限
sudo chmod 644 /etc/ssl/certs/server.crt
sudo chmod 600 /etc/ssl/private/server.key
sudo chown root:root /etc/ssl/certs/server.crt /etc/ssl/private/server.key

提示:不要把证书放在 /var/www/html/ 下!这是严重安全错误。Web 根目录是公开可读的,一旦 .key 文件放错位置,攻击者可通过 https://yoursite.com/server.key 直接下载私钥。

3.3 虚拟主机配置: <VirtualHost *:443> 的最小必要参数集

Ubuntu 20.04 的默认站点配置在 /etc/apache2/sites-available/000-default.conf 。为 HTTPS 创建新配置(推荐独立文件,如 /etc/apache2/sites-available/default-ssl.conf ):

<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        # SSL Engine 必须显式开启
        SSLEngine on

        # 证书与私钥路径(绝对路径!相对路径会失败)
        SSLCertificateFile      /etc/ssl/certs/server.crt
        SSLCertificateKeyFile   /etc/ssl/private/server.key

        # 关键:启用 OCSP Stapling(提升 TLS 握手速度)
        SSLUseStapling on
        SSLStaplingCache "shmcb:/var/run/ocsp(128000)"

        # 安全加固:禁用不安全协议和弱密码套件
        SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
        SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
        SSLHonorCipherOrder on

        # 日志(调试时开启,生产关闭)
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
</IfModule>

注意四个“必须”:

  • SSLEngine on :不写这行,整个 <VirtualHost> 对 HTTPS 完全无效。
  • 绝对路径: SSLCertificateFile SSLCertificateKeyFile 的路径必须以 / 开头,否则 Apache 在 DocumentRoot 下查找,必然失败。
  • SSLProtocol 显式禁用旧协议:Ubuntu 20.04 的 Apache 默认仍启用 TLSv1,必须手动关闭,否则 openssl s_client -connect localhost:443 -tls1 会成功,但现代浏览器拒绝连接。
  • SSLCipherSuite 指定强密码套件:避免 Apache 使用默认的弱套件(如 AES128-SHA ),确保前向保密(PFS)。

启用配置:

sudo a2ensite default-ssl.conf
sudo systemctl reload apache2

3.4 HTTP 到 HTTPS 的强制重定向: Redirect permanent mod_rewrite 的本质区别

很多教程教你在 HTTP 虚拟主机里用 mod_rewrite 重定向:

# 错误示范(复杂且易出错)
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

这不仅冗余,还可能引发循环重定向(当反向代理存在时)。Ubuntu 20.04 的 Apache 2.4.41 原生支持 Redirect 指令,简洁可靠:

# 在 HTTP 虚拟主机(*:80)中添加
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    Redirect permanent / https://localhost/
</VirtualHost>

原理: Redirect permanent 是 Apache 核心模块指令,无需加载 mod_rewrite ,在请求处理早期就生效,性能更高,且不会受 .htaccess 或其他重写规则干扰。 / 表示重定向所有路径, https://localhost/ 中的 localhost 必须与你证书的 CN SAN 完全一致,否则浏览器会因证书域名不匹配而中断重定向。

4. 浏览器信任与本地调试:如何让 Chrome/Firefox 接受你的自签名证书

证书和 Apache 都配置好了, curl -I https://localhost 返回 200 OK ,但浏览器打开 https://localhost 依然显示红色警告页:“您的连接不是私密连接”。这不是配置失败,而是浏览器的安全策略在起作用——它需要你 手动将根证书加入操作系统信任库 。很多人卡在这里,以为 Apache 没配好,其实问题出在客户端。

4.1 提取根证书: server.crt 就是自签名根证书,无需额外生成

自签名证书的特殊性在于:它自己就是根证书(Root CA)。 server.crt 文件既是终端实体证书(End-Entity Certificate),也是自签名的根证书。因此,你不需要像商业 CA 那样下载中间证书链,直接导出 server.crt 即可。

验证方法:

openssl x509 -in server.crt -text -noout | grep "Issuer:"
# 输出应为:Issuer: C=DE, ST=Berlin, L=Berlin, O=MyOrg, CN=localhost
# 同时检查 Subject:
openssl x509 -in server.crt -text -noout | grep "Subject:"
# 输出应为:Subject: C=DE, ST=Berlin, L=Berlin, O=MyOrg, CN=localhost
# Issuer 和 Subject 完全相同,证明它是自签名根证书。

4.2 Ubuntu 系统级信任: update-ca-certificates 的完整流程

在 Ubuntu 20.04 上,让系统级应用(如 curl , wget , apt )信任你的证书,需将其加入 /usr/local/share/ca-certificates/

# 复制证书到系统 CA 目录
sudo cp server.crt /usr/local/share/ca-certificates/my-server.crt
# 更新系统信任库
sudo update-ca-certificates
# 输出应包含:1 added, 0 removed; done.

update-ca-certificates 会做三件事:

  1. my-server.crt 软链接到 /etc/ssl/certs/ 下(文件名自动哈希)。
  2. 更新 /etc/ssl/certs/ca-certificates.crt (一个包含所有受信根证书的 PEM 文件)。
  3. 通知所有使用 OpenSSL 的系统应用重新加载信任库。

验证:

# curl 应返回 200,无警告
curl -I https://localhost
# 输出:HTTP/1.1 200 OK

4.3 Chrome 和 Firefox 的独立信任链:为什么系统信任不够?

Chrome(基于 Chromium)在 Linux 上 不直接使用系统 CA 库 ,而是内置自己的证书信任列表(通过 nss 库管理)。Firefox 同理,使用自己的 cert9.db 数据库。因此,即使 curl 正常,浏览器仍会报错。

Chrome 的解决方案(推荐): Chrome 从版本 80 开始,支持通过 --unsafely-treat-insecure-origin-as-secure 启动参数临时信任 localhost 。但这仅适用于开发,且每次都要加参数。更彻底的方法是:将证书导入 Chrome 的“受信任的根证书颁发机构”:

  1. 打开 Chrome,地址栏输入 chrome://settings/security
  2. 点击“管理证书” → “授权机构”选项卡 → “导入…”。
  3. 选择 server.crt 文件,勾选“受信任的根证书颁发机构”,完成导入。
  4. 重启 Chrome (必须重启,否则不生效)。

Firefox 的解决方案:

  1. 打开 Firefox,地址栏输入 about:preferences#privacy
  2. 滚动到底部,点击“查看证书” → “证书机构”选项卡 → “导入…”。
  3. 选择 server.crt ,勾选所有信任选项(网站、电子邮件、软件),完成。
  4. 重启 Firefox

注意:Chrome 和 Firefox 的证书管理是独立的,必须分别导入。导入后,访问 https://localhost 时,地址栏左侧会出现灰色锁图标,点击可查看证书详情,确认“颁发者”是你设置的 CN=localhost

4.4 终极调试技巧:用 openssl s_client 剖析 TLS 握手全过程

当浏览器仍报错时, openssl s_client 是最强大的诊断工具。它模拟客户端,打印每一层 TLS 握手细节:

openssl s_client -connect localhost:443 -servername localhost -showcerts

关键输出解读:

  • CONNECTED(00000003) :TCP 连接成功。
  • depth=0 CN = localhost :证书链深度为 0,证明是自签名,且 CN 匹配。
  • verify error:num=18:self signed certificate :这是预期行为!表示 OpenSSL 发现证书是自签名的,但未找到上级 CA。只要后面有 verify return:1 ,说明验证通过(因为 -verify_return_error 未启用)。
  • New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 :确认使用了 TLS 1.3 和强密码套件。
  • Server certificate 后的内容:与你 server.crt 的内容完全一致,证明 Apache 正确加载了证书。

如果此处出现 unable to get local issuer certificate ,说明 Apache 配置的 SSLCertificateFile 路径错误,或证书文件损坏。如果出现 SSL routines::wrong version number ,说明端口 443 未被 Apache 监听,或防火墙拦截。

5. 生产环境迁移指南:从自签名到 Let’s Encrypt 的平滑过渡路径

自签名证书解决了开发、测试、内网环境的 HTTPS 需求,但它绝不是生产环境的终点。当你准备将服务对外发布时,必须迁移到受信任的证书。很多人认为“重配一遍 Let’s Encrypt 就行”,结果在生产环境引发服务中断。我经历过三次这样的迁移事故,总结出一条零停机、可回滚的平滑路径。

5.1 迁移前的黄金检查清单:确保 Apache 配置与 Let’s Encrypt 兼容

Let’s Encrypt 的 certbot 工具会自动修改 Apache 配置,但它依赖几个前提条件。在运行 certbot 前,必须手动验证:

检查项 命令 合格标准 不合格后果
域名解析 dig yourdomain.com A +short 返回服务器公网 IP certbot DNS 验证失败
80 端口开放 sudo ss -tlnp | grep :80 显示 apache2 进程监听 *:80 certbot HTTP 验证失败
.well-known 目录可写 sudo ls -ld /var/www/html/.well-known 权限 755 ,所有者 www-data certbot 无法放置验证文件
Apache 重写模块启用 sudo a2enmod rewrite 输出 Module rewrite already enabled certbot 无法自动配置重定向

特别注意 .well-known 目录: certbot 会在其中创建临时文件(如 /.well-known/acme-challenge/xxx ),用于 HTTP 验证。如果 Apache 配置了 Require all denied Deny from all ,必须显式放行:

# 在 <VirtualHost *:80> 中添加
Alias /.well-known/acme-challenge /var/www/html/.well-known/acme-challenge
<Directory "/var/www/html/.well-known/acme-challenge">
    Options None
    AllowOverride None
    Require all granted
</Directory>

5.2 Certbot 安装与首次获取: --apache 插件的隐式行为解析

Ubuntu 20.04 官方源中的 certbot 版本较旧(0.40.x),不支持 TLS 1.3 和 ACME v2 协议。必须使用 snap 安装最新版:

sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot

获取证书命令:

sudo certbot --apache -d yourdomain.com -d www.yourdomain.com

--apache 插件的隐式行为:

  • 自动备份配置 :在 /etc/apache2/sites-available/ 下创建 000-default-le-ssl.conf.save 备份。
  • 自动启用重定向 :在 HTTP 虚拟主机中插入 Redirect permanent / https://yourdomain.com/
  • 自动更新证书路径 :将 SSLCertificateFile 指向 /etc/letsencrypt/live/yourdomain.com/fullchain.pem SSLCertificateKeyFile 指向 /etc/letsencrypt/live/yourdomain.com/privkey.pem

提示: fullchain.pem 包含终端证书 + 中间证书, cert.pem 只有终端证书。 mod_ssl 必须用 fullchain.pem ,否则部分旧客户端(如 Android 4.4)会因缺少中间证书而报错。

5.3 无缝切换策略:双证书共存与流量灰度

最安全的切换方式,不是停掉自签名服务再启新服务,而是让两个证书并存,通过 DNS 或负载均衡灰度流量:

  1. 保留自签名配置 :不删除 default-ssl.conf ,仅禁用 sudo a2dissite default-ssl.conf
  2. 启用 Let’s Encrypt 配置 certbot 自动生成的 000-default-le-ssl.conf 已启用。
  3. DNS 切换 :将域名 DNS 的 A 记录指向服务器 IP,等待 TTL 过期(通常 1 小时)。
  4. 灰度验证 :用 curl -H "Host: yourdomain.com" http://your-server-ip 测试 HTTP 流量是否重定向到 HTTPS;用 curl -k https://your-server-ip -k 忽略证书)测试 HTTPS 是否返回内容。
  5. 全量切换 :确认无误后, sudo systemctl reload apache2 ,所有流量走 Let’s Encrypt。

5.4 自动续期与监控: systemctl list-timers 的实战用法

Let’s Encrypt 证书 90 天过期, certbot 自带续期服务,但必须验证其是否激活:

# 查看 certbot 续期定时器状态
sudo systemctl list-timers | grep certbot
# 应输出:certbot.timer loaded active waiting 12h left 12h ago certbot.service

# 手动触发续期测试(不发送邮件)
sudo certbot renew --dry-run
# 输出应为:Congratulations, all renewals succeeded.

关键监控点:

  • 定时器状态 certbot.timer 必须为 active (waiting) ,否则续期不会执行。
  • 日志检查 sudo journalctl -u certbot --since "1 week ago" ,确认无 error failed
  • 证书有效期 sudo openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -dates -noout ,确保 notAfter 日期在 90 天后。

如果续期失败,常见原因是磁盘空间不足( /var/log/letsencrypt 占满)或网络连通性问题( curl https://acme-v02.api.letsencrypt.org/directory 超时)。此时, certbot renew --force-renewal 可强制重签,但应先解决根本原因。

我在实际操作中发现,最可靠的续期保障,是在 crontab 中添加兜底任务(尽管 systemd 定时器已足够):

# 每天凌晨 2:15 执行续期
15 2 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2" >> /var/log/le-renewal.log 2>&1

--post-hook 确保续期成功后自动重载 Apache, --quiet 避免日志刷屏。这条命令,是我部署的 17 个生产站点三年来零证书过期事故的核心保障。

最后再分享一个小技巧:如果你的服务器没有固定域名(比如用动态 DNS 或内网 IP),但又想获得浏览器信任,可以考虑使用 mkcert 工具。它能在本地生成一个受操作系统信任的根证书,然后用该根证书为你任意域名(包括 localhost 192.168.1.100 )签发证书。整个过程无需联网,且生成的证书会被 Chrome/Firefox/Edge 自动信任。这比自签名更进一步,又比 Let’s Encrypt 更灵活,是开发环境的终极方案。不过,它的原理仍是“本地 CA”,所以本质上,你今天掌握的 OpenSSL 密钥生成、CSR 构造、X.509 编码这些底层能力,依然是所有方案的基石。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值