1. 从一次“神秘”的请求失败说起那天下午我正调试一个需要从内网服务器拉取数据的自动化脚本脚本的核心就是用了curl命令去访问一个 HTTPS 接口。环境是 Ubuntu 22.04curl版本也是最新的网络通畅目标服务器也确认在线。但每次执行命令行都冷冰冰地抛回一个错误curl: (60) SSL certificate problem: unable to get local issuer certificate更具体一点如果加上-v参数看详细过程会在 SSL 握手阶段卡住提示找不到证书的颁发者。这个错误对于刚接触 HTTPS 交互的开发者来说简直像一堵无形的墙。你明确知道目的地就在那里但手中的“通行证”SSL/TLS 证书却不被认可。这个问题本质上就是CURL 访问 HTTPS 时的 CA 证书问题。它不仅仅是curl的问题更是理解整个 HTTPS 信任链、操作系统证书管理以及安全通信基础的一个绝佳切入点。无论是调用第三方 API、拉取 Docker 镜像、安装软件脚本比如常见的curl -fsSL https://... | sh还是内部服务间通信只要走 HTTPS就绕不开 CA 证书这一关。接下来我就把自己排查和解决这类问题的完整链路、背后的原理以及不同场景下的应对策略系统地梳理一遍。2. 理解 HTTPS 与 CA 证书信任链为什么curl会“不认账”要解决问题得先明白curl在抱怨什么。当我们用curl https://example.com时背后发生了一系列加密握手过程其中关键一环就是证书验证。2.1 HTTPS 连接的核心步骤TCP 连接curl首先与服务器的 443 端口建立 TCP 连接。SSL/TLS 握手连接建立后双方开始 TLS 握手。服务器会将其 SSL 证书发送给客户端curl。证书验证这是出错的高发区。curl实际上是它底层链接的 SSL 库如 OpenSSL、GnuTLS 或 LibreSSL需要验证收到的服务器证书是否可信。验证包括证书有效性是否在有效期内。域名匹配证书中的Common Name (CN)或Subject Alternative Name (SAN)是否包含我们正在访问的域名。信任链验证最关键服务器证书必须由一个客户端信任的证书颁发机构 (CA)签发。客户端需要确认为服务器证书签名的 CA 证书其本身是否受信。这通常涉及一条“信任链”服务器证书 - 中间 CA 证书 - 根 CA 证书。根 CA 证书必须预先安装在客户端的“信任存储”中。2.2curl如何寻找信任的根证书curl自身不维护证书列表。它依赖于编译时指定的 SSL 库如 OpenSSL。该 SSL 库在运行时查找的证书存储路径。通常这个路径指向一个包含众多受信根 CA 证书的文件如ca-certificates.crt或一个目录如/etc/ssl/certs。在大多数 Linux 发行版上这个证书包由ca-certificates这个软件包提供和更新。当curl报告unable to get local issuer certificate时根本原因是它收到的服务器证书的签发者Issuer在其信任存储里找不到对应的 CA 证书。这可能是因为服务器使用的是自签名证书自己给自己签发没有公认的 CA。服务器证书由某个私有 CA或企业内网 CA签发而该 CA 的根证书没有安装到你的系统信任存储中。系统的ca-certificates包损坏、过期或未安装。curl被指向了一个错误的证书存储路径。注意错误curl: (35) SSL connect error可能涵盖更底层的网络或协议问题但证书问题也是常见原因之一。而(60)错误则更明确地指向证书验证失败。3. 诊断与排查定位证书问题的具体原因遇到证书错误别急着改配置。先按以下步骤诊断可以事半功倍。3.1 使用-v和--insecure进行初步判断首先使用-v详细模式查看握手全过程错误信息会更具体。curl -v https://your-target-site.com在输出中找到 SSL 相关的部分看错误具体描述。其次使用--insecure或-k参数。这个参数会让curl跳过证书验证。curl -k https://your-target-site.com如果-k能成功那几乎可以确定问题100%出在证书验证环节而不是网络或协议兼容性。如果-k也失败那可能是其他问题如网络不通、SSL 协议版本不匹配、加密套件不支持等。3.2 检查系统的 CA 证书存储确认系统证书包是否正常。# 查看 ca-certificates 包是否安装Debian/Ubuntu dpkg -l | grep ca-certificates # 查看证书文件是否存在及其大小 ls -lh /etc/ssl/certs/ca-certificates.crt # 或检查证书目录 ls -lh /etc/ssl/certs/ | head -20 # 对于 CentOS/RHEL/Fedora rpm -qa | grep ca-certificates ls -lh /etc/pki/tls/certs/ca-bundle.crt如果文件很小比如只有几KB或不存在说明证书存储可能有问题。3.3 检查curl本身的信息和链接的 SSL 库# 查看 curl 版本和支持的协议 curl --version输出中会显示curl链接的 SSL 库如OpenSSL/1.1.1f和一些特性。记下 SSL 库类型。3.4 使用openssl命令独立验证证书链openssl工具可以更精细地诊断。# 获取服务器证书链并验证 openssl s_client -connect your-target-site.com:443 -showcerts这个命令会模拟一个 SSL 客户端连接并打印出服务器返回的所有证书。仔细看输出末尾的验证结果Verify return code: 0 (ok) # 成功 Verify return code: 20 (unable to get local issuer certificate) # 失败找不到签发者同时你可以看到服务器证书的签发者Issuer信息。把这个信息和你的系统信任存储对比。3.5 检查特定场景自签名证书与私有 CA如果你访问的是开发、测试环境或内网服务很可能用的是自签名证书或私有 CA 签发的证书。自签名证书证书的 Issuer 和 Subject 是同一个实体。私有 CA你需要获取这个私有 CA 的根证书通常是一个.crt或.pem文件。4. 解决方案大全从临时绕过到永久修复根据不同的原因和场景解决方案也不同。请对号入座。4.1 方案一临时跳过验证仅用于测试使用-k或--insecure参数。这是最不安全的做法因为它完全禁用了 TLS 的核心安全特性——身份验证。绝对不要在生产环境脚本、自动化工具或任何涉及敏感数据的场景中使用。它只适用于快速测试一个已知的、非生产环境的服务是否可达。curl -k https://internal-test-server.local4.2 方案二指定自定义 CA 证书文件如果你有服务器证书的签发者 CA 证书例如公司内网的根证书my-company-root-ca.crt你可以让curl使用这个特定的证书文件来验证。curl --cacert /path/to/my-company-root-ca.crt https://internal.company.com--cacert参数告诉curl“除了系统默认的信任证书也信任我提供的这个证书。” 如果服务器证书链的根正好是这个文件验证就会通过。4.3 方案三将私有 CA 证书添加到系统全局信任存储这是访问内网服务最规范、一劳永逸的方法。以 Ubuntu/Debian 为例获取 CA 根证书文件.crt或.pem格式。将其复制到/usr/local/share/ca-certificates/目录下。sudo cp my-company-root-ca.crt /usr/local/share/ca-certificates/运行更新命令让系统将新证书加入到全局信任包中。sudo update-ca-certificates这个命令会更新/etc/ssl/certs/ca-certificates.crt文件。之后所有使用系统证书存储的工具包括curl,wget,git,apt等都会信任该 CA 签发的证书。对于 CentOS/RHEL/Fedora步骤类似但路径不同sudo cp my-company-root-ca.crt /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust实操心得在 Docker 容器内如果基础镜像很精简如alpine可能没有ca-certificates包。你需要在 Dockerfile 中显式安装并添加证书例如FROM alpine:latest RUN apk add --no-cache ca-certificates curl COPY my-company-root-ca.crt /usr/local/share/ca-certificates/ RUN update-ca-certificates4.4 方案四修复或更新系统的 CA 证书包如果是因为系统证书包损坏或过时导致连公共网站如https://github.com都失败你需要修复它。# Debian/Ubuntu sudo apt update sudo apt install --reinstall ca-certificates # CentOS/RHEL/Fedora sudo yum update ca-certificates # 或 sudo dnf update ca-certificates4.5 方案五使用--capath指定证书目录如果你的证书是以单独文件的形式存放在一个目录中如/etc/ssl/certs/该目录下通常是到/usr/share/ca-certificates/中证书的符号链接你可以用--capath指定。curl --capath /etc/ssl/certs https://example.com但通常curl会自动使用系统默认路径除非你编译curl时指定了不同的路径或者环境被破坏。4.6 方案六处理自签名证书开发环境专用对于开发环境自签名的证书你有两个选择将自签名证书本身作为 CA 证书传入使用--cacert参数指向你服务器的自签名证书文件。这相当于告诉curl“我就信任这一个证书。”将自签名证书添加到系统信任库按照方案三的步骤把自签名证书文件当作一个“CA 证书”添加到系统中。但请注意自签名证书本质上不是 CA这样做会降低安全性仅限封闭的开发/测试环境。5. 进阶场景与疑难杂症排查5.1 场景使用curl -fsSL https://... | sh安装脚本时失败这是非常常见的场景例如安装 Docker、Ollama 等。脚本通常来自get.docker.com,ollama.com等。失败时错误信息可能被管道吞掉。建议分两步# 1. 先单独下载脚本看错误 curl -fsSL -o install.sh https://ollama.com/install.sh # 如果失败会显示具体错误 # 2. 如果确认是证书问题根据情况处理 # 如果是公网知名网站先尝试更新系统证书 sudo apt update sudo apt install --reinstall ca-certificates # 如果还是不行可能是网络中间环节问题如公司代理考虑使用 -k风险自负或配置正确的代理。5.2 场景在 CI/CD 流水线或容器中遇到证书问题在 GitLab CI、Jenkins 或 Docker 容器内运行curl命令时环境可能非常干净。确保基础镜像包含ca-certificates。如果访问内网服务需要在构建阶段或运行时注入私有 CA 证书。可以通过 Docker 的COPY指令或者 Kubernetes 的ConfigMap/Secret挂载证书到容器内然后执行update-ca-certificates。考虑使用多阶段构建在构建阶段处理证书运行阶段使用精简镜像。5.3 场景交叉编译的curl或嵌入式环境当你为其他平台如 ARM交叉编译curl时需要特别注意--with-ca-bundle或--with-ca-path配置选项。你必须为目标平台准备正确的 CA 证书包并在编译时指定其路径。否则编译出的curl将没有可用的信任根访问任何 HTTPS 都会失败。# 编译时指定 CA 证书包路径示例 ./configure --hostarm-linux-gnueabihf --with-ca-bundle/path/to/target/ca-bundle.crt ...5.4 排查curl使用的具体证书路径如果以上方法都无效可以检查curl到底在用什么路径。curl --version | grep -A2 -B2 “CA”或者更直接地使用strace追踪需要 root 权限strace -e openat curl https://example.com 21 | grep -i cert这会显示curl尝试打开了哪些证书文件。5.5 错误curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure这个错误比单纯的证书未知更复杂。它可能表示协议/加密套件不匹配服务器和客户端支持的 SSL/TLS 版本或加密算法没有交集。可以尝试指定协议curl --tlsv1.2 https://example.com # 强制使用 TLS 1.2服务器要求客户端证书双向 TLS/mTLS而你没有提供。这需要--cert和--key参数。SNI服务器名称指示问题某些老旧的服务器或配置有误的负载均衡器可能需要正确设置 SNI。现代curl默认会发送 SNI。如果怀疑是此问题可以显式指定或禁用不推荐curl --resolve example.com:443:1.2.3.4 https://example.com # 或使用 --connect-to6. 安全警告与最佳实践总结处理 CA 证书问题时务必牢记安全底线永远不要在生产环境使用-k(--insecure)这会让你暴露在中间人攻击的风险之下HTTPS 形同虚设。谨慎添加私有 CA只添加你完全信任的组织如你的公司的 CA 证书。不要随意添加来源不明的证书。保持系统 CA 证书更新定期更新ca-certificates包以确保信任最新的公共根 CA 和吊销列表。为不同环境配置不同策略开发/测试环境可以为了方便添加自签名证书但生产环境必须使用由公共或严格管理的私有 CA 签发的有效证书。考虑使用证书钉扎 (Certificate Pinning)对于特别重要的客户端可以固定期望的服务器证书公钥或指纹但这增加了运维复杂性需谨慎使用。回到开头我遇到的那个问题原因正是我们的内网测试服务器使用了自建的私有 CA 签发的证书而我这台新装的开发机上没有安装该私有 CA 的根证书。通过将 CA 根证书文件放到/usr/local/share/ca-certificates/并执行sudo update-ca-certificates问题迎刃而解。整个curl访问 HTTPS 的过程其实就是客户端与服务器之间建立信任的一次握手而 CA 证书就是这场握手得以成功的信任基石。理解并妥善管理这份信任是每一个与网络打交道的开发者的必修课。