test(mqtt): Mosquitto TLS+auth fixture + env-gated plain live suite
Adds the MQTT driver integration-test project: an eclipse-mosquitto fixture running auth AND TLS (allow_anonymous false on both listeners), a mosquitto_pub-based JSON publisher emitting retained messages, a cert/password generator script whose output is gitignored, and a live suite gated on MQTT_FIXTURE_ENDPOINT that skips clean offline. The fixture is never anonymous by design: an anonymous broker would let the driver's TLS/CA-pin and auth paths go untested, and those fail silently. The CA-pin leg carries its own falsifiability control (a foreign CA generated in-process must be rejected). Live gate on 10.100.0.35: 7/7 passed. It found a driver defect, reported not fixed here (src/ is out of this task's scope): MqttConnection.ConnectAsync RETURNS NORMALLY when Mosquitto rejects a CONNECT as not-authorized -- MQTTnet 5 does not throw on an unsuccessful CONNACK, so a wrong broker password yields DriverState.Healthy from InitializeAsync. The auth-negative test therefore asserts the invariant that holds either way (no session is ever established) and documents the finding. Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# Eclipse Mosquitto configuration for the OtOpcUa MQTT driver integration fixture.
|
||||
#
|
||||
# SECURITY POSTURE — deliberate, and the whole reason this fixture exists.
|
||||
# `allow_anonymous false` is set GLOBALLY (with per_listener_settings false), so it
|
||||
# governs BOTH listeners. An anonymous fixture would let the driver's auth and TLS
|
||||
# code paths — including the CA-pin logic in MqttConnection.ConfigureTls /
|
||||
# ValidateAgainstPinnedCa — go completely untested, which is exactly backwards: those
|
||||
# are the paths whose failure mode is silent (a pin that never rejects anything looks
|
||||
# identical to a pin that works).
|
||||
#
|
||||
# The password file and the TLS material are NOT in git. Generate them first with
|
||||
# `./gen-fixture-material.sh` (see that script's banner); it writes ./secrets/, which
|
||||
# .gitignore excludes.
|
||||
|
||||
# One global security policy rather than per-listener ones: with
|
||||
# per_listener_settings true, `allow_anonymous`/`password_file` would have to be
|
||||
# repeated under EVERY listener block, and a listener that forgot them would silently
|
||||
# fall back to allowing anonymous connections. Global + fail-closed is the safer shape.
|
||||
per_listener_settings false
|
||||
|
||||
allow_anonymous false
|
||||
password_file /mosquitto/secrets/passwd
|
||||
|
||||
# No persistence: the fixture is throwaway, and a persisted retained-message store
|
||||
# surviving a `docker compose down` would make the retained-seed test's outcome depend
|
||||
# on a previous run rather than on this one's publisher.
|
||||
persistence false
|
||||
|
||||
log_dest stdout
|
||||
log_type error
|
||||
log_type warning
|
||||
log_type notice
|
||||
log_type information
|
||||
connection_messages true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plain (non-TLS) listener — smoke only.
|
||||
#
|
||||
# Still authenticated (allow_anonymous false above applies here too); it is the
|
||||
# TRANSPORT that is plaintext, not the access policy. It exists so the subscribe /
|
||||
# retained-seed / unauthored-topic legs can be exercised without dragging the TLS
|
||||
# handshake into every assertion, and so an operator can `mosquitto_sub` the fixture
|
||||
# by hand. Never a model for a deployment.
|
||||
# ---------------------------------------------------------------------------
|
||||
listener 1883 0.0.0.0
|
||||
protocol mqtt
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TLS listener — the real one.
|
||||
#
|
||||
# `require_certificate false`: the driver authenticates with username/password over
|
||||
# TLS (the shape MqttDriverOptions models today); mutual TLS is not a driver feature
|
||||
# yet, so demanding a client certificate here would make the fixture untestable rather
|
||||
# than more secure.
|
||||
#
|
||||
# tls_version is deliberately NOT pinned: mosquitto's `tls_version` selects exactly
|
||||
# one version, so pinning 1.2 would refuse a .NET client that negotiated 1.3. Leaving
|
||||
# it unset lets OpenSSL negotiate the best mutually-supported version.
|
||||
# ---------------------------------------------------------------------------
|
||||
listener 8883 0.0.0.0
|
||||
protocol mqtt
|
||||
cafile /mosquitto/secrets/ca.crt
|
||||
certfile /mosquitto/secrets/server.crt
|
||||
keyfile /mosquitto/secrets/server.key
|
||||
require_certificate false
|
||||
Reference in New Issue
Block a user