Skip to content

Compatibility with updates to the Python SSL module for client cert authentication #318

@tonybourke

Description

@tonybourke

The HTTPSConnection.init method from SSL has deprecated key_file and cert_file, so eAPI doesn't work with https_certs. (more here: https://docs.python.org/3.12/library/http.client.html#http.client.HTTPSConnection)

Ansible ran into a similar problem a while back.

Trying 3.10 and 3.11 doesn't seem to work as it looks like the change was backported.

I needed a fix quick so I asked ChatGPT to come up with something and it works (so far), though I'm not confident enough in vibe coding to submit it as a fix.

class HTTPSCertConnection(HTTPSConnection):
    """HTTPS connection with client certificate authentication."""

    def __init__(self, path, host, port, key_file, cert_file, ca_file,
                 timeout=None):

        # Build SSL context the modern (Py3.12) way
        if ca_file:
            ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=ca_file)
            ctx.verify_mode = ssl.CERT_REQUIRED
        else:
            # No CA: disable verification (legacy behavior)
            ctx = ssl._create_unverified_context()
            ctx.check_hostname = False
            ctx.verify_mode = ssl.CERT_NONE

        # Load client certificate + private key
        ctx.load_cert_chain(certfile=cert_file, keyfile=key_file)

        # Initialize HTTPSConnection with modern arguments
        super().__init__(host, port=port, timeout=timeout, context=ctx)

        self.key_file = key_file
        self.cert_file = cert_file
        self.ca_file = ca_file
        self.timeout = timeout
        self.path = path
        self.port = port
        self.context = ctx   # store the context for connect()

    def __str__(self):
        return f"https://{self.host}:{self.port}/{self.path} - {self.key_file},{self.cert_file}"

    def __repr__(self):
        return self.__str__()

    def connect(self):
        # Create raw TCP socket
        sock = socket.create_connection((self.host, self.port), self.timeout)

        # Handle proxies / tunnels
        if self._tunnel_host:
            self.sock = sock
            self._tunnel()

        # Wrap in SSL using the built SSLContext
        self.sock = self.context.wrap_socket(
            sock,
            server_hostname=self.host  # required for SNI
        )

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions