# mTLS Setup (/sdks/python/how-to/tls/mtls-setup)



## Overview [#overview]

Standard TLS only proves the server's identity — the server itself accepts any client that knows the address and client ID. &#x2A;*Mutual TLS (mTLS)** closes that gap: the client also presents a certificate, so the server verifies who is connecting before accepting the connection. That matters on zero-trust networks and in regulated environments where "reaches the port" isn't an acceptable authorization model — the certificate becomes the credential.

`TLSConfig` with `cert_file`, `key_file`, and `ca_file` wires in three artifacts at client construction: the CA certificate (to verify the server, same as one-way TLS) plus the client's own certificate and private key (for the server to verify in return). Verification happens during the handshake, before any messaging traffic flows.

**Gotchas:** the certificate and key must be a matched pair signed by a CA the server trusts — a mismatch fails the handshake outright; all three files must be valid, unexpired PEM, and expiry breaks connections with no warning; and the CA that signed the *client* cert isn't necessarily the CA that verifies the *server* — mixing them up causes "works with TLS, fails with mTLS" confusion.

## Prerequisites [#prerequisites]

* KubeMQ server running with mTLS enabled
* Python SDK installed (`pip install kubemq`)
* TLS certificates (client certificate, client key, and CA certificate)

## Code [#code]

```python title="mtls_setup.py"
"""Example: Mutual TLS (mTLS) — client and server authenticate each other."""

from __future__ import annotations

import asyncio

from kubemq import TLSConfig
from kubemq import AsyncPubSubClient, EventMessage


async def main() -> None:
    # Mutual TLS: both client cert+key and CA cert are provided
    tls_config = TLSConfig(
        enabled=True,
        cert_file="/path/to/client-cert.pem",
        key_file="/path/to/client-key.pem",
        ca_file="/path/to/ca.pem",
    )

    async with AsyncPubSubClient(
        address="kubemq-server:50000",
        client_id="python-tls-mtls-setup-client",
        tls=tls_config,
    ) as client:
        info = await client.ping()
        print(f"Connected via mTLS to {info.host}")

        await client.publish_event(
            EventMessage(
                channel="python-tls.mtls-setup",
                body=b"mTLS secured message",
            )
        )
        print("Message sent over mTLS connection")


if __name__ == "__main__":
    asyncio.run(main())
```

## How It Works [#how-it-works]

* `TLSConfig` with `cert_file`, `key_file`, and `ca_file` enables mutual TLS authentication.
* The server verifies the client's certificate, and the client verifies the server's certificate, establishing bidirectional trust.
* This is the strongest transport-level security option, recommended for production environments.
* All three certificate files must be valid PEM-encoded files and must match each other.

## Related [#related]

* [Python SDK Reference](/sdks/python/reference)
* [TLS Setup](/sdks/python/how-to/tls/tls-setup)
