From 839a895d4f91860caeb1fb42643fb78477e01809 Mon Sep 17 00:00:00 2001 From: Billy Olsen <billy.olsen@gmail.com> Date: Fri, 15 Oct 2021 15:30:58 -0700 Subject: [PATCH] Configure TLS for OVN This patch configures TLS for OVN to use the local CA cert on the controller. The compute nodes request certificates to be provided by the CA cert and will use those certificates to configure local controller connections to the OVN SB database via TLS. The client certificates are validated against the control nodes CA. Local connections on the control node continue to use the local unix socket, which should be considered to be secure since it does not egress the node. Change-Id: Iacf5d5637c3a093bd80879c2ebb58efb16b52e66 --- tools/init/init/main.py | 2 +- tools/init/init/questions/__init__.py | 73 ++++++++++++++++++++------- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/tools/init/init/main.py b/tools/init/init/main.py index 1658bb2..4b71232 100644 --- a/tools/init/init/main.py +++ b/tools/init/init/main.py @@ -177,12 +177,12 @@ def init() -> None: question_list = [ questions.DnsServers(), questions.DnsDomain(), + questions.TlsCertificates(), questions.NetworkSettings(), questions.OsPassword(), # TODO: turn this off if COMPUTE. # The following are not yet implemented: # questions.VmSwappiness(), # questions.FileHandleLimits(), - questions.TlsCertificates(), questions.DashboardAccess(), questions.RabbitMq(), questions.DatabaseSetup(), diff --git a/tools/init/init/questions/__init__.py b/tools/init/init/questions/__init__.py index 7ebae41..02e7f47 100644 --- a/tools/init/init/questions/__init__.py +++ b/tools/init/init/questions/__init__.py @@ -26,6 +26,7 @@ limitations under the License. import json import platform import os +import socket import stat from time import sleep @@ -210,30 +211,72 @@ class NetworkSettings(Question): network.ExtGateway().ask() network.ExtCidr().ask() - control_ip = check_output('snapctl', 'get', 'config.network.control-ip') + + # The cacert will always be the same, regardless of the current + # node's role (control or compute) + ssl_cacert = shell.config_get('config.tls.cacert-path') + system_id = socket.getfqdn() + if role == 'control': nb_conn = 'unix:{SNAP_COMMON}/run/ovn/ovnnb_db.sock'.format(**_env) sb_conn = 'unix:{SNAP_COMMON}/run/ovn/ovnsb_db.sock'.format(**_env) - check_output('ovs-vsctl', 'set', 'open', '.', - f'external-ids:ovn-encap-ip={control_ip}') + ssl_key = shell.config_get('config.tls.key-path') + ssl_cert = shell.config_get('config.tls.cert-path') + ovn_encap_ip = control_ip elif role == 'compute': - sb_conn = f'tcp:{control_ip}:6642' + # Use the local compute node's ssl_key and ssl_cert for OVN + # configuration. + ssl_key = shell.config_get('config.tls.compute.key-path') + ssl_cert = shell.config_get('config.tls.compute.cert-path') + sb_conn = f'ssl:{control_ip}:6642' # Not used by any compute node services. nb_conn = '' compute_ip = check_output('snapctl', 'get', 'config.network.compute-ip') # Set the IP address to be used for a tunnel endpoint. - check_output('ovs-vsctl', 'set', 'open', '.', - f'external-ids:ovn-encap-ip={compute_ip}') + ovn_encap_ip = compute_ip else: raise Exception(f'Unexpected node role: {role}') # ovn-controller does not start unless both the ovn-encap-ip and the # ovn-encap-type are set. - check_output('ovs-vsctl', 'set', 'open', '.', - 'external-ids:ovn-encap-type=geneve') + log.debug('Configuring Open vSwitch geneve tunnels and system id. ' + f'ovn-encap-ip = {ovn_encap_ip}, system-id = {system_id}') + check_output( + 'ovs-vsctl', + 'set', 'open', '.', 'external-ids:ovn-encap-type=geneve', + '--', + 'set', 'open', '.', f'external-ids:ovn-encap-ip={ovn_encap_ip}', + '--', + 'set', 'open', '.', f'external-ids:system-id={system_id}' + ) + + # Configure the TLS settings for Open vSwitch and OVN. Note, the + # ovn-wrapper is used as it has the necessary configuration to use + # socket connections. + log.debug('Configuring TLS for Open vSwitch and OVN') + check_output( + 'ovs-vsctl', 'set-ssl', ssl_key, ssl_cert, ssl_cacert + ) + + if role == 'control': + check_output( + 'ovn-wrapper', 'ovn-sbctl', 'set-ssl', ssl_key, ssl_cert, + ssl_cacert + ) + check_output( + 'ovn-wrapper', 'ovn-sbctl', 'set-connection', 'pssl:6642' + ) + # Note: by default, the control node will serve as a gateway node + # for the cluster. Only set the enable-chassis-as-gw for the + # control node. + check_output( + 'ovs-vsctl', 'set', 'open', '.', + 'external-ids:ovn-cms-options=enable-chassis-as-gw' + ) + restart('ovn-ovsdb-server-sb') # Configure OVN SB and NB sockets based on the role node. For # single-node deployments there is no need to use a TCP socket. @@ -244,22 +287,17 @@ class NetworkSettings(Question): # Set SB database connection details for ovn-controller to pick up. check_output( - 'ovs-vsctl', 'set', 'open', '.', - f'external-ids:ovn-remote={sb_conn}' - ) - check_output( - 'ovs-vsctl', 'set', 'open', '.', - 'external-ids:ovn-cms-options=enable-chassis-as-gw' + 'ovs-vsctl', 'set', 'open', '.', + f'external-ids:ovn-remote={sb_conn}' ) - # Now that we have default or overriden values, setup the + # Now that we have default or overridden values, setup the # bridge and write all the proper values into our config # files. check('setup-br-ex') check('snap-openstack', 'setup') if role == 'control': - enable('ovn-northd') enable('ovn-controller') @@ -321,8 +359,9 @@ class TlsCertificates(Question): Path(shell.config_get('config.tls.cert-path')), Path(shell.config_get('config.tls.key-path')), ) + control_ip = shell.config_get('config.network.control-ip') tls.generate_self_signed(cert_path, key_path, - ip=_env['control_ip']) + ip=control_ip) copyfile(Path(shell.config_get('config.tls.cert-path')), Path(shell.config_get('config.tls.cacert-path'))) restart('nginx')