Fix unsafe SSL connection on TrustedFilter
TrustedFilter was using httplib which doesn't check for CAs.
Here the change is using Requests and verifies local CAs by default (or another
one if provided)
This effort is related to CVE 2013-2255.
SecurityImpact
ReleaseNote
This patch adds an option attestation_insecure_ssl in TrustedFilter which can be
used to verify CAs. The default value is set to True, disabling SSL certificate
verification. While this is the insecure option, it was selected for backward
compatibility reasons.
Closes-Bug: #1373993
(cherry picked from commit 30871e8702
)
Conflicts:
nova/tests/scheduler/test_host_filters.py
Change-Id: I0b8e6319a4cc39876b1e396ef705f0fc5def1e44
This commit is contained in:
parent
3efc858d67
commit
d7c8e936f3
|
@ -42,11 +42,8 @@ the Open Attestation project at:
|
|||
https://github.com/OpenAttestation/OpenAttestation
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
from oslo.config import cfg
|
||||
import requests
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
|
@ -74,6 +71,9 @@ trusted_opts = [
|
|||
cfg.IntOpt('attestation_auth_timeout',
|
||||
default=60,
|
||||
help='Attestation status cache valid period length'),
|
||||
cfg.BoolOpt('attestation_insecure_ssl',
|
||||
default=True,
|
||||
help='Disable SSL cert verification for Attestation service')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -82,37 +82,6 @@ CONF.register_group(trust_group)
|
|||
CONF.register_opts(trusted_opts, group=trust_group)
|
||||
|
||||
|
||||
class HTTPSClientAuthConnection(httplib.HTTPSConnection):
|
||||
"""Class to make a HTTPS connection, with support for full client-based
|
||||
SSL Authentication
|
||||
"""
|
||||
|
||||
def __init__(self, host, port, key_file, cert_file, ca_file, timeout=None):
|
||||
httplib.HTTPSConnection.__init__(self, host,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file)
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.ca_file = ca_file
|
||||
self.timeout = timeout
|
||||
|
||||
def connect(self):
|
||||
"""Connect to a host on a given (SSL) port.
|
||||
If ca_file is pointing somewhere, use it to check Server Certificate.
|
||||
|
||||
Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
|
||||
This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to
|
||||
ssl.wrap_socket(), which forces SSL to check server certificate
|
||||
against our client certificate.
|
||||
"""
|
||||
sock = socket.create_connection((self.host, self.port), self.timeout)
|
||||
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
|
||||
ca_certs=self.ca_file,
|
||||
cert_reqs=ssl.CERT_REQUIRED)
|
||||
|
||||
|
||||
class AttestationService(object):
|
||||
# Provide access wrapper to attestation server to get integrity report.
|
||||
|
||||
|
@ -125,29 +94,36 @@ class AttestationService(object):
|
|||
self.cert_file = None
|
||||
self.ca_file = CONF.trusted_computing.attestation_server_ca_file
|
||||
self.request_count = 100
|
||||
# If the CA file is not provided, let's check the cert if verification
|
||||
# asked
|
||||
self.verify = (not CONF.trusted_computing.attestation_insecure_ssl
|
||||
and self.ca_file or True)
|
||||
self.cert = (self.cert_file, self.key_file)
|
||||
|
||||
def _do_request(self, method, action_url, body, headers):
|
||||
# Connects to the server and issues a request.
|
||||
# :returns: result data
|
||||
# :raises: IOError if the request fails
|
||||
|
||||
action_url = "%s/%s" % (self.api_url, action_url)
|
||||
action_url = "https://%s:%d%s/%s" % (self.host, self.port,
|
||||
self.api_url, action_url)
|
||||
try:
|
||||
c = HTTPSClientAuthConnection(self.host, self.port,
|
||||
key_file=self.key_file,
|
||||
cert_file=self.cert_file,
|
||||
ca_file=self.ca_file)
|
||||
c.request(method, action_url, body, headers)
|
||||
res = c.getresponse()
|
||||
status_code = res.status
|
||||
if status_code in (httplib.OK,
|
||||
httplib.CREATED,
|
||||
httplib.ACCEPTED,
|
||||
httplib.NO_CONTENT):
|
||||
return httplib.OK, res
|
||||
res = requests.request(method, action_url, data=body,
|
||||
headers=headers, cert=self.cert,
|
||||
verify=self.verify)
|
||||
status_code = res.status_code
|
||||
# pylint: disable=E1101
|
||||
if status_code in (requests.codes.OK,
|
||||
requests.codes.CREATED,
|
||||
requests.codes.ACCEPTED,
|
||||
requests.codes.NO_CONTENT):
|
||||
try:
|
||||
return requests.codes.OK, jsonutils.loads(res.text)
|
||||
except ValueError:
|
||||
return requests.codes.OK, res.text
|
||||
return status_code, None
|
||||
|
||||
except (socket.error, IOError):
|
||||
except requests.exceptions.RequestException:
|
||||
return IOError, None
|
||||
|
||||
def _request(self, cmd, subcmd, hosts):
|
||||
|
@ -161,11 +137,7 @@ class AttestationService(object):
|
|||
if self.auth_blob:
|
||||
headers['x-auth-blob'] = self.auth_blob
|
||||
status, res = self._do_request(cmd, subcmd, cooked, headers)
|
||||
if status == httplib.OK:
|
||||
data = res.read()
|
||||
return status, jsonutils.loads(data)
|
||||
else:
|
||||
return status, None
|
||||
return status, res
|
||||
|
||||
def do_attestation(self, hosts):
|
||||
"""Attests compute nodes through OAT service.
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
Tests For Scheduler Host Filters.
|
||||
"""
|
||||
|
||||
import httplib
|
||||
|
||||
from oslo.config import cfg
|
||||
import requests
|
||||
import stubout
|
||||
|
||||
from nova import context
|
||||
|
@ -242,7 +241,7 @@ class HostFiltersTestCase(test.NoDBTestCase):
|
|||
def fake_oat_request(self, *args, **kwargs):
|
||||
"""Stubs out the response from OAT service."""
|
||||
self.oat_attested = True
|
||||
return httplib.OK, self.oat_data
|
||||
return requests.codes.OK, self.oat_data
|
||||
|
||||
def setUp(self):
|
||||
super(HostFiltersTestCase, self).setUp()
|
||||
|
|
Loading…
Reference in New Issue