replace httplib.HTTPSConnection in EC2KeystoneAuth

httplib.HTTPSConnection is known to not verify SSL certificates
in Python 2.x. This change replaces use of httplib.HTTPSConnection
with the requests module. It imports config settings related to SSL
verification: ssl.key_file, ssl.cert_file, and ssl.ca_file. It also
adds one config setting: keystone_ec2_insecure. By default, SSL
verification is on, but can be disabled by setting:

keystone_ec2_insecure=true

This patch is based on the keystone middleware ec2 token patch:

https://review.openstack.org/#/c/76476

SecurityImpact
DocImpact
Closes-Bug: #1373992

Change-Id: I8e46d41164e9478b820cad569ba82f25de244620
This commit is contained in:
melanie witt 2014-09-26 05:15:16 +00:00
parent e5a231844b
commit cff14b3763
2 changed files with 38 additions and 45 deletions

View File

@ -18,13 +18,12 @@ Starting point for routing EC2 requests.
"""
from eventlet.green import httplib
from oslo.config import cfg
from oslo.serialization import jsonutils
from oslo.utils import importutils
from oslo.utils import timeutils
import requests
import six
import six.moves.urllib.parse as urlparse
import webob
import webob.dec
import webob.exc
@ -71,11 +70,14 @@ ec2_opts = [
cfg.IntOpt('ec2_timestamp_expiry',
default=300,
help='Time in seconds before ec2 timestamp expires'),
cfg.BoolOpt('keystone_ec2_insecure', default=False, help='Disable SSL '
'certificate verification.'),
]
CONF = cfg.CONF
CONF.register_opts(ec2_opts)
CONF.import_opt('use_forwarded_for', 'nova.api.auth')
CONF.import_group('ssl', 'nova.openstack.common.sslutils')
# Fault Wrapper around all EC2 requests
@ -216,23 +218,28 @@ class EC2KeystoneAuth(wsgi.Middleware):
creds_json = jsonutils.dumps(creds)
headers = {'Content-Type': 'application/json'}
o = urlparse.urlparse(CONF.keystone_ec2_url)
if o.scheme == "http":
conn = httplib.HTTPConnection(o.netloc)
else:
conn = httplib.HTTPSConnection(o.netloc)
conn.request('POST', o.path, body=creds_json, headers=headers)
response = conn.getresponse()
data = response.read()
if response.status != 200:
if response.status == 401:
verify = not CONF.keystone_ec2_insecure
if verify and CONF.ssl.ca_file:
verify = CONF.ssl.ca_file
cert = None
if CONF.ssl.cert_file and CONF.ssl.key_file:
cert = (CONF.ssl.cert_file, CONF.ssl.key_file)
elif CONF.ssl.cert_file:
cert = CONF.ssl.cert_file
response = requests.request('POST', CONF.keystone_ec2_url,
data=creds_json, headers=headers,
verify=verify, cert=cert)
status_code = response.status_code
if status_code != 200:
if status_code == 401:
msg = response.reason
else:
msg = _("Failure communicating with keystone")
return faults.ec2_error_response(request_id, "AuthFailure", msg,
status=response.status)
result = jsonutils.loads(data)
conn.close()
status=status_code)
result = response.json()
try:
token_id = result['access']['token']['id']

View File

@ -14,11 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from eventlet.green import httplib
from lxml import etree
from mox3 import mox
import mock
from oslo.config import cfg
from oslo.utils import timeutils
import requests
import webob
import webob.dec
import webob.exc
@ -153,11 +153,11 @@ class ExecutorTestCase(test.NoDBTestCase):
class FakeResponse(object):
reason = "Test Reason"
def __init__(self, status=400):
self.status = status
def __init__(self, status_code=400):
self.status_code = status_code
def read(self):
return '{}'
def json(self):
return {}
class KeystoneAuthTestCase(test.NoDBTestCase):
@ -188,38 +188,24 @@ class KeystoneAuthTestCase(test.NoDBTestCase):
resp = self.kauth(req)
self._validate_ec2_error(resp, 400, 'AuthFailure')
def test_communication_failure(self):
@mock.patch.object(requests, 'request', return_value=FakeResponse())
def test_communication_failure(self, mock_request):
req = wsgi.Request.blank('/test')
req.GET['Signature'] = 'test-signature'
req.GET['AWSAccessKeyId'] = 'test-key-id'
conn = httplib.HTTPConnection('/mock')
self.mox.StubOutWithMock(httplib.HTTPConnection, 'request')
self.mox.StubOutWithMock(httplib.HTTPConnection, 'getresponse')
conn.request('POST', mox.IgnoreArg(), body=mox.IgnoreArg(),
headers=mox.IgnoreArg())
resp = FakeResponse()
conn.getresponse().AndReturn(resp)
self.mox.ReplayAll()
resp = self.kauth(req)
self._validate_ec2_error(resp, 400, 'AuthFailure')
mock_request.assert_called_with('POST', CONF.keystone_ec2_url,
data=mock.ANY, headers=mock.ANY,
verify=mock.ANY, cert=mock.ANY)
def test_no_result_data(self):
@mock.patch.object(requests, 'request', return_value=FakeResponse(200))
def test_no_result_data(self, mock_request):
req = wsgi.Request.blank('/test')
req.GET['Signature'] = 'test-signature'
req.GET['AWSAccessKeyId'] = 'test-key-id'
conn = httplib.HTTPConnection('/mock')
self.mox.StubOutWithMock(httplib.HTTPConnection, 'request')
self.mox.StubOutWithMock(httplib.HTTPConnection, 'getresponse')
self.mox.StubOutWithMock(httplib.HTTPConnection, 'close')
conn.request('POST', mox.IgnoreArg(), body=mox.IgnoreArg(),
headers=mox.IgnoreArg())
resp = FakeResponse(200)
conn.getresponse().AndReturn(resp)
conn.close()
self.mox.ReplayAll()
resp = self.kauth(req)
self._validate_ec2_error(resp, 400, 'AuthFailure')
mock_request.assert_called_with('POST', CONF.keystone_ec2_url,
data=mock.ANY, headers=mock.ANY,
verify=mock.ANY, cert=mock.ANY)