# Copyright 2017 VMware, Inc. # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from vmware_nsxlib.v3 import utils LOG = logging.getLogger(__name__) VPN_IPSEC_PATH = 'vpn/ipsec/' # The following classes define IPSEC NSX constants that are alo relevant to the # policy implementation: class IkeVersionTypes(object): """Supported IKE versions (NSX default is V2)""" IKE_VERSION_V1 = 'IKE_V1' IKE_VERSION_V2 = 'IKE_V2' IKE_VERSION_Flex = 'IKE_FLEX' class EncryptionAlgorithmTypes(object): """Supported encryption algorithms (NSX default is AES_128)""" ENCRYPTION_ALGORITHM_128 = 'AES_128' ENCRYPTION_ALGORITHM_256 = 'AES_256' ENCRYPTION_ALGORITHM_GCM_128 = 'AES_GCM_128' # only with IKE_V2 ENCRYPTION_ALGORITHM_GCM_192 = 'AES_GCM_192' # only with IKE_V2 ENCRYPTION_ALGORITHM_GCM_256 = 'AES_GCM_256' # only with IKE_V2 class DigestAlgorithmTypes(object): """Supported digest (auth) algorithms (NSX default is SHA2_256)""" DIGEST_ALGORITHM_SHA1 = 'SHA1' DIGEST_ALGORITHM_SHA256 = 'SHA2_256' DIGEST_ALGORITHM_SHA2_384 = 'SHA2_384' DIGEST_ALGORITHM_SHA2_512 = 'SHA2_512' DIGEST_ALGORITHM_GMAC_128 = 'GMAC_128' # only for tunnel profile DIGEST_ALGORITHM_GMAC_192 = 'GMAC_192' # only for tunnel profile DIGEST_ALGORITHM_GMAC_256 = 'GMAC_256' # only for tunnel profile class DHGroupTypes(object): """Supported DH groups for Perfect Forward Secrecy""" DH_GROUP_2 = 'GROUP2' DH_GROUP_5 = 'GROUP5' DH_GROUP_14 = 'GROUP14' DH_GROUP_15 = 'GROUP15' DH_GROUP_16 = 'GROUP16' DH_GROUP_19 = 'GROUP19' DH_GROUP_20 = 'GROUP20' DH_GROUP_21 = 'GROUP21' class EncapsulationModeTypes(object): """Supported encapsulation modes for ipsec tunnel profile""" ENCAPSULATION_MODE_TUNNEL = 'TUNNEL_MODE' class TransformProtocolTypes(object): """Supported transform protocols for ipsec tunnel profile""" TRANSFORM_PROTOCOL_ESP = 'ESP' class AuthenticationModeTypes(object): """Supported authentication modes for ipsec peer endpoint (default PSK)""" AUTH_MODE_PSK = 'PSK' AUTH_MODE_CERT = 'CERTIFICATE' class DpdProfileActionTypes(object): """Supported DPD profile actions""" DPD_PROFILE_ACTION_HOLD = 'HOLD' class DpdProfileTimeoutLimits(object): """Supported DPD timeout range""" DPD_TIMEOUT_MIN = 3 DPD_TIMEOUT_MAX = 360 class IkeSALifetimeLimits(object): """Limits to the allowed SA lifetime in seconds (NSX default is 1 day)""" SA_LIFETIME_MIN = 21600 SA_LIFETIME_MAX = 31536000 class IPsecSALifetimeLimits(object): """Limits to the allowed SA lifetime in seconds (NSX default is 3600)""" SA_LIFETIME_MIN = 900 SA_LIFETIME_MAX = 31536000 class ConnectionInitiationModeTypes(object): """Supported connection initiation mode type""" INITIATION_MODE_INITIATOR = 'INITIATOR' INITIATION_MODE_RESPOND_ONLY = 'RESPOND_ONLY' INITIATION_MODE_ON_DEMAND = 'ON_DEMAND' class IkeLogLevelTypes(object): """Supported service IKE log levels (default ERROR)""" LOG_LEVEL_DEBUG = 'DEBUG' LOG_LEVEL_INFO = 'INFO' LOG_LEVEL_WARN = 'WARN' LOG_LEVEL_ERROR = 'ERROR' class IkeProfile(utils.NsxLibApiBase): @property def resource_type(self): return 'IPSecVPNIKEProfile' @property def uri_segment(self): return VPN_IPSEC_PATH + 'ike-profiles' def create(self, name, description=None, encryption_algorithm=None, digest_algorithm=None, ike_version=None, dh_group=None, sa_life_time=None, tags=None): # mandatory parameters body = {'display_name': name} # optional parameters if description: body['description'] = description if encryption_algorithm: body['encryption_algorithms'] = [encryption_algorithm] if digest_algorithm: body['digest_algorithms'] = [digest_algorithm] if ike_version: body['ike_version'] = ike_version if sa_life_time: body['sa_life_time'] = sa_life_time if dh_group: body['dh_groups'] = [dh_group] if tags: body['tags'] = tags return self.client.create(self.get_path(), body=body) class IPSecTunnelProfile(utils.NsxLibApiBase): @property def resource_type(self): return 'IPSecVPNTunnelProfile' @property def uri_segment(self): return VPN_IPSEC_PATH + 'tunnel-profiles' def create(self, name, description=None, encryption_algorithm=None, digest_algorithm=None, pfs=None, dh_group=None, sa_life_time=None, tags=None): # mandatory parameters body = {'display_name': name} # optional parameters if description: body['description'] = description if encryption_algorithm: body['encryption_algorithms'] = [encryption_algorithm] if digest_algorithm: body['digest_algorithms'] = [digest_algorithm] if sa_life_time: body['sa_life_time'] = sa_life_time if dh_group: body['dh_groups'] = [dh_group] if tags: body['tags'] = tags # Boolean parameters if pfs is not None: body['enable_perfect_forward_secrecy'] = pfs return self.client.create(self.get_path(), body=body) class IPSecDpdProfile(utils.NsxLibApiBase): @property def resource_type(self): return 'IPSecVPNDPDProfile' @property def uri_segment(self): return VPN_IPSEC_PATH + 'dpd-profiles' def create(self, name, description=None, enabled=None, timeout=None, tags=None): # mandatory parameters body = {'display_name': name} # optional parameters if description: body['description'] = description if timeout: body['dpd_probe_interval'] = timeout # Boolean parameters if enabled is not None: body['enabled'] = enabled if tags: body['tags'] = tags return self.client.create(self.get_path(), body=body) def update(self, profile_id, name=None, description=None, enabled=None, timeout=None, tags=None): body = self.get(profile_id) if name: body['display_name'] = name if description: body['description'] = description if timeout: body['dpd_probe_interval'] = timeout if enabled is not None: body['enabled'] = enabled if tags is not None: body['tags'] = tags return self.client.update(self.get_path(profile_id), body=body) class IPSecPeerEndpoint(utils.NsxLibApiBase): @property def resource_type(self): return 'IPSecVPNPeerEndpoint' @property def uri_segment(self): return VPN_IPSEC_PATH + 'peer-endpoints' def create(self, name, peer_address, peer_id, description=None, authentication_mode=None, dpd_profile_id=None, ike_profile_id=None, ipsec_tunnel_profile_id=None, connection_initiation_mode=None, psk=None, tags=None): # mandatory parameters body = {'display_name': name, 'peer_address': peer_address, 'peer_id': peer_id} # optional parameters if description: body['description'] = description if authentication_mode: body['authentication_mode'] = authentication_mode if dpd_profile_id: body['dpd_profile_id'] = dpd_profile_id if ike_profile_id: body['ike_profile_id'] = ike_profile_id if ipsec_tunnel_profile_id: body['ipsec_tunnel_profile_id'] = ipsec_tunnel_profile_id if psk: body['psk'] = psk if connection_initiation_mode: body['connection_initiation_mode'] = connection_initiation_mode if tags: body['tags'] = tags return self.client.create(self.get_path(), body=body) def update(self, uuid, name=None, description=None, peer_address=None, peer_id=None, connection_initiation_mode=None, psk=None, tags=None): body = self.get(uuid) if description: body['description'] = description if name: body['display_name'] = name if psk: body['psk'] = psk if connection_initiation_mode: body['connection_initiation_mode'] = connection_initiation_mode if peer_address: body['peer_address'] = peer_address if peer_id: body['peer_id'] = peer_id if tags is not None: body['tags'] = tags return self.client.update(self.get_path(uuid), body=body) class LocalEndpoint(utils.NsxLibApiBase): @property def resource_type(self): return 'IPSecVPNLocalEndpoint' @property def uri_segment(self): return VPN_IPSEC_PATH + 'local-endpoints' def create(self, name, local_address, ipsec_vpn_service_id, description=None, local_id=None, certificate_id=None, trust_ca_ids=None, trust_crl_ids=None, tags=None): # mandatory parameters body = {'display_name': name, 'local_address': local_address, 'ipsec_vpn_service_id': {'target_id': ipsec_vpn_service_id}} # optional parameters if description: body['description'] = description if local_id: body['local_id'] = local_id if certificate_id: body['certificate_id'] = certificate_id if trust_ca_ids: body['trust_ca_ids'] = trust_ca_ids if trust_crl_ids: body['trust_crl_ids'] = trust_crl_ids if tags: body['tags'] = tags return self.client.create(self.get_path(), body=body) def update(self, uuid, name=None, description=None, local_address=None, ipsec_vpn_service_id=None, local_id=None, certificate_id=None, trust_ca_ids=None, trust_crl_ids=None, tags=None): body = self.get(uuid) if description: body['description'] = description if name: body['display_name'] = name if local_address: body['local_address'] = local_address if ipsec_vpn_service_id: body['ipsec_vpn_service_id'] = {'target_id': ipsec_vpn_service_id} if local_id: body['local_id'] = local_id if certificate_id: body['certificate_id'] = certificate_id if trust_ca_ids: body['trust_ca_ids'] = trust_ca_ids if trust_crl_ids: body['trust_crl_ids'] = trust_crl_ids if tags is not None: body['tags'] = tags return self.client.update(self.get_path(uuid), body=body) class Session(utils.NsxLibApiBase): @property def resource_type(self): return 'PolicyBasedIPSecVPNSession' @property def uri_segment(self): return VPN_IPSEC_PATH + 'sessions' def create(self, name, local_endpoint_id, peer_endpoint_id, policy_rules, description=None, enabled=True, tags=None): # mandatory parameters body = {'display_name': name, 'description': description, 'local_endpoint_id': local_endpoint_id, 'peer_endpoint_id': peer_endpoint_id, 'enabled': enabled, 'resource_type': self.resource_type, 'policy_rules': policy_rules} if tags: body['tags'] = tags return self.client.create(self.get_path(), body=body) def get_rule_obj(self, sources, destinations): src_subnets = [{'subnet': src} for src in sources] dst_subnets = [{'subnet': dst} for dst in destinations] return { 'sources': src_subnets, 'destinations': dst_subnets } def update(self, uuid, name=None, description=None, policy_rules=None, tags=None, enabled=None): body = self.get(uuid) if description: body['description'] = description if name: body['display_name'] = name if name: body['display_name'] = name if policy_rules is not None: body['policy_rules'] = policy_rules if enabled is not None: body['enabled'] = enabled return self.client.update(self.get_path(uuid), body=body) def get_status(self, uuid, source='realtime'): try: return self.client.get( self.get_path(uuid + '/status?source=%s' % source)) except Exception as e: LOG.warning("No status found for session %s: %s", uuid, e) return class Service(utils.NsxLibApiBase): @property def resource_type(self): return 'IPSecVPNService' @property def uri_segment(self): return VPN_IPSEC_PATH + 'services' def create(self, name, logical_router_id, enabled=True, ike_log_level="ERROR", tags=None, bypass_rules=None): # mandatory parameters body = {'display_name': name, 'logical_router_id': logical_router_id} # optional parameters if ike_log_level: body['ike_log_level'] = ike_log_level if enabled is not None: body['enabled'] = enabled if tags: body['tags'] = tags if bypass_rules: body['bypass_rules'] = bypass_rules return self.client.create(self.get_path(), body=body) class VpnIpSec(object): """This is the class that have all vpn ipsec resource clients""" def __init__(self, client, nsxlib_config, nsxlib=None): self.ike_profile = IkeProfile(client, nsxlib_config, nsxlib=nsxlib) self.tunnel_profile = IPSecTunnelProfile(client, nsxlib_config, nsxlib=nsxlib) self.dpd_profile = IPSecDpdProfile(client, nsxlib_config, nsxlib=nsxlib) self.peer_endpoint = IPSecPeerEndpoint(client, nsxlib_config, nsxlib=nsxlib) self.local_endpoint = LocalEndpoint(client, nsxlib_config, nsxlib=nsxlib) self.session = Session(client, nsxlib_config, nsxlib=nsxlib) self.service = Service(client, nsxlib_config, nsxlib=nsxlib)