mTLS Setup
Mutual TLS with client certificate authentication.
Overview
Standard TLS only proves the server's identity — the server itself accepts any client that knows the address and client ID. 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.
The tls: option's cacertfile, certfile, and keyfile, combined with verify: :verify_peer, wire in three artifacts at KubeMQ.Client.start_link/1: 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 — a failed handshake surfaces as an {:error, reason} tuple.
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
- KubeMQ server running with mTLS enabled
- CA certificate, client certificate, and client key files available
- Elixir SDK installed (
{:kubemq, "~> 1.0"}in mix.exs)
Code
ca_cert = System.get_env("KUBEMQ_CA_CERT", "/path/to/ca.pem")
client_cert = System.get_env("KUBEMQ_CLIENT_CERT", "/path/to/client.pem")
client_key = System.get_env("KUBEMQ_CLIENT_KEY", "/path/to/client-key.pem")
IO.puts("Connecting with mTLS (mutual TLS)...")
IO.puts(" CA cert: #{ca_cert}")
IO.puts(" Client cert: #{client_cert}")
IO.puts(" Client key: #{client_key}")
case KubeMQ.Client.start_link(
address: "localhost:50000",
client_id: "elixir-mtls-example",
tls: [
cacertfile: ca_cert,
certfile: client_cert,
keyfile: client_key,
verify: :verify_peer
]
) do
{:ok, client} ->
IO.puts("mTLS connection established!")
case KubeMQ.Client.ping(client) do
{:ok, info} -> IO.puts("Server version: #{info.version}")
{:error, err} -> IO.puts("Ping failed: #{err.message}")
end
KubeMQ.Client.close(client)
{:error, reason} ->
IO.puts("mTLS connection failed: #{inspect(reason)}")
IO.puts("Ensure all certificate paths are correct and broker has mTLS enabled.")
endHow It Works
certfileandkeyfilespecify the client's certificate and private keyverify: :verify_peerenables mutual verification- The server validates the client certificate against its trusted CA
- All certificate paths should be stored as environment variables in production
Related
Was this page helpful?