Skip to main content
Version: Pro Edition for Eclipse Mosquitto 3.2

mTLS

Client certificates are a popular way of adding an extra layer of security to your client authentication. It can either be added on top or instead of the regular authentication provided by the role based authentication using username and password. While connecting, a client identifies using a client certificate. The broker has stored a client certificate authority and allows a connection, if the client certificate gets validated, the connection is allowed.

The CA Management allows the upload of client CAs to the broker via a UI.

Listener configuration

MTLS has to be activated on a port (listener) level.

Default Cloud configuration

The default broker configuration, when mTLS is selected in a cloud subscription for a port looks like this (shown with port 8883):

# Listener for the application, MQTTS over TCP with client certs
listener 8883
certfile /mosquitto/config/certs/server.pem
keyfile /mosquitto/config/certs/server.key
capath /mosquitto/config/capath_mqtts
require_certificate true

enable_control_api true

plugin /usr/lib/cedalo_certificate_management.so

If you need any changes to this default please contact our support team.

On-premises configuration

The following options can be used for mTLS configuration.

Certificate and Key Settings (Server-side)

  • cafile <path>: Specifies the path to the Certificate Authority (CA) file that the broker will use to validate client certificates.
  • capath <path>: Specifies the path to the Certificate Authority (CA) folder that the broker will use to validate client certificates. This is needed if more then one file is present.
  • certfile <path>: The server's public certificate file that will be presented to clients during the TLS handshake.
  • keyfile <path>: The server's private key file used to establish the TLS connection. The private key should remain secure and not be shared.

The path of the capath configuration should contain at least a root CA cert.

Note: The cafile configuration with a single PEM file containing the CA chain is currently not supported for this command.

Certificate and Key Settings (Client-side validation)

  • require_certificate <true|false>: Determines whether clients are required to present a valid certificate for authentication. Set this to true to enforce mTLS.
  • tls_version <version>: Specifies the minimum TLS version allowed for the connection. Common values include tlsv1.2 or tlsv1.3 to ensure a secure connection.
  • use_identity_as_username <true|false>: If set to true, the broker will use the client certificate's Common Name (CN) field as the client's username for authentication.

Plugin configuration

To enable the plugin it must be loaded into the broker with, by adding the following to your mosquitto.conf:

plugin /usr/lib/cedalo_certificate_management.so

Control API

In addition to enable the brokers internal $CONTROL/broker/v1 API we need to add:

enable_control_api true

This is required to determine the listeners identifier, where the changes should be applied to.

This plugin provides a Mosquitto control API which manages client CA certificates for certificate based authentication/validation. The topic of the plugin's control API is $CONTROL/certificate-management/v1. Currently, supported commands offered by the API are insertCACertificate, which can be used to extend the existing Certificate Authority (CA) Chain or just the Root CA by an additional signing/validating certificate. To get rid of a previously added CA cert, the deleteCACertificate command can be used.

Mutual TLS (mTLS) on Kubernetes and OpenShift

This guide matches the 3.2 Helm charts in this repository.

PlatformSingle nodeHA cluster (HAProxy in front of MQTT)
Kubernetesmosquitto-3.2-platform-3.2-kubernetes-snmosquitto-3.2-platform-3.2-kubernetes-cluster
OpenShiftmosquitto-3.2-platform-3.2-openshift-snmosquitto-3.2-platform-3.2-openshift-cluster

Use kubectl on Kubernetes and oc on OpenShift unless noted otherwise.


How this chart’s TLS modes differ (short)

The charts support three different MQTT TLS stories. This document covers only mosquitto.mtls. For install values, ports, and secrets for the other two, see serverOnlyTLS.md.

ModeHelm switch (MQTT)Who terminates TLS for clientsClient certificate
HAProxy TLShaproxy.tls.enabledHAProxyNot validated by Mosquitto on the MQTT path (HAProxy presents the server cert).
Server-only TLS on Mosquittomosquitto.serverOnlyTls.enabledMosquittoOptional (one-way TLS: server proves identity to the client).
mTLS on Mosquittomosquitto.mtls.enabledMosquittoRequired when requireCertificate is true: broker validates clients against capath.

Do not enable mosquitto.mtls and mosquitto.serverOnlyTls together. Both render a secure listener on mosquitto.ports.secureListenerTarget (default 8883); use exactly one broker-side TLS mode.


What mosquitto.mtls does

When mosquitto.mtls.enabled is true, the chart configures Mosquitto with:

  • A server certificate and key (your broker identity).
  • A capath of client CA material so Mosquitto can validate client certificates.
  • Optional require_certificate, use_identity_as_username, and tls_version (see parameters below).

On HA deployments, HAProxy exposes 8883 as TCP passthrough to the same broker port (it does not decrypt MQTT). Clients still perform TLS with Mosquitto; HAProxy only forwards bytes.

