RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1382682 - python-requests module cannot communicate with HTTPS servers that require SNI support even if python2-ndg_httpsclient is installed
Summary: python-requests module cannot communicate with HTTPS servers that require SNI...
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 6
Classification: Red Hat
Component: python-requests
Version: 6.8
Hardware: All
OS: Linux
unspecified
urgent
Target Milestone: rc
: ---
Assignee: Charalampos Stratakis
QA Contact: Jan Houska
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2016-10-07 11:54 UTC by Jason Woods
Modified: 2017-03-21 11:06 UTC (History)
6 users (show)

Fixed In Version: python-requests-2.6.0-4.el6
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-03-21 11:06:23 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)
Patch to fix requests vendor import code (1.69 KB, patch)
2016-10-07 11:54 UTC, Jason Woods
no flags Details | Diff


Links
System ID Private Priority Status Summary Last Updated
Red Hat Product Errata RHBA-2017:0713 0 normal SHIPPED_LIVE python-requests bug fix update 2017-03-21 12:41:10 UTC

Description Jason Woods 2016-10-07 11:54:16 UTC
Created attachment 1208140 [details]
Patch to fix requests vendor import code

Description of problem:
The python-requests module cannot communicate with HTTPS servers that require SNI support, even if python2-ndg_httpsclient is installed.
Early on when urllib3 is included, requests patches the urllib3.connection module to support SNI. However, due to a bug in the packager's vendoring code inside requests/packages/__init__.py, any further use by requests of urllib3.connection triggers a second import of that module fresh, and doesn't get repatched.
This causes connections to any HTTPS server requiring SNI support to fail, even though it should not as everything that is needed to support this is available.

Version-Release number of selected component (if applicable):
python-requests-2.6.0-3.el6.noarch

How reproducible:
Always

Steps to Reproduce:
1. Create following test python script, sni.py:
import requests
r = requests.head('https://downloads.atlassian.com')
print 'Status: ', r.status_code

2. Run: python sni.py

Actual results:
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    r = requests.head('https://downloads.atlassian.com')
  File "/usr/lib/python2.6/site-packages/requests/api.py", line 94, in head
    return request('head', url, **kwargs)
  File "/usr/lib/python2.6/site-packages/requests/api.py", line 50, in request
    response = session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python2.6/site-packages/requests/sessions.py", line 464, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python2.6/site-packages/requests/sessions.py", line 576, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python2.6/site-packages/requests/adapters.py", line 431, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [Errno 1] _ssl.c:492: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

Expected results:
Status:  302

Additional info:
I have attached a patch for consideration that when applied to /usr/lib/python2.6/site-packages/requests/packages/__init__.py resolves the issue.

Comment 2 Jason Woods 2016-10-08 08:18:42 UTC
Just to try outline the underlying behaviour here.

