Documentation TLS & SSL Setup

TLS & SSL Setup

By default, MQTT traffic is unencrypted. Enabling TLS encrypts all data in transit between your clients and the broker, preventing eavesdropping and man-in-the-middle attacks. This guide covers generating certificates and configuring Mosquitto for TLS inside BunkerM.

Why TLS Matters for MQTT

Without TLS, MQTT credentials (username and password) and message payloads are transmitted in plain text. Anyone with network access between a client and the broker can read or modify the traffic. TLS is essential for:

  • Production deployments on the internet or shared networks
  • IoT devices sending sensitive data (health metrics, security systems, industrial telemetry)
  • Compliance with security requirements

Step 1: Generate a Self-Signed Certificate

For internal networks and testing, a self-signed CA is sufficient. Run these commands on your host machine:

# Create a directory for your certs
mkdir -p ~/bunkerm-certs && cd ~/bunkerm-certs

# Generate a CA key and certificate
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
  -subj "/CN=BunkerM-CA"

# Generate a server key and certificate signing request
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr \
  -subj "/CN=your-broker-hostname"

# Sign the server certificate with your CA
openssl x509 -req -days 3650 -in server.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt

Replace your-broker-hostname with the hostname or IP your MQTT clients use to connect.

Step 2: Mount Certificates into the Container

Place your certificates where Mosquitto can read them by adding bind mounts to your Docker run command or Compose file:

docker run -d \
  -p 1900:1900 \
  -p 8883:8883 \
  -p 2000:2000 \
  -v ~/bunkerm-certs/ca.crt:/etc/mosquitto/certs/ca.crt:ro \
  -v ~/bunkerm-certs/server.crt:/etc/mosquitto/certs/server.crt:ro \
  -v ~/bunkerm-certs/server.key:/etc/mosquitto/certs/server.key:ro \
  -v mosquitto_data:/var/lib/mosquitto \
  -v mosquitto_conf:/etc/mosquitto \
  bunkeriot/bunkerm:latest

Port 8883 is the standard MQTT-over-TLS port.

Step 3: Configure Mosquitto for TLS

Add a TLS listener to your Mosquitto configuration. If you are using a persistent config volume, edit the config file on the host, or mount a custom config snippet:

# /etc/mosquitto/conf.d/tls.conf
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.2
require_certificate false

Set require_certificate true if you want mutual TLS (clients must present a certificate too). For most setups, false is correct - clients verify the server certificate but authenticate with username/password.

Step 4: Restart the Container

docker restart bunkerm

Check the logs to confirm Mosquitto started successfully on port 8883:

docker logs bunkerm | grep 8883

Step 5: Connect an MQTT Client with TLS

Your clients now need to trust your CA certificate and connect on port 8883. Example with mosquitto_pub:

mosquitto_pub \
  --cafile ~/bunkerm-certs/ca.crt \
  -h your-broker-hostname \
  -p 8883 \
  -u your-username \
  -P your-password \
  -t test/topic \
  -m "hello tls"

For other MQTT clients (MQTT Explorer, Paho, etc.), load the ca.crt file as the trusted CA in the TLS settings.

Common Errors

  • Certificate verify failed - the client does not trust the CA. Make sure the client has ca.crt loaded.
  • SSL handshake error - hostname mismatch between the certificate CN and the broker address used by the client. Regenerate the certificate with the correct CN, or add a SAN (Subject Alternative Name).
  • Connection refused on 8883 - confirm the TLS listener is configured and the container exposes port 8883.
  • Expired certificate - certificates have a validity period. Renew them before expiry and restart the broker.

Using Let's Encrypt

If your BunkerM instance is accessible from the internet with a real domain name, you can use Let's Encrypt for a trusted certificate. Run Certbot on your host and mount the resulting fullchain.pem and privkey.pem files into the container in place of the self-signed files. Set cafile to an empty string (not needed for publicly trusted CAs).