HAProxy PROXY protocol: with mosquitto.mtls.enabled, set haproxy.proxyProtocol.enabled to false. The chart fails template validation if proxy protocol stays enabled while mTLS (or serverOnlyTls) is on (templates/mosquitto-config.yaml).


Prerequisites: files and secrets

Server identity (broker)

Typical files (default Secret key names in the chart: server.pem, server.key):

  • server.pem — Server certificate (PEM; may include chain).
  • server.key — Matching private key.

Client trust store (who may connect as a client)

Mosquitto uses a capath: a directory of CA certificates with OpenSSL-style hash symlinks (files named like 4b073644.0). The chart expects a second Secret that is always created outside Helm (chart does not template it):

  • ca.crt — Human-readable CA (also copied into capath by the init logic).
  • <hash>.0 — Same CA (or additional CAs), named with the subject hash so Mosquitto can look up trust anchors.

Compute the hash name (use -subject_hash_old on OpenSSL 3 if your Mosquitto/OpenSSL combo expects the legacy hash):

HASH=$(openssl x509 -subject_hash_old -noout -in ca.crt 2>/dev/null || openssl x509 -hash -noout -in ca.crt)
cp ca.crt "${HASH}.0"

Issue client certificates from this CA (or a CA trusted under that capath) for MQTT clients.


Create Kubernetes / OpenShift Secrets

Set your namespace:

NAMESPACE="your-namespace"

1) Server cert Secret (name must match mosquitto.mtls.secretName, default mosquitto-mtls-server-tls):

kubectl create secret generic mosquitto-mtls-server-tls \
-n "$NAMESPACE" \
--from-file=server.pem=./server.pem \
--from-file=server.key=./server.key

2) Client CA Secret (name must match mosquitto.mtls.clientCaSecretName, default mosquitto-client-ca). This Secret cannot be created by Helm for mTLS:

kubectl create secret generic mosquitto-client-ca \
-n "$NAMESPACE" \
--from-file=ca.crt=./ca.crt \
--from-file=${HASH}.0=./${HASH}.0

For OpenShift, use oc create secret generic with the same arguments.

When using externally created server material, set mosquitto.mtls.createSecret=false. With validateSecrets: true (chart default), both Secrets must exist before helm install / helm upgrade.


How certificates are mounted (defaults)

Values are configurable; defaults match mosquitto-3.2-platform-3.2-kubernetes-cluster values.yaml.

RoleSecretMount directory in MosquittoFiles (defaults)
Servermosquitto.mtls.secretNamemosquitto.mtls.tlsMountPath (/mosquitto/config/certs)server.pem, server.key
Client CAsmosquitto.mtls.clientCaSecretNamemosquitto.mtls.caMountPath (/mosquitto/config/capath_mqtts)ca.crt, <hash>.0, …

An init container copies CA files from the Secret mount into the capath volume and fixes permissions so Mosquitto can read capath (templates/statefulset.yaml).


mosquitto.mtls configuration parameters

Server certificate (Helm-managed or external)

  • mosquitto.mtls.enabled — Turn on the mTLS listener on mosquitto.ports.secureListenerTarget (default 8883). Default: false.
  • mosquitto.mtls.secretName — Secret holding server.pem / server.key (or your certFile / keyFile names). Default: mosquitto-mtls-server-tls.
  • mosquitto.mtls.createSecrettrue: Helm creates the server Secret from certContent / keyContent. false: create the server Secret with kubectl/oc (recommended for ops). Default: true.
  • mosquitto.mtls.certFile / mosquitto.mtls.keyFile — Keys inside the server Secret. Defaults: server.pem, server.key.
  • mosquitto.mtls.tlsMountPathDirectory only where server PEM/key are mounted. Default: /mosquitto/config/certs.
  • mosquitto.mtls.certName / mosquitto.mtls.keyName — Filenames used in mosquitto.conf (certfile / keyfile). Defaults: server.pem, server.key.
  • mosquitto.mtls.certContent / mosquitto.mtls.keyContent — Base64; only when createSecret: true.

Client CA (always external Secret)

  • mosquitto.mtls.clientCaSecretName — Secret with ca.crt and hash-named files. Default: mosquitto-client-ca. Helm does not create this Secret.
  • mosquitto.mtls.caMountPath — Directory used as Mosquitto capath. Default: /mosquitto/config/capath_mqtts.

Mosquitto TLS behavior

  • mosquitto.mtls.requireCertificate — Maps to require_certificate. true: clients must present a cert the broker trusts. Default: true.
  • mosquitto.mtls.useIdentityAsUsername — When true, emits use_identity_as_username true so the cert identity can drive username semantics. Default: false.
  • mosquitto.mtls.tlsVersion — When non-empty, sets tls_version (e.g. tlsv1.2, tlsv1.3). Default: "" (Mosquitto default).