import requests
> This imports requests module, which inside has:
from requests.packages.urllib3.contrib import pyopenssl
> This triggers the stub code in packages/__init__.py to run for requests.packages.urllib3. This stub code fails import and so then imports urllib3 from the system:
__import__(real_name)
> That enters sys.modules as 'urllib3', but inside the system urllib3 it then imports connectionpool:
from .connectionpool import (
> This enters sys.modules as 'urllib3.connectionpool' and then inside there we import connection:
from .connection import (
> This enters sys.modules as 'urllib3.connection'. Lots other bits happen now which aren't relevant. Then at end of it we copy sys.modules['urllib3'] to sys.modules['requests.packages.urllib3']. Now the first stage done, requests.packages.urllib3.contrib needs importing and so it runs the stub code again. This time import succeeds because the parent is already loaded:
__import__(name)
> Then we get to requests.packages.urllib3.contrib.pyopenssl and import that, and inside that we have:
from .. import connection
> However, this then resolves to 'requests.packages.urllib3.connection'. We don't have this loaded so stub code runs again. We DO have 'urllib3.connection' loaded though... So we actually load it AGAIN, as a new module. Now we do the injection:
pyopenssl.inject_into_urllib3()
> Problem here is that injection happens into 'requests.packages.urllib3.connection' entry in sys.modules. When actually, when you look at the 'import requests' results above... everything inside the requests module is using sys.modules entries for 'urllib3.X'. So the patch completely misses.

Solution is to ensure that after a system module is imported, we scour EVERY submodule imported by it, and ensure we create a corresponding 'requests.packages.urllib3.X' entry in sys.modules that is a direct copy. This ensures there is only one urllib3.connection module loaded instead of two, and ensures the patch hits the same one the requests module is actually using.

SNI now works. Hopefully clears up any unknowns!

Comment 6 Charalampos Stratakis 2016-10-13 09:29:21 UTC
Hello and thank for this report as well as the reproducers. As I see from the discussions on github, upstream decided not to apply the patch as they will target python 2.7, so it seems we'll have to divert a bit from upstream in order for the component to work correctly on RHEL 6.8.

Will wait a bit for a final verdict from upstream, as it would be ideal if they merge your patch and then we carry the same patch downstream.

Comment 7 Jason Woods 2016-10-13 11:19:47 UTC
Hi Charalampos Stratakis,

Apologies, that wasn't anything to do with upstream requests module. I'll remove it from the ticket. That was a downstream product (SaltStack) that is relying on python-requests, that is affected by this issue on RedHat 6 (though the issue itself was about Amazon Linux it was the same issue on RedHat). The patch I mentioned there was a workaround for the issue for the time being, so of course they would be unwilling to include it in source, as the root issue resides within the python-requests packaging on RedHat 6. Their statement regarding moving to Python 2.7 only applies to Amazon Linux as of course RedHat 6 does not ship Python 2.7 (that I know) so would not work.

If the python-requests module is fixed, SaltStack works on RedHat 6 without any modification, and it would only need requests_lib to be configured and ndg_httpsclient installed.

This is an issue that needs to be rectified within the python-requests module, specifically the stub code.

Regards,

Jason

Comment 8 Jason Woods 2016-10-13 11:21:32 UTC
Just to add also. This impacts all python applications on RedHat 6 that attempt to use the requests module with a server using SNI, as the reproduction script demonstrates. So it is not isolated to SaltStack.

Comment 9 Charalampos Stratakis 2016-10-18 14:30:50 UTC
It seems the patch doesn't work though. Trying the reproducer with a patched requests provides the same traceback.

Comment 10 Jason Woods 2016-10-18 16:05:10 UTC
Did you install python2-ndg_httpsclient? It is required by urllib3 (and therefore also requests) to support SNI. I probably didn't make that clear in the steps, sorry. So even with the patch if you don't have it installed it won't work. Only with it installed AND the patch will the desired result be achieved.

When I get a straining next few days though I will test thoroughly again on a new machine as I can't rule out I messed up somewhere :-) Unless you manage to get the desired result before I test again!

Comment 11 Jason Woods 2016-10-18 16:06:23 UTC
s/straining/moment in/ ...

Comment 12 Charalampos Stratakis 2016-10-19 11:41:55 UTC
You were right, by installing python-ndg_httpsclient and also installing python-requests with your patch I was able to verify that your patch indeed works.

The bug and your patch will be evaluated for inclusion at the next release.

Thank you very much for your contribution here.

Comment 13 Charalampos Stratakis 2016-10-19 13:48:18 UTC
Also it seems that this is the issue upstream [0]

[0] https://github.com/kennethreitz/requests/issues/3287

Comment 15 Jan Houska 2016-12-14 10:16:09 UTC
Verified:

Old Failed:
python-requests-2.6.0-3.el6.noarch

Output:

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: [   LOG    ] :: Test
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: [  BEGIN   ] :: Running reproducer :: actually running 'python reproducer.py 2>&1 | tee output.log'
Traceback (most recent call last):
  File "reproducer.py", line 5, in <module>
    r = requests.head('https://downloads.atlassian.com')
  File "/usr/lib/python2.6/site-packages/requests/api.py", line 94, in head
    return request('head', url, **kwargs)
  File "/usr/lib/python2.6/site-packages/requests/api.py", line 50, in request
    response = session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python2.6/site-packages/requests/sessions.py", line 464, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python2.6/site-packages/requests/sessions.py", line 576, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python2.6/site-packages/requests/adapters.py", line 431, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [Errno 1] _ssl.c:492: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
:: [   FAIL   ] :: Running reproducer (Expected 0, got 1)
:: [   FAIL   ] :: File 'output.log' should contain 'Status:  302' 
:: [   FAIL   ] :: File 'output.log' should not contain 'Traceback (most recent call last):' 
:: [   FAIL   ] :: File 'output.log' should not contain 'error:14077410:SSL' 
'76a35ff0-e028-43b3-8afa-587fcce79bed'
Test result: FAIL
   metric: 4
   Log: /var/tmp/beakerlib-49057101/journal.txt
    Info: Searching AVC errors produced since 1481640131.54 (Tue Dec 13 09:42:11 2016)
     Searching logs...
     Info: No AVC messages found.
 Writing to /mnt/testarea/tmp.YzAn9V
:
   AvcLog: /mnt/testarea/tmp.YzAn9V

:::::

NEW pass:
Version:    python-requests-2.6.0-4.el6.noarch

Output:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: [   LOG    ] :: Test
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: [  BEGIN   ] :: Running reproducer :: actually running 'python reproducer.py 2>&1 | tee output.log'
Status:  302
:: [   PASS   ] :: Running reproducer (Expected 0, got 0)
:: [   PASS   ] :: File 'output.log' should contain 'Status:  302' 
:: [   PASS   ] :: File 'output.log' should not contain 'Traceback (most recent call last):' 
:: [   PASS   ] :: File 'output.log' should not contain 'error:14077410:SSL' 
'c9d2a0a3-bf12-4ba0-878e-44cea3221895'
Test result: PASS
   metric: 0
   Log: /var/tmp/beakerlib-49057103/journal.txt
    Info: Searching AVC errors produced since 1481640221.66 (Tue Dec 13 09:43:41 2016)
     Searching logs...
     Info: No AVC messages found.
 Writing to /mnt/testarea/tmp.IYp4y8
:
   AvcLog: /mnt/testarea/tmp.IYp4y8

:::::::::::::::::::

Comment 17 errata-xmlrpc 2017-03-21 11:06:23 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://rhn.redhat.com/errata/RHBA-2017-0713.html


Note You need to log in before you can comment on or make changes to this bug.