Remove native ssl support
As eventlet ssl termination is broken with python 3 and we won't be supporting python 2.7 anymore we will just remove ssl termination to glance-api and expect the termination being handled by something else, like HAProxy. This patch also removes the broken ssl test job as the non-existing feature is not broken anymore. Change-Id: Iaf16dfcfdb3a2c93312dcad1ea1229e6b3c8caaa
This commit is contained in:
parent
ce79c8ed78
commit
06b2465f59
21
.zuul.yaml
21
.zuul.yaml
@ -9,25 +9,6 @@
|
||||
- openstack/devstack-gate
|
||||
- openstack/glance
|
||||
|
||||
- job:
|
||||
name: glance-eventlet-ssl-handshake-broken-py3
|
||||
parent: tox
|
||||
description: |
|
||||
See https://bugs.launchpad.net/glance/+bug/1482633
|
||||
vars:
|
||||
tox_envlist: broken-py3-ssl-tests
|
||||
irrelevant-files:
|
||||
- ^(test-|)requirements.txt$
|
||||
- ^lower-constraints.txt$
|
||||
- ^.*\.rst$
|
||||
- ^api-ref/.*$
|
||||
- ^doc/.*$
|
||||
- ^etc/.*$
|
||||
- ^releasenotes/.*$
|
||||
- ^setup.cfg$
|
||||
- ^tox.ini$
|
||||
- ^\.zuul\.yaml$
|
||||
|
||||
- job:
|
||||
name: glance-code-constants-check
|
||||
parent: tox
|
||||
@ -231,8 +212,6 @@
|
||||
jobs:
|
||||
- openstack-tox-functional
|
||||
- openstack-tox-functional-py37
|
||||
- glance-eventlet-ssl-handshake-broken-py3:
|
||||
voting: false
|
||||
- glance-code-constants-check
|
||||
- devstack-plugin-ceph-tempest:
|
||||
voting: false
|
||||
|
@ -1,2 +0,0 @@
|
||||
^glance\.tests\.functional\.test_reload\.TestReload\.test_reload$
|
||||
^glance\.tests\.functional\.test_ssl\.TestSSL\.test_ssl_ok$
|
@ -31,12 +31,9 @@ from eventlet.green import socket
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
|
||||
from OpenSSL import crypto
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import netutils
|
||||
from oslo_utils import strutils
|
||||
@ -46,7 +43,7 @@ from webob import exc
|
||||
|
||||
from glance.common import exception
|
||||
from glance.common import timeutils
|
||||
from glance.i18n import _, _LE, _LW
|
||||
from glance.i18n import _, _LE
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -440,61 +437,6 @@ def setup_remote_pydev_debug(host, port):
|
||||
LOG.exception(error_msg)
|
||||
|
||||
|
||||
def validate_key_cert(key_file, cert_file):
|
||||
try:
|
||||
error_key_name = "private key"
|
||||
error_filename = key_file
|
||||
with open(key_file, 'r') as keyfile:
|
||||
key_str = keyfile.read()
|
||||
key = crypto.load_privatekey(crypto.FILETYPE_PEM, key_str)
|
||||
|
||||
error_key_name = "certificate"
|
||||
error_filename = cert_file
|
||||
with open(cert_file, 'r') as certfile:
|
||||
cert_str = certfile.read()
|
||||
cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_str)
|
||||
except IOError as ioe:
|
||||
raise RuntimeError(_("There is a problem with your %(error_key_name)s "
|
||||
"%(error_filename)s. Please verify it."
|
||||
" Error: %(ioe)s") %
|
||||
{'error_key_name': error_key_name,
|
||||
'error_filename': error_filename,
|
||||
'ioe': ioe})
|
||||
except crypto.Error as ce:
|
||||
raise RuntimeError(_("There is a problem with your %(error_key_name)s "
|
||||
"%(error_filename)s. Please verify it. OpenSSL"
|
||||
" error: %(ce)s") %
|
||||
{'error_key_name': error_key_name,
|
||||
'error_filename': error_filename,
|
||||
'ce': ce})
|
||||
|
||||
try:
|
||||
data = str(uuid.uuid4())
|
||||
# On Python 3, explicitly encode to UTF-8 to call crypto.sign() which
|
||||
# requires bytes. Otherwise, it raises a deprecation warning (and
|
||||
# will raise an error later).
|
||||
data = encodeutils.to_utf8(data)
|
||||
digest = CONF.digest_algorithm
|
||||
if digest == 'sha1':
|
||||
LOG.warn(
|
||||
_LW('The FIPS (FEDERAL INFORMATION PROCESSING STANDARDS)'
|
||||
' state that the SHA-1 is not suitable for'
|
||||
' general-purpose digital signature applications (as'
|
||||
' specified in FIPS 186-3) that require 112 bits of'
|
||||
' security. The default value is sha1 in Kilo for a'
|
||||
' smooth upgrade process, and it will be updated'
|
||||
' with sha256 in next release(L).'))
|
||||
out = crypto.sign(key, data, digest)
|
||||
crypto.verify(cert, out, data, digest)
|
||||
except crypto.Error as ce:
|
||||
raise RuntimeError(_("There is a problem with your key pair. "
|
||||
"Please verify that cert %(cert_file)s and "
|
||||
"key %(key_file)s belong together. OpenSSL "
|
||||
"error %(ce)s") % {'cert_file': cert_file,
|
||||
'key_file': key_file,
|
||||
'ce': ce})
|
||||
|
||||
|
||||
def get_test_suite_socket():
|
||||
global GLANCE_TEST_SOCKET_FD_STR
|
||||
if GLANCE_TEST_SOCKET_FD_STR in os.environ:
|
||||
|
@ -34,7 +34,6 @@ import threading
|
||||
import time
|
||||
|
||||
from eventlet.green import socket
|
||||
from eventlet.green import ssl
|
||||
import eventlet.greenio
|
||||
import eventlet.wsgi
|
||||
import glance_store
|
||||
@ -143,67 +142,6 @@ Possible values:
|
||||
Related options:
|
||||
* None
|
||||
|
||||
""")),
|
||||
|
||||
cfg.StrOpt('ca_file',
|
||||
sample_default='/etc/ssl/cafile',
|
||||
help=_("""
|
||||
Absolute path to the CA file.
|
||||
|
||||
Provide a string value representing a valid absolute path to
|
||||
the Certificate Authority file to use for client authentication.
|
||||
|
||||
A CA file typically contains necessary trusted certificates to
|
||||
use for the client authentication. This is essential to ensure
|
||||
that a secure connection is established to the server via the
|
||||
internet.
|
||||
|
||||
Possible values:
|
||||
* Valid absolute path to the CA file
|
||||
|
||||
Related options:
|
||||
* None
|
||||
|
||||
""")),
|
||||
|
||||
cfg.StrOpt('cert_file',
|
||||
sample_default='/etc/ssl/certs',
|
||||
help=_("""
|
||||
Absolute path to the certificate file.
|
||||
|
||||
Provide a string value representing a valid absolute path to the
|
||||
certificate file which is required to start the API service
|
||||
securely.
|
||||
|
||||
A certificate file typically is a public key container and includes
|
||||
the server's public key, server name, server information and the
|
||||
signature which was a result of the verification process using the
|
||||
CA certificate. This is required for a secure connection
|
||||
establishment.
|
||||
|
||||
Possible values:
|
||||
* Valid absolute path to the certificate file
|
||||
|
||||
Related options:
|
||||
* None
|
||||
|
||||
""")),
|
||||
|
||||
cfg.StrOpt('key_file',
|
||||
sample_default='/etc/ssl/key/key-file.pem',
|
||||
help=_("""
|
||||
Absolute path to a private key file.
|
||||
|
||||
Provide a string value representing a valid absolute path to a
|
||||
private key file which is required to establish the client-server
|
||||
connection.
|
||||
|
||||
Possible values:
|
||||
* Absolute path to the private key file
|
||||
|
||||
Related options:
|
||||
* None
|
||||
|
||||
""")),
|
||||
]
|
||||
|
||||
@ -402,30 +340,6 @@ def get_bind_addr(default_port=None):
|
||||
return (CONF.bind_host, CONF.bind_port or default_port)
|
||||
|
||||
|
||||
def ssl_wrap_socket(sock):
|
||||
"""
|
||||
Wrap an existing socket in SSL
|
||||
|
||||
:param sock: non-SSL socket to wrap
|
||||
|
||||
:returns: An SSL wrapped socket
|
||||
"""
|
||||
utils.validate_key_cert(CONF.key_file, CONF.cert_file)
|
||||
|
||||
ssl_kwargs = {
|
||||
'server_side': True,
|
||||
'certfile': CONF.cert_file,
|
||||
'keyfile': CONF.key_file,
|
||||
'cert_reqs': ssl.CERT_NONE,
|
||||
}
|
||||
|
||||
if CONF.ca_file:
|
||||
ssl_kwargs['ca_certs'] = CONF.ca_file
|
||||
ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED
|
||||
|
||||
return ssl.wrap_socket(sock, **ssl_kwargs)
|
||||
|
||||
|
||||
def get_socket(default_port):
|
||||
"""
|
||||
Bind socket to bind ip:port in conf
|
||||
@ -434,8 +348,7 @@ def get_socket(default_port):
|
||||
|
||||
:param default_port: port to bind to if none is specified in conf
|
||||
|
||||
:returns: a socket object as returned from socket.listen or
|
||||
ssl.wrap_socket if conf specifies cert_file
|
||||
:returns: a socket object as returned from socket.listen
|
||||
"""
|
||||
bind_addr = get_bind_addr(default_port)
|
||||
|
||||
@ -450,12 +363,6 @@ def get_socket(default_port):
|
||||
if addr[0] in (socket.AF_INET, socket.AF_INET6)
|
||||
][0]
|
||||
|
||||
use_ssl = CONF.key_file or CONF.cert_file
|
||||
if use_ssl and (not CONF.key_file or not CONF.cert_file):
|
||||
raise RuntimeError(_("When running server in SSL mode, you must "
|
||||
"specify both a cert_file and key_file "
|
||||
"option value in your configuration file"))
|
||||
|
||||
sock = utils.get_test_suite_socket()
|
||||
retry_until = time.time() + 30
|
||||
|
||||
@ -701,16 +608,6 @@ class BaseServer(object):
|
||||
new_sock = (old_conf is None or (
|
||||
has_changed('bind_host') or
|
||||
has_changed('bind_port')))
|
||||
# Will we be using https?
|
||||
use_ssl = not (not CONF.cert_file or not CONF.key_file)
|
||||
# Were we using https before?
|
||||
old_use_ssl = (old_conf is not None and not (
|
||||
not old_conf.get('key_file') or
|
||||
not old_conf.get('cert_file')))
|
||||
# Do we now need to perform an SSL wrap on the socket?
|
||||
wrap_sock = use_ssl is True and (old_use_ssl is False or new_sock)
|
||||
# Do we now need to perform an SSL unwrap on the socket?
|
||||
unwrap_sock = use_ssl is False and old_use_ssl is True
|
||||
|
||||
if new_sock:
|
||||
self._sock = None
|
||||
@ -722,25 +619,7 @@ class BaseServer(object):
|
||||
# sockets can hang around forever without keepalive
|
||||
_sock.setsockopt(socket.SOL_SOCKET,
|
||||
socket.SO_KEEPALIVE, 1)
|
||||
self._sock = _sock
|
||||
|
||||
if wrap_sock:
|
||||
self.sock = ssl_wrap_socket(self._sock)
|
||||
|
||||
if unwrap_sock:
|
||||
self.sock = self._sock
|
||||
|
||||
if new_sock and not use_ssl:
|
||||
self.sock = self._sock
|
||||
|
||||
# Pick up newly deployed certs
|
||||
if old_conf is not None and use_ssl is True and old_use_ssl is True:
|
||||
if has_changed('cert_file') or has_changed('key_file'):
|
||||
utils.validate_key_cert(CONF.key_file, CONF.cert_file)
|
||||
if has_changed('cert_file'):
|
||||
self.sock.certfile = CONF.cert_file
|
||||
if has_changed('key_file'):
|
||||
self.sock.keyfile = CONF.key_file
|
||||
self.sock = _sock
|
||||
|
||||
if new_sock or (old_conf is not None and has_changed('tcp_keepidle')):
|
||||
# This option isn't available in the OS X version of eventlet
|
||||
@ -982,19 +861,13 @@ class Win32Server(BaseServer):
|
||||
|
||||
def configure_socket(self, old_conf=None, has_changed=None):
|
||||
fresh_start = not (old_conf or has_changed)
|
||||
use_ssl = CONF.cert_file or CONF.key_file
|
||||
pipe_handle = getattr(CONF, 'pipe_handle', None)
|
||||
|
||||
if not (fresh_start and pipe_handle):
|
||||
return super(Win32Server, self).configure_socket(
|
||||
old_conf, has_changed)
|
||||
|
||||
self._sock = self._get_sock_from_parent()
|
||||
|
||||
if use_ssl:
|
||||
self.sock = ssl_wrap_socket(self._sock)
|
||||
else:
|
||||
self.sock = self._sock
|
||||
self.sock = self._get_sock_from_parent()
|
||||
|
||||
if hasattr(socket, 'TCP_KEEPIDLE'):
|
||||
# This was introduced in WS 2016 RS3
|
||||
|
@ -379,8 +379,6 @@ class ApiServer(Server):
|
||||
self.default_store = kwargs.get("default_store", "file")
|
||||
self.bind_host = "127.0.0.1"
|
||||
self.registry_host = "127.0.0.1"
|
||||
self.key_file = ""
|
||||
self.cert_file = ""
|
||||
self.metadata_encryption_key = "012345678901234567890123456789ab"
|
||||
self.image_dir = os.path.join(self.test_dir, "images")
|
||||
self.pid_file = pid_file or os.path.join(self.test_dir, "api.pid")
|
||||
@ -421,8 +419,6 @@ debug = %(debug)s
|
||||
default_log_levels = eventlet.wsgi.server=DEBUG
|
||||
bind_host = %(bind_host)s
|
||||
bind_port = %(bind_port)s
|
||||
key_file = %(key_file)s
|
||||
cert_file = %(cert_file)s
|
||||
metadata_encryption_key = %(metadata_encryption_key)s
|
||||
registry_host = %(registry_host)s
|
||||
registry_port = %(registry_port)s
|
||||
@ -560,8 +556,6 @@ class ApiServerForMultipleBackend(Server):
|
||||
self.default_backend = kwargs.get("default_backend", "file1")
|
||||
self.bind_host = "127.0.0.1"
|
||||
self.registry_host = "127.0.0.1"
|
||||
self.key_file = ""
|
||||
self.cert_file = ""
|
||||
self.metadata_encryption_key = "012345678901234567890123456789ab"
|
||||
self.image_dir_backend_1 = os.path.join(self.test_dir, "images_1")
|
||||
self.image_dir_backend_2 = os.path.join(self.test_dir, "images_2")
|
||||
@ -605,8 +599,6 @@ debug = %(debug)s
|
||||
default_log_levels = eventlet.wsgi.server=DEBUG
|
||||
bind_host = %(bind_host)s
|
||||
bind_port = %(bind_port)s
|
||||
key_file = %(key_file)s
|
||||
cert_file = %(cert_file)s
|
||||
metadata_encryption_key = %(metadata_encryption_key)s
|
||||
registry_host = %(registry_host)s
|
||||
registry_port = %(registry_port)s
|
||||
|
@ -23,6 +23,7 @@ from six.moves import http_client as http
|
||||
|
||||
from glance.tests import functional
|
||||
from glance.tests.utils import execute
|
||||
from glance.tests.utils import skip_if_disabled
|
||||
|
||||
TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../', 'var'))
|
||||
@ -44,11 +45,16 @@ class TestReload(functional.FunctionalTest):
|
||||
"""Test configuration reload"""
|
||||
|
||||
def setUp(self):
|
||||
self.workers = 1
|
||||
super(TestReload, self).setUp()
|
||||
self.cleanup()
|
||||
self.workers = 1
|
||||
self.include_scrubber = False
|
||||
self.disabled = True
|
||||
self.disabled_message = "Reload is broken, Bug 1855708"
|
||||
|
||||
def tearDown(self):
|
||||
self.stop_servers()
|
||||
if not self.disabled:
|
||||
self.stop_servers()
|
||||
super(TestReload, self).tearDown()
|
||||
|
||||
def ticker(self, message, seconds=60, tick=0.01):
|
||||
@ -101,6 +107,7 @@ class TestReload(functional.FunctionalTest):
|
||||
def _url(self, protocol, path):
|
||||
return '%s://127.0.0.1:%d%s' % (protocol, self.api_port, path)
|
||||
|
||||
@skip_if_disabled
|
||||
def test_reload(self):
|
||||
"""Test SIGHUP picks up new config values"""
|
||||
def check_pids(pre, post=None, workers=2):
|
||||
@ -121,6 +128,13 @@ class TestReload(functional.FunctionalTest):
|
||||
pre_pids = {}
|
||||
post_pids = {}
|
||||
|
||||
path = self._url('http', '/')
|
||||
response = requests.get(path)
|
||||
self.assertEqual(http.MULTIPLE_CHOICES, response.status_code)
|
||||
del response # close socket so that process audit is reliable
|
||||
|
||||
pre_pids['api'] = self._get_children('api')
|
||||
|
||||
# Test changing the workers value creates all new children
|
||||
# This recycles the existing socket
|
||||
msg = 'Start timeout'
|
||||
@ -145,88 +159,6 @@ class TestReload(functional.FunctionalTest):
|
||||
if check_pids(pre_pids['api'], post_pids['api']):
|
||||
break
|
||||
|
||||
# Test changing from http to https
|
||||
# This recycles the existing socket
|
||||
path = self._url('http', '/')
|
||||
response = requests.get(path)
|
||||
self.assertEqual(http.MULTIPLE_CHOICES, response.status_code)
|
||||
del response # close socket so that process audit is reliable
|
||||
|
||||
pre_pids['api'] = self._get_children('api')
|
||||
key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
|
||||
set_config_value(self._conffile('api'), 'key_file', key_file)
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
set_config_value(self._conffile('api'), 'cert_file', cert_file)
|
||||
cmd = "kill -HUP %s" % self._get_parent('api')
|
||||
execute(cmd, raise_error=True)
|
||||
|
||||
msg = 'http to https timeout'
|
||||
for _ in self.ticker(msg):
|
||||
post_pids['api'] = self._get_children('api')
|
||||
if check_pids(pre_pids['api'], post_pids['api']):
|
||||
break
|
||||
|
||||
ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
path = self._url('https', '/')
|
||||
response = requests.get(path, verify=ca_file)
|
||||
self.assertEqual(http.MULTIPLE_CHOICES, response.status_code)
|
||||
del response
|
||||
|
||||
# Test https restart
|
||||
# This recycles the existing socket
|
||||
pre_pids['api'] = self._get_children('api')
|
||||
cmd = "kill -HUP %s" % self._get_parent('api')
|
||||
execute(cmd, raise_error=True)
|
||||
|
||||
msg = 'https restart timeout'
|
||||
for _ in self.ticker(msg):
|
||||
post_pids['api'] = self._get_children('api')
|
||||
if check_pids(pre_pids['api'], post_pids['api']):
|
||||
break
|
||||
|
||||
ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
path = self._url('https', '/')
|
||||
response = requests.get(path, verify=ca_file)
|
||||
self.assertEqual(http.MULTIPLE_CHOICES, response.status_code)
|
||||
del response
|
||||
|
||||
# Test changing the https bind_host
|
||||
# This requires a new socket
|
||||
pre_pids['api'] = self._get_children('api')
|
||||
set_config_value(self._conffile('api'), 'bind_host', '127.0.0.1')
|
||||
cmd = "kill -HUP %s" % self._get_parent('api')
|
||||
execute(cmd, raise_error=True)
|
||||
|
||||
msg = 'https bind_host timeout'
|
||||
for _ in self.ticker(msg):
|
||||
post_pids['api'] = self._get_children('api')
|
||||
if check_pids(pre_pids['api'], post_pids['api']):
|
||||
break
|
||||
|
||||
path = self._url('https', '/')
|
||||
response = requests.get(path, verify=ca_file)
|
||||
self.assertEqual(http.MULTIPLE_CHOICES, response.status_code)
|
||||
del response
|
||||
|
||||
# Test https -> http
|
||||
# This recycles the existing socket
|
||||
pre_pids['api'] = self._get_children('api')
|
||||
set_config_value(self._conffile('api'), 'key_file', '')
|
||||
set_config_value(self._conffile('api'), 'cert_file', '')
|
||||
cmd = "kill -HUP %s" % self._get_parent('api')
|
||||
execute(cmd, raise_error=True)
|
||||
|
||||
msg = 'https to http timeout'
|
||||
for _ in self.ticker(msg):
|
||||
post_pids['api'] = self._get_children('api')
|
||||
if check_pids(pre_pids['api'], post_pids['api']):
|
||||
break
|
||||
|
||||
path = self._url('http', '/')
|
||||
response = requests.get(path)
|
||||
self.assertEqual(http.MULTIPLE_CHOICES, response.status_code)
|
||||
del response
|
||||
|
||||
# Test changing the http bind_host
|
||||
# This requires a new socket
|
||||
pre_pids['api'] = self._get_children('api')
|
||||
|
@ -1,83 +0,0 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import httplib2
|
||||
from six.moves import http_client as http
|
||||
|
||||
from glance.tests import functional
|
||||
|
||||
TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'..', 'var'))
|
||||
|
||||
|
||||
class TestSSL(functional.FunctionalTest):
|
||||
|
||||
"""Functional tests verifying SSL communication"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestSSL, self).setUp()
|
||||
|
||||
if getattr(self, 'inited', False):
|
||||
return
|
||||
|
||||
self.inited = False
|
||||
self.disabled = True
|
||||
|
||||
# NOTE (stevelle): Test key/cert/CA file created as per:
|
||||
# http://nrocco.github.io/2013/01/25/
|
||||
# self-signed-ssl-certificate-chains.html
|
||||
# For these tests certificate.crt must be created with 'Common Name'
|
||||
# set to 127.0.0.1
|
||||
|
||||
self.key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
|
||||
if not os.path.exists(self.key_file):
|
||||
self.disabled_message = ("Could not find private key file %s" %
|
||||
self.key_file)
|
||||
self.inited = True
|
||||
return
|
||||
|
||||
self.cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
if not os.path.exists(self.cert_file):
|
||||
self.disabled_message = ("Could not find certificate file %s" %
|
||||
self.cert_file)
|
||||
self.inited = True
|
||||
return
|
||||
|
||||
self.ca_file = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
if not os.path.exists(self.ca_file):
|
||||
self.disabled_message = ("Could not find CA file %s" %
|
||||
self.ca_file)
|
||||
self.inited = True
|
||||
return
|
||||
|
||||
self.inited = True
|
||||
self.disabled = False
|
||||
|
||||
def tearDown(self):
|
||||
super(TestSSL, self).tearDown()
|
||||
if getattr(self, 'inited', False):
|
||||
return
|
||||
|
||||
def test_ssl_ok(self):
|
||||
"""Make sure the public API works with HTTPS."""
|
||||
self.cleanup()
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
path = "https://%s:%d/versions" % ("127.0.0.1", self.api_port)
|
||||
https = httplib2.Http(ca_certs=self.ca_file)
|
||||
response, content = https.request(path, 'GET')
|
||||
self.assertEqual(http.OK, response.status)
|
@ -15,7 +15,6 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from oslo_log import log as logging
|
||||
@ -326,46 +325,6 @@ class TestUtils(test_utils.BaseTestCase):
|
||||
result = utils.mutating(fake_function)
|
||||
self.assertEqual("test passed", result(req, Fake()))
|
||||
|
||||
def test_validate_key_cert_key(self):
|
||||
self.config(digest_algorithm='sha256')
|
||||
var_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../../', 'var'))
|
||||
keyfile = os.path.join(var_dir, 'privatekey.key')
|
||||
certfile = os.path.join(var_dir, 'certificate.crt')
|
||||
utils.validate_key_cert(keyfile, certfile)
|
||||
|
||||
def test_validate_key_cert_no_private_key(self):
|
||||
with tempfile.NamedTemporaryFile('w+') as tmpf:
|
||||
self.assertRaises(RuntimeError,
|
||||
utils.validate_key_cert,
|
||||
"/not/a/file", tmpf.name)
|
||||
|
||||
def test_validate_key_cert_cert_cant_read(self):
|
||||
with tempfile.NamedTemporaryFile('w+') as keyf:
|
||||
with tempfile.NamedTemporaryFile('w+') as certf:
|
||||
os.chmod(certf.name, 0)
|
||||
self.assertRaises(RuntimeError,
|
||||
utils.validate_key_cert,
|
||||
keyf.name, certf.name)
|
||||
|
||||
def test_validate_key_cert_key_cant_read(self):
|
||||
with tempfile.NamedTemporaryFile('w+') as keyf:
|
||||
with tempfile.NamedTemporaryFile('w+') as certf:
|
||||
os.chmod(keyf.name, 0)
|
||||
self.assertRaises(RuntimeError,
|
||||
utils.validate_key_cert,
|
||||
keyf.name, certf.name)
|
||||
|
||||
def test_invalid_digest_algorithm(self):
|
||||
self.config(digest_algorithm='fake_algorithm')
|
||||
var_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../../', 'var'))
|
||||
keyfile = os.path.join(var_dir, 'privatekey.key')
|
||||
certfile = os.path.join(var_dir, 'certificate.crt')
|
||||
self.assertRaises(ValueError,
|
||||
utils.validate_key_cert,
|
||||
keyfile, certfile)
|
||||
|
||||
def test_valid_hostname(self):
|
||||
valid_inputs = ['localhost',
|
||||
'glance04-a'
|
||||
|
@ -705,17 +705,11 @@ class GetSocketTestCase(test_utils.BaseTestCase):
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
"glance.common.wsgi.utils.validate_key_cert",
|
||||
lambda *x: None))
|
||||
wsgi.CONF.cert_file = '/etc/ssl/cert'
|
||||
wsgi.CONF.key_file = '/etc/ssl/key'
|
||||
wsgi.CONF.ca_file = '/etc/ssl/ca_cert'
|
||||
wsgi.CONF.tcp_keepidle = 600
|
||||
|
||||
@mock.patch.object(prefetcher, 'Prefetcher')
|
||||
def test_correct_configure_socket(self, mock_prefetcher):
|
||||
mock_socket = mock.Mock()
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'glance.common.wsgi.ssl.wrap_socket',
|
||||
mock_socket))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'glance.common.wsgi.eventlet.listen',
|
||||
lambda *x, **y: mock_socket))
|
||||
@ -731,23 +725,16 @@ class GetSocketTestCase(test_utils.BaseTestCase):
|
||||
socket.SO_KEEPALIVE,
|
||||
1), mock_socket.mock_calls)
|
||||
if hasattr(socket, 'TCP_KEEPIDLE'):
|
||||
self.assertIn(mock.call().setsockopt(
|
||||
self.assertIn(mock.call.setsockopt(
|
||||
socket.IPPROTO_TCP,
|
||||
socket.TCP_KEEPIDLE,
|
||||
wsgi.CONF.tcp_keepidle), mock_socket.mock_calls)
|
||||
|
||||
def test_get_socket_without_all_ssl_reqs(self):
|
||||
wsgi.CONF.key_file = None
|
||||
self.assertRaises(RuntimeError, wsgi.get_socket, 1234)
|
||||
|
||||
def test_get_socket_with_bind_problems(self):
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'glance.common.wsgi.eventlet.listen',
|
||||
mock.Mock(side_effect=(
|
||||
[wsgi.socket.error(socket.errno.EADDRINUSE)] * 3 + [None]))))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'glance.common.wsgi.ssl.wrap_socket',
|
||||
lambda *x, **y: None))
|
||||
|
||||
self.assertRaises(RuntimeError, wsgi.get_socket, 1234)
|
||||
|
||||
@ -755,9 +742,6 @@ class GetSocketTestCase(test_utils.BaseTestCase):
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'glance.common.wsgi.eventlet.listen',
|
||||
mock.Mock(side_effect=wsgi.socket.error(socket.errno.ENOMEM))))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'glance.common.wsgi.ssl.wrap_socket',
|
||||
lambda *x, **y: None))
|
||||
self.assertRaises(wsgi.socket.error, wsgi.get_socket, 1234)
|
||||
|
||||
|
||||
|
15
releasenotes/notes/remove_native_ssl-c16d5a127b57583d.yaml
Normal file
15
releasenotes/notes/remove_native_ssl-c16d5a127b57583d.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
If upgrade is conducted from PY27 where ssl connections has been terminated
|
||||
into glance-api, the termination needs to happen externally from now on.
|
||||
security:
|
||||
- |
|
||||
The ssl support from Glance has been removed as it worked only under PY27
|
||||
which is not anymore supported environment. Termination of encrypted
|
||||
connections needs to happen externally as soon as move to PY3 happens. Any
|
||||
deployment needing end to end encryption would need to put either reverse
|
||||
proxy (using fully blown http server like Apache or Nginx will cause
|
||||
significant performance hit and we advice using something more simple that
|
||||
does not break the http protocol) in front of the service or utilize
|
||||
ssl tunneling (like stunnel) between loadbalancers and glance-api.
|
15
tox.ini
15
tox.ini
@ -57,7 +57,7 @@ ignore_errors = True
|
||||
whitelist_externals =
|
||||
bash
|
||||
commands =
|
||||
stestr run --blacklist-file ./broken-functional-py3-ssl-tests.txt {posargs}
|
||||
stestr run {posargs}
|
||||
|
||||
[testenv:functional-py37]
|
||||
basepython = python3.7
|
||||
@ -67,18 +67,7 @@ ignore_errors = True
|
||||
whitelist_externals =
|
||||
bash
|
||||
commands =
|
||||
stestr run --blacklist-file ./broken-functional-py3-ssl-tests.txt {posargs}
|
||||
|
||||
[testenv:broken-py3-ssl-tests]
|
||||
# NOTE(rosmaita): these tests were being skipped due to bug #1482633, but we
|
||||
# want it to be obvious that Glance is affected by the eventlet ssl-handshake
|
||||
# problem under py3. (When this testenv is removed, don't forget to adjust
|
||||
# the blacklist generation in the functional-py3 testenv.)
|
||||
basepython = python3
|
||||
setenv =
|
||||
TEST_PATH = ./glance/tests/functional
|
||||
commands =
|
||||
stestr run --whitelist-file ./broken-functional-py3-ssl-tests.txt {posargs}
|
||||
stestr run {posargs}
|
||||
|
||||
[testenv:gateonly]
|
||||
# NOTE(rosmaita): these tests catch configuration problems for some code
|
||||
|
Loading…
Reference in New Issue
Block a user