Reqwest throws an SSL error


#1

Okay, so recently I found that the link https://api.cobinhood.com/v1/market/trading_pairs if fetched by reqwest (which uses hyper under the hood) throws an SSL error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
 kind: Io(Custom { 
     kind: Other, error: Ssl(ErrorStack([Error { 
         code: 336134278, 
         library: "SSL routines", 
         function: "SSL3_GET_SERVER_CERTIFICATE", 
         reason: "certificate verify failed", 
         file: "s3_clnt.c", 
         line: 1180
 }])) }), 
    url: Some("https://api.cobinhood.com/v1/market/trading_pairs/") }', libcore/result.rs:945:5

This error might be related the following stackoverflow issue - here. However, I’m not too familiar with how and why this happens and how to approach solving, given that my browsers work fine. I have a good sucpicion that given that Hyper uses the Ubuntu native ssl library, this is not a Rust issue at all, but was hoping someone could advise me on how to fix it.


#2

This works for me:

extern crate reqwest;

fn main() {
    println!("{}", reqwest::get("https://api.cobinhood.com/v1/market/trading_pairs").unwrap().text().unwrap());
}

I’m on Arch Linux.


#3

Yeah that same line copied gives me same error still… I’m on Ubuntu 14.04 LTS. Which version of reqwest you used?


#4

The latest. What is the output of this command?

openssl s_client -connect api.cobinhood.com:443 -servername api.cobinhood.com

#5

The output is:

vername api.cobinhood.com
CONNECTED(00000003)
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = GeoTrust Inc., CN = RapidSSL SHA256 CA
verify return:1
depth=0 CN = *.cobinhood.com
verify return:1
---
Certificate chain
 0 s:/CN=*.cobinhood.com
   i:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
 1 s:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
   i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
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
8ZocD0jU1PY=
-----END CERTIFICATE-----
subject=/CN=*.cobinhood.com
issuer=/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
---
No client certificate CA names sent
Peer signing digest: SHA256
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4316 bytes and written 459 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: 93AB7E6A8DE6EC5F13D3C35B9982F21DC268582587AAC1E7A824E6EE5A4B449A
    Session-ID-ctx: 
    Master-Key: 792B9784E50A9AC0D33A93590E947A0D5E2C27E9E59DABC84BA26A334043EB9AD76B0F121263A327E8A3652E6E29B4C6
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 64800 (seconds)
    TLS session ticket:
    0000 - c7 8d ea 4b 7b 08 3e 61-cb 1e ff 15 d5 bb 24 23   ...K{.>a......$#
    0010 - 2e 90 27 b6 a1 7e 60 0e-ee 02 48 b7 f5 2e b7 87   ..'..~`...H.....
    0020 - 8d df e4 68 1d ce 46 52-ea 21 bb 64 e6 c1 ca 39   ...h..FR.!.d...9
    0030 - 06 73 f5 15 33 6b 0e 41-67 62 d4 52 8f 5b 4c 5b   .s..3k.Agb.R.[L[
    0040 - 7c 41 1f 82 95 7e f7 2e-72 68 a8 f5 9b ad 48 81   |A...~..rh....H.
    0050 - 23 40 18 0e 2c b1 4f c8-7c 15 c9 53 77 01 ef 46   #@..,.O.|..Sw..F
    0060 - 27 92 0f 45 3c d9 8c bf-4d 83 02 b2 76 ce 62 4a   '..E<...M...v.bJ
    0070 - cf 74 fd f3 7d 64 2f 77-10 df c4 77 06 5a de bb   .t..}d/w...w.Z..
    0080 - 07 3f 08 2c ab 92 2d 5e-c2 0f c8 54 5c cd 04 31   .?.,..-^...T\..1
    0090 - 8a ba bf 14 f4 10 1f c5-a3 f8 9c 78 c6 f2 b4 99   ...........x....
    00a0 - 48 f8 b3 dc 58 4e 5d 99-8d e0 f3 4c ce ac f3 3b   H...XN]....L...;

    Start Time: 1522010135
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

It’s interesting, cause I think (75% sure) that I did this fine when I was in the UK. I’m now in Bulgaria testing this and it gives me this… could that be somehow related?

PS: So indeed just tested the code on my machine at work, which is in the UK and I don’t get that error.


#6

The output of s_client looks normal. I just tested this on a fresh 14.04 container and it works fine too. There must be something wrong with the system you’re running the test on: outdated packages, crates, etc.


#7

Hmm… all of my dependencies are listed as "*" and I’ve just done cargo clean on the project after updating my Rust compiler to the latest one (now at 1.26-nightly). But if the system package output in normal how come the reqwest can crash? And as mentioned is it possible somehow my location to matter?


#8

This is the only https address that throws this error?

Absolutely. Someone might be trying to man-in-the-middle your traffic. Hotel wifis are common culprits.


#9

However, I’m in a home wifi. Unless my ISP is doing something weird…


#10

I would suggest calling with my mio_httpc library. As it is unrelated to reqwest/hyper/tokio. If it also does not work it is actually an SSL issue not a http client library issue.

You can try calling with both rustls or openssl (example in README). If it is an ssl issue, I’m not sure there are more than two possibilities. Someone is either MITMing or system time on your machine is wildly wrong.


#11

Wow, so very interesting results. I’ve tried as suggested to run with mio_httpc since its a totally unrelated package to my current stack.

  1. Running it with native:
thread 'main' panicked at 'Call failed: Tls(Ssl(ErrorStack([Error {
    code: 336134278, 
    library: "SSL routines", 
    function: "SSL3_GET_SERVER_CERTIFICATE", 
    reason: "certificate verify failed", 
    file: "s3_clnt.c",
    line: 1180 }])))', 
