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 不报错的关键。
生成后,立刻验证三件事:
-
证书是否真的包含 SAN?
openssl x509 -in server.crt -text -noout | grep -A1 "Subject Alternative Name" # 输出应为:DNS:localhost, IP Address:127.0.0.1, IP Address: ::1 -
私钥和证书是否匹配?
# 提取证书公钥 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!" -
证书指纹是否与 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
会做三件事:
-
将
my-server.crt软链接到/etc/ssl/certs/下(文件名自动哈希)。 -
更新
/etc/ssl/certs/ca-certificates.crt(一个包含所有受信根证书的 PEM 文件)。 - 通知所有使用 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 的“受信任的根证书颁发机构”:
-
打开 Chrome,地址栏输入
chrome://settings/security。 - 点击“管理证书” → “授权机构”选项卡 → “导入…”。
-
选择
server.crt文件,勾选“受信任的根证书颁发机构”,完成导入。 - 重启 Chrome (必须重启,否则不生效)。
Firefox 的解决方案:
-
打开 Firefox,地址栏输入
about:preferences#privacy。 - 滚动到底部,点击“查看证书” → “证书机构”选项卡 → “导入…”。
-
选择
server.crt,勾选所有信任选项(网站、电子邮件、软件),完成。 - 重启 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 或负载均衡灰度流量:
-
保留自签名配置
:不删除
default-ssl.conf,仅禁用sudo a2dissite default-ssl.conf。 -
启用 Let’s Encrypt 配置
:
certbot自动生成的000-default-le-ssl.conf已启用。 - DNS 切换 :将域名 DNS 的 A 记录指向服务器 IP,等待 TTL 过期(通常 1 小时)。
-
灰度验证
:用
curl -H "Host: yourdomain.com" http://your-server-ip测试 HTTP 流量是否重定向到 HTTPS;用curl -k https://your-server-ip(-k忽略证书)测试 HTTPS 是否返回内容。 -
全量切换
:确认无误后,
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 编码这些底层能力,依然是所有方案的基石。

341

被折叠的 条评论
为什么被折叠?



