KubeMQ
Client SDKsElixirHow-to guidesTLS

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

main.exs
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.")
end

How It Works

  • certfile and keyfile specify the client's certificate and private key
  • verify: :verify_peer enables mutual verification
  • The server validates the client certificate against its trusted CA
  • All certificate paths should be stored as environment variables in production

Was this page helpful?

On this page