Support connecting OVN DB over SSL

This patch supports using SSL to connect to OVN_Northbound DB and
OVN_Southbound DB. New options are introduced in ovn option group.

[ovn]
...
ovn_nb_connection = ssl:IP_ADDRESS:6641
ovn_nb_private_key = /PATH/ovn-nb-privkey.pem
ovn_nb_certificate = /PATH/ovn-nb-cert.pem
ovn_nb_ca_cert = /PATH/ovn-nb-cacert.pem

ovn_sb_connection = ssl:IP_ADDRESS:6642
ovn_sb_private_key = /PATH/ovn-sb-privkey.pem
ovn_sb_certificate = /PATH/ovn-sb-cert.pem
ovn_sb_ca_cert = /PATH/ovn-sb-cacert.pem

Change-Id: If1df512075aa68d41c0dbef410ee147396b68c13
Closes-Bug: #1659182
Signed-off-by: Guoshuai Li <ligs@dtdream.com>
Signed-off-by: Dong Jun <dongj@dtdream.com>
Co-authored-by: Dong Jun <dongj@dtdream.com>
changes/69/424969/8
Dong Jun 6 years ago committed by Russell Bryant
parent 5a2b888b6f
commit 21a2f5782e

@ -19,10 +19,44 @@ from neutron_lib.api.definitions import portbindings
ovn_opts = [
cfg.StrOpt('ovn_nb_connection',
default='tcp:127.0.0.1:6641',
help=_('The connection string for the OVN_Northbound OVSDB')),
help=_('The connection string for the OVN_Northbound OVSDB.\n'
'Use tcp:IP:PORT for TCP connection.\n'
'Use ssl:IP:PORT for SSL connection. The '
'ovn_nb_private_key, ovn_nb_certificate and '
'ovn_nb_ca_cert are mandatory.\n'
'Use unix:FILE for unix domain socket connection.')),
cfg.StrOpt('ovn_nb_private_key',
default='',
help=_('The PEM file with private key for SSL connection to '
'OVN-NB-DB')),
cfg.StrOpt('ovn_nb_certificate',
default='',
help=_('The PEM file with certificate that certifies the '
'private key specified in ovn_nb_private_key')),
cfg.StrOpt('ovn_nb_ca_cert',
default='',
help=_('The PEM file with CA certificate that OVN should use to'
' verify certificates presented to it by SSL peers')),
cfg.StrOpt('ovn_sb_connection',
default='tcp:127.0.0.1:6642',
help=_('The connection string for the OVN_Southbound OVSDB')),
help=_('The connection string for the OVN_Southbound OVSDB.\n'
'Use tcp:IP:PORT for TCP connection.\n'
'Use ssl:IP:PORT for SSL connection. The '
'ovn_sb_private_key, ovn_sb_certificate and '
'ovn_sb_ca_cert are mandatory.\n'
'Use unix:FILE for unix domain socket connection.')),
cfg.StrOpt('ovn_sb_private_key',
default='',
help=_('The PEM file with private key for SSL connection to '
'OVN-SB-DB')),
cfg.StrOpt('ovn_sb_certificate',
default='',
help=_('The PEM file with certificate that certifies the '
'private key specified in ovn_sb_private_key')),
cfg.StrOpt('ovn_sb_ca_cert',
default='',
help=_('The PEM file with CA certificate that OVN should use to'
' verify certificates presented to it by SSL peers')),
cfg.IntOpt('ovsdb_connection_timeout',
default=180,
help=_('Timeout in seconds for the OVSDB '
@ -96,10 +130,34 @@ def get_ovn_nb_connection():
return cfg.CONF.ovn.ovn_nb_connection
def get_ovn_nb_private_key():
return cfg.CONF.ovn.ovn_nb_private_key
def get_ovn_nb_certificate():
return cfg.CONF.ovn.ovn_nb_certificate
def get_ovn_nb_ca_cert():
return cfg.CONF.ovn.ovn_nb_ca_cert
def get_ovn_sb_connection():
return cfg.CONF.ovn.ovn_sb_connection
def get_ovn_sb_private_key():
return cfg.CONF.ovn.ovn_sb_private_key
def get_ovn_sb_certificate():
return cfg.CONF.ovn.ovn_sb_certificate
def get_ovn_sb_ca_cert():
return cfg.CONF.ovn.ovn_sb_ca_cert
def get_ovn_ovsdb_timeout():
return cfg.CONF.ovn.ovsdb_connection_timeout

@ -21,6 +21,7 @@ import threading
from oslo_log import log
from ovs.db import idl
from ovs import poller
from ovs.stream import Stream
from networking_ovn._i18n import _LE
from networking_ovn.common import config as ovn_config
@ -297,6 +298,17 @@ class OvnSbIdl(OvnIdl):
self.notify_handler.watch_events([self._chassis_event])
def _set_ssl_files(schema_name):
if schema_name == 'OVN_Southbound':
Stream.ssl_set_private_key_file(ovn_config.get_ovn_sb_private_key())
Stream.ssl_set_certificate_file(ovn_config.get_ovn_sb_certificate())
Stream.ssl_set_ca_cert_file(ovn_config.get_ovn_sb_ca_cert())
else:
Stream.ssl_set_private_key_file(ovn_config.get_ovn_nb_private_key())
Stream.ssl_set_certificate_file(ovn_config.get_ovn_nb_certificate())
Stream.ssl_set_ca_cert_file(ovn_config.get_ovn_nb_ca_cert())
class OvnBaseConnection(connection.Connection):
def get_schema_helper(self):
@ -304,6 +316,7 @@ class OvnBaseConnection(connection.Connection):
# The implementation of this function is same as the base class method
# without the enable_connection_uri() called (since ovs-vsctl won't
# exist on the controller node when using the reference architecture).
_set_ssl_files(self.schema_name)
try:
helper = idlutils._get_schema_helper(self.connection,
self.schema_name)

@ -89,6 +89,17 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase):
self._start_ovsdb_server_and_idls()
def tearDown(self):
# Set Mock() to idl to avoid SSL file access errors.
# This is because, destroying temporary directory containing SSL files
# is earlier than terminating thread in Connection() object that
# the errors are likely to occur in the short period of time.
# However, Connection() does not provider a stop method for run(), we
# replace idl with Mock() to avoid accessing.
if self._ovsdb_protocol == 'ssl':
impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection.idl = mock.Mock()
impl_idl_ovn.OvsdbSbOvnIdl.ovsdb_connection.idl = mock.Mock()
self.monitor_nb_idl_con.idl = mock.Mock()
self.monitor_sb_idl_con.idl = mock.Mock()
# Need to set OvsdbNbOvnIdl.ovsdb_connection and
# OvsdbSbOvnIdl.ovsdb_connection to None.
# This is because, when the test worker runs the next functional test
@ -98,6 +109,10 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase):
impl_idl_ovn.OvsdbSbOvnIdl.ovsdb_connection = None
super(TestOVNFunctionalBase, self).tearDown()
@property
def _ovsdb_protocol(self):
return self.get_ovsdb_server_protocol()
def get_ovsdb_server_protocol(self):
return 'unix'
@ -109,15 +124,20 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase):
self.ovsdb_server_mgr = self.useFixture(
process.OvsdbServer(self.temp_dir, self.OVS_INSTALL_SHARE_PATH,
ovn_nb_db=True, ovn_sb_db=True,
protocol=self.get_ovsdb_server_protocol()))
cfg.CONF.set_override(
'ovn_nb_connection',
self.ovsdb_server_mgr.get_ovsdb_connection_path(),
'ovn')
cfg.CONF.set_override(
'ovn_sb_connection',
self.ovsdb_server_mgr.get_ovsdb_connection_path(db_type='sb'),
'ovn')
protocol=self._ovsdb_protocol))
set_cfg = cfg.CONF.set_override
set_cfg('ovn_nb_connection',
self.ovsdb_server_mgr.get_ovsdb_connection_path(), 'ovn')
set_cfg('ovn_sb_connection',
self.ovsdb_server_mgr.get_ovsdb_connection_path(
db_type='sb'), 'ovn')
set_cfg('ovn_nb_private_key', self.ovsdb_server_mgr.private_key, 'ovn')
set_cfg('ovn_nb_certificate', self.ovsdb_server_mgr.certificate, 'ovn')
set_cfg('ovn_nb_ca_cert', self.ovsdb_server_mgr.ca_cert, 'ovn')
set_cfg('ovn_sb_private_key', self.ovsdb_server_mgr.private_key, 'ovn')
set_cfg('ovn_sb_certificate', self.ovsdb_server_mgr.certificate, 'ovn')
set_cfg('ovn_sb_ca_cert', self.ovsdb_server_mgr.ca_cert, 'ovn')
num_attempts = 0
# 5 seconds should be more than enough for the transaction to complete
# for the test cases.
@ -188,6 +208,11 @@ class TestOVNFunctionalBase(test_plugin.Ml2PluginV2TestCase):
if self.ovsdb_server_mgr:
self.ovsdb_server_mgr.stop()
if self._ovsdb_protocol == 'ssl':
impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection.idl = mock.Mock()
impl_idl_ovn.OvsdbSbOvnIdl.ovsdb_connection.idl = mock.Mock()
self.monitor_nb_idl_con.idl = mock.Mock()
self.monitor_sb_idl_con.idl = mock.Mock()
impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection = None
impl_idl_ovn.OvsdbSbOvnIdl.ovsdb_connection = None
self.mech_driver._nb_ovn = None

