def test_check_alpn_protocols(self): # Test valid list validate.check_alpn_protocols(['h2', 'http/1.1', 'http/1.0']) # Test invalid list self.assertRaises(exceptions.ValidationException, validate.check_alpn_protocols, ['httpie', 'foobar/1.2.3']) # Test empty list self.assertRaises(exceptions.ValidationException, validate.check_alpn_protocols, [])
def _validate_create_pool(self, lock_session, pool_dict, listener_id=None): """Validate creating pool on load balancer. Update database for load balancer and (optional) listener based on provisioning status. """ # Make sure we have a client CA if they specify a CRL if (pool_dict.get('crl_container_id') and not pool_dict.get('ca_tls_certificate_id')): raise exceptions.ValidationException( detail=_("A CA certificate reference is required to " "specify a revocation list.")) tls_certificate_id = pool_dict.get('tls_certificate_id', None) tls_refs = [tls_certificate_id] if tls_certificate_id else [] self._validate_tls_refs(tls_refs) # Validate the client CA cert and optional client CRL if pool_dict.get('ca_tls_certificate_id'): self._validate_client_ca_and_crl_refs( pool_dict.get('ca_tls_certificate_id'), pool_dict.get('crl_container_id', None)) # Check TLS cipher prohibit list if 'tls_ciphers' in pool_dict and pool_dict['tls_ciphers']: rejected_ciphers = validate.check_cipher_prohibit_list( pool_dict['tls_ciphers']) if rejected_ciphers: raise exceptions.ValidationException(detail=_( 'The following ciphers have been prohibited by an ' 'administrator: ' + ', '.join(rejected_ciphers))) if pool_dict['tls_enabled']: # Validate TLS version list validate.check_tls_version_list(pool_dict['tls_versions']) # Validate TLS versions against minimum validate.check_tls_version_min(pool_dict['tls_versions']) # Validate ALPN protocol list validate.check_alpn_protocols(pool_dict['alpn_protocols']) try: return self.repositories.create_pool_on_load_balancer( lock_session, pool_dict, listener_id=listener_id) except odb_exceptions.DBDuplicateEntry as e: raise exceptions.IDAlreadyExists() from e except odb_exceptions.DBError as e: # TODO(blogan): will have to do separate validation protocol # before creation or update since the exception messages # do not give any information as to what constraint failed raise exceptions.InvalidOption(value='', option='') from e
def _validate_listener_PUT(self, listener, db_listener): # TODO(rm_work): Do we need something like this? What do we do on an # empty body for a PUT? if not listener: raise exceptions.ValidationException( detail='No listener object supplied.') # Check for UDP/SCTP compatibility if (db_listener.protocol in (constants.PROTOCOL_UDP, lib_consts.PROTOCOL_SCTP) and self._is_tls_or_insert_header(listener.to_dict())): raise exceptions.ValidationException(detail=_( "%s protocol listener does not support TLS or header " "insertion.") % db_listener.protocol) # Check for certs when not TERMINATED_HTTPS if (db_listener.protocol != constants.PROTOCOL_TERMINATED_HTTPS and self._has_tls_container_refs(listener.to_dict())): raise exceptions.ValidationException(detail=_( "Certificate container references are only allowed on " "%s protocol listeners.") % constants.PROTOCOL_TERMINATED_HTTPS) # Make sure we have a client CA cert if they enable client auth if ((listener.client_authentication != wtypes.Unset and listener.client_authentication != constants.CLIENT_AUTH_NONE) and not (db_listener.client_ca_tls_certificate_id or listener.client_ca_tls_container_ref)): raise exceptions.ValidationException(detail=_( "Client authentication setting %s requires a client CA " "container reference.") % listener.client_authentication) if listener.insert_headers: self._validate_insert_headers( list(listener.insert_headers.keys()), db_listener.protocol) sni_containers = listener.sni_container_refs or [] tls_refs = list(sni_containers) if listener.default_tls_container_ref: tls_refs.append(listener.default_tls_container_ref) self._validate_tls_refs(tls_refs) ca_ref = None if (listener.client_ca_tls_container_ref and listener.client_ca_tls_container_ref != wtypes.Unset): ca_ref = listener.client_ca_tls_container_ref elif db_listener.client_ca_tls_certificate_id: ca_ref = db_listener.client_ca_tls_certificate_id crl_ref = None if (listener.client_crl_container_ref and listener.client_crl_container_ref != wtypes.Unset): crl_ref = listener.client_crl_container_ref elif db_listener.client_crl_container_id: crl_ref = db_listener.client_crl_container_id if crl_ref and not ca_ref: raise exceptions.ValidationException(detail=_( "A client authentication CA reference is required to " "specify a client authentication revocation list.")) if ca_ref or crl_ref: self._validate_client_ca_and_crl_refs(ca_ref, crl_ref) # Validate allowed CIDRs if (listener.allowed_cidrs and listener.allowed_cidrs != wtypes.Unset): vip_address = db_listener.load_balancer.vip.ip_address self._validate_cidr_compatible_with_vip( vip_address, listener.allowed_cidrs) # Check TLS cipher prohibit list if listener.tls_ciphers: rejected_ciphers = validate.check_cipher_prohibit_list( listener.tls_ciphers) if rejected_ciphers: raise exceptions.ValidationException(detail=_( 'The following ciphers have been prohibited by an ' 'administrator: ' + ', '.join(rejected_ciphers))) if listener.tls_versions is not wtypes.Unset: # Validate TLS version list validate.check_tls_version_list(listener.tls_versions) # Validate TLS versions against minimum validate.check_tls_version_min(listener.tls_versions) if listener.alpn_protocols is not wtypes.Unset: # Validate ALPN protocol list validate.check_alpn_protocols(listener.alpn_protocols)
def _validate_create_listener(self, lock_session, listener_dict): """Validate listener for wrong protocol or duplicate listeners Update the load balancer db when provisioning status changes. """ listener_protocol = listener_dict.get('protocol') if listener_dict and listener_dict.get('insert_headers'): self._validate_insert_headers( listener_dict['insert_headers'].keys(), listener_protocol) # Check for UDP/SCTP compatibility if (listener_protocol in (constants.PROTOCOL_UDP, lib_consts.PROTOCOL_SCTP) and self._is_tls_or_insert_header(listener_dict)): raise exceptions.ValidationException( detail=_("%s protocol listener does not " "support TLS.") % listener_protocol) # Check for TLS disabled if (not CONF.api_settings.allow_tls_terminated_listeners and listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS): raise exceptions.DisabledOption( value=constants.PROTOCOL_TERMINATED_HTTPS, option='protocol') # Check for certs when not TERMINATED_HTTPS if (listener_protocol != constants.PROTOCOL_TERMINATED_HTTPS and self._has_tls_container_refs(listener_dict)): raise exceptions.ValidationException(detail=_( "Certificate container references are only allowed on " "%s protocol listeners.") % constants.PROTOCOL_TERMINATED_HTTPS) # Make sure a base certificate exists if specifying a client ca if (listener_dict.get('client_ca_tls_certificate_id') and not (listener_dict.get('tls_certificate_id') or listener_dict.get('sni_containers'))): raise exceptions.ValidationException(detail=_( "An SNI or default certificate container reference must " "be provided with a client CA container reference.")) # Make sure a certificate container is specified for TERMINATED_HTTPS if (listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS and not (listener_dict.get('tls_certificate_id') or listener_dict.get('sni_containers'))): raise exceptions.ValidationException(detail=_( "An SNI or default certificate container reference must " "be provided for %s protocol listeners.") % constants.PROTOCOL_TERMINATED_HTTPS) # Make sure we have a client CA cert if they enable client auth if (listener_dict.get('client_authentication') != constants.CLIENT_AUTH_NONE and not listener_dict.get('client_ca_tls_certificate_id')): raise exceptions.ValidationException(detail=_( "Client authentication setting %s requires a client CA " "container reference.") % listener_dict.get('client_authentication')) # Make sure we have a client CA if they specify a CRL if (listener_dict.get('client_crl_container_id') and not listener_dict.get('client_ca_tls_certificate_id')): raise exceptions.ValidationException(detail=_( "A client authentication CA reference is required to " "specify a client authentication revocation list.")) # Check TLS cipher prohibit list if 'tls_ciphers' in listener_dict and listener_dict['tls_ciphers']: rejected_ciphers = validate.check_cipher_prohibit_list( listener_dict['tls_ciphers']) if rejected_ciphers: raise exceptions.ValidationException(detail=_( 'The following ciphers have been prohibited by an ' 'administrator: ' + ', '.join(rejected_ciphers))) # Validate the TLS containers sni_containers = listener_dict.pop('sni_containers', []) tls_refs = [sni['tls_container_id'] for sni in sni_containers] if listener_dict.get('tls_certificate_id'): tls_refs.append(listener_dict.get('tls_certificate_id')) self._validate_tls_refs(tls_refs) # Validate the client CA cert and optional client CRL if listener_dict.get('client_ca_tls_certificate_id'): self._validate_client_ca_and_crl_refs( listener_dict.get('client_ca_tls_certificate_id'), listener_dict.get('client_crl_container_id', None)) # Validate that the L4 protocol (UDP, TCP or SCTP) is not already used # for the specified protocol_port in this load balancer pcontext = pecan_request.context query_filter = { 'project_id': listener_dict['project_id'], 'load_balancer_id': listener_dict['load_balancer_id'], 'protocol_port': listener_dict['protocol_port'] } # Get listeners on the same load balancer that use the same # protocol port db_listeners = self.repositories.listener.get_all_API_list( lock_session, show_deleted=False, pagination_helper=pcontext.get(constants.PAGINATION_HELPER), **query_filter)[0] if db_listeners: l4_protocol = constants.L4_PROTOCOL_MAP[listener_protocol] # List supported protocols that share the same L4 protocol as our # new listener disallowed_protocols = [ p for p in constants.L4_PROTOCOL_MAP if constants.L4_PROTOCOL_MAP[p] == l4_protocol ] for db_l in db_listeners: # Check if l4 protocol ports conflict if db_l.protocol in disallowed_protocols: raise exceptions.DuplicateListenerEntry( protocol=db_l.protocol, port=listener_dict.get('protocol_port')) # Validate allowed CIDRs allowed_cidrs = listener_dict.get('allowed_cidrs', []) or [] lb_id = listener_dict.get('load_balancer_id') vip_db = self.repositories.vip.get( lock_session, load_balancer_id=lb_id) vip_address = vip_db.ip_address self._validate_cidr_compatible_with_vip(vip_address, allowed_cidrs) if listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS: # Validate TLS version list validate.check_tls_version_list(listener_dict['tls_versions']) # Validate TLS versions against minimum validate.check_tls_version_min(listener_dict['tls_versions']) # Validate ALPN protocol list validate.check_alpn_protocols(listener_dict['alpn_protocols']) try: db_listener = self.repositories.listener.create( lock_session, **listener_dict) if sni_containers: for container in sni_containers: sni_dict = {'listener_id': db_listener.id, 'tls_container_id': container.get( 'tls_container_id')} self.repositories.sni.create(lock_session, **sni_dict) # DB listener needs to be refreshed db_listener = self.repositories.listener.get( lock_session, id=db_listener.id) return db_listener except odb_exceptions.DBDuplicateEntry as de: column_list = ['load_balancer_id', 'protocol', 'protocol_port'] constraint_list = ['uq_listener_load_balancer_id_protocol_port'] if ['id'] == de.columns: raise exceptions.IDAlreadyExists() if (set(column_list) == set(de.columns) or set(constraint_list) == set(de.columns)): raise exceptions.DuplicateListenerEntry( protocol=listener_dict.get('protocol'), port=listener_dict.get('protocol_port')) except odb_exceptions.DBError as e: raise exceptions.InvalidOption(value=listener_dict.get('protocol'), option='protocol') from e
def _validate_pool_PUT(self, pool, db_pool): if db_pool.protocol in (constants.PROTOCOL_UDP, lib_consts.PROTOCOL_SCTP): self._validate_pool_request_for_udp_sctp(pool) else: if (pool.session_persistence and (pool.session_persistence.persistence_timeout or pool.session_persistence.persistence_granularity)): raise exceptions.ValidationException( detail=_("persistence_timeout and persistence_granularity " "is only for UDP protocol pools.")) if pool.session_persistence: sp_dict = pool.session_persistence.to_dict(render_unsets=False) validate.check_session_persistence(sp_dict) crl_ref = None # If we got a crl_ref and it's not unset, use it if (pool.crl_container_ref and pool.crl_container_ref != wtypes.Unset): crl_ref = pool.crl_container_ref # If we got Unset and a CRL exists in the DB, use the DB crl_ref elif (db_pool.crl_container_id and pool.crl_container_ref == wtypes.Unset): crl_ref = db_pool.crl_container_id ca_ref = None db_ca_ref = db_pool.ca_tls_certificate_id if pool.ca_tls_container_ref != wtypes.Unset: if not pool.ca_tls_container_ref and db_ca_ref and crl_ref: raise exceptions.ValidationException( detail=_("A CA reference cannot be removed when a " "certificate revocation list is present.")) if not pool.ca_tls_container_ref and not db_ca_ref and crl_ref: raise exceptions.ValidationException( detail=_("A CA reference is required to " "specify a certificate revocation list.")) if pool.ca_tls_container_ref: ca_ref = pool.ca_tls_container_ref elif db_ca_ref and pool.ca_tls_container_ref == wtypes.Unset: ca_ref = db_ca_ref elif crl_ref and not db_ca_ref: raise exceptions.ValidationException( detail=_("A CA reference is required to " "specify a certificate revocation list.")) if pool.tls_container_ref: self._validate_tls_refs([pool.tls_container_ref]) # Validate the client CA cert and optional client CRL if ca_ref: self._validate_client_ca_and_crl_refs(ca_ref, crl_ref) # Check TLS cipher prohibit list if pool.tls_ciphers: rejected_ciphers = validate.check_cipher_prohibit_list( pool.tls_ciphers) if rejected_ciphers: raise exceptions.ValidationException(detail=_( "The following ciphers have been prohibited by an " "administrator: " + ', '.join(rejected_ciphers))) if pool.tls_versions is not wtypes.Unset: # Validate TLS version list validate.check_tls_version_list(pool.tls_versions) # Validate TLS version against minimum validate.check_tls_version_min(pool.tls_versions) if pool.alpn_protocols is not wtypes.Unset: # Validate ALPN protocol list validate.check_alpn_protocols(pool.alpn_protocols)