-
-
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 SNI #89
Conversation
this makes urrlib3 use SNI if available
Hi @t-8ch, I haven't heard of SNI, very cool. Some things I'd like to see before we can merge this into urllib3:
Thank you! |
@shazow: Quote from the docs: "Specifying I could add config files for apache/nginx if this would be more useful. |
Mmm good point. http://bugs.python.org/issue8109 I don't feel very comfortable merging in untested code at this point and I would really like to avoid having to fire up nginx/apache to test some code. Some ideas:
Either way, I'd still perform the suggested refactoring. :) |
Should have read your last post earlier. I have also a test ready (using mentioned website). Addition: |
If you merge your sni branch into add_sni, it should appear in this pull request. You could merge it and then rename add_sni to sni, if you prefer. |
Wouldn't the merging include all the useless stuff from |
What do you mean by useless stuff? Commits that got overwritten later? I'm not very obsessive about keeping the commit history super clean. I rather keep context. |
Conflicts: urllib3/connectionpool.py
well, I did a clean stepwise rewrite of the patchset, which introduces |
Very nice, thank you @t-8ch. Any plans for the unit testing part? Don't worry about the patchset history. Worst case, I can squash it before I merge. :) |
Im not sure about handling situations, where the tests are run in an offline or in amanaged environment. |
All tests must be able to run offline, ideally completely automated (without explicitly running other commands/servers alongside). |
What about checking if the desired hostname is present in the raw handshake? This only tests if the hostname somehow ends up in the request but everything else would be unittesting python or openssl. |
That would be a great start. :) Especially we can get full code coverage with that, then I'd be happy to merge it in. |
Do you have any hints on how to eavesdrop my own socket? |
I think doing something like the tests here should do the trick: https://github.com/shazow/urllib3/blob/master/test/with_dummyserver/test_socketlevel.py |
This reverts commit 7f7cb5b.
Would you also accept an # Do normal stuff
urllib3.util.ssl_wrap_socket = urllib3.contrib.pyopenssl.ssl_wrap_socket
# begin using urllib3 Some quick testing indicated, overwriting things like this should work. For the record: |
Just took a look at the latest diff for this pull request, looking great! Thank you for seeing it through, @t-8ch. Regarding pyopenssl: I'm definitely +1 adding pyopenssl support. As you probably noticed, we already do some conditional support for SSL depending if Python is compiled with +ssl enabled. We could do the same thing for pyopenssl, to use it if it's available. Regarding whether to put it in contrib: I'm treating contrib as "experimental" code which isn't used by default but users can import it and use it explicitly if they want. So we could have something like That said, if we can get appropriate test coverage on the new pyopenssl code paths, then I'd be happy to include it in the core library. If we're unsure, then we can start with contrib and move it out of contrib later. If you're up for taking a crack at this, maybe we should start a separate pull request thread for pyopenssl? I'll take a closer look at this SNI pull request over the next week and hopefully merge it in. :) |
I tried to use sni with urllib3 and I see this problem that when I goto https://randomstring.mydomain.com -- it lands on one of the servers that actually is running on mydomain.com and does not fail (randomstring should actually give an error). This is the github install of urllib3. I also tried with https://github.com/t-8ch/urllib3.git and had the same problem. I hope this can be fixed soon. |
Are you using a version of python >= 3.2? |
Thanks for the quick response. I'm using python 2.7. Is it not supposed to work with 2.xx? |
Indeed it will require pyopenssl. I think that should be top priority :-) |
return context.wrap_socket(sock, server_hostname=server_hostname) | ||
return context.wrap_socket(sock) | ||
|
||
else: # Platform-specific: Python < 3.2 |
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 you're going to move that code into a seperate function, I would prefer to do that switch outside of the function definition, that it is evaluated only during import and not on every call.
if SSLContext:
ssl_wrap_socket(...) # Python 3.2 implementation using SSLContext
else:
ssl_wrap_socket(...) # Fallback implementation
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.
@shazow, @wallunit
What would be nicer? The same API for both method, ignoring server_name for the fallback function,
or something like this:
if SSLContext:
def ssl_wrap_socket(...): # new implementation, arguments a superset of the fallback one
else:
ssl_wrap_socket = ssl.wrap_socket
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 think the best behaviour would be to silently ignore non critical arguments like server_name but fail if security critical arguments get added later.
@wallunit The review is much appreciated, thank you! Do you think it makes sense to merge your changes into this pull? (I agree with much of what you commented.) |
Are you talking about the code cleanup changes, I suggested above. Or are you talking about my own SNI patch? |
@wallunit Both. :) |
if SSLContext: # Platform-specific: Python >= 3.2 | ||
context = SSLContext(PROTOCOL_SSLv23) | ||
context.verify_mode = cert_reqs | ||
if context.verify_mode != CERT_NONE: |
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.
Is there any reason, for doing that check? The old/fallback implementation doesn't explicitly ignores ca_certs
if cert_reqs
is CERT_NONE
. So why do we do so, here?
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.
Gonnna 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.
Got it again:
If ca_certs is None
, SSLContext.verify_locations()
dies with:
TypeError: cafile and capath cannot be both omitted
whereas ssl.wrap_socket()
just doesn't care.
This way people, not wanting to do verification had to specify ca_certs
nevertheless.
The try-catch is necessary, as SSLContext.load_verify_location()
raises a TypeError
whereas the guts and unittests of urllib3 expect a SSLError.
We could remove the check, catch the Error and check if it's about a invalid ca_path, swallow it in this case and reraise it otherwaise. But I think this wouldn't be really nice.
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.
Of course, you have to check whether ca_certs is given, but that's not the check you do in the code I commented on, above. In fact SSLSocket.__init__
as called by wrap_socket
in Python 3.2 uses following code:
if ca_certs:
self.context.load_verify_locations(ca_certs)
But it doesn't check for verify_mode != CERT_NONE
, like you do above.
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.
Ah, now I got it.
@shazow Of course I think that my suggestions regarding your patch make sense, otherwise I wouldn't have suggested them, right? ;) However my patch is still missing a test case. But feel free to merge my implementation with your test case together. |
@wallunit Not my patch so much as @t-8ch's. ;) Anyways, thanks again for the feedback, keep it coming! I'll take a look at it next week. |
thanks @wallunit
thanks @wallunit
if SSLContext: # Platform-specific: Python >= 3.2 | ||
context = SSLContext(PROTOCOL_SSLv23) | ||
context.verify_mode = cert_reqs | ||
if context.verify_mode != CERT_NONE: |
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.
Still should be if ca_certs:
instead of if context.verify_mode != CERT_NONE:
. See discussion form previous version.
thanks @wallunit
The patch looks good now. I hope it will be merged soon. |
I have pushed a new branch to my repo which provides a new argument |
@shazow any updates on this? |
@@ -83,7 +93,7 @@ def split_first(s, delims): | |||
|
|||
def parse_url(url): | |||
""" | |||
Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is | |||
Given a url, return a parsed :class:`.Url` named tuple. Best-effort is |
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.
namedtuple
is a thing. :)
I took a quick look, looks very reasonable. Unfortunately I don't have much time to validate and merge in non-trivial changes right now. If this is urgently blocking anyone, I suggest maintaining a fork with this patch for the time being. Hopefully I'll have the time for some of the bigger bugs in the next few weeks. Please keep bugging me until then. :) |
still fails, if util.ssl_wrap_socket() is called without enabled ssl. (old behaviour)
this makes urrlib3 use SNI if available.
testing can be done using https://sni.velox.ch/
I didn't include an automatic test, as this would fail on old versions of python or (open)ssl.
(SNI was added in 3.2)
I could however add a test which tests the version of python.