@ -16,6 +16,7 @@
from distutils import spawn
import fixtures
import os
import psutil
import tenacity
@ -34,6 +35,10 @@ class OvsdbServer(fixtures.Fixture):
# The value of the protocol must be unix or tcp or ssl
self.protocol = protocol
self.ovsdb_server_processes = []
self.private_key = os.path.join(self.temp_dir, 'ovn-privkey.pem')
self.certificate = os.path.join(self.temp_dir, 'ovn-cert.pem')
self.ca_cert = os.path.join(self.temp_dir, 'controllerca',
'cacert.pem')
def _setUp(self):
if self.ovn_nb_db:
@ -62,7 +67,19 @@ class OvsdbServer(fixtures.Fixture):
self.addCleanup(self.stop)
self.start()
def _init_ovsdb_pki(self):
os.chdir(self.temp_dir)
pki_init_cmd = [spawn.find_executable('ovs-pki'), 'init',
'-d', self.temp_dir, '-l',
os.path.join(self.temp_dir, 'pki.log'), '--force']
utils.execute(pki_init_cmd)
pki_req_sign = [spawn.find_executable('ovs-pki'), 'req+sign', 'ovn',
'controller', '-d', self.temp_dir, '-l',
os.path.join(self.temp_dir, 'pki.log'), '--force']
utils.execute(pki_req_sign)
def start(self):
pki_done = False
for ovsdb_process in self.ovsdb_server_processes:
# create the db from the schema using ovsdb-tool
ovsdb_tool_cmd = [spawn.find_executable('ovsdb-tool'),
@ -81,6 +98,13 @@ class OvsdbServer(fixtures.Fixture):
'--remote=p%s:0:%s' % (ovsdb_process['protocol'],
ovsdb_process['remote_ip'])
)
if ovsdb_process['protocol'] == 'ssl':
if not pki_done:
pki_done = True
self._init_ovsdb_pki()
ovsdb_server_cmd.append('--private-key=%s' % self.private_key)
ovsdb_server_cmd.append('--certificate=%s' % self.certificate)
ovsdb_server_cmd.append('--ca-cert=%s' % self.ca_cert)
ovsdb_server_cmd.append(ovsdb_process['db_path'])
obj, _ = utils.create_process(ovsdb_server_cmd)

