Merge "[NSXv] Add SSL support for metadata service in NSX-V plugin"

This commit is contained in:
Jenkins 2015-12-02 13:31:05 +00:00 committed by Gerrit Code Review
commit 8177d68dfd
8 changed files with 138 additions and 7 deletions

View File

@ -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"
}

View File

@ -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

View File

@ -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 "

View File

@ -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 = ("<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>")

View File

@ -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)})

View File

@ -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

View File

@ -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,

View File

@ -23,6 +23,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
@ -44,6 +45,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"
@ -67,6 +69,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):
@ -816,5 +822,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)