Port (shared with other secure modes)

  • mosquitto.ports.secureListenerTarget — Broker listener port for mTLS. Default: 8883.

Rendered mosquitto.conf (conceptual)

With mTLS enabled, the chart adds a listener similar to:

listener <secureListenerTarget>
protocol mqtt
certfile <tlsMountPath>/<certName>
keyfile <tlsMountPath>/<keyName>
capath <caMountPath>
require_certificate <requireCertificate>
# use_identity_as_username true (only if useIdentityAsUsername is true)
# tls_version <value> (only if tlsVersion is non-empty)

HA cluster: install and upgrade

  1. Create both Secrets (server + client CA).
  2. Set mosquitto.mtls.enabled=true, mosquitto.mtls.createSecret=false if the server Secret is external.
  3. Set haproxy.proxyProtocol.enabled=false.
  4. Install or upgrade, then restart HAProxy so it picks up the ConfigMap.

Kubernetes

helm upgrade --install mosquitto-platform ./mosquitto-3.2-platform-3.2-kubernetes-cluster \
-n "$NAMESPACE" \
-f values.yaml \
--set mosquitto.mtls.enabled=true \
--set mosquitto.mtls.createSecret=false \
--set haproxy.proxyProtocol.enabled=false

kubectl rollout restart deployment haproxy -n "$NAMESPACE"

OpenShift

helm upgrade --install mosquitto-platform ./mosquitto-3.2-platform-3.2-openshift-cluster \
-n "$NAMESPACE" \
-f values.yaml \
--set mosquitto.mtls.enabled=true \
--set mosquitto.mtls.createSecret=false \
--set haproxy.proxyProtocol.enabled=false

oc rollout restart deployment haproxy -n "$NAMESPACE"

Single node: install and upgrade

No HAProxy. Enable mTLS and external server Secret the same way; omit the deployment restart.

Kubernetes

helm upgrade --install mosquitto-platform ./mosquitto-3.2-platform-3.2-kubernetes-sn \
-n "$NAMESPACE" \
-f values.yaml \
--set mosquitto.mtls.enabled=true \
--set mosquitto.mtls.createSecret=false

OpenShift

helm upgrade --install mosquitto-platform ./mosquitto-3.2-platform-3.2-openshift-sn \
-n "$NAMESPACE" \
-f values.yaml \
--set mosquitto.mtls.enabled=true \
--set mosquitto.mtls.createSecret=false

Verification

# Server material
kubectl exec mosquitto-0 -n "$NAMESPACE" -- ls -la /mosquitto/config/certs/

# Client CA capath (after init)
kubectl exec mosquitto-0 -n "$NAMESPACE" -- ls -la /mosquitto/config/capath_mqtts/

# Listener and TLS directives
kubectl exec mosquitto-0 -n "$NAMESPACE" -- grep -A 15 "listener.*8883" /mosquitto/config/mosquitto.conf

Connect with an MQTT client using TLS to port 8883, server CA trust for server.pem, and a client certificate + key issued under your client CA. Example with mosquitto_sub (adjust paths and host):

mosquitto_sub -h <broker-or-haproxy-host> -p 8883 \
--cafile ca.crt \
--cert client.crt --key client.key \
-u '<user-if-needed>' -P '<pass-if-needed>' -t '#'

Rotating certificates

Server Secret

kubectl create secret generic mosquitto-mtls-server-tls \
-n "$NAMESPACE" \
--from-file=server.pem=./server.pem \
--from-file=server.key=./server.key \
--dry-run=client -o yaml | kubectl apply -f -

kubectl rollout restart statefulset mosquitto -n "$NAMESPACE"

Client CA Secret (update ca.crt and all required <hash>.0 entries, then):

kubectl create secret generic mosquitto-client-ca \
-n "$NAMESPACE" \
--from-file=ca.crt=./ca.crt \
--from-file=${HASH}.0=./${HASH}.0 \
--dry-run=client -o yaml | kubectl apply -f -

kubectl rollout restart statefulset mosquitto -n "$NAMESPACE"

For HA, restart HAProxy after broker or HAProxy ConfigMap–affecting changes:

kubectl rollout restart deployment haproxy -n "$NAMESPACE"

Troubleshooting

IssueWhat to check
Helm fails on proxy protocolSet haproxy.proxyProtocol.enabled=false when mTLS is enabled.
Helm / install says client CA Secret missingCreate mosquitto.mtls.clientCaSecretName before install when validateSecrets is true.
Clients fail handshake / “not authorized”Client cert signed by a CA present under capath; hash files present; require_certificate expectations; username/password vs use_identity_as_username.
Mosquitto rejects CA layoutRegenerate <hash>.0 with -subject_hash_old if you are on OpenSSL 3 and Mosquitto expects legacy hashes.
Duplicate listener / broken configEnsure mosquitto.serverOnlyTls.enabled is false when using mTLS.

Further reading