def is_ssl_sasl_client_accepted(self, listener_port, tls_protocol): """ Attempts to connect a proton client to the management address on the given listener_port using the specific tls_protocol provided. If connection was established and accepted, returns True and False otherwise. :param listener_port: :param tls_protocol: :return: """ # Management address to connect using the given TLS protocol url = Url("amqps://0.0.0.0:%d/$management" % listener_port) # Preparing SSLDomain (client cert) and SASL authentication info domain = SSLDomain(SSLDomain.MODE_CLIENT) domain.set_credentials(self.ssl_file('client-certificate.pem'), self.ssl_file('client-private-key.pem'), 'client-password') # Enforcing given TLS protocol cproton.pn_ssl_domain_set_protocols(domain._domain, tls_protocol) # Try opening the secure and authenticated connection try: connection = BlockingConnection(url, sasl_enabled=True, ssl_domain=domain, allowed_mechs='PLAIN', user='******', password='******') except proton.ConnectionException: return False # TLS version provided was accepted connection.close() return True
def __init__(self, urls, certificate=None, private_key=None, trusted_certificates=None): self.urls = urls self.ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) self.ssl_domain.set_credentials(certificate, private_key, None) self.ssl_domain.set_trusted_ca_db(trusted_certificates) self.ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER)
def __init__(self, host="0.0.0.0", port=5672, router: Router = None): self._logger = logging.getLogger(self.__module__) self.port = port self.host = host self._router = router self._connection_options = { 'sasl_enabled': False, 'ssl_domain': None, 'allow_insecure_mechs': True, 'user': None, 'password': None } if self._router: # Enable SASL when credentials provided self._connection_options[ 'sasl_enabled'] = self._router.has_ssl_keys( ) or self._router.has_credentials() # If SSL certificates provided, use them if self._router.has_ssl_keys(): ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) ssl_domain.set_credentials(self._router.pem_file, self._router.key_file, self._router.key_password) self._connection_options['ssl_domain'] = ssl_domain # If User and Password provided if self._router.has_credentials(): self._connection_options['user'] = self._router.user self._connection_options['password'] = self._router.password
def opts_ssl_domain(opts, mode=SSLDomain.MODE_CLIENT): """Return proton.SSLDomain from command line options or None if no SSL options specified. @param opts: Parsed optoins including connection_options() """ certificate, key, trustfile, password, password_file, ssl_disable_peer_name_verify = opts.ssl_certificate,\ opts.ssl_key,\ opts.ssl_trustfile,\ opts.ssl_password,\ opts.ssl_password_file, \ opts.ssl_disable_peer_name_verify if not (certificate or trustfile): return None if password_file: password = get_password(password_file) domain = SSLDomain(mode) if trustfile: domain.set_trusted_ca_db(str(trustfile)) if ssl_disable_peer_name_verify: domain.set_peer_authentication(SSLDomain.VERIFY_PEER, str(trustfile)) else: domain.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, str(trustfile)) if certificate: domain.set_credentials(str(certificate), str(key), str(password)) return domain
class AuthService(MessagingHandler): def __init__(self, address): super(AuthService, self).__init__() self.address = address self.permissions = {} self.allow('admin', '*', ['send', 'recv']) self.allow('guest', 'foo', ['send', 'recv']) self.listener = None self.tmo = 0.1 # seconds self.stop_req = False self.acceptor = None self.ssl_domain = SSLDomain(SSLDomain.MODE_SERVER) self.ssl_domain.set_credentials(self.ssl_file('server-certificate.pem'), self.ssl_file('server-private-key.pem'), password="******") self.ssl_domain.set_trusted_ca_db(self.ssl_file('ca-certificate.pem')) def ssl_file(self, name): return os.path.join(system_test.DIR, 'ssl_certs', name) def allow(self, user, address, permissions): if not self.permissions.get(user): self.permissions[user] = {} self.permissions[user][address] = Array(UNDESCRIBED, Data.STRING, *permissions) def on_start(self, event): self.listener = event.container.listen(self.address, ssl_domain=self.ssl_domain) event.container.schedule(self.tmo, self) def stop(self): self.stop_req = True def on_connection_opening(self, event): if self.permissions.get(event.transport.user): event.connection.properties = { symbol('authenticated-identity'): "%s" % event.transport.user, symbol('address-authz'): self.permissions[event.transport.user] } else: event.connection.properties = { symbol('authenticated-identity'): "%s" % event.transport.user, symbol('address-authz'): {} } def on_timer_task(self, event): if self.stop_req: if self.listener: self.listener.close() else: sys.exit(0) else: event.reactor.schedule(self.tmo, self)
def run(self): try: ssl = SSLDomain(SSLDomain.MODE_CLIENT) ssl.set_credentials(str(self.options.accountPublicKey), str(self.options.accountPrivateKey), str("")) ssl.set_trusted_ca_db(str(self.options.brokerPublicKey)) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, trusted_CAs=str( self.options.brokerPublicKey)) connection = BlockingConnection(self.address, ssl_domain=ssl, heartbeat=60000) receiver = connection.create_receiver(self.broadcast_address, credit=self.capacity) while True: received_message = None try: received_message = receiver.receive( timeout=self.options.timeout) except Timeout, e: print("-I- No message received for ", self.options.timeout, " seconds") break self.message_counter += 1 print("-I- Received broadcast message: " + received_message.body) receiver.accept() print("-I- " + str(self.message_counter) + " messages received") connection.close()
def __init__(self, router_host, router_port, cert_dir): self.router_host = router_host self.router_port = router_port self.cert_dir = cert_dir ssl_domain = None allowed_mechs = [] sasl_enabled = False if self.cert_dir != None: ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) ssl_domain.set_trusted_ca_db( str(os.path.join(self.cert_dir, 'ca.crt'))) ssl_domain.set_credentials( str(os.path.join(self.cert_dir, "tls.crt")), str(os.path.join(self.cert_dir, "tls.key")), None) ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER) allowed_mechs = str("EXTERNAL") sasl_enabled = True self.client = SyncRequestResponse( BlockingConnection("amqps://" + self.router_host + ":" + str(self.router_port), 30, None, ssl_domain, allowed_mechs=allowed_mechs, sasl_enabled=sasl_enabled), "$management")
def __init__(self, address): super(AuthService, self).__init__() self.address = address self.permissions = {} self.allow('admin', '*', ['send', 'recv']) self.allow('guest', 'foo', ['send', 'recv']) self.listener = None self.tmo = 0.1 # seconds self.stop_req = False self.acceptor = None self.ssl_domain = SSLDomain(SSLDomain.MODE_SERVER) self.ssl_domain.set_credentials(self.ssl_file('server-certificate.pem'), self.ssl_file('server-private-key.pem'), password="******") self.ssl_domain.set_trusted_ca_db(self.ssl_file('ca-certificate.pem'))
def is_proto_allowed(self, listener_port, tls_protocol): """ Opens a simple proton client connection to the provided TCP port using a specific TLS protocol version and returns True in case connection was established and accepted or False otherwise. :param listener_port: TCP port number :param tls_protocol: TLSv1, TLSv1.1 or TLSv1.2 (string) :return: """ # Management address to connect using the given TLS protocol url = Url("amqps://0.0.0.0:%d/$management" % listener_port) # Preparing SSLDomain (client cert) and SASL authentication info domain = SSLDomain(SSLDomain.MODE_CLIENT) # Enforcing given TLS protocol cproton.pn_ssl_domain_set_protocols(domain._domain, tls_protocol) # Try opening the secure and authenticated connection try: connection = BlockingConnection(url, sasl_enabled=False, ssl_domain=domain, timeout=self.TIMEOUT) except proton.Timeout: return False except proton.ConnectionException: return False # TLS version provided was accepted connection.close() return True
def __init__(self, server, address, count, peer_hostname): super(Recv, self).__init__() self.server = server self.address = address self.expected = count self.received = 0 self.peer_hostname = peer_hostname self.ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT)
def run(self): try: ssl = SSLDomain(SSLDomain.MODE_CLIENT) ssl.set_credentials(str(self.options.accountPublicKey), str(self.options.accountPrivateKey), str("")) ssl.set_trusted_ca_db(str(self.options.brokerPublicKey)) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, trusted_CAs=str(self.options.brokerPublicKey)) connection = BlockingConnection(self.address, ssl_domain=ssl, heartbeat=60000) receiver = connection.create_receiver(self.broadcast_address, credit=self.capacity) while True: received_message = None try: received_message = receiver.receive(timeout=self.options.timeout) except Timeout, e: print("-I- No message received for ", self.options.timeout, " seconds") break self.message_counter += 1 print("-I- Received broadcast message: " + received_message.body) receiver.accept() print("-I- " + str(self.message_counter) + " messages received") connection.close()
def isSSLPresent(): """ True if a suitable SSL library is available. """ try: xxx = SSLDomain(SSLDomain.MODE_CLIENT) return True except SSLUnavailable, e: # SSL libraries not installed return False
def __init__(self, router_host, router_port, cert_dir): self.router_host = router_host self.router_port = router_port self.cert_dir = cert_dir self.ssl_domain = None self.allowed_mechs = [] self.sasl_enabled = False if self.cert_dir != None: self.ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) self.ssl_domain.set_trusted_ca_db( str(os.path.join(self.cert_dir, 'ca.crt'))) self.ssl_domain.set_credentials( str(os.path.join(self.cert_dir, "tls.crt")), str(os.path.join(self.cert_dir, "tls.key")), None) self.ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER) self.allowed_mechs = str("EXTERNAL") self.sasl_enabled = True
def __init__(self, server, address, messages, peer_hostname): super(Send, self).__init__() self.server = server self.address = address self.sent = 0 self.confirmed = 0 self.total = messages self.peer_hostname = peer_hostname self.ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT)
class SSLConfig(object): def __init__(self): self.client = SSLDomain(SSLDomain.MODE_CLIENT) self.server = SSLDomain(SSLDomain.MODE_SERVER) def set_credentials(self, cert_file, key_file, password): self.client.set_credentials(cert_file, key_file, password) self.server.set_credentials(cert_file, key_file, password) def set_trusted_ca_db(self, certificate_db): self.client.set_trusted_ca_db(certificate_db) self.server.set_trusted_ca_db(certificate_db)
def opts_ssl_domain(opts, mode=SSLDomain.MODE_CLIENT): """Return proton.SSLDomain from command line options or None if no SSL options specified. @param opts: Parsed optoins including connection_options() """ certificate, key, trustfile, password = opts.ssl_certificate, opts.ssl_key, opts.ssl_trustfile, opts.ssl_password if not (certificate or trustfile): return None domain = SSLDomain(mode) if trustfile: domain.set_trusted_ca_db(trustfile) domain.set_peer_authentication(SSLDomain.VERIFY_PEER, trustfile) if certificate: domain.set_credentials(certificate, key, password) return domain
def on_start(self, event): self.container = event.container ssl = SSLDomain(SSLDomain.MODE_CLIENT) ssl.set_credentials(str(self.options.accountPublicKey), str(self.options.accountPrivateKey), str("")) ssl.set_trusted_ca_db(str(self.options.brokerPublicKey)) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, trusted_CAs=str(self.options.brokerPublicKey)) conn = event.container.connect(self.address, ssl_domain=ssl, heartbeat=60000, allowed_mechs=str("EXTERNAL")) event.container.create_receiver(conn, self.broadcast_address)
def on_start(self, event): ssl_domain = SSLDomain(mode=SSLDomain.MODE_CLIENT) ssl_domain.set_trusted_ca_db(env.certificate_db) ssl_domain.set_credentials(env.cert_file, env.key_file, env.password) conn = event.container.connect(urls=self.urls, ssl_domain=ssl_domain) #, user=env.username, password=env.password) # at-least-once delivery semantics for message delivery event.container.create_sender(conn, self.address, options=AtLeastOnce()) self.start = time.time()
def on_start(self, event): self.container = event.container ssl = SSLDomain(SSLDomain.MODE_CLIENT) ssl.set_credentials(str(self.options.accountPublicKey), str(self.options.accountPrivateKey), str("")) ssl.set_trusted_ca_db(str(self.options.brokerPublicKey)) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, trusted_CAs=str( self.options.brokerPublicKey)) conn = event.container.connect(self.address, ssl_domain=ssl, heartbeat=60000, allowed_mechs=str("EXTERNAL")) event.container.create_receiver(conn, self.broadcast_address)
def on_start(self, event): ssl_domain = SSLDomain(mode=SSLDomain.MODE_CLIENT) ssl_domain.set_trusted_ca_db(env.certificate_db) ssl_domain.set_credentials(env.cert_file, env.key_file, env.password) conn = event.container.connect( urls=self.urls, ssl_domain=ssl_domain ) #, user=env.username, password=env.password) event.container.create_receiver(conn, self.address) self.start = time.time()
def on_start(self, event): # Write the UMB cert and key out to disk but immediately delete # them once the connection has been established. There may be a # better way to do this if we can be assured of a secure directory. temp_dir = tempfile.mkdtemp() mktemp = functools.partial(tempfile.NamedTemporaryFile, delete=False, dir=temp_dir) try: temp_cert = mktemp() temp_key = mktemp() temp_cert.write(self.certificate) temp_key.write(self.key) temp_cert.close() temp_key.close() domain = SSLDomain(SSLDomain.MODE_CLIENT) domain.set_credentials(temp_cert.name, temp_key.name, b'') conn = event.container.connect(self.server, ssl_domain=domain) finally: shutil.rmtree(temp_dir) event.container.create_sender(conn, "topic://" + self.topic)
def on_start(self, event): self.log.debug('Container starting') event.container.connected = False if self.conf.has_option('broker', 'cert') and self.conf.has_option('broker', 'cacert'): ssl = SSLDomain(SSLDomain.MODE_CLIENT) cert = self.conf.get('broker', 'cert') ssl.set_credentials(cert, cert, None) ssl.set_trusted_ca_db(self.conf.get('broker', 'cacert')) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER) else: ssl = None self.log.debug('connecting to %s', self.url) event.container.connect(url=self.url, reconnect=False, ssl_domain=ssl) connect_timeout = self.conf.getint('broker', 'connect_timeout') self.connect_task = event.container.schedule(connect_timeout, self) send_timeout = self.conf.getint('broker', 'send_timeout') self.timeout_task = event.container.schedule(send_timeout, self)
def on_start(self, event): log.debug('Container starting') event.container.connected = False event.container.error_msgs = [] if 'cert' in self.conf and 'key' in self.conf and 'cacert' in self.conf: ssl = SSLDomain(SSLDomain.MODE_CLIENT) ssl.set_credentials(self.conf['cert'], self.conf['key'], None) ssl.set_trusted_ca_db(self.conf['cacert']) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER) else: ssl = None log.debug('connecting to %s', self.url) event.container.connect(url=self.url, reconnect=False, ssl_domain=ssl) connect_timeout = self.conf['connect_timeout'] self.connect_task = event.container.schedule(connect_timeout, self) send_timeout = self.conf['send_timeout'] self.timeout_task = event.container.schedule(send_timeout, self)
def create_ssl_domain(self, ssl_options_dict, mode=SSLDomain.MODE_CLIENT): """Return proton.SSLDomain from command line options or None if no SSL options specified. @param opts: Parsed optoins including connection_options() """ certificate, key, trustfile, password = ssl_options_dict.get('ssl-certificate'), \ ssl_options_dict.get('ssl-key'), \ ssl_options_dict.get('ssl-trustfile'), \ ssl_options_dict.get('ssl-password') if not (certificate or trustfile): return None domain = SSLDomain(mode) if trustfile: domain.set_trusted_ca_db(str(trustfile)) domain.set_peer_authentication(SSLDomain.VERIFY_PEER, str(trustfile)) if certificate: domain.set_credentials(str(certificate), str(key), str(password)) return domain
def run(self): try: ssl = SSLDomain(SSLDomain.MODE_CLIENT) ssl.set_credentials(str(self.options.accountPublicKey), str(self.options.accountPrivateKey), str("")) ssl.set_trusted_ca_db(str(self.options.brokerPublicKey)) ssl.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, trusted_CAs=str(self.options.brokerPublicKey)) connection = BlockingConnection(self.address, ssl_domain=ssl, heartbeat=60000) receiver = connection.create_receiver(self.response_address) sender = connection.create_sender(self.request_address) message = Message(body="<FIXML>...</FIXML>", reply_to=self.reply_adress) print("-I- Sending request message: " + message.body) sender.send(message); try: received_message = receiver.receive(timeout=self.options.timeout) print("-I- Received response message: " + received_message.body) self.message_counter += 1 receiver.accept() except Timeout, e: print("-I- No message received for ", self.options.timeout, " seconds") connection.close()
def ssl_domain(connector): """ Get the ssl domain using the broker settings. :param connector: A broker. :type connector: Connector :return: The populated domain. :rtype: SSLDomain :raise: SSLException :raise: ValueError """ domain = None if connector.use_ssl(): connector.ssl.validate() domain = SSLDomain(SSLDomain.MODE_CLIENT) domain.set_trusted_ca_db(connector.ssl.ca_certificate) domain.set_credentials( connector.ssl.client_certificate, connector.ssl.client_key or connector.ssl.client_certificate, None) if connector.ssl.host_validation: mode = SSLDomain.VERIFY_PEER_NAME else: mode = SSLDomain.VERIFY_PEER domain.set_peer_authentication(mode) return domain
class AMQConsumer(object): def __init__(self, urls, certificate=None, private_key=None, trusted_certificates=None): self.urls = urls self.ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) self.ssl_domain.set_credentials(certificate, private_key, None) self.ssl_domain.set_trusted_ca_db(trusted_certificates) self.ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER) def consume(self, address, callback, data=None, selector=None, auto_accept=True, subscription_name=None): """Start to consume messages :param str address: refer to ``ReceiverHandler.__init__``. :param callable callback: refer to ``ReceiverHandler.__init__``. :param data: refer to ``ReceiverHandler.__init__``. :param str selector: refer to ``ReceiverHandler.__init__``. :param bool auto_accept: whether to accept message automatically. Defaults to ``True``, that is to use default behaivor of ``proton.MessagingHandler``. Otherwise, received message will be accepted according to return value from ``callback``. For detailed information, refer to ``ReceiverHandler.on_message``. :param str subscription_name: name to use to identify the durable subscription. It will also be used as the client ID. If it is None, the subscription will be non-durable, and the client ID will be random. :return: the return value of ``callback`` once a message is handled. Or, the value returned when the last time ``callback`` is called. """ handler = ReceiverHandler(self.urls, address, callback, data=data, selector=selector, ssl_domain=self.ssl_domain, auto_accept=auto_accept, subscription_name=subscription_name) Container(handler).run() return handler.result
class RouterCollector(object): def __init__(self, router_host, router_port, cert_dir): self.router_host = router_host self.router_port = router_port self.cert_dir = cert_dir self.ssl_domain = None self.allowed_mechs = [] self.sasl_enabled = False if self.cert_dir != None: self.ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) self.ssl_domain.set_trusted_ca_db( str(os.path.join(self.cert_dir, 'ca.crt'))) self.ssl_domain.set_credentials( str(os.path.join(self.cert_dir, "tls.crt")), str(os.path.join(self.cert_dir, "tls.key")), None) self.ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER) self.allowed_mechs = str("EXTERNAL") self.sasl_enabled = True def create_collector_map(self): metrics = [ MetricCollector('connectionCount', 'Number of connections to router', ['container']), MetricCollector('connectionCount', 'Total number of connections to router', ['routerId'], id="totalConnectionCount"), MetricCollector('linkCount', 'Number of links to router', ['address']), MetricCollector('linkCount', 'Number of consumers to router', ['address'], id="consumerCount", filter={"linkDir": "out"}), MetricCollector('linkCount', 'Number of producers to router', ['address'], id="producerCount", filter={"linkDir": "in"}), MetricCollector('linkCount', 'Total number of links to router', ['routerId'], id="totalLinkCount"), MetricCollector('addrCount', 'Number of addresses defined in router', ['routerId']), MetricCollector('autoLinkCount', 'Number of auto links defined in router', ['routerId']), MetricCollector('linkRouteCount', 'Number of link routers defined in router', ['routerId']), MetricCollector('unsettledCount', 'Number of unsettled messages', ['address']), MetricCollector('deliveryCount', 'Number of delivered messages', ['address'], "COUNTER"), MetricCollector('releasedCount', 'Number of released messages', ['address'], "COUNTER"), MetricCollector('rejectedCount', 'Number of rejected messages', ['address'], "COUNTER"), MetricCollector('acceptedCount', 'Number of accepted messages', ['address'], "COUNTER"), MetricCollector('undeliveredCount', 'Number of undelivered messages', ['address']), MetricCollector('capacity', 'Capacity of link', ['address']) ] m = {} for metric in metrics: m[metric.id] = metric return m def create_entity_map(self, collector_map): return { self.get_router: [ collector_map['totalConnectionCount'], collector_map['totalLinkCount'], collector_map['addrCount'], collector_map['autoLinkCount'], collector_map['linkRouteCount'] ], self.get_connections: [collector_map['connectionCount']], self.get_links: [ collector_map['linkCount'], collector_map['unsettledCount'], collector_map['deliveryCount'], collector_map['releasedCount'], collector_map['rejectedCount'], collector_map['acceptedCount'], collector_map['undeliveredCount'], collector_map['capacity'], collector_map['consumerCount'], collector_map['producerCount'] ] } def get_router(self, client): return self.collect_metric('org.apache.qpid.dispatch.router', client) def get_links(self, client): links = self.collect_metric('org.apache.qpid.dispatch.router.link', client) if links == None: return links connections = self.get_connections(client) if connections == None: return connections links.add_field_from("address", "owningAddr", clean_address) links.add_field("linkCount", 1) links.add_field_from( "container", "connectionId", lambda connection_id: get_container_from_connections( connection_id, connections)) return links def get_connections(self, client): response = self.collect_metric('org.apache.qpid.dispatch.connection', client) if response == None: return response response.add_field("connectionCount", 1) return response def collect(self): collector_map = self.create_collector_map() fetcher_map = self.create_entity_map(collector_map) connection = None print("Collecting metrics") try: connection = BlockingConnection("amqps://" + self.router_host + ":" + str(self.router_port), 30, None, self.ssl_domain, allowed_mechs=self.allowed_mechs, sasl_enabled=self.sasl_enabled, container_id="router-metrics") client = SyncRequestResponse(connection, "$management") for fetcher in fetcher_map: response = fetcher(client) if response != None: for collector in fetcher_map[fetcher]: for entity in response.get_results(): if response.contains(entity, collector.filter): labels = [] for l in collector.labels: label_idx = response.get_index(l) if label_idx != None and entity[ label_idx] != None: labels.append(entity[label_idx]) else: labels.append("") value = entity[response.get_index( collector.name)] collector.add(labels, int(value)) finally: if connection != None: connection.close() for collector in collector_map.itervalues(): yield collector.metric() def collect_metric(self, entityType, client): result = None try: properties = {} properties["entityType"] = entityType properties["operation"] = "QUERY" properties["name"] = "self" message = Message(body=None, properties=properties) response = client.call(message) if response != None: result = RouterResponse(response) except NameError as e: print("Error querying router for metrics: %s" % e) return result
def __init__(self): self.client = SSLDomain(SSLDomain.MODE_CLIENT) self.server = SSLDomain(SSLDomain.MODE_SERVER)
class RouterTestSslClient(RouterTestSslBase): """ Starts a router with multiple listeners, all of them using an sslProfile. Then it runs multiple tests to validate that only the allowed protocol versions are being accepted through the related listener. """ # Listener ports for each TLS protocol definition PORT_TLS1 = 0 PORT_TLS11 = 0 PORT_TLS12 = 0 PORT_TLS13 = 0 PORT_TLS1_TLS11 = 0 PORT_TLS1_TLS12 = 0 PORT_TLS11_TLS12 = 0 PORT_TLS_ALL = 0 PORT_TLS_SASL = 0 PORT_SSL3 = 0 TIMEOUT = 3 # If using OpenSSL 1.1 or greater, TLSv1.2 is always being allowed OPENSSL_OUT_VER = None try: OPENSSL_VER_1_1_GT = ssl.OPENSSL_VERSION_INFO[:2] >= (1, 1) except AttributeError: OPENSSL_VER_1_1_GT = False # If still False, try getting it from "openssl version" (command output) # The version from ssl.OPENSSL_VERSION_INFO reflects OpenSSL version in which # Python was compiled with, not the one installed in the system. if not OPENSSL_VER_1_1_GT: print("Python libraries SSL Version < 1.1") try: p = Popen(['openssl', 'version'], stdout=PIPE, universal_newlines=True) openssl_out = p.communicate()[0] m = re.search(r'[0-9]+\.[0-9]+\.[0-9]+', openssl_out) OPENSSL_OUT_VER = m.group(0) OPENSSL_VER_1_1_GT = StrictVersion( OPENSSL_OUT_VER) >= StrictVersion('1.1') print("OpenSSL Version found = %s" % OPENSSL_OUT_VER) except: pass # Following variables define TLS versions allowed by openssl OPENSSL_MIN_VER = 0 OPENSSL_MAX_VER = 9999 OPENSSL_ALLOW_TLSV1 = True OPENSSL_ALLOW_TLSV1_1 = True OPENSSL_ALLOW_TLSV1_2 = True OPENSSL_ALLOW_TLSV1_3 = False # Test if OpenSSL has TLSv1_3 OPENSSL_HAS_TLSV1_3 = False if OPENSSL_VER_1_1_GT: try: ssl.TLSVersion.TLSv1_3 OPENSSL_HAS_TLSV1_3 = True except: pass # Test if Proton supports TLSv1_3 try: dummydomain = SSLDomain(SSLDomain.MODE_CLIENT) PROTON_HAS_TLSV1_3 = cproton.PN_OK == cproton.pn_ssl_domain_set_protocols( dummydomain._domain, "TLSv1.3") print("TLSV1_3? Proton has: %s, OpenSSL has: %s" % (PROTON_HAS_TLSV1_3, OPENSSL_HAS_TLSV1_3)) except SSLUnavailable: PROTON_HAS_TLSV1_3 = False # When using OpenSSL >= 1.1 and python >= 3.7, we can retrieve OpenSSL min and max protocols if OPENSSL_VER_1_1_GT: if sys.version_info >= (3, 7): if OPENSSL_HAS_TLSV1_3 and not PROTON_HAS_TLSV1_3: # If OpenSSL has 1.3 but proton won't let us turn it on and off then # this test fails because v1.3 runs unexpectedly. RouterTestSslBase.DISABLE_SSL_TESTING = True RouterTestSslBase.DISABLE_REASON = "Proton version does not support TLSv1.3 but OpenSSL does" else: OPENSSL_CTX = ssl.create_default_context() OPENSSL_MIN_VER = OPENSSL_CTX.minimum_version OPENSSL_MAX_VER = OPENSSL_CTX.maximum_version if OPENSSL_CTX.maximum_version > 0 else 9999 OPENSSL_ALLOW_TLSV1 = OPENSSL_MIN_VER <= ssl.TLSVersion.TLSv1 <= OPENSSL_MAX_VER OPENSSL_ALLOW_TLSV1_1 = OPENSSL_MIN_VER <= ssl.TLSVersion.TLSv1_1 <= OPENSSL_MAX_VER OPENSSL_ALLOW_TLSV1_2 = OPENSSL_MIN_VER <= ssl.TLSVersion.TLSv1_2 <= OPENSSL_MAX_VER OPENSSL_ALLOW_TLSV1_3 = OPENSSL_HAS_TLSV1_3 and PROTON_HAS_TLSV1_3 \ and OPENSSL_MIN_VER <= ssl.TLSVersion.TLSv1_3 <= OPENSSL_MAX_VER else: # At this point we are not able to precisely determine what are the minimum and maximum # TLS versions allowed in the system, so tests will be disabled RouterTestSslBase.DISABLE_SSL_TESTING = True RouterTestSslBase.DISABLE_REASON = "OpenSSL >= 1.1 but Python < 3.7 - Unable to determine MinProtocol" else: if OPENSSL_HAS_TLSV1_3 and not PROTON_HAS_TLSV1_3: # If OpenSSL has 1.3 but proton won't let us turn it on and off then # this test fails because v1.3 runs unexpectedly. RouterTestSslBase.DISABLE_SSL_TESTING = True RouterTestSslBase.DISABLE_REASON = "Proton version does not support TLSv1.3 but OpenSSL does" @classmethod def setUpClass(cls): """ Prepares a single router with multiple listeners, each one associated with a particular sslProfile and each sslProfile has its own specific set of allowed protocols. """ super(RouterTestSslClient, cls).setUpClass() cls.routers = [] if SASL.extended(): router = ('router', { 'id': 'QDR.A', 'mode': 'interior', 'saslConfigName': 'tests-mech-PLAIN', 'saslConfigDir': os.getcwd() }) # Generate authentication DB super(RouterTestSslClient, cls).create_sasl_files() else: router = ('router', {'id': 'QDR.A', 'mode': 'interior'}) # Saving listener ports for each TLS definition cls.PORT_TLS1 = cls.tester.get_port() cls.PORT_TLS11 = cls.tester.get_port() cls.PORT_TLS12 = cls.tester.get_port() cls.PORT_TLS13 = cls.tester.get_port() cls.PORT_TLS1_TLS11 = cls.tester.get_port() cls.PORT_TLS1_TLS12 = cls.tester.get_port() cls.PORT_TLS11_TLS12 = cls.tester.get_port() cls.PORT_TLS_ALL = cls.tester.get_port() cls.PORT_TLS_SASL = cls.tester.get_port() cls.PORT_SSL3 = cls.tester.get_port() conf = [ router, # TLSv1 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS1, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls1' }), # TLSv1.1 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS11, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls11' }), # TLSv1.2 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS12, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls12' }), # TLSv1 and TLSv1.1 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS1_TLS11, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls1-tls11' }), # TLSv1 and TLSv1.2 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS1_TLS12, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls1-tls12' }), # TLSv1.1 and TLSv1.2 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS11_TLS12, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls11-tls12' }), # All TLS versions ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS_ALL, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls-all' }), # Invalid protocol version ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_SSL3, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-ssl3' }) ] # Adding SASL listener only when SASL is available if SASL.extended(): conf += [ # TLS 1 and 1.2 with SASL PLAIN authentication for proton client validation ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS_SASL, 'authenticatePeer': 'yes', 'saslMechanisms': 'PLAIN', 'requireSsl': 'yes', 'requireEncryption': 'yes', 'sslProfile': 'ssl-profile-tls1-tls12' }) ] # Adding SSL profiles conf += [ # SSL Profile for TLSv1 ('sslProfile', {'name': 'ssl-profile-tls1', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'TLSv1', 'password': '******'}), # SSL Profile for TLSv1.1 ('sslProfile', {'name': 'ssl-profile-tls11', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'TLSv1.1', 'password': '******'}), # SSL Profile for TLSv1.2 ('sslProfile', {'name': 'ssl-profile-tls12', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'TLSv1.2', 'password': '******'}), # SSL Profile for TLSv1 and TLSv1.1 ('sslProfile', {'name': 'ssl-profile-tls1-tls11', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'TLSv1 TLSv1.1', 'password': '******'}), # SSL Profile for TLSv1 and TLSv1.2 ('sslProfile', {'name': 'ssl-profile-tls1-tls12', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'TLSv1 TLSv1.2', 'password': '******'}), # SSL Profile for TLSv1.1 and TLSv1.2 ('sslProfile', {'name': 'ssl-profile-tls11-tls12', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'TLSv1.1 TLSv1.2', 'password': '******'}), # SSL Profile for all TLS versions (protocols element not defined) ('sslProfile', {'name': 'ssl-profile-tls-all', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'password': '******'}), # SSL Profile for invalid protocol version SSLv23 ('sslProfile', {'name': 'ssl-profile-ssl3', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'ciphers': 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ 'DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS', 'protocols': 'SSLv23', 'password': '******'}) ] if cls.OPENSSL_ALLOW_TLSV1_3: conf += [ # TLSv1.3 only ('listener', { 'host': '0.0.0.0', 'role': 'normal', 'port': cls.PORT_TLS13, 'authenticatePeer': 'no', 'sslProfile': 'ssl-profile-tls13' }), # SSL Profile for TLSv1.3 ('sslProfile', { 'name': 'ssl-profile-tls13', 'caCertFile': cls.ssl_file('ca-certificate.pem'), 'certFile': cls.ssl_file('server-certificate.pem'), 'privateKeyFile': cls.ssl_file('server-private-key.pem'), 'protocols': 'TLSv1.3', 'password': '******' }) ] config = Qdrouterd.Config(conf) cls.routers.append(cls.tester.qdrouterd("A", config, wait=False)) cls.routers[0].wait_ports() def get_allowed_protocols(self, listener_port): """ Loops through TLSv1, TLSv1.1 and TLSv1.2 and attempts to connect to the listener_port using each version. The result is a boolean list with results in respective order for TLSv1 [0], TLSv1.1 [1] and TLSv1.2 [2]. :param listener_port: :return: """ results = [] for proto in ['TLSv1', 'TLSv1.1', 'TLSv1.2']: results.append(self.is_proto_allowed(listener_port, proto)) if self.OPENSSL_ALLOW_TLSV1_3: results.append(self.is_proto_allowed(listener_port, 'TLSv1.3')) else: results.append(False) return results def is_proto_allowed(self, listener_port, tls_protocol): """ Opens a simple proton client connection to the provided TCP port using a specific TLS protocol version and returns True in case connection was established and accepted or False otherwise. :param listener_port: TCP port number :param tls_protocol: TLSv1, TLSv1.1 or TLSv1.2 (string) :return: """ # Management address to connect using the given TLS protocol url = Url("amqps://0.0.0.0:%d/$management" % listener_port) # Preparing SSLDomain (client cert) and SASL authentication info domain = SSLDomain(SSLDomain.MODE_CLIENT) # Enforcing given TLS protocol cproton.pn_ssl_domain_set_protocols(domain._domain, tls_protocol) # Try opening the secure and authenticated connection try: connection = BlockingConnection(url, sasl_enabled=False, ssl_domain=domain, timeout=self.TIMEOUT) except proton.Timeout: return False except proton.ConnectionException: return False except: return False # TLS version provided was accepted connection.close() return True def is_ssl_sasl_client_accepted(self, listener_port, tls_protocol): """ Attempts to connect a proton client to the management address on the given listener_port using the specific tls_protocol provided. If connection was established and accepted, returns True and False otherwise. :param listener_port: :param tls_protocol: :return: """ # Management address to connect using the given TLS protocol url = Url("amqps://0.0.0.0:%d/$management" % listener_port) # Preparing SSLDomain (client cert) and SASL authentication info domain = SSLDomain(SSLDomain.MODE_CLIENT) domain.set_credentials(self.ssl_file('client-certificate.pem'), self.ssl_file('client-private-key.pem'), 'client-password') # Enforcing given TLS protocol cproton.pn_ssl_domain_set_protocols(domain._domain, tls_protocol) # Try opening the secure and authenticated connection try: connection = BlockingConnection(url, sasl_enabled=True, ssl_domain=domain, allowed_mechs='PLAIN', user='******', password='******') except proton.ConnectionException: return False # TLS version provided was accepted connection.close() return True def get_expected_tls_result(self, expected_results): """ Expects a list with three boolean elements, representing TLSv1, TLSv1.1 and TLSv1.2 (in the respective order). When using OpenSSL >= 1.1.x, allowance of a given TLS version is based on MinProtocol / MaxProtocol definitions. It is also important to mention that TLSv1.2 is being allowed even when not specified in a listener when using OpenSSL >= 1.1.x. :param expected_results: :return: """ (tlsv1, tlsv1_1, tlsv1_2, tlsv1_3) = expected_results return [ self.OPENSSL_ALLOW_TLSV1 and tlsv1, self.OPENSSL_ALLOW_TLSV1_1 and tlsv1_1, self.OPENSSL_ALLOW_TLSV1_2 and tlsv1_2, self.OPENSSL_ALLOW_TLSV1_3 and tlsv1_3 ] @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls1_only(self): """ Expects TLSv1 only is allowed """ self.assertEqual( self.get_expected_tls_result([True, False, False, False]), self.get_allowed_protocols(self.PORT_TLS1)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls11_only(self): """ Expects TLSv1.1 only is allowed """ self.assertEqual( self.get_expected_tls_result([False, True, False, False]), self.get_allowed_protocols(self.PORT_TLS11)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls12_only(self): """ Expects TLSv1.2 only is allowed """ self.assertEqual( self.get_expected_tls_result([False, False, True, False]), self.get_allowed_protocols(self.PORT_TLS12)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls13_only(self): """ Expects TLSv1.3 only is allowed """ self.assertEqual( self.get_expected_tls_result([False, False, False, True]), self.get_allowed_protocols(self.PORT_TLS13)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls1_tls11_only(self): """ Expects TLSv1 and TLSv1.1 only are allowed """ self.assertEqual( self.get_expected_tls_result([True, True, False, False]), self.get_allowed_protocols(self.PORT_TLS1_TLS11)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls1_tls12_only(self): """ Expects TLSv1 and TLSv1.2 only are allowed """ self.assertEqual( self.get_expected_tls_result([True, False, True, False]), self.get_allowed_protocols(self.PORT_TLS1_TLS12)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls11_tls12_only(self): """ Expects TLSv1.1 and TLSv1.2 only are allowed """ self.assertEqual( self.get_expected_tls_result([False, True, True, False]), self.get_allowed_protocols(self.PORT_TLS11_TLS12)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_tls_all(self): """ Expects all supported versions: TLSv1, TLSv1.1, TLSv1.2 and TLSv1.3 to be allowed """ self.assertEqual( self.get_expected_tls_result([True, True, True, True]), self.get_allowed_protocols(self.PORT_TLS_ALL)) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING, RouterTestSslBase.DISABLE_REASON) def test_ssl_invalid(self): """ Expects connection is rejected as SSL is no longer supported """ self.assertEqual(False, self.is_proto_allowed(self.PORT_SSL3, 'SSLv3')) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING or not SASL.extended(), "Cyrus library not available. skipping test") def test_ssl_sasl_client_valid(self): """ Attempts to connect a Proton client using a valid SASL authentication info and forcing the TLS protocol version, which should be accepted by the listener. :return: """ if not SASL.extended(): self.skipTest("Cyrus library not available. skipping test") exp_tls_results = self.get_expected_tls_result( [True, False, True, False]) self.assertEqual( exp_tls_results[0], self.is_ssl_sasl_client_accepted(self.PORT_TLS_SASL, "TLSv1")) self.assertEqual( exp_tls_results[2], self.is_ssl_sasl_client_accepted(self.PORT_TLS_SASL, "TLSv1.2")) @SkipIfNeeded(RouterTestSslBase.DISABLE_SSL_TESTING or not SASL.extended(), "Cyrus library not available. skipping test") def test_ssl_sasl_client_invalid(self): """ Attempts to connect a Proton client using a valid SASL authentication info and forcing the TLS protocol version, which should be rejected by the listener. :return: """ if not SASL.extended(): self.skipTest("Cyrus library not available. skipping test") exp_tls_results = self.get_expected_tls_result( [True, False, True, False]) self.assertEqual( exp_tls_results[1], self.is_ssl_sasl_client_accepted(self.PORT_TLS_SASL, "TLSv1.1"))
parser.add_option("-a", "--address", default="amqps://127.0.0.1:5672", help="address to which messages are sent (default %default)") parser.add_option("-m", "--messages", type="int", default=100, help="number of messages to send (default %default)") parser.add_option("-t", "--ssl-trustfile", default="/home/gmurthy/opensource/dispatch/tests/config-2/ca-certificate.pem", help="The trust file") parser.add_option("-c", "--ssl-certificate", default="/home/gmurthy/opensource/dispatch/tests/config-2/client-certificate.pem", help="The cert file") parser.add_option("-k", "--ssl-key", default="/home/gmurthy/opensource/dispatch/tests/config-2/client-private-key.pem", help="The trust key") parser.add_option("-p", "--ssl-password", default="client-password", help="The trust file") opts, args = parser.parse_args() try: ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) ssl_domain.set_trusted_ca_db(str(opts.ssl_trustfile)) ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER, str(opts.ssl_trustfile)) # for client authentication and private key password protected #ssl_domain.set_credentials(str(opts.ssl_certificate), str(opts.ssl_key), str(opts.ssl_password)) # for client authentication and private key NOT password protected #ssl_domain.set_credentials(str(opts.ssl_certificate), str(opts.ssl_key), None) Container(Send(opts.address, opts.messages, ssl_domain=ssl_domain)).run() except KeyboardInterrupt: pass
def _has_ssl(): try: SSLDomain(SSLDomain.MODE_CLIENT) return True except SSLUnavailable: return False
"Can not find numeric priority in '%s'" % lines[i]) priority = int(pri[0]) # make sure the priority is from 0 to 9 self.assertTrue(priority >= 0, "Priority was less than 0") self.assertTrue(priority <= 9, "Priority was greater than 9") # mark this priority as present priorities[priority] = True # make sure that all priorities are present in the list (currently 0-9) self.assertEqual(len(priorities.keys()), 10, "Not all priorities are present") try: SSLDomain(SSLDomain.MODE_CLIENT) class QdstatSslTest(system_test.TestCase): """Test qdstat tool output""" @staticmethod def ssl_file(name): return os.path.join(system_test.DIR, 'ssl_certs', name) @staticmethod def sasl_path(): return os.path.join(system_test.DIR, 'sasl_configs') @classmethod def setUpClass(cls): super(QdstatSslTest, cls).setUpClass() # Write SASL configuration file: