@@ -596,6 +596,68 @@ def balance_load(self, raw_socket: socket.socket) -> socket.socket:
596596 self ._logger .warning (no_load_balancing )
597597
598598 return raw_socket
599+ def enable_ssl (self , raw_socket , ssl_options ):
600+ # Send SSL request and read server response
601+ self ._logger .debug ('=> %s' , messages .SslRequest ())
602+ raw_socket .sendall (messages .SslRequest ().get_message ())
603+ response = raw_socket .recv (1 )
604+ self ._logger .debug ('<= SslResponse: %s' , response )
605+
606+ if response == b'S' :
607+ self ._logger .info ('Enabling SSL' )
608+ try :
609+ server_host = self .address_list .peek_host ()
610+ if server_host is None : # This should not happen
611+ msg = 'Cannot get the connected server host while enabling SSL'
612+ self ._logger .error (msg )
613+ raise errors .ConnectionError (msg )
614+
615+ if isinstance (ssl_options , ssl .SSLContext ):
616+ context = ssl_options
617+ else :
618+ context = ssl .create_default_context ()
619+
620+ # Load user-provided certificates if available
621+ cafile = self .options .get ('tls_cafile' )
622+ certfile = self .options .get ('tls_certfile' )
623+ keyfile = self .options .get ('tls_keyfile' )
624+
625+ if cafile :
626+ self ._logger .info (f'Loading CA file: { cafile } ' )
627+ context .load_verify_locations (cafile )
628+ if certfile and keyfile :
629+ self ._logger .info (f'Loading client cert: { certfile } and key: { keyfile } ' )
630+ context .load_cert_chain (certfile = certfile , keyfile = keyfile )
631+ else :
632+ self ._logger .warning ('Client certificate/key not provided; connection may fail if server requires mutual TLS.' )
633+
634+ # Allow automatic negotiation between TLS 1.2 and 1.3
635+ context .minimum_version = ssl .TLSVersion .TLSv1_2
636+ context .maximum_version = ssl .TLSVersion .TLSv1_3
637+
638+ # Enable ALPN for Vertica protocol negotiation
639+ try :
640+ context .set_alpn_protocols (['http/1.1' , 'vertica' , 'postgresql' ])
641+ except NotImplementedError :
642+ self ._logger .warning ("ALPN not supported on this system; skipping." )
643+
644+ # Disable hostname verification for testing (not for production)
645+ context .check_hostname = True
646+ context .verify_mode = ssl .CERT_REQUIRED
647+
648+ raw_socket = context .wrap_socket (raw_socket , server_hostname = server_host )
649+
650+ except ssl .CertificateError as e :
651+ raise_from (errors .ConnectionError (str (e )), e )
652+ except ssl .SSLError as e :
653+ raise_from (errors .ConnectionError (str (e )), e )
654+ else :
655+ err_msg = "SSL requested but not supported by server"
656+ self ._logger .error (err_msg )
657+ raise errors .SSLNotSupported (err_msg )
658+
659+ return raw_socket
660+
599661
600662 def enable_ssl (self ,
601663 raw_socket : socket .socket ,
0 commit comments