# Section 11 — Understand MQTT security boundaries Continue the same isolated lab. This lesson explains the MQTT security design and gives integration exercises; certificate provisioning depends on your chosen lab hostname. Keep remote deployment separate until these checks work. ## 1. Trace the checks in order ```text TCP → TLS certificate verification → MQTT CONNECT authentication → topic authorization → application identity/schema validation → permission to perform the business operation ``` TLS protects the connection and verifies the server identity. A username/password authenticates an MQTT client. ACLs restrict topic actions. Application validation checks the payload. HTTP user authorization determines whether an API caller may command that device. Passing one check never substitutes for the next. A Client ID is session identity, not a secret. A payload `device_id` is a claim. The broker normally does not attach the authenticated publisher username to each forwarded PUBLISH, so the backend cannot authenticate the sender merely by reading a JSON field. Bind a unique device credential to that device's permitted topics and validate topic/body agreement. ## 2. Configure verified TLS in Go Provision a lab CA and server certificate for the exact DNS name or IP used in the client URL. An IP connection needs that IP in the certificate's Subject Alternative Name. Keep the CA private key outside the broker; the broker needs its server key and certificate. Mount files with broker-readable permissions. Configure Mosquitto's TLS listener using `certfile` and `keyfile`; require client certificates only when deliberately introducing mutual TLS. This fragment belongs in your configuration/transport setup and requires `crypto/tls`, `crypto/x509`, `fmt`, and `os`. `caPath` is a configured path to the public lab CA certificate and `brokerHostname` is the validated URL hostname (without a port): ```go caPEM, err := os.ReadFile(caPath) if err != nil { return fmt.Errorf("read lab CA: %w", err) } roots := x509.NewCertPool() if !roots.AppendCertsFromPEM(caPEM) { return fmt.Errorf("lab CA contains no usable certificate") } tlsConfig := &tls.Config{ RootCAs: roots, ServerName: brokerHostname, MinVersion: tls.VersionTLS12, } ``` Assign this configuration to AutoPaho's `TlsCfg` and use a `tls://` broker URL for the TLS listener. Set `ServerName` explicitly from the validated URL host so certificate verification also has the intended identity on the pinned proxy transport path. Do not disable verification to make a hostname failure disappear; correct the certificate or URL. Update Lesson 3's URL validation deliberately: its early allowlist accepts only the plaintext `mqtt://` scheme. Server-only TLS can still use the existing MQTT credentials and ACLs. Mutual TLS additionally authenticates a client certificate; mapping certificate identity to topic permissions is another deliberate broker policy. Do not assume enabling encryption creates per-device authorization. Use the official [Mosquitto TLS configuration reference](https://mosquitto.org/man/mosquitto-conf-5.html) and [Go TLS configuration API](https://pkg.go.dev/crypto/tls#Config) while provisioning. Match broker options to the pinned 2.0.22 image. ## 3. Test failures at the intended layer | Change one input | Expected failure layer | Evidence to collect | | --- | --- | --- | | Wrong CA | TLS verification | No authenticated MQTT session established | | Valid CA, wrong server name | TLS identity verification | Hostname/IP mismatch | | Valid TLS, wrong MQTT password | CONNECT authentication | Connection error / unsuccessful CONNACK | | Valid login, other device's publication topic | Topic authorization | Rejection/delivery denial plus authorized observer and broker logs | | Permitted topic, mismatched body device | Application validation | Rejection before any state mutation | | Valid API user, unauthorized target device | HTTP business authorization | Denial before creating/publishing a command | Never fix an ACL test by replacing narrow permissions with `#`. A successful broad SUBACK alone is not proof of cross-device access: test actual delivery. Restrict Response Topic before replying so a device cannot be used to publish a response into an unexpected namespace. ## 4. Rotate credentials without confusing state Updating a password file affects authentication policy; it does not prove every existing connection was disconnected. A fresh connection using the old password must fail, a new one must succeed, and the device must regain its subscriptions. [Broker operations](broker-operations.md) explains which credential volume this workspace actually uses and why editing the old host copy does nothing. Treat MQTT 5 enhanced authentication (`AUTH` plus an agreed authentication method) as an optional separate extension. It requires compatible client and broker mechanisms; adding User Properties or renaming a username does not enable it. The two-user password-file lab does not demonstrate enhanced authentication.
Why is TLS with a shared fleet password still insufficient for per-device isolation? TLS protects traffic, but a shared credential does not distinguish individual devices for authorization. A compromised device can exercise that shared identity's permissions. Use separate identities and topic policies, then validate application claims independently.
**Done when:** explain every boundary, run the failure matrix against the lab, and show that a denied user or device cannot produce a simulated effect. Continue with Section 12 in [COURSE.md](../COURSE.md); the [advanced MQTT lesson](16-mqtt5-controls.md) belongs after the persistence/process exercises.