Random wanderings through Microsoft Azure esp. PaaS plumbing, the IoT bits, AI on Micro controllers, AI on Edge Devices, .NET nanoFramework, .NET Core on *nix and ML.NET+ONNX
After hours of fail trying to get nanoMQ TCP bridge running on my Windows11 development system it was time to walk away. I ran nanoMQ with different log levels but “nng_dialer_create failed 9” was the initial error message displayed.
The setup looked good…
bridges.mqtt.MyBridgeDeviceID {
## Azure Event Grid MQTT broker endpoint
server = "tls+mqtt-tcp://xxxx.newzealandnorth-1.ts.eventgrid.azure.net:8883"
proto_ver = 5
clientid = "MyBridgeDeviceID"
username = "MyBridgeDeviceID"
clean_start = true
keepalive = "60s"
## TLS client certificate authentication
ssl = {
# key_password = ""
keyfile = "certificates/MyBridgeDeviceID.key"
certfile = "certificates/MyBridgeDeviceID.crt"
cacertfile = "certificates/xxxx.crt"
}
## ------------------------------------------------------------
## Topic forwarding (NanoMQ → Azure Event Grid)
## ------------------------------------------------------------
## These are the topics your device publishes locally.
## They will be forwarded upstream to Event Grid.
##
forwards = [xxxx]
## ------------------------------------------------------------
## Topic subscription (Azure Event Grid → NanoMQ)
## ------------------------------------------------------------
## This is the topic your device subscribes to from Event Grid.
subscription = [xxxx]
}
Most of my applications have focused on telemetry but I had been thinking about local control for solutions that have to run disconnected. In “real-world” deployments connectivity to Azure EventGrid MQTT Broker isn’t 100% reliable (also delay and jitter issues) which are an issue for control at the edge.
For a non-trivial system there should be a number of intermediate certificates. I have tried creating intermediate certificates for a device type, geography, application, customer and combinations of these. The first couple of times got it wrong so start with a field trial so that it isn’t so painful to go back and fix. (beware the sunk cost fallacy)
I found creating an intermediate certificate that could sign device certificates required a conf file for the basicConstraints and keyUsage configuration.
critical-The extension must be understood and processed by any application validating the certificate. If the application does not understand it, the certificate must be rejected.
CA:TRUE-This certificate is allowed to act as a Certificate Authority (CA), meaning it can sign other certificates.
pathlen:0-This CA can only issue end-entity (leaf) certificates and cannot issue further intermediate CA certificates.
keyCertSig- The certificate can be used to sign other certificates (i.e., it’s a CA certificate).
For production systems putting some thought into the Common name(CN), Organizational unit name(OU), Organization name(O), locality name(L), state or province name(S) and Country name(C)
Establishing a connection to the Azure Event Grid MQTT broker often failed which surprised me. Initially I didn’t have any retry logic which meant I wasted quite a bit of time trying to debug failed connections
I left these three NuGets to the last as I have had problems updating them before, and this time was no different. The updated NuGets “broke” my code because the way that security definitions and security requirements were implemented had changed.
These articles were the inspiration for my approach
However, starting with .NET 9, ASP.NET Core introduced native OpenAPI document generation via Microsoft.AspNetCore.OpenApi. This made WithOpenApi unnecessary because the new pipeline already supports operation customization through transformers.
app.MapGet("Version", () =>
{
return Results.Ok(typeof(Program).Assembly.GetName().Version?.ToString());
}).RequireAuthorization()
.WithName("Version")
.Produces<string>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized)
.AddOpenApiOperationTransformer((operation, context, ct) =>
{
// Per-endpoint tweaks
operation.Summary = "Returns version of the application";
operation.Description = "Returns the version of the application from project metadata.";
return Task.CompletedTask;
});