TLS for the MQTT connection (iteration 2, step 5)
ca_file or the system's CAs (MOSQ_OPT_TLS_USE_OS_CERTS), client certificate, broker certificate and host name verified, settings errors reported before connecting. The client thread blocks SIGPIPE: with TLS, a write to a connection the broker closed killed the process. The reason of a failed handshake (libmosquitto's log) is appended to the error. tst_mqtt covers TLS with test certificates in tests/data (D44). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+17
-8
@@ -315,11 +315,19 @@ MQTT (D17).
|
||||
- **Normal exit:** `disconnectFromBroker(timeout)` publishes the will payload itself, waits up to
|
||||
the timeout for all acknowledgements (`waitForPublished()`), sends DISCONNECT and joins the
|
||||
thread. After that no callback can arrive.
|
||||
- **TLS:** `mosquitto_tls_set()` with `ca_file`; with an empty `ca_file`,
|
||||
`MOSQ_OPT_TLS_USE_OS_CERTS` (libmosquitto ≥ 2.0). Client certificate if configured.
|
||||
(Step 5; until then a TLS setting is reported as an error.)
|
||||
- **Errors** are reported in English regardless of the locale (`strerrordesc_np()`). A DNS
|
||||
failure has no reason: libmosquitto overwrites `getaddrinfo()`'s code in `errno` before it
|
||||
- **TLS:** `mosquitto_tls_set()` with `ca_file` (and the client certificate, if configured);
|
||||
the broker's certificate and host name are verified. With an empty `ca_file`, only
|
||||
`MOSQ_OPT_TLS_USE_OS_CERTS`: libmosquitto's `tls_set()` insists on a CA file or directory,
|
||||
so this option alone enables TLS, and **a client certificate needs `ca_file`**. Unreadable
|
||||
files and a certificate without key are reported before connecting.
|
||||
- **SIGPIPE:** with TLS, OpenSSL writes with `write()`, not `send(MSG_NOSIGNAL)`, so writing
|
||||
to a connection the broker closed raised SIGPIPE and ended the process (found by
|
||||
`tst_mqtt`). The client thread blocks SIGPIPE (`pthread_sigmask`); only it writes to the
|
||||
socket.
|
||||
- **Errors** are reported in English regardless of the locale (`strerrordesc_np()`). A failed
|
||||
TLS handshake returns only "TLS error" (2.0) or "Protocol error" (2.1); libmosquitto's
|
||||
error log line of the attempt is appended, e.g. "Protocol error: certificate verify
|
||||
failed". A DNS failure has no reason: libmosquitto overwrites `getaddrinfo()`'s code in `errno` before it
|
||||
returns. The log line "connecting to host:port" names the host.
|
||||
- **Logging:** every connection state change and error (`qInfo`/`qWarning`, so also in the log
|
||||
file); repeated failures with the same reason are reported once. Retries after a failure
|
||||
@@ -401,12 +409,13 @@ delete).
|
||||
|---|---|
|
||||
| `tst_haentity` | discovery component (optional keys only when set), topics, throttling (several values within an interval → one state with the newest value; unchanged or reverted values not emitted), first value → state then available, cleared → unavailable |
|
||||
| `tst_hadevice` (`tests/FakeMqttClient.h`) | topics, discovery prefix, last will, discovery payload, what is published after a connect and in which order, entity without value unavailable, changes while connected, nothing buffered and current state after reconnect, birth message (delayed; cancelled by a disconnect; other messages ignored), `removeFromHa()` |
|
||||
| `tst_mqtt` (integration; real `mosquitto` on a free port, QSKIP without it) | connect and messages; retained message for a later subscriber; nothing published while disconnected; first connect retried until the broker runs; reconnect after a broker restart; unresolvable host; last will after `kill -9` of a helper process (the test binary itself, `--will-helper`); `disconnectFromBroker()` publishes the will; login right and wrong; TLS with a test CA (step 5) |
|
||||
| `tst_mqtt` (integration; real `mosquitto` on a free port, QSKIP without it) | connect and messages; retained message for a later subscriber; nothing published while disconnected; first connect retried until the broker runs; reconnect after a broker restart; unresolvable host; last will after `kill -9` of a helper process (the test binary itself, `--will-helper`); `disconnectFromBroker()` publishes the will; login right and wrong; TLS: connect with the test CA, wrong CA rejected with reason, system CAs (`ca_file` empty), retry after a TLS error (the case that ends libmosquitto's own loop), client certificate required (with/without), settings errors |
|
||||
| `tst_surfaces` | `--desktop-novr`; app command: hook called, no surface created, exit code returned |
|
||||
| Manual | from the PC against the user's HA: device appears, online/offline, broker restart, two devices, `--ha-remove`, UI-1 in `--desktop`. On the Frame: the package with libmosquitto runs |
|
||||
|
||||
- TLS test certificates (CA, server certificate and key, 100-year validity) are committed to
|
||||
`tests/data/` (D44).
|
||||
- TLS test certificates (CA, server and client certificates with keys, a second CA; EC P-256,
|
||||
100-year validity) are committed to `tests/data/` (D44), with the commands that made them
|
||||
in `tests/data/README.md`.
|
||||
|
||||
### Implementation order
|
||||
|
||||
|
||||
+15
-1
@@ -25,7 +25,7 @@ CLAUDE.md ("Working rules").
|
||||
- **Implementation** follows the order in DESIGN.md (steps 1–7). Steps 1 (libmosquitto in
|
||||
CMake and the containers), 2 (`MqttClient`, `MosquittoClient`, `tst_mqtt`) 3
|
||||
(`HaEntity`, `HaSensor`, `HaDevice`, unit tests) and 4 (app wiring, `--desktop-novr`,
|
||||
UI-1) done; next: step 5 (TLS).
|
||||
UI-1) and 5 (TLS) done; next: step 6 (app commands, `--ha-remove`, `uninstall.sh`).
|
||||
|
||||
## Next steps
|
||||
|
||||
@@ -164,6 +164,20 @@ Known limitation: the bundled-Qt package does not support WebEngine in this iter
|
||||
`libmosquitto.so.1`, at most `GLIBC_2.34`, private Qt symbols only `QVulkan*`.
|
||||
- Not checked yet: the graceful exit through `aboutToQuit` in the app (only in
|
||||
`tst_mqtt`); it needs a real window close or "Quit app" in SteamVR (manual test).
|
||||
- Step 5: TLS in `MosquittoClient` (`ca_file` or the system's CAs, client certificate,
|
||||
checks before connecting); test certificates in `tests/data/` (D44); 9 TLS cases in
|
||||
`tst_mqtt`. Findings:
|
||||
- **SIGPIPE crash:** with TLS, a write to a connection the broker closed killed the
|
||||
process (OpenSSL uses `write()`). Fixed by blocking SIGPIPE in the client thread.
|
||||
- libmosquitto's `tls_set()` needs a CA file or directory, so with system CAs only
|
||||
`MOSQ_OPT_TLS_USE_OS_CERTS` is set, and a client certificate needs `ca_file`
|
||||
(documented, reported as a settings error).
|
||||
- A failed handshake returns only "TLS error"/"Protocol error"; the reason is in
|
||||
libmosquitto's log, which is now appended.
|
||||
- Checked: host 3 × 21/21; `container-test.sh` 12/12 (libmosquitto 2.0.21), no Vulkan
|
||||
validation messages.
|
||||
- Still open: which CA store the Frame's OpenSSL uses by default (for `ca_file` empty);
|
||||
check on the device.
|
||||
|
||||
### 2026-10-05: requirements approved
|
||||
|
||||
|
||||
Reference in New Issue
Block a user