Open HoneyryderChuck opened 7 years ago
Humm, I've worked around another issue, and finally managed to establish proper connectivity.
My client is using net/http
, which allows to set the SSL version, and by default this is :SSLv23
.
This can be set to :TLSv1
, :TLSv1_1
, and :TLSv1_2
. My test was failing because the minimal version set in my test was :TLSv1_1
. After removing this, everything worked.
However, this means that ruby-tls currently only works with :SSLv23
. ALPN protocols should only work with TLSv1_2
, right?
I've tried to patch the lib and attach other openssl functions like TLSv1_2_server_method
, but I'm getting the following error:
FFI::NotFoundError: Function 'TLSv1_2_server_method' not found in [libssl.dylib]
Does this make sense? This lib should be FFI'ing into the same libssl object file that my ruby's openssl is, right? Which is, in this case, ruby 2.4, which enables TLS 1-up-to-1.2 .
It doesn't use the ruby libssl and uses the systems libssl - which is probably where the issue is being introduced - we needed features before they were available in Ruby and it is used for SSL connections in https://github.com/cotag/libuv
It doesn't use the ruby libssl and uses the systems libssl
Indeed. I just confirmed that in my MacOS Sierra, the libssl.dylib
file doesn't contain the necessary symbols:
$ nm -g /usr/lib/libssl.dylib | grep server_method
00000000000080c0 T _DTLSv1_server_method
000000000000b450 T _SSLv23_server_method
000000000000eff0 T _SSLv2_server_method
000000000001ad10 T _SSLv3_server_method
0000000000030830 T _TLSv1_server_method
However, this is one of 5 possible ssl lib files (in my system, I have libssl.0.9.7.dylib
, libssl.0.9.8.dylib
, libssl.35.dylib
, libssl.39.dylib
, libssl.dylib
, and two of them contain the necessary symbols for TLS 1.1 and 1.2 support.
I don't know what's the best way to approach this, as I'm afraid that in most cases, the default lib file will be an old version of ssl (in the mac OS, waaaay old), and this disables a lot of the benefits I was hoping to get, like TLS 1.2 / ALPN support, which is necessary for http2.
It'll pick the first library it discovers - this hasn't been an issue for us as we do most of our work in docker containers - I think I used homebrew to install a new version of openssl for development work.
Ruby's version of OpenSSL isn't distributed as a dynamic link library so we can't access the methods we need without using a 3rd party lib.
One option would be to add openssl as a subproject to this project and compile it at GEM install time then link to the local copy of openssl - which would keep versions tightly coupled. I've done something similar for the libuv gem we maintain
I've also installed a new version of openssl using brew, and link it. But any program will compile with the system openssl by default. I've compiled latest cURL's against this new version, but had to specify the path myself. Unfortunately I can't specify the path using a 3rd-party gem.
Your last solution seems the one where you can control what you expect. I've seen that only SSLv23_client_method
and SSLv23_server_method
, and maybe the reason is this. I mean, what version of TLS are your docker programs using? SSLv23
, right? You don't have a specific workaround for newer versions?
I've forked and am running a local version of this lib, and added this:
ffi_lib ['ssl.39', 'ssl.35', 'ssl']
attached the missing symbols and reran my tests. And it all just works. A notable change is that I get a protocol on handshake completion ("h2") instead of nothing (which is what happens if I run my tests in SSLv23 mode, for lack of ALPN support).
I'll see if I can create a client with this lib to demonstrate what happens locally, and also to get tests passing in jruby (which was my main point to use this lib, as jruby-openssl
is wildly buggy and behind). I think that it makes sense to not expect stock-openssl to be updated, but what's definitely missing is a way to relefct is version (i.e. something like a Ruby::TLS::SSL.version
, which returns i.e. 1.0.0
). This could help me dynamically load the missing symbols.
I'll see if I can get a patch.
btw, what are you using for verifying certs against a chain? I see that this should be verified in the #verify_cb
callback.
Awesome! Eventmachine has some good documentation on the verify_cb method http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection#ssl_verify_peer-instance_method
Thx for the link, but I'm afraid that it doesn't help me, at least what concerns the actual job of verifying the cert. My question would be more "how to do this in plain Ruby?", as ruby's "openssl" uses an openssl specific API for that apparently (it even uses the system default ca dir if none is provided).
So the question would be: does it bring value to show how to do this in plain ruby, or could this gem provide the same solution as openssl, i.e. verify the server certificate against the local provided ca bundle?
Looks like we could just use the ruby OpenSSL code: https://github.com/ruby/openssl/blob/master/lib/openssl/ssl.rb#L261
OpenSSL::SSL.verify_certificate_identity(cert, hostname)
Isn't that to verify the hostname (SNI)? Not that I have anything against it being done.
I meant more this snippet: https://github.com/ruby/openssl/blob/e72d960db2623b21ee001b5a7b9d9e6ff55bdf94/ext/openssl/ossl_ssl.c#L877-L892
If passed a ca file bundle and VERIFY_PEER
. From what I saw, there's nothing like this happening, am I right?
That is the same as what this library does when you set the verify_peer: true
option
https://github.com/cotag/ruby-tls/blob/master/spec/verify_spec.rb#L78
Then the code to perform a standard verification is https://github.com/ruby/openssl/blob/master/lib/openssl/ssl.rb#L261
The blob you've referenced above is where they are setting up the callbacks
I have a test with verify_peer: true
and no passed CA path or dir, and my test isn't failing. Given that this is a generated CA pem file for my tests, and my test certificates are signed by it, my tests should be failing but they're not.
The only thing that this verify_peer: true
is deciding, is whether to call the verify_cb
callback, and leave the actual verification job for the user. But it is not using SSL functions to verify the server certificate against a selected cert store.
If I do the same using OpenSSL, I get a:
ctx.ca_file_path = 'path/to/ca_file'
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed
Shouldn't this gem be doing the same and handle verification?
Ahh yeah, you could be right. Based on this example https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_verify(3) I still think the verification has to happen in that callback.. Maybe we just do it for the user?
Or I could be looking at the wrong thing again and totally misunderstanding what we should be verifying
Maybe we just do it for the user?
I'd say so.
I'm just struggling with finding where openssl
is doing it though. I had doubts about a previous comment and tried to implement the verify callback with OpenSSL::SSL.verify_certificate_identity
, and I just confirmed that it only does hostname verification, so I'm back to square one...
I've been looking for an alternative for 'openssl', that plays well with jruby. This seemed to be it, as it supports ALPN as well. However, I'm struggling to make it work. I'm testing against an openssl-enabled client, And it fails when the tls server starts and sends the first message:
The thing I saw was that there is (currently) no way to set the version.
Do you have any working example with sockets/network protocols?