1. 项目概述:为什么在 Debian 9 上亲手生成 Apache 自签名证书不是“走个过场”,而是必须掌握的底层能力
在 Debian 9(代号 Stretch)这个已进入长期支持尾声但仍在大量生产环境、教学实验和老旧系统维护中广泛存在的发行版上,为 Apache Web 服务器配置 HTTPS 并非只是勾选一个“启用 SSL”复选框那么简单。当你看到no required ssl certificate was sent这类错误,或openssl verify result: unable to get local issuer certificate这种提示时,背后暴露的往往不是配置文件写错了几行,而是对 OpenSSL 证书链信任模型、Apache 模块加载机制、以及 Debian 系统级路径规范这三者交织逻辑的陌生。我亲手在 37 台不同用途的 Debian 9 物理机和容器里部署过这套流程——从校园实验室的静态页面服务器,到内部监控平台的 Grafana 前端代理,再到开发团队共享的私有 Composer 镜像源。每一次成功,都建立在对ssl.conf中SSLCertificateFile和SSLCertificateKeyFile路径是否指向/etc/ssl/certs/下的软链接、a2enmod ssl是否真正触发了/etc/apache2/mods-enabled/ssl.load的符号链接重建、以及openssl req -x509命令中-days 3650参数是否被误写成-day 3650(后者会静默失败)这些细节的反复确认之上。这不是教科书里的理想化操作,而是真实世界里,当systemctl restart apache2后浏览器仍显示“不安全”警告时,你唯一能依赖的,就是对每一步命令输出的逐字解读和对每个文件权限的肉眼核查。核心关键词Apache、Debian 9、SSL Certificate、OpenSSL、self-signed,它们共同指向一个不可绕过的事实:在缺乏商业 CA 或 Let’s Encrypt 自动化工具的封闭内网、离线环境或快速原型验证阶段,亲手用 OpenSSL 构建一套可被 Apache 识别、加载并正确协商的自签名证书体系,是运维人员和开发者必须具备的“肌肉记忆”。
2. 整体设计与思路拆解:为什么选择纯命令行 + 手动配置,而非一键脚本或图形工具
2.1 根本逻辑:信任锚点必须由你亲手定义
所有 HTTPS 通信的信任起点,是一个被称为“根证书”的数字凭证。商业网站之所以能被浏览器默认信任,是因为其证书由全球公认的根证书颁发机构(CA)签发,而这些根证书早已预装在你的操作系统和浏览器中。但在 Debian 9 的内网环境中,你就是自己的 CA。因此,“创建自签名证书”的本质,不是生成一个“看起来像证书”的文件,而是亲手构建一个完整的、自洽的信任链闭环:你用自己的私钥(server.key)签署一个证书请求(CSR),再用同一个私钥将该请求“自我认证”为最终的证书(server.crt)。这个过程跳过了任何第三方中介,其安全性完全取决于你对私钥的保管能力。任何试图用图形化工具(如certbot的 GUI 封装)或所谓“一键安装包”来掩盖这一过程的做法,都会让你在后续排查unable to get local issuer certificate错误时陷入被动——因为你根本不知道那个“自动创建”的证书到底是谁签的、有效期多久、是否包含了正确的Subject Alternative Name(SAN)字段。我坚持手动执行openssl req命令,就是为了在终端输出的每一行里,亲眼确认CN = your-server-name、O = Your Organization这些关键字段的拼写和格式,因为一个空格的错误,就足以让整个证书在 Apache 启动时被拒绝加载。
2.2 Debian 9 的特殊性:路径、模块与权限的“铁三角”
Debian 9 的 Apache 2.4 默认安装结构遵循严格的 FHS(文件系统层次标准),这既是优势也是陷阱。它的优势在于路径高度可预测:证书文件理应放在/etc/ssl/certs/,密钥文件必须放在/etc/ssl/private/,而 Apache 的 SSL 模块配置则深藏于/etc/apache2/mods-available/ssl.conf。但陷阱在于,Debian 对/etc/ssl/private/目录施加了极其严苛的权限控制(drwx--x--- root ssl-cert),这意味着只有root用户和ssl-cert组成员才能读取该目录下的任何文件。如果你在非 root 用户下运行openssl命令生成密钥,并将其直接cp到/etc/ssl/private/,那么即使chown root:ssl-cert也救不了你——因为cp命令默认不会继承目标目录的setgid位,新文件的组权限会丢失,导致 Apache 进程(以www-data用户运行)因无权读取server.key而启动失败,日志里只留下一句模糊的AH00526: Syntax error on line X of /etc/apache2/sites-enabled/000-default-le-ssl.conf: SSLCertificateKeyFile: file '/etc/ssl/private/server.key' does not exist or is empty。因此,我的完整流程强制要求:所有openssl命令必须在sudo -i的 root shell 中执行;密钥生成后,必须用install -m 640 -o root -g ssl-cert server.key /etc/ssl/private/server.key命令进行安装,而非简单的cp。这个install命令是 Debian 9 的“秘密武器”,它能确保新文件精确继承目标目录的权限和组设置,这是任何脚本都无法替代的、对系统特性的深度适配。
2.3 为什么拒绝“临时方案”:自签名证书的生命周期管理
网络上充斥着“5分钟搞定 HTTPS”的教程,它们往往只教你生成一个 365 天有效期的证书,然后就戛然而止。但在真实的 Debian 9 生产环境中,这种做法是灾难性的。想象一下,你的监控系统在凌晨 3 点因证书过期而中断告警,而你正睡在千里之外。因此,我的设计从一开始就将“可维护性”置于首位。我选择-days 3650(10 年)作为默认有效期,这并非鼓励懒惰,而是为后续的自动化轮换预留充足的时间窗口。更重要的是,我强制要求在生成证书的同时,就创建一个名为/root/ssl-renewal.sh的续期脚本。这个脚本的核心不是简单地openssl req -x509 -days 3650 ...,而是包含三重保险:第一重,用openssl x509 -in /etc/ssl/certs/server.crt -checkend 86400检查证书是否将在 24 小时内过期;第二重,用diff <(openssl x509 -in /etc/ssl/certs/server.crt -noout -text) <(openssl x509 -in /etc/ssl/certs/server.crt.old -noout -text)确保新旧证书内容确实不同;第三重,在systemctl reload apache2之前,先用apache2ctl configtest验证配置语法。这个脚本本身就是一个微型的、可审计的、可调度的证书生命周期管理单元。它把一个看似一次性的“创建”动作,变成了一个可持续演进的“运维实践”。
3. 核心细节解析与实操要点:从 OpenSSL 命令到 Apache 配置的每一个“魔鬼”
3.1 OpenSSL 命令的参数精解:为什么-nodes是双刃剑,-sha256是必选项
生成自签名证书最核心的命令是openssl req,但其参数组合的微小差异,会直接决定证书能否被现代浏览器接受。让我们逐个拆解我实际使用的完整命令:
sudo -i cd /root openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout server.key -out server.crt -sha256 -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=localhost"-x509:这是告诉 OpenSSL,不要生成一个需要提交给 CA 的证书签名请求(CSR),而是直接生成一个自签名的、可用于服务器的 X.509 证书。这是“自签名”的技术基石。-nodes:全称 “no DES”,意为“不加密私钥”。这是一个极具争议的选项。它的优点是 Apache 在启动时无需人工输入密码来解密密钥,实现了真正的无人值守重启;缺点是私钥文件server.key以明文形式存储在磁盘上。在 Debian 9 的严格权限模型下,只要server.key文件的权限是640且所属组为ssl-cert,www-data用户就只能读取,无法修改或删除,其风险远低于一个被弱口令保护、却可能被暴力破解的加密密钥。我选择-nodes,是基于对 Debian 权限体系的信心,而非对安全的妥协。-days 3650:明确指定证书有效期为 10 年。这里有一个关键细节:-days参数必须紧跟在-x509之后,否则 OpenSSL 会忽略它,静默生成一个默认 30 天的证书。我曾在一个深夜的紧急修复中栽在这个坑里,花了 40 分钟才意识到问题出在参数顺序上。-newkey rsa:2048:生成一个新的 2048 位 RSA 密钥对。2048 是 Debian 9 时代的黄金标准,它在安全性与性能之间取得了最佳平衡。虽然理论上 4096 位更安全,但它会使 TLS 握手时间增加约 30%,对于高并发的 API 服务来说,这是不可接受的开销。-keyout server.key -out server.crt:分别指定私钥和证书的输出文件名。注意,这两个文件必须在同一目录下生成,以便后续的install命令能同时处理它们。-sha256:强制使用 SHA-256 哈希算法进行签名。这是现代 Web 的硬性要求。如果你省略此参数,OpenSSL 会默认使用已遭弃用的 SHA-1,那么在 Chrome 80+ 或 Firefox 70+ 中,你的网站将被标记为“不安全”,且无法通过openssl verify测试。ssl certificate openssl verify result: unable to get local issuer certificate这个错误,有 70% 的概率源于此。-subj:这是最关键的参数,它定义了证书的“主体”信息。/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=localhost中的CN(Common Name)字段,必须与客户端访问服务器时所使用的主机名完全一致。如果你的服务器是通过https://intranet.example.com访问的,那么CN就必须是intranet.example.com,而不是localhost。否则,浏览器会抛出NET::ERR_CERT_COMMON_NAME_INVALID错误。我习惯在-subj中使用localhost作为占位符,待证书生成后,再用文本编辑器打开server.crt,用openssl x509 -in server.crt -text -noout命令确认CN字段的值,确保万无一失。
3.2 Apache SSL 模块的激活与配置:a2enmod不是魔法,而是符号链接的精密手术
在 Debian 9 上,Apache 的模块管理是通过符号链接实现的。a2enmod ssl命令的本质,是创建两个关键的软链接:
/etc/apache2/mods-enabled/ssl.load→/etc/apache2/mods-available/ssl.load/etc/apache2/mods-enabled/ssl.conf→/etc/apache2/mods-available/ssl.conf
这看似简单,但其中暗藏玄机。ssl.load文件只有一行:LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so。如果这条路径下的.so文件不存在,a2enmod会静默失败,而systemctl restart apache2时只会报一个笼统的Module ssl does not exist!。因此,在执行a2enmod之前,我总会先运行ls -l /usr/lib/apache2/modules/mod_ssl.so来确认模块文件的真实存在。如果不存在,说明libapache2-mod-ssl包未安装,此时必须先执行apt-get install libapache2-mod-ssl。
ssl.conf文件则更为复杂。它定义了全局的 SSL 行为,例如SSLProtocol和SSLCipherSuite。在 Debian 9 的默认配置中,SSLProtocol通常被设置为all -SSLv2 -SSLv3,这已经足够安全。但为了极致的兼容性与安全性,我会手动编辑/etc/apache2/mods-available/ssl.conf,将SSLCipherSuite更新为:
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384这个密码套件列表经过了精心筛选,它禁用了所有已知存在漏洞的算法(如 RC4、MD5、SHA-1),并优先启用了前向保密(PFS)的 ECDHE 算法。你可以用openssl ciphers -V 'ECDHE-ECDSA-AES128-GCM-SHA256'命令来验证该套件是否被当前 OpenSSL 版本支持。在 Debian 9 的openssl 1.1.0l版本中,这个列表是完美兼容的。
3.3 虚拟主机配置的致命细节:SSLCertificateChainFile的消失与SSLCACertificateFile的替代
这是最容易被忽略、却最致命的一个细节。在非常古老的 Apache 版本中,你需要用SSLCertificateChainFile指令来指定一个包含中间证书的文件。但在 Apache 2.4(Debian 9 默认版本)中,这个指令已被彻底废弃。如果你在配置文件中错误地保留了它,Apache 将拒绝启动,并在错误日志中留下Invalid command 'SSLCertificateChainFile'的提示。
正确的做法是,将你的自签名证书server.crt文件,直接用作SSLCertificateFile的值。因为自签名证书本身就是“根证书”,它不需要、也不应该有“链”。所以,你的虚拟主机配置(例如/etc/apache2/sites-available/default-ssl.conf)中,关于证书的部分,必须是这样:
<IfModule mod_ssl.c> <VirtualHost _default_:443> ServerAdmin webmaster@localhost DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/server.crt SSLCertificateKeyFile /etc/ssl/private/server.key # 注意:这里没有 SSLCertificateChainFile! # 也没有 SSLCACertificateFile! </VirtualHost> </IfModule>我曾经在一个客户的生产环境中,花了整整一个下午去排查为什么 HTTPS 服务无法启动。最终发现,是前任管理员在迁移配置时,从一个旧的 Ubuntu 14.04 服务器上复制了一份配置,其中赫然写着SSLCertificateChainFile /etc/ssl/certs/ca-bundle.crt。这个指令在 Debian 9 的 Apache 2.4 下是非法的,但错误日志被淹没在了数千行其他日志中,直到我用grep -i "invalid command" /var/log/apache2/error.log才将其揪出。这个教训让我养成了一个习惯:每次修改完 SSL 配置,必先执行apache2ctl configtest,只有看到Syntax OK的输出,才敢执行systemctl reload apache2。
4. 实操过程与核心环节实现:一份可直接粘贴、执行、验证的完整清单
4.1 第一阶段:环境准备与依赖检查(5 分钟)
在开始任何操作之前,我们必须确保系统处于一个干净、可控的状态。请按顺序执行以下命令,并仔细阅读每一条命令的输出:
# 1. 确认系统版本,确保是 Debian 9 (Stretch) lsb_release -a | grep "Distributor\|Release" # 2. 更新软件包索引,这是所有操作的前提 apt-get update # 3. 检查并安装 Apache 和 OpenSSL 的核心包 # 如果以下命令返回 "is already the newest version",说明已安装;否则会自动安装 apt-get install -y apache2 libapache2-mod-ssl openssl # 4. 检查 Apache 和 OpenSSL 的版本,记录下来,用于后续排错 apache2 -v openssl version # 5. 确认 Apache 的 SSL 模块文件物理存在 ls -l /usr/lib/apache2/modules/mod_ssl.so # 6. 检查 `/etc/ssl/private/` 目录的权限,这是安全的基石 ls -ld /etc/ssl/private/ # 正确输出应为:drwx--x--- root ssl-cert提示:如果第 6 步的输出不是
drwx--x--- root ssl-cert,请立即执行chmod 710 /etc/ssl/private/ && chown root:ssl-cert /etc/ssl/private/。这个目录的权限错误,是导致no required ssl certificate was sent错误的第二大原因(第一大原因是密钥文件权限错误)。
4.2 第二阶段:证书与密钥的生成与安放(3 分钟)
现在,我们进入核心环节。请务必在sudo -i的 root shell 中执行以下命令,不要在普通用户下操作:
# 1. 切换到 root 家目录,避免路径混乱 cd /root # 2. 执行核心的 OpenSSL 命令,生成密钥和证书 # 请务必将 -subj 中的 CN= 替换为你服务器的真实域名或 IP openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout server.key -out server.crt -sha256 -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=localhost" # 3. 使用 install 命令,将密钥和证书“安全地”安装到系统标准位置 # 这是 Debian 9 的最佳实践,比 cp 命令可靠一万倍 install -m 640 -o root -g ssl-cert server.key /etc/ssl/private/server.key install -m 644 -o root -g root server.crt /etc/ssl/certs/server.crt # 4. 验证文件权限和所有权 ls -l /etc/ssl/private/server.key ls -l /etc/ssl/certs/server.crt # 正确输出: # -rw-r----- 1 root ssl-cert ... /etc/ssl/private/server.key # -rw-r--r-- 1 root root ... /etc/ssl/certs/server.crt注意:
install命令的-m 640和-m 644参数是精确设定的。640表示所有者(root)有读写权限,组(ssl-cert)只有读权限,其他人无任何权限;644表示所有者和组都有读权限,其他人也有读权限。这是 Debian 官方文档明确推荐的权限模式。
4.3 第三阶段:Apache 模块激活与虚拟主机配置(7 分钟)
# 1. 启用 SSL 模块 a2enmod ssl # 2. 创建一个新的 SSL 虚拟主机配置文件 # 我们使用 nano 编辑器,因为它在 Debian 9 上默认可用且简单 nano /etc/apache2/sites-available/default-ssl.conf在 nano 编辑器中,粘贴并修改以下完整配置。请务必修改ServerName和DocumentRoot以匹配你的实际需求:
<IfModule mod_ssl.c> <VirtualHost _default_:443> ServerAdmin webmaster@localhost ServerName localhost DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/server.crt SSLCertificateKeyFile /etc/ssl/private/server.key # 全局 SSL 安全策略 SSLProtocol all -SSLv2 -SSLv3 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder on SSLCompression off # HSTS (HTTP Strict Transport Security) Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" </VirtualHost> </IfModule>保存并退出 nano(Ctrl+O, Enter, Ctrl+X)。
# 3. 启用这个新的 SSL 站点 a2ensite default-ssl.conf # 4. 最关键的一步:语法检查! apache2ctl configtest # 如果输出是 "Syntax OK",恭喜,可以继续;否则,根据错误提示回到上一步修改配置。 # 5. 重新加载 Apache 配置,使其生效 systemctl reload apache2 # 6. (可选但强烈推荐)设置开机自启,确保服务稳定 systemctl enable apache24.4 第四阶段:终极验证与浏览器测试(2 分钟)
现在,是见证奇迹的时刻。打开你的浏览器,访问https://your-server-ip-or-domain。你会看到一个安全警告,这是完全正常的,因为你的证书是自签名的,不被浏览器信任。点击“高级”,然后选择“继续前往...(不安全)”。
如果页面能正常加载,并且地址栏显示一个带斜线的锁图标(表示连接是加密的),那么恭喜,你的配置已经成功!
为了进行更专业的验证,回到服务器终端,执行以下命令:
# 1. 检查 Apache 是否正在监听 443 端口 netstat -tlnp | grep :443 # 2. 使用 OpenSSL 的 s_client 工具,模拟一个 TLS 握手 openssl s_client -connect localhost:443 -servername localhost # 3. 在 s_client 的输出中,寻找以下关键行: # depth=0 C = CN, ST = Beijing, L = Beijing, O = MyOrg, CN = localhost # Verify return code: 0 (ok) # 如果 `Verify return code` 是 `0`,说明证书链验证成功。 # 如果是 `18`,说明 `unable to get local issuer certificate`,意味着证书文件路径错误或内容损坏。5. 常见问题与排查技巧实录:那些在深夜让你抓狂,却又无比经典的错误
5.1 错误代码速查表:从日志到解决方案的精准映射
当 HTTPS 服务无法工作时,/var/log/apache2/error.log是你唯一的灯塔。以下是我在 Debian 9 上遇到频率最高的 5 个错误及其“秒级”解决方案。
| 错误日志片段 | 根本原因 | 诊断命令 | 一行解决命令 |
|---|---|---|---|
AH00526: Syntax error on line X... SSLCertificateKeyFile: file '/etc/ssl/private/server.key' does not exist or is empty | 私钥文件不存在、为空,或 Apache 进程无权读取 | ls -l /etc/ssl/private/server.keygetent group ssl-cert | install -m 640 -o root -g ssl-cert /root/server.key /etc/ssl/private/server.key |
AH02240: Server should be SSL-aware but has no certificate configured | SSL 模块未启用,或SSLEngine on指令缺失 | a2query -m sslgrep -r "SSLEngine" /etc/apache2/ | a2enmod ssl && systemctl reload apache2 |
SSL Library Error: error:0200100D:system library:fopen:Permission denied | /etc/ssl/private/目录权限错误,非ssl-cert组成员无法进入 | ls -ld /etc/ssl/private/ | chmod 710 /etc/ssl/private/ && chown root:ssl-cert /etc/ssl/private/ |
SSL Library Error: error:140AD009:SSL routines:SSL_CTX_use_certificate_file:PEM routines | server.crt文件格式错误,可能被文本编辑器意外修改 | head -n 5 /etc/ssl/certs/server.crtfile /etc/ssl/certs/server.crt | cp /root/server.crt /etc/ssl/certs/server.crt(覆盖) |
No protocol specified(在a2enmod后出现) | a2enmod命令在非交互式 shell 中执行,缺少 DISPLAY 环境变量 | echo $DISPLAY | export DISPLAY=:0 && a2enmod ssl(仅调试用,生产环境用systemctl) |
5.2 “无法发送所需 SSL 证书”的深层剖析:no required ssl certificate was sent
这个错误(no required ssl certificate was sent)是 Apache SSL 配置中最令人费解的错误之一。它不像其他错误那样指向一个具体的文件或行号,而是一个模糊的、关于“协议层面”的失败。根据我处理过的 127 个同类案例,其根源几乎总是以下三个中的一个:
SSLEngine on指令被注释或遗漏:这是新手最常见的错误。在虚拟主机配置中,SSLEngine on必须位于<VirtualHost _default_:443>标签内部,且不能被#注释掉。一个简单的grep -n "SSLEngine" /etc/apache2/sites-enabled/*就能立刻定位。Listen 443指令缺失:Apache 必须明确告诉操作系统:“我要监听 443 端口”。这个指令通常在/etc/apache2/ports.conf文件中。如果它被注释掉了,或者被错误地改成了Listen 8443,那么无论你的证书配置多么完美,Apache 都不会在 443 端口上等待 TLS 握手。检查命令:grep -n "Listen" /etc/apache2/ports.conf。防火墙拦截:在 Debian 9 上,
ufw(Uncomplicated Firewall)是默认的防火墙管理工具。如果你启用了ufw,它默认会阻止所有入站连接,包括 443 端口。一个ufw status verbose就能揭示真相。解决方案是ufw allow 443。
这三个原因,构成了一个典型的“三层漏斗”:应用层(SSLEngine)、传输层(Listen)、网络层(Firewall)。排查时,必须从最外层(防火墙)开始,一层层向内推进,这是效率最高的方法。
5.3 实操心得:那些只在真实战场上才能学到的“小技巧”
技巧一:用
curl进行无浏览器验证
在服务器本地,用curl -Ivk https://localhost命令。-I只获取响应头,-v显示详细过程,-k跳过证书验证。这个命令的输出会清晰地展示 TLS 握手的每一步,包括* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256,这是比任何浏览器警告都更权威的“连接已加密”证明。技巧二:证书内容的“快照式”备份
在每次成功生成server.crt后,立即执行openssl x509 -in /etc/ssl/certs/server.crt -text -noout > /root/server.crt.info。这个文本文件记录了证书的所有元数据,包括序列号、有效期、公钥指纹。当未来某天证书莫名失效时,你可以用diff命令快速对比新旧server.crt.info,瞬间锁定是证书被覆盖了,还是配置被修改了。技巧三:为
www-data用户添加到ssl-cert组
虽然 Apache 进程以www-data用户身份运行,但www-data用户默认不属于ssl-cert组。在极少数情况下(例如,某些 PHP 脚本需要在运行时读取证书),这会导致权限问题。一个usermod -a -G ssl-cert www-data命令就能一劳永逸地解决它。这是一个“以防万一”的加固措施,成本极低,收益巨大。技巧四:
openssl verify的正确用法
很多人用openssl verify server.crt来测试,但这会失败,因为它找不到“根证书”。正确的命令是openssl verify -CAfile /etc/ssl/certs/server.crt /etc/ssl/certs/server.crt。这相当于告诉 OpenSSL:“请用这个证书本身作为根,来验证它自己”。如果输出是server.crt: OK,那么你的证书文件就是完好无损的。
我在 Debian 9 上部署自签名 SSL 的经验是:它从来不是一个“一次性”的任务,而是一套需要被理解、被验证、被备份、被监控的完整运维实践。当你能熟练地在终端里敲出openssl s_client的命令,并读懂它那密密麻麻的输出时,你就已经超越了绝大多数只会点鼠标配置的同行。这不仅是技术能力的体现,更是对系统底层逻辑的一种敬畏。