@ -127,3 +127,8 @@ class TestPortBinding(base.TestOVNFunctionalBase):
class TestPortBindingOverTcp(TestPortBinding):
def get_ovsdb_server_protocol(self):
return 'tcp'
class TestPortBindingOverSsl(TestPortBinding):
def get_ovsdb_server_protocol(self):
return 'ssl'

@ -605,3 +605,8 @@ class TestNBDbResources(base.TestOVNFunctionalBase):
class TestNBDbResourcesOverTcp(TestNBDbResources):
def get_ovsdb_server_protocol(self):
return 'tcp'
class TestNBDbResourcesOverSsl(TestNBDbResources):
def get_ovsdb_server_protocol(self):
return 'ssl'

@ -1214,3 +1214,13 @@ class TestOvnNbSyncOverTcp(TestOvnNbSync):
class TestOvnSbSyncOverTcp(TestOvnSbSync):
def get_ovsdb_server_protocol(self):
return 'tcp'
class TestOvnNbSyncOverSsl(TestOvnNbSync):
def get_ovsdb_server_protocol(self):
return 'ssl'
class TestOvnSbSyncOverSsl(TestOvnSbSync):
def get_ovsdb_server_protocol(self):
return 'ssl'

@ -140,3 +140,8 @@ class TestNBDbMonitor(base.TestOVNFunctionalBase):
class TestNBDbMonitorOverTcp(TestNBDbMonitor):
def get_ovsdb_server_protocol(self):
return 'tcp'
class TestNBDbMonitorOverSsl(TestNBDbMonitor):
def get_ovsdb_server_protocol(self):
return 'ssl'

@ -0,0 +1,7 @@
---
prelude: >
networking-ovn now supports the use of SSL for its
OVSDB connections to the OVN databases.
features:
- networking-ovn now supports the use of SSL for its
OVSDB connections to the OVN databases.

@ -7,6 +7,7 @@ neutron-lib>=1.3.0 # Apache-2.0
oslo.config>=3.22.0 # Apache-2.0
ovs>=2.7.0 # Apache-2.0
pbr>=2.0.0 # Apache-2.0
pyOpenSSL>=0.14 # Apache-2.0
tenacity>=3.2.1 # Apache-2.0
Babel>=2.3.4 # BSD
six>=1.9.0 # MIT

Loading…
Cancel
Save