curl:(60)服务器证书验证失败 CRLfile:无

curl:(60)服务器证书验证失败 CRLfile:无

我正在慢慢地从专职开发人员的角色转变为公司中混合型 DevOps 的角色。这意味着我对很多事情都很陌生,请对我宽容一点... :-p

我客户的服务器运行的是 Ubuntu 16.04,带有 PHP 5.6.4,并且他们网站的管理门户中有一个功能,它curl(本质上)运行命令回到自身以进行某种文件同步。而且它已经失败了一段时间(几周/几个月)。问题是(我思考) 是证书验证失败,因此该功能即将失效。

当我 ssh 进入服务器时,我可以轻松地 curl 到任何地方,没有任何问题(Google、example.org 等...)。但尝试仅对网站主 URL 进行基本 curl博克斯

$ curl -v https://www.[my-site-name].com

*   Trying [my-site-IP]...
* Connected to [my-site-name] ([my-site-IP]) port 443 (#0)
* found 258 certificates in /etc/ssl/certs/ca-certificates.crt
* found 908 certificates in /etc/ssl/certs/
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256
* server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
* Closing connection 0
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

我知道我可以运行-k不安全的 curl,但我犹豫不决。我想第一个问题是,我是否应该担心使用不安全标志运行它,因为它在技术上根本没有离开服务器?我已经在运行 Ubuntu 18.x 的较新机器之一上以及运行 v20 的 DigitalOcean 上测试了完全相同的 curl 命令,完全没有问题——外部和内部 curl 都运行良好。

我甚至可以在另一台服务器上,然后回到遇到问题的服务器,而且它也运行良好。

我已经尝试了所有我能想到的方法(虽然确实不多),但似乎没有任何效果。

  • 运行 curl 和 certbot 包的更新
  • 迫使update-ca-certificates
  • 添加/etc/ssl/certs/cacert.pemphp.ini 中的和curl.cainfovarsopenssl.cafile

我知道这可能并不重要,但为了完整起见,我还通过各种在线验证服务运行了该网站:

所有测试结果都是积极的。唯一的缺点(我猜)是 SSLLabs 给我们打了“B”,因为显然 TLS 1.0 仍然处于启用状态。


任何帮助都将不胜感激。我觉得阅读失败警告中提到的文档并没有太大帮助。

建议/提示/技巧??

提前一千次谢谢您!

答案1

Meta:不是答案,至少现在还没有,但有一些信息和建议。

好吧,简单的情况不适用。正如您所遇到的,curl当它从底层 SSL/TLS 堆栈获得证书验证错误时,不会提供非常有用的信息,至少在使用 GnuTLS 时不会提供非常有用的信息,就像 Ubuntu 版本一样。但是,OpenSSL(应该存在,因为 curl 有它作为依赖项,虽然我不明白为什么)提供了一个名为 simply 的命令行程序,openssl它主要是测试工具,并且更详细,也更容错。例如,在一个故意制造的恶意网站上,在 Ubuntu 16.04 的 docker 版本中:

# curl -v https://untrusted-root.badssl.com/
*   Trying 104.154.89.105...
* Connected to untrusted-root.badssl.com (104.154.89.105) port 443 (#0)
* found 129 certificates in /etc/ssl/certs/ca-certificates.crt
* found 516 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256
* server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
* Closing connection 0
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html

[snip canned text -- same as yours]
# openssl s_client -connect untrusted-root.badssl.com:443 -servername untrusted-root.badssl.com
CONNECTED(00000003)
depth=1 C = US, ST = California, L = San Francisco, O = BadSSL, CN = BadSSL Untrusted Root Certificate Authority
verify error:num=19:self signed certificate in certificate chain
---
Certificate chain
 0 s:/C=US/ST=California/L=San Francisco/O=BadSSL/CN=*.badssl.com
   i:/C=US/ST=California/L=San Francisco/O=BadSSL/CN=BadSSL Untrusted Root Certificate Authority
 1 s:/C=US/ST=California/L=San Francisco/O=BadSSL/CN=BadSSL Untrusted Root Certificate Authority
   i:/C=US/ST=California/L=San Francisco/O=BadSSL/CN=BadSSL Untrusted Root Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEmTCCAoGgAwIBAgIJAMJ1vCpOBAlkMA0GCSqGSIb3DQEBCwUAMIGBMQswCQYD
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j
aXNjbzEPMA0GA1UECgwGQmFkU1NMMTQwMgYDVQQDDCtCYWRTU0wgVW50cnVzdGVk
IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIxMTIwNDAwMDgxOVoXDTIz
MTIwNDAwMDgxOVowYjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzANBgNVBAoMBkJhZFNTTDEVMBMGA1UE
AwwMKi5iYWRzc2wuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
wgTs+IzuBMKz2FDVcFjMkxjrXKhoSbAitfmVnrErLHY+bMBLYExM6rK0wA+AtrD5
csmGAvlcQV0TK39xxEu86ZQuUDemZxxhjPZBQsVG0xaHJ5906wqdEVImIXNshEx5
VeTRa+gGPUgVUq2zKNuq/27/YJVKd2s58STRMbbdTcDE/FO5bUKttXz+rvUV0jNI
5yJxx8IUemwo6jdK3+pstXK0flqiFtxpsVdE2woSq97DD0d0XEEi4Zr5G5PmrSIG
KS6xukkcDCeeo/uL90ByAKySCNmMV4RTgQXL5v5rVJhAJ4XHELtzcO9pGEEHRVV8
+WQ/PSzDqXzrkxpMhtHKhQIDAQABozIwMDAJBgNVHRMEAjAAMCMGA1UdEQQcMBqC
DCouYmFkc3NsLmNvbYIKYmFkc3NsLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEADVgB
Ias+fb/Ckdnw5iSYsfRIcfhnTBNgvroorUQe09Psx0tAbjlIYqOCtloNhvCbJYa7
tQQOO65s7RKArpAA1qoapWfDti2aHuMNvGwImwX538RiLf4Rm4MEF6vuF6MZMH/u
Ts1iugB3+d7oSWl/K+RvA4NMRNrlxOLelBJwaTsExsQ5QalpPamongnyWXHZ2Sna
dsw9hBku9ZmlRqYOCE/TajsydqCIhCc2QC5xdd3fxXlcfq5h7G0oOuCYvW7BscTk
AQZYkwS+y3mTHF9wSlxJB4iEGC0NovdM5GsVgfvZ5+jtXGuDlsphAIFLxpIJi1bR
+NsAkEthHoQZDNttvtVJPFPp83PdRDmL6IwrbbXvAwZWWgYmS6HpbF5bxR09JIOA
KttGK1wnd6bh2d9Xy6kfoxZ1gz6i2y5OpxbMoi6z8o1Y2hSOv2kgnD2fxtR9X4OO
wrwWZWnhwsiq7pnZbZiA9GFR4tKZVcJ5ny5aul/MZ0wb5MST7wHW/7qhydfBpOy6
hZ5BSbwfmBao+CJ7NxNJb5c03W5+/Vf1uxXZhodpag6Z5p0rru0v7ea9nMk5dNUm
qR+2XGwzDk9n8jCWwfvSKCa77rf2HqKi8ZaQN/NRp7uVqfY++JVI+h3CLyMk8wTL
FifbLPKbSCW7PAFEfM3wh76VQg1CHpHOPVp/wno=
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=San Francisco/O=BadSSL/CN=*.badssl.com
issuer=/C=US/ST=California/L=San Francisco/O=BadSSL/CN=BadSSL Untrusted Root Certificate Authority
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3561 bytes and written 425 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: AC5EB655FAEA1C1A320CA930E6A087FBFE020D3D9C451A72DD7CE77A8080AF0D
    Session-ID-ctx:
    Master-Key: 419065F69901D74454A224DD22F9D459FA85E6EB4D6F043C649C8FDFA2E5E1FD9C168AC270087B88511B0939927F9A0A
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - cc 91 34 52 b1 ce c6 7c-8a 97 27 b0 b4 50 d6 9d   ..4R...|..'..P..
    0010 - 27 b8 87 13 0b 70 92 e7-23 be 57 5c 00 31 f7 90   '....p..#.W\.1..
    0020 - 65 e3 08 d5 63 14 e9 cd-cc 50 59 3f 2a 98 31 d1   e...c....PY?*.1.
    0030 - ab f8 ca a8 09 aa 66 bf-7c ff b6 66 42 10 8b 6e   ......f.|..fB..n
    0040 - c7 e8 7b f3 2b 35 b3 76-8c 95 b3 b5 37 85 09 42   ..{.+5.v....7..B
    0050 - 83 a9 c3 f7 54 f4 c5 f5-b2 0d d5 fa c6 24 a6 e2   ....T........$..
    0060 - fb 03 cf 35 9c 0d cc 48-e0 bc fc 43 11 3c 19 51   ...5...H...C.<.Q
    0070 - 0a 23 7d b2 6c ff 9b e2-bc 64 1d 74 34 cb 13 7b   .#}.l....d.t4..{
    0080 - c8 55 47 95 71 9d 94 00-33 a0 a0 87 d4 4a fb 81   .UG.q...3....J..
    0090 - 34 36 28 2e ac d7 22 36-36 5f 9c cb 3f 0d e7 00   46(..."66_..?...
    00a0 - e2 b7 d0 35 48 81 2f c7-9d a1 8a f6 52 a3 11 17   ...5H./.....R...
    00b0 - 67 89 94 d3 66 2b 5c c4-73 c5 72 1e 84 46 57 a5   g...f+\.s.r..FW.
    00c0 - 77 05 bc 49 a3 1a fe e6-da a8 0f 40 e2 17 f0 40   w..I.......@...@

    Start Time: 1644060097
    Timeout   : 300 (sec)
    Verify return code: 19 (self signed certificate in certificate chain)
---
Q
DONE

请注意,该行后面的第一个输出CONNECTEDdepth=1 ... / verify error:num=19:....表示证书验证失败;但是openssl忽略此错误并完成握手,导致输出的最后一部分:SSL-Session: / Protocol: (valid) / Cipher: (valid) / ... / Verify return code: ...之后您必须键入Q并返回或 control-D(单独)退出程序,或 control-C 终止,除非您</dev/null在命令行上添加,其效果与自动键入 control-D 相同。现在您不需要了解协议和密码的具体值,只要知道它们不是“none”、“error”或“missing”之类的东西即可。但请注意,OpenSSL 的 verify=19 消息文本不完整,甚至令人困惑;通常,来自 SSL/TLS 服务器的证书链包含自签名的根证书是完全正常的,并且如果该根位于本地信任库中OpenSSL 确实接受(curl/GnuTLS 也应该接受)这样的链。只有当根在链中且不是本地信任时,您才能获得此验证码。其他验证码(如“已过期”)的消息大多更清晰。

另请注意,我使用了-servername主机名;这将调用SNI=服务器名称指示如今,许多 SSL/TLS 服务器(包括badssl.com)都需要它来提供正确的证书。但是,Ubuntu16.04 中的 curl 版本不会发送 SNI;没有研究,我不知道这是由于 curl 版本稍旧(但不是很旧)、使用了 GnuTLS 还是只是构建错误。但无论如何作为测试尝试openssl s_client针对您的服务器执行带有和不带有该部分的命令,-servername host并保存输出(使用重定向或 tee,最好包括带有&>|& tee或等效项的 stderr),因为我们可能需要它们。如果带 SNI 的版本可以工作,而没有 SNI 的版本出现验证错误,那就是您的问题——缺乏 SNI。(可能很难修复,但至少您知道它是什么。)

否则,查看 without-SNI 的输出。如果 OpenSSL 报告验证错误(与 curl/GnuTLS 一致),请查看相关消息 - 它至少应该指出问题的方向,即使不是确切的方向。如果 OpenSSL 未报告验证错误(而 curl/GnuTLS 报告了,两者都使用系统在 /etc/ssl 中提供的相同信任库),情况会更加复杂,所以我暂时先不讨论这个问题,以后再根据需要添加。

相关内容