-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for self-signed certificates #97
Conversation
|
4564a33
to
569d334
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fantastic work! Thanks for starting it!
I have a thought, aimed at staying conservative and not exposing internal dependencies here.
What if instead of exposing NativeTlsClient
and friends, at least to start with, we just added the ability to set certificates on reqwest::ClientBuilder
? And by certificate, I mean also have a local reqwest::Certificate
type, instead of hyper_native_tls::Certificate
.
@@ -11,7 +11,7 @@ categories = ["web-programming::http-client"] | |||
|
|||
[dependencies] | |||
hyper = "0.10.2" | |||
hyper-native-tls = "0.2" | |||
hyper-native-tls = { git = "https://github.com/sfackler/hyper-native-tls" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just making a note here for myself to not merge until we can remove the git dependency.
src/client.rs
Outdated
} | ||
|
||
/// Disable hostname verification | ||
pub fn disable_hostname_verification(&mut self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should have a scary name like the others, danger_disable_hostname_verification
.
src/client.rs
Outdated
let mut tls_client = try_!( | ||
self.inner.build().map_err(|e| ::hyper::Error::Ssl(Box::new(e)))); | ||
if ! self.hostname_verification { | ||
tls_client.dannger_disable_hostname_verification(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dannger
, with 2 n
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes probably a typo, I'll make a PR to fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been fixed already. I'll update the PR.
src/client.rs
Outdated
let mut client = try_!(new_hyper_client()); | ||
client.set_redirect_policy(::hyper::client::RedirectPolicy::FollowNone); | ||
Ok(Client { | ||
impl From<NativeTlsClient> for Client { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we did the conservative change like I suggest in the review comment, we'd probablt want to remove this impl.
Hi @seanmonstar ! Thanks for the kind words :) I'm not sure to understand your comment. |
reqwest already depends on hyper-native-tls, so I'm not suggesting we remove that. I'm saying let's see if we can prevent exposing that details as part of the public API. There is an interest in trying to get reqwest to a stable 1.0, so part of that requires not exporting or showing any types from other crates that aren't 1.0 stable yet. So, to a user, all they see is a |
I think I got it. Do you thinkg the last two commits address this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly like that, great!
let mut client = try_!(new_hyper_client()); | ||
client.set_redirect_policy(::hyper::client::RedirectPolicy::FollowNone); | ||
/// Represent an X509 certificate. | ||
pub struct Certificate(hyper_native_tls::Certificate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly!
src/client.rs
Outdated
/// let _ = ::std::fs::File::open("my-cert.der").unwrap().read_to_end(&mut buf).unwrap(); | ||
/// let cert = reqwest::Certificate::from_der(&buf).unwrap(); | ||
/// # } | ||
/// ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Rust community is starting to encourage a different way of writing doc examples, that don't use unwrap
and stuff. It could be written like this:
/// ```
/// # use std::fs::File;
/// # fn cert() -> Result<(), Box<std::error::Error>> {
/// let mut buf = Vec::new();
/// File::open("my_cert.der")?
/// .read_to_end(&mut buf)?;
/// let cert = reqwest::Certificate::from_der(&buf)?;
/// # drop(cert);
/// # Ok(())
/// # }
/// ```
src/client.rs
Outdated
/// | ||
/// // [...] | ||
/// } | ||
/// ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment about the example here: put it some sort of # fn foo() -> Result<(), Box<std::error::Error>>
, and use ?
instead of unwrap
.
src/client.rs
Outdated
/// } | ||
/// ``` | ||
/// | ||
pub struct TlsClient { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually liked that naming you have before, ClientBuilder
. We can eventually move several of the config properties to the builder and remove the need for the Mutex
and atomics...
src/client.rs
Outdated
} | ||
|
||
/// Returns a `Client` that uses this `TlsClient` configuration. | ||
pub fn into_client(self) -> ::Result<Client> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer the method name build
.
src/client.rs
Outdated
redirect_policy: Mutex::new(RedirectPolicy::default()), | ||
auto_referer: AtomicBool::new(true), | ||
auto_ungzip: AtomicBool::new(true), | ||
}), | ||
}) | ||
} | ||
|
||
/// Add a custom root certificate. This can be used to connect to a server that has a | ||
/// self-signed certificate for example. | ||
pub fn add_root_certificate(&mut self, cert: Certificate) -> ::Result<&mut TlsClient> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we have a builder that needs to consume itself to create a final Client
, we should probably follow the consuming builder guidelines.
Ugh, but at the same time, there are methods that could fail, such as this one, and a Result
is needed. That'd mean that using the consuming pattern, you could lose the builder if it errors...
I'll have to think on this part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how it's done in hyper-native-tls
.
I've not been very consistent here, because danger_disable_hostname_verification
and enable_hostname_verification
don't return anything. I should probably make them return &mut ClientBuilder
to be consistent with add_root_certificate
, or return ClientBuilder
as suggested in the guildelines.
815012f
to
a99f168
Compare
It's kind of unrelated to certificates, but I'm thinking that the let client = ClientBuilder::new()?.proxy_url(url).proxy_auth(auth).build()?; I'd be interested in giving it a try if this get merged, what do you think? |
@little-dude yes, I'd expect proxy methods would be part of the (For the actual API, I like the |
For some reason, this PR is not working in my case. Certificate verification still fails.
|
Not sure how to debug that... both I'll try to find a public server with a self signed certificate to see if I have the same issue. |
226b6f5
to
b9239b1
Compare
So, this doesn't quite work? |
9472dae
to
8e11deb
Compare
@seanmonstar I rebased, squashed, and added tests (poorly written). I made it work yesterday with certificates I generated. But after making changed to the tests and automating certificates generation, it does not work anymore... but I'm still working on it. I'm not very familiar with TLS/openssl/certificates so I may be doing stupid mistakes. Regarding the tests: I have a script that generates a root CA, and an intermediate CA. I use the intermediate CA to make two server certificates:
Assuming I can make these tests pass, what are your feelings about needing a shell script to run, for the tests? |
6207e9f
to
edd7f71
Compare
- test custom root certificate - test custom root certificate with hostname verification disabled
a30c012
to
12c71fa
Compare
with this commit, tests hang sometimes. Not sure if this can be related to openssl/openssl#3505 since I also saw the tests hang when working on sfackler/hyper-native-tls#7 However, it seems to be more frequent here.
I'm not sure I'll be able to debug the windows issue, so I'll close this for now, sorry...
|
I'm going to resurrect this, because there is a lot of good work in here! To start, we can just assume that rust-native-tls has tests of its own, and that we don't need to test that its own functionality does what it says. We can open a new issue for getting solid TLS tests running in reqwest on target platforms. |
I can test on MacOS, have a bit of experience with openssl and certs in general (nothing stellar), so please ping me if I can help. |
Re-opened in #124, without the TLS tests. |
wow thanks a lot @seanmonstar. I was kind of disappointed that I could no finish this up, so I'm glad to see this was not useless. Once openssl/openssl#3525 is merged, I'll try again to see if tests still hang on linux. |
There's a new version of openssl-sys that works around the OpenSSL issue. |
The tests still hang on my machine with |
gdb and lldb work. |
The hang is not directly related to openssl I think. Sometimes certificate verification fails, so the test panics, and the code that stops the server is never run: #[test]
fn test_custom_ca_hostname_verification_disabled() {
let listening = start_server("tests/certificates/server-wrong.hostname.com.pfx");
let client = get_client(HostnameVerification::Disabled);
// panic occurs here:
// Error { kind: Http(Ssl(Failure(Ssl(ErrorStack([Error { code: 337047686, library: "SSL routines", function: "tls_process_server_certificate", reason: "certificate verify failed", file: "ssl/statem/statem_clnt.c", line: 1230 }]))))), url: Some("https://localhost:12345/") }
let mut resp = client.get("https://localhost:12345").unwrap().send().unwrap();
let mut body = vec![];
resp.read_to_end(&mut body).unwrap();
assert_eq!(body, b"ok");
// this does not run
mem::forget(listening);
} After handling the error and calling The fact that sometimes certificate verification fails is not good though... |
This is untested, and based on my PR for hyper-native-tls (sfackler/hyper-native-tls#7), but I'd like to know if you think this is the right direction.
This is a step toward fixing #15