033b3700c4
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
66 lines
3.0 KiB
Plaintext
66 lines
3.0 KiB
Plaintext
# 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
|