diff --git a/devstack/lib/vmware_nsx_v b/devstack/lib/vmware_nsx_v index bed6a93f22..68a09fadc4 100644 --- a/devstack/lib/vmware_nsx_v +++ b/devstack/lib/vmware_nsx_v @@ -104,6 +104,9 @@ function neutron_plugin_configure_service { _nsxv_ini_set nova_metadata_port "$NSXV_NOVA_METADATA_PORT" _nsxv_ini_set nova_metadata_ips "$NSXV_NOVA_METADATA_IPS" _nsxv_ini_set metadata_shared_secret "$NSXV_METADATA_SHARED_SECRET" + _nsxv_ini_set metadata_insecure "$NSXV_METADATA_INSECURE" + _nsxv_ini_set metadata_nova_client_cert "$NSXV_METADATA_NOVA_CERT" + _nsxv_ini_set metadata_nova_client_priv_key "$NSXV_METADATA_NOVA_PRIV_KEY" _nsxv_ini_set edge_ha "$NSXV_EDGE_HA" _nsxv_ini_set exclusive_router_appliance_size "$NSXV_EXCLUSIVE_ROUTER_APPLIANCE_SIZE" } diff --git a/etc/nsx.ini b/etc/nsx.ini index 143c8a3e17..cc1756989c 100644 --- a/etc/nsx.ini +++ b/etc/nsx.ini @@ -71,10 +71,10 @@ # Specify a CA bundle file to use in verifying the NSXv server certificate. # ca_file = -# If true, the NSXv server certificate is not verified. If false, +# If True, the NSXv server certificate is not verified. If False, # then the default CA truststore is used for verification. This option # is ignored if "ca_file" is set. -# insecure = true +# insecure = True # (Required) Datacenter MoRef ID for Edge deployment # datacenter_moid = @@ -143,6 +143,17 @@ # (Optional) Shared secret to sign metadata requests # metadata_shared_secret = +# (Optional) If True, the end to end connection for metadata service is +# not verified. If False, the default CA truststore is used for verification. +# metadata_insecure = + +# (Optional) Client certificate to use when metadata connection is to be +# verified. If not provided, a self signed certificate will be used. +# metadata_nova_client_cert = + +# (Optional) Private key to use for client certificate +# metadata_nova_client_priv_key = + # (Optional) Indicates if Nsxv spoofguard component is used to implement # port-security feature. # spoofguard_enabled = True diff --git a/vmware_nsx/common/config.py b/vmware_nsx/common/config.py index fe38cb3bdc..a2a5af90cd 100644 --- a/vmware_nsx/common/config.py +++ b/vmware_nsx/common/config.py @@ -303,6 +303,15 @@ nsxv_opts = [ cfg.StrOpt('metadata_shared_secret', secret=True, help=_('Shared secret to sign metadata requests')), + cfg.BoolOpt('metadata_insecure', + default=True, + help=_('If True, the end to end connection for metadata ' + 'service is not verified. If False, the default CA ' + 'truststore is used for verification')), + cfg.StrOpt('metadata_nova_client_cert', + help=_('Client certificate for nova metadata api server')), + cfg.StrOpt('metadata_nova_client_priv_key', + help=_('Private key of client certificate')), cfg.BoolOpt('spoofguard_enabled', default=True, help=_("If True then plugin will use NSXV spoofguard " diff --git a/vmware_nsx/common/nsxv_constants.py b/vmware_nsx/common/nsxv_constants.py index 9243cdd2bd..1975893e37 100644 --- a/vmware_nsx/common/nsxv_constants.py +++ b/vmware_nsx/common/nsxv_constants.py @@ -35,3 +35,20 @@ INTERNAL_TENANT_ID = 'a1b2c3d4-e5f6-eeff-ffee-6f5e4d3c2b1a' # L2 gateway edge name prefix L2_GATEWAY_EDGE = 'L2 bridging' + +# LoadBalancer Certificate constants +#NOTE(abhiraut): Number of days specify the total number of days for which the +# the certificate will be active. This certificate will expire +# in 10 years. Once the backend API allows creation of certs +# which do not expire, the following constant should be removed. +CERT_NUMBER_OF_DAYS = 3650 +CSR_REQUEST = ("" + "CNmetadata.nsx.local" + "" + "OOrganization" + "OUUnit" + "LLocality" + "STState" + "CUS" + "RSA2048" + "") diff --git a/vmware_nsx/common/utils.py b/vmware_nsx/common/utils.py index a9872fdcbd..4ace5edf51 100644 --- a/vmware_nsx/common/utils.py +++ b/vmware_nsx/common/utils.py @@ -16,6 +16,7 @@ import hashlib from neutron.api.v2 import attributes +from neutron.i18n import _LE from neutron import version from oslo_config import cfg from oslo_log import log @@ -147,3 +148,12 @@ def dict_match(dict1, dict2): elif v1 != v2: return False return True + + +def read_file(path): + try: + with open(path) as file: + return file.read().strip() + except IOError as e: + LOG.error(_LE("Error while opening file " + "%(path)s: %(err)s"), {'path': path, 'err': str(e)}) diff --git a/vmware_nsx/plugins/nsx_v/md_proxy.py b/vmware_nsx/plugins/nsx_v/md_proxy.py index de31cd70e3..796f439665 100644 --- a/vmware_nsx/plugins/nsx_v/md_proxy.py +++ b/vmware_nsx/plugins/nsx_v/md_proxy.py @@ -28,16 +28,20 @@ from neutron.i18n import _LE from vmware_nsx.common import exceptions as nsxv_exc from vmware_nsx.common import locking from vmware_nsx.common import nsxv_constants +from vmware_nsx.common import utils from vmware_nsx.db import nsxv_db from vmware_nsx.plugins.nsx_v.vshield import ( nsxv_loadbalancer as nsxv_lb) from vmware_nsx.plugins.nsx_v.vshield.common import ( constants as vcns_const) from vmware_nsx.plugins.nsx_v.vshield import edge_utils +from vmware_nsx.services.lbaas.nsx_v import lbaas_common METADATA_VSE_NAME = 'MdSrv' METADATA_IP_ADDR = '169.254.169.254' METADATA_TCP_PORT = 80 +METADATA_HTTPS_PORT = 443 +METADATA_HTTPS_VIP_PORT = 8775 INTERNAL_SUBNET = '169.254.128.0/17' MAX_INIT_THREADS = 3 @@ -486,6 +490,39 @@ class NsxVMetadataProxyHandler: address_groups.append(address_group) return address_groups + def _create_ssl_cert(self, edge_id=None): + # Create a self signed certificate in the backend if both Cert details + # and private key are not supplied in nsx.ini + if (not cfg.CONF.nsxv.metadata_nova_client_cert and + not cfg.CONF.nsxv.metadata_nova_client_priv_key): + h = self.nsxv_plugin.nsx_v.vcns.create_csr(edge_id)[0] + # Extract the CSR ID from header + csr_id = lbaas_common.extract_resource_id(h['location']) + # Create a self signed certificate + cert = self.nsxv_plugin.nsx_v.vcns.create_csr_cert(csr_id)[1] + cert_id = cert['objectId'] + else: + # Raise an error if either the Cert path or the private key is not + # configured + error = None + if not cfg.CONF.nsxv.metadata_nova_client_cert: + error = _('Metadata certificate path not configured') + elif not cfg.CONF.nsxv.metadata_nova_client_priv_key: + error = _('Metadata client private key not configured') + if error: + raise nsxv_exc.NsxPluginException(err_msg=error) + pem_encoding = utils.read_file( + cfg.CONF.nsxv.metadata_nova_client_cert) + priv_key = utils.read_file( + cfg.CONF.nsxv.metadata_nova_client_priv_key) + request = { + 'pemEncoding': pem_encoding, + 'privateKey': priv_key} + cert = self.nsxv_plugin.nsx_v.vcns.upload_edge_certificate( + edge_id, request)[1] + cert_id = cert.get('certificates')[0]['objectId'] + return cert_id + def _setup_metadata_lb(self, rtr_id, vip, v_port, s_port, member_ips, proxy_lb=False, context=None): @@ -497,10 +534,26 @@ class NsxVMetadataProxyHandler: lb_obj = nsxv_lb.NsxvLoadbalancer() + protocol = 'HTTP' + ssl_pass_through = False + cert_id = None + # Set protocol to HTTPS with default port of 443 if metadata_insecure + # is set to False. + if not cfg.CONF.nsxv.metadata_insecure: + protocol = 'HTTPS' + if proxy_lb: + v_port = METADATA_HTTPS_VIP_PORT + else: + v_port = METADATA_HTTPS_PORT + # Create the certificate on the backend + cert_id = self._create_ssl_cert(edge_id) + ssl_pass_through = proxy_lb + mon_type = protocol if proxy_lb else 'tcp' # Create virtual server virt_srvr = nsxv_lb.NsxvLBVirtualServer( name=METADATA_VSE_NAME, ip_address=vip, + protocol=protocol, port=v_port) # For router Edge, we add X-LB-Proxy-ID header @@ -525,8 +578,11 @@ class NsxVMetadataProxyHandler: # XFF is inserted in router LBs app_profile = nsxv_lb.NsxvLBAppProfile( name='MDSrvProxy', - template='HTTP', - insert_xff=not proxy_lb) + template=protocol, + server_ssl_enabled=not cfg.CONF.nsxv.metadata_insecure, + ssl_pass_through=ssl_pass_through, + insert_xff=not proxy_lb, + client_ssl_cert=cert_id) virt_srvr.set_app_profile(app_profile) @@ -534,8 +590,8 @@ class NsxVMetadataProxyHandler: pool = nsxv_lb.NsxvLBPool( name='MDSrvPool') - monitor = nsxv_lb.NsxvLBMonitor( - name='MDSrvMon', mon_type='http' if proxy_lb else 'icmp') + monitor = nsxv_lb.NsxvLBMonitor(name='MDSrvMon', + mon_type=mon_type.lower()) pool.add_monitor(monitor) i = 0 diff --git a/vmware_nsx/plugins/nsx_v/vshield/nsxv_loadbalancer.py b/vmware_nsx/plugins/nsx_v/vshield/nsxv_loadbalancer.py index 62b7075133..15ad1f1d00 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/nsxv_loadbalancer.py +++ b/vmware_nsx/plugins/nsx_v/vshield/nsxv_loadbalancer.py @@ -235,6 +235,7 @@ class NsxvLBAppProfile(object): ssl_pass_through=False, template='TCP', insert_xff=False, + client_ssl_cert=None, persist=False, persist_method='cookie', persist_cookie_name='JSESSIONID', @@ -256,6 +257,12 @@ class NsxvLBAppProfile(object): self.payload['persistence']['cookieMode'] = persist_cookie_mode self.payload['persistence']['cookieName'] = persist_cookie_name + if client_ssl_cert: + self.payload['clientSsl'] = { + 'clientAuth': 'ignore', + 'serviceCertificate': [client_ssl_cert] + } + def set_persistence( self, persist=False, diff --git a/vmware_nsx/plugins/nsx_v/vshield/vcns.py b/vmware_nsx/plugins/nsx_v/vshield/vcns.py index 541e619bf4..efa724059e 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/vcns.py +++ b/vmware_nsx/plugins/nsx_v/vshield/vcns.py @@ -21,6 +21,7 @@ import retrying import six import xml.etree.ElementTree as et +from vmware_nsx.common import nsxv_constants from vmware_nsx.plugins.nsx_v.vshield.common import exceptions from vmware_nsx.plugins.nsx_v.vshield.common import VcnsApiClient @@ -42,6 +43,7 @@ SECURITYGROUP_PREFIX = '/api/2.0/services/securitygroup' VDN_PREFIX = '/api/2.0/vdn' SERVICES_PREFIX = '/api/2.0/services' SPOOFGUARD_PREFIX = '/api/4.0/services/spoofguard' +TRUSTSTORE_PREFIX = '%s/%s' % (SERVICES_PREFIX, 'truststore') #LbaaS Constants LOADBALANCER_SERVICE = "loadbalancer/config" @@ -65,6 +67,10 @@ SYSCTL_SERVICE = 'systemcontrol/config' # L2 gateway constants BRIDGE = "bridging/config" +# Self Signed Certificate constants +CSR = "csr" +CERTIFICATE = "certificate" + def retry_upon_exception(exc, delay=500, max_delay=2000, max_attempts=cfg.CONF.nsxv.retries): @@ -810,5 +816,17 @@ class Vcns(object): def upload_edge_certificate(self, edge_id, request): """Creates a certificate on the specified Edge appliance.""" - uri = '/api/2.0/services/truststore/certificate/%s' % edge_id + uri = '%s/%s/%s' % (TRUSTSTORE_PREFIX, CERTIFICATE, edge_id) return self.do_request(HTTP_POST, uri, request, decode=True) + + def create_csr(self, edge_id, request=nsxv_constants.CSR_REQUEST): + """Create a CSR on the specified Edge appliance.""" + uri = '%s/%s/%s' % (TRUSTSTORE_PREFIX, CSR, edge_id) + return self.do_request(HTTP_POST, uri, request, format='xml', + decode=False) + + def create_csr_cert(self, csr_id): + """Create a CSR self signed cert on the specified Edge appliance.""" + uri = '%s/%s/%s?noOfDays=%s' % (TRUSTSTORE_PREFIX, CSR, csr_id, + nsxv_constants.CERT_NUMBER_OF_DAYS) + return self.do_request(HTTP_PUT, uri)