[NSXv] Add SSL support for metadata service in NSX-V plugin
Metadata service in the NSX-V plugin is handled by a Edge DHCP or router VM. Currently the traffic between nova and the metadata service is insecure. This patch adds the SSL support for metadata service which will make the connection secure. The certificate used for secure communication will be created on the VC under the edge scope. If user does not supply the certificate and private key for secure communication, a self signed certificate will be generated in the backend. This self signed certificate will last for a period of 10yrs. A certifcate with the given details will be created in the backend if such a configuration exists in nsx.ini Appropriate config is pushed for the loadbalancer with the protocol set to HTTPS if SSL is enabled for metadata service. DocImpact Change-Id: I5582cc1186ef4b8451f999b46e55bc2c684b1be3
This commit is contained in:
parent
bd4a4ef306
commit
ea77b5f857
@ -104,6 +104,9 @@ function neutron_plugin_configure_service {
|
|||||||
_nsxv_ini_set nova_metadata_port "$NSXV_NOVA_METADATA_PORT"
|
_nsxv_ini_set nova_metadata_port "$NSXV_NOVA_METADATA_PORT"
|
||||||
_nsxv_ini_set nova_metadata_ips "$NSXV_NOVA_METADATA_IPS"
|
_nsxv_ini_set nova_metadata_ips "$NSXV_NOVA_METADATA_IPS"
|
||||||
_nsxv_ini_set metadata_shared_secret "$NSXV_METADATA_SHARED_SECRET"
|
_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 edge_ha "$NSXV_EDGE_HA"
|
||||||
_nsxv_ini_set exclusive_router_appliance_size "$NSXV_EXCLUSIVE_ROUTER_APPLIANCE_SIZE"
|
_nsxv_ini_set exclusive_router_appliance_size "$NSXV_EXCLUSIVE_ROUTER_APPLIANCE_SIZE"
|
||||||
}
|
}
|
||||||
|
15
etc/nsx.ini
15
etc/nsx.ini
@ -71,10 +71,10 @@
|
|||||||
# Specify a CA bundle file to use in verifying the NSXv server certificate.
|
# Specify a CA bundle file to use in verifying the NSXv server certificate.
|
||||||
# ca_file =
|
# 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
|
# then the default CA truststore is used for verification. This option
|
||||||
# is ignored if "ca_file" is set.
|
# is ignored if "ca_file" is set.
|
||||||
# insecure = true
|
# insecure = True
|
||||||
|
|
||||||
# (Required) Datacenter MoRef ID for Edge deployment
|
# (Required) Datacenter MoRef ID for Edge deployment
|
||||||
# datacenter_moid =
|
# datacenter_moid =
|
||||||
@ -143,6 +143,17 @@
|
|||||||
# (Optional) Shared secret to sign metadata requests
|
# (Optional) Shared secret to sign metadata requests
|
||||||
# metadata_shared_secret =
|
# 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
|
# (Optional) Indicates if Nsxv spoofguard component is used to implement
|
||||||
# port-security feature.
|
# port-security feature.
|
||||||
# spoofguard_enabled = True
|
# spoofguard_enabled = True
|
||||||
|
@ -303,6 +303,15 @@ nsxv_opts = [
|
|||||||
cfg.StrOpt('metadata_shared_secret',
|
cfg.StrOpt('metadata_shared_secret',
|
||||||
secret=True,
|
secret=True,
|
||||||
help=_('Shared secret to sign metadata requests')),
|
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',
|
cfg.BoolOpt('spoofguard_enabled',
|
||||||
default=True,
|
default=True,
|
||||||
help=_("If True then plugin will use NSXV spoofguard "
|
help=_("If True then plugin will use NSXV spoofguard "
|
||||||
|
@ -35,3 +35,20 @@ INTERNAL_TENANT_ID = 'a1b2c3d4-e5f6-eeff-ffee-6f5e4d3c2b1a'
|
|||||||
|
|
||||||
# L2 gateway edge name prefix
|
# L2 gateway edge name prefix
|
||||||
L2_GATEWAY_EDGE = 'L2 bridging'
|
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 = ("<csr><subject>"
|
||||||
|
"<attribute><key>CN</key><value>metadata.nsx.local</value>"
|
||||||
|
"</attribute>"
|
||||||
|
"<attribute><key>O</key><value>Organization</value></attribute>"
|
||||||
|
"<attribute><key>OU</key><value>Unit</value></attribute>"
|
||||||
|
"<attribute><key>L</key><value>Locality</value></attribute>"
|
||||||
|
"<attribute><key>ST</key><value>State</value></attribute>"
|
||||||
|
"<attribute><key>C</key><value>US</value></attribute>"
|
||||||
|
"</subject><algorithm>RSA</algorithm><keySize>2048</keySize>"
|
||||||
|
"</csr>")
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
|
from neutron.i18n import _LE
|
||||||
from neutron import version
|
from neutron import version
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@ -147,3 +148,12 @@ def dict_match(dict1, dict2):
|
|||||||
elif v1 != v2:
|
elif v1 != v2:
|
||||||
return False
|
return False
|
||||||
return True
|
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)})
|
||||||
|
@ -28,16 +28,20 @@ from neutron.i18n import _LE
|
|||||||
from vmware_nsx.common import exceptions as nsxv_exc
|
from vmware_nsx.common import exceptions as nsxv_exc
|
||||||
from vmware_nsx.common import locking
|
from vmware_nsx.common import locking
|
||||||
from vmware_nsx.common import nsxv_constants
|
from vmware_nsx.common import nsxv_constants
|
||||||
|
from vmware_nsx.common import utils
|
||||||
from vmware_nsx.db import nsxv_db
|
from vmware_nsx.db import nsxv_db
|
||||||
from vmware_nsx.plugins.nsx_v.vshield import (
|
from vmware_nsx.plugins.nsx_v.vshield import (
|
||||||
nsxv_loadbalancer as nsxv_lb)
|
nsxv_loadbalancer as nsxv_lb)
|
||||||
from vmware_nsx.plugins.nsx_v.vshield.common import (
|
from vmware_nsx.plugins.nsx_v.vshield.common import (
|
||||||
constants as vcns_const)
|
constants as vcns_const)
|
||||||
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
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_VSE_NAME = 'MdSrv'
|
||||||
METADATA_IP_ADDR = '169.254.169.254'
|
METADATA_IP_ADDR = '169.254.169.254'
|
||||||
METADATA_TCP_PORT = 80
|
METADATA_TCP_PORT = 80
|
||||||
|
METADATA_HTTPS_PORT = 443
|
||||||
|
METADATA_HTTPS_VIP_PORT = 8775
|
||||||
INTERNAL_SUBNET = '169.254.128.0/17'
|
INTERNAL_SUBNET = '169.254.128.0/17'
|
||||||
MAX_INIT_THREADS = 3
|
MAX_INIT_THREADS = 3
|
||||||
|
|
||||||
@ -486,6 +490,39 @@ class NsxVMetadataProxyHandler:
|
|||||||
address_groups.append(address_group)
|
address_groups.append(address_group)
|
||||||
return address_groups
|
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,
|
def _setup_metadata_lb(self, rtr_id, vip, v_port, s_port, member_ips,
|
||||||
proxy_lb=False, context=None):
|
proxy_lb=False, context=None):
|
||||||
|
|
||||||
@ -497,10 +534,26 @@ class NsxVMetadataProxyHandler:
|
|||||||
|
|
||||||
lb_obj = nsxv_lb.NsxvLoadbalancer()
|
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
|
# Create virtual server
|
||||||
virt_srvr = nsxv_lb.NsxvLBVirtualServer(
|
virt_srvr = nsxv_lb.NsxvLBVirtualServer(
|
||||||
name=METADATA_VSE_NAME,
|
name=METADATA_VSE_NAME,
|
||||||
ip_address=vip,
|
ip_address=vip,
|
||||||
|
protocol=protocol,
|
||||||
port=v_port)
|
port=v_port)
|
||||||
|
|
||||||
# For router Edge, we add X-LB-Proxy-ID header
|
# For router Edge, we add X-LB-Proxy-ID header
|
||||||
@ -525,8 +578,11 @@ class NsxVMetadataProxyHandler:
|
|||||||
# XFF is inserted in router LBs
|
# XFF is inserted in router LBs
|
||||||
app_profile = nsxv_lb.NsxvLBAppProfile(
|
app_profile = nsxv_lb.NsxvLBAppProfile(
|
||||||
name='MDSrvProxy',
|
name='MDSrvProxy',
|
||||||
template='HTTP',
|
template=protocol,
|
||||||
insert_xff=not proxy_lb)
|
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)
|
virt_srvr.set_app_profile(app_profile)
|
||||||
|
|
||||||
@ -534,8 +590,8 @@ class NsxVMetadataProxyHandler:
|
|||||||
pool = nsxv_lb.NsxvLBPool(
|
pool = nsxv_lb.NsxvLBPool(
|
||||||
name='MDSrvPool')
|
name='MDSrvPool')
|
||||||
|
|
||||||
monitor = nsxv_lb.NsxvLBMonitor(
|
monitor = nsxv_lb.NsxvLBMonitor(name='MDSrvMon',
|
||||||
name='MDSrvMon', mon_type='http' if proxy_lb else 'icmp')
|
mon_type=mon_type.lower())
|
||||||
pool.add_monitor(monitor)
|
pool.add_monitor(monitor)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -235,6 +235,7 @@ class NsxvLBAppProfile(object):
|
|||||||
ssl_pass_through=False,
|
ssl_pass_through=False,
|
||||||
template='TCP',
|
template='TCP',
|
||||||
insert_xff=False,
|
insert_xff=False,
|
||||||
|
client_ssl_cert=None,
|
||||||
persist=False,
|
persist=False,
|
||||||
persist_method='cookie',
|
persist_method='cookie',
|
||||||
persist_cookie_name='JSESSIONID',
|
persist_cookie_name='JSESSIONID',
|
||||||
@ -256,6 +257,12 @@ class NsxvLBAppProfile(object):
|
|||||||
self.payload['persistence']['cookieMode'] = persist_cookie_mode
|
self.payload['persistence']['cookieMode'] = persist_cookie_mode
|
||||||
self.payload['persistence']['cookieName'] = persist_cookie_name
|
self.payload['persistence']['cookieName'] = persist_cookie_name
|
||||||
|
|
||||||
|
if client_ssl_cert:
|
||||||
|
self.payload['clientSsl'] = {
|
||||||
|
'clientAuth': 'ignore',
|
||||||
|
'serviceCertificate': [client_ssl_cert]
|
||||||
|
}
|
||||||
|
|
||||||
def set_persistence(
|
def set_persistence(
|
||||||
self,
|
self,
|
||||||
persist=False,
|
persist=False,
|
||||||
|
@ -21,6 +21,7 @@ import retrying
|
|||||||
import six
|
import six
|
||||||
import xml.etree.ElementTree as et
|
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 exceptions
|
||||||
from vmware_nsx.plugins.nsx_v.vshield.common import VcnsApiClient
|
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'
|
VDN_PREFIX = '/api/2.0/vdn'
|
||||||
SERVICES_PREFIX = '/api/2.0/services'
|
SERVICES_PREFIX = '/api/2.0/services'
|
||||||
SPOOFGUARD_PREFIX = '/api/4.0/services/spoofguard'
|
SPOOFGUARD_PREFIX = '/api/4.0/services/spoofguard'
|
||||||
|
TRUSTSTORE_PREFIX = '%s/%s' % (SERVICES_PREFIX, 'truststore')
|
||||||
|
|
||||||
#LbaaS Constants
|
#LbaaS Constants
|
||||||
LOADBALANCER_SERVICE = "loadbalancer/config"
|
LOADBALANCER_SERVICE = "loadbalancer/config"
|
||||||
@ -65,6 +67,10 @@ SYSCTL_SERVICE = 'systemcontrol/config'
|
|||||||
# L2 gateway constants
|
# L2 gateway constants
|
||||||
BRIDGE = "bridging/config"
|
BRIDGE = "bridging/config"
|
||||||
|
|
||||||
|
# Self Signed Certificate constants
|
||||||
|
CSR = "csr"
|
||||||
|
CERTIFICATE = "certificate"
|
||||||
|
|
||||||
|
|
||||||
def retry_upon_exception(exc, delay=500, max_delay=2000,
|
def retry_upon_exception(exc, delay=500, max_delay=2000,
|
||||||
max_attempts=cfg.CONF.nsxv.retries):
|
max_attempts=cfg.CONF.nsxv.retries):
|
||||||
@ -810,5 +816,17 @@ class Vcns(object):
|
|||||||
|
|
||||||
def upload_edge_certificate(self, edge_id, request):
|
def upload_edge_certificate(self, edge_id, request):
|
||||||
"""Creates a certificate on the specified Edge appliance."""
|
"""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)
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user