libcore/result.rs:945:5
  1. Running with openssl:
thread 'main' panicked at 'Call failed: Tls(Error { code: ErrorCode(1), cause: Some(Ssl(ErrorStack([Error { 
    code: 336134278, 
    library: "SSL routines", 
    function: "SSL3_GET_SERVER_CERTIFICATE", 
    reason: "certificate verify failed", 
    file: "s3_clnt.c", 
    line: 1180 }]))) })', 
libcore/result.rs:945:5
  1. With rustls:
Headers={
    "date": "Mon, 26 Mar 2018 14:45:04 GMT", 
    "content-type": "application/json", 
    "transfer-encoding": "chunked", 
    "connection": "keep-alive", 
    "set-cookie": "__cfduid=dd68aa5eef5cf7503350ce21af08170841522075504; expires=Tue, 26-Mar-19 14:45:04 GMT; path=/; domain=.cobinhood.com; HttpOnly; Secure", 
    "access-control-allow-origin": "*", 
    "via": "1.1 google", 
    "alt-svc": "clear", 
    "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", 
    "server": "cloudflare", 
    "cf-ray": "401a661decce4050-SOF", 
    "content-encoding": "gzip"}
Body: {"success":true,"result":{"trading_pairs": [...]}}

As a result it works with rustls and fails with openssl. Unfortunately, I’m no expert on SSL details to be able to make any reasonable conclusion (my assumption is this is potential issue with the openssl library installed on my Ubuntu 14.10?).


#12

rustls uses different root certificates (https://github.com/ctz/webpki-roots) compared to openssl which uses the system root certificates. Perhaps those need to be updated.


#13

I’ve tried adding the website CA certificate in my ubuntu but still same issue… Weird that in the 16.04 at work this does not happen yet the OpenSSL version is the same.


#14

Could you post an strace of the failing native or openssl example?


#15

Ok so running:

cargo run --example get --features "openssl" -- "https://api.cobinhood.com/v1/market/trading_pairs"

The result is:

Get https://api.cobinhood.com/v1/market/trading_pairs
thread 'main' panicked at 'Call failed: Tls(Error { code: ErrorCode(1), cause: Some(Ssl(ErrorStack([Error { code: 336134278, 
library: "SSL routines", 
function: "SSL3_GET_SERVER_CERTIFICATE", 
reason: "certificate verify failed", 
file: "s3_clnt.c", 
line: 1180 }]))) })', libcore/result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Same using native.