mirror of
https://github.com/meshtastic/mqtt.git
synced 2026-03-28 17:42:35 +01:00
Hello world
This commit is contained in:
11
.dockerignore
Normal file
11
.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
||||
bin/
|
||||
obj/
|
||||
*.user
|
||||
*.suo
|
||||
*.log
|
||||
.vs/
|
||||
.vscode/
|
||||
.git/
|
||||
.gitignore
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -51,4 +51,5 @@ CodeCoverage/
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
nunit-*.xml
|
||||
.DS_Store
|
||||
|
||||
23
Dockerfile
Normal file
23
Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy csproj and restore dependencies
|
||||
COPY Meshtastic.Mqtt.csproj ./
|
||||
RUN dotnet restore
|
||||
|
||||
# Copy the rest of the code
|
||||
COPY . ./
|
||||
RUN dotnet publish -c Release -o /app
|
||||
|
||||
# Build runtime image
|
||||
FROM mcr.microsoft.com/dotnet/runtime:9.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 1883 8883
|
||||
|
||||
# Set environment variable to control SSL mode
|
||||
# ENV SSL=true # Uncomment to enable SSL by default
|
||||
|
||||
ENTRYPOINT ["dotnet", "Meshtastic.Mqtt.dll"]
|
||||
30
Meshtastic.Mqtt.csproj
Normal file
30
Meshtastic.Mqtt.csproj
Normal file
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Meshtastic" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
|
||||
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
|
||||
<PackageReference Include="MQTTnet.Server" Version="5.0.1.1416" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="certificate.pfx;cert.pem">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
Meshtastic.Mqtt.sln
Normal file
24
Meshtastic.Mqtt.sln
Normal file
@@ -0,0 +1,24 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.2.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meshtastic.Mqtt", "Meshtastic.Mqtt.csproj", "{7467C293-5E7D-1ADF-451D-7E36BDAC9410}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7467C293-5E7D-1ADF-451D-7E36BDAC9410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7467C293-5E7D-1ADF-451D-7E36BDAC9410}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7467C293-5E7D-1ADF-451D-7E36BDAC9410}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7467C293-5E7D-1ADF-451D-7E36BDAC9410}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {62FE50C6-004E-4C4D-BE74-89E28A6A1064}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
178
Program.cs
Normal file
178
Program.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using MQTTnet.Server;
|
||||
using Meshtastic.Protobufs;
|
||||
using Google.Protobuf;
|
||||
using Serilog;
|
||||
using MQTTnet.Protocol;
|
||||
using System.Runtime.Loader;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog.Formatting.Compact;
|
||||
using Meshtastic.Crypto;
|
||||
using Meshtastic;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Reflection;
|
||||
|
||||
var mqttFactory = new MqttServerFactory();
|
||||
|
||||
// #if SSL
|
||||
var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
#pragma warning disable SYSLIB0057 // Type or member is obsolete
|
||||
var certificate = new X509Certificate2(Path.Combine(currentPath, "certificate.pfx"), "large4cats", X509KeyStorageFlags.Exportable);
|
||||
#pragma warning restore SYSLIB0057 // Type or member is obsolete
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder()
|
||||
.WithoutDefaultEndpoint() // This call disables the default unencrypted endpoint on port 1883
|
||||
.WithEncryptedEndpoint()
|
||||
.WithEncryptedEndpointPort(8883)
|
||||
.WithEncryptionCertificate(certificate.Export(X509ContentType.Pfx))
|
||||
.WithEncryptionSslProtocol(SslProtocols.Tls12)
|
||||
.Build();
|
||||
Log.Logger.Information("Using SSL certificate for MQTT server");
|
||||
|
||||
// If you want to use a non-encrypted MQTT server, you can uncomment the following lines instead of the above
|
||||
// var mqttServerOptions = new MqttServerOptionsBuilder()
|
||||
// .WithDefaultEndpoint()
|
||||
// .WithDefaultEndpointPort(1883)
|
||||
// .Build();
|
||||
// Log.Logger.Information("Using unencrypted MQTT server");
|
||||
// #endif
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.WriteTo.Console(new RenderedCompactJsonFormatter())
|
||||
// .WriteTo.File(new RenderedCompactJsonFormatter(), "log.json", rollingInterval: RollingInterval.Hour) // File logging can be enabled if needed
|
||||
.CreateLogger();
|
||||
|
||||
using var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions);
|
||||
|
||||
static Data? DecryptMeshPacket(ServiceEnvelope serviceEnvelope)
|
||||
{
|
||||
var nonce = new NonceGenerator(serviceEnvelope.Packet.From, serviceEnvelope.Packet.Id).Create();
|
||||
|
||||
var decrypted = PacketEncryption.TransformPacket(serviceEnvelope.Packet.Encrypted.ToByteArray(), nonce, Resources.DEFAULT_PSK);
|
||||
var payload = Data.Parser.ParseFrom(decrypted);
|
||||
|
||||
if (payload.Portnum > PortNum.UnknownApp && payload.Payload.Length > 0)
|
||||
return payload;
|
||||
|
||||
// Was not able to decrypt the payload
|
||||
return null;
|
||||
}
|
||||
|
||||
mqttServer.InterceptingPublishAsync += async (args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.ApplicationMessage.Payload.Length == 0)
|
||||
{
|
||||
Log.Logger.Warning("Received empty payload on topic {@Topic} from {@ClientId}", args.ApplicationMessage.Topic, args.ClientId);
|
||||
args.ProcessPublish = false; // This will block empty packets
|
||||
return;
|
||||
}
|
||||
var serviceEnvelope = ServiceEnvelope.Parser.ParseFrom(args.ApplicationMessage.Payload);
|
||||
|
||||
// Block malformed service envelopes / packets
|
||||
if (
|
||||
String.IsNullOrWhiteSpace(serviceEnvelope.ChannelId) ||
|
||||
String.IsNullOrWhiteSpace(serviceEnvelope.GatewayId) ||
|
||||
serviceEnvelope.Packet == null ||
|
||||
serviceEnvelope.Packet.Id < 1 ||
|
||||
serviceEnvelope.Packet.From < 1 ||
|
||||
serviceEnvelope.Packet.Encrypted == null ||
|
||||
serviceEnvelope.Packet.Encrypted.Length < 1 ||
|
||||
serviceEnvelope.Packet.Decoded != null)
|
||||
{
|
||||
Log.Logger.Warning("Service envelope or packet is malformed. Blocking packet on topic {@Topic} from {@ClientId}", args.ApplicationMessage.Topic, args.ClientId);
|
||||
args.ProcessPublish = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var data = DecryptMeshPacket(serviceEnvelope);
|
||||
// If we were not able to decrypt the packet, it is likely encrypted with an unknown PSK
|
||||
// Uncomment the following lines if you want to block these packets
|
||||
// if (data == null)
|
||||
// {
|
||||
// Log.Logger.Warning("Service envelope does not contain a valid packet. Blocking packet");
|
||||
// args.ProcessPublish = false; // This will block packets that are not valid protobuf packets
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (data?.Portnum == PortNum.TextMessageApp)
|
||||
{
|
||||
Log.Logger.Information("Received text message on topic {@Topic} from {@ClientId}: {@Message}",
|
||||
args.ApplicationMessage.Topic, args.ClientId, data.Payload.ToStringUtf8());
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Logger.Information("Received packet on topic {@Topic} from {@ClientId} with port number: {@Portnum}",
|
||||
args.ApplicationMessage.Topic, args.ClientId, data?.Portnum);
|
||||
}
|
||||
|
||||
// Any further validation logic to block a packet can be added here
|
||||
args.ProcessPublish = true;
|
||||
}
|
||||
catch (InvalidProtocolBufferException)
|
||||
{
|
||||
Log.Logger.Warning("Failed to decode presumed protobuf packet. Blocking");
|
||||
args.ProcessPublish = false; // This will block packets encrypted on unknown PSKs
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error("Exception occured while attempting to decode packet on {@Topic} from {@ClientId}: {@Exception}", args.ApplicationMessage.Topic, args.ClientId, ex.Message);
|
||||
args.ProcessPublish = false; // This will block packets that caused us to encounter an exception
|
||||
}
|
||||
};
|
||||
|
||||
mqttServer.InterceptingSubscriptionAsync += (args) =>
|
||||
{
|
||||
args.ProcessSubscription = true; // Subscription filtering logic can be added here to only allow certain topics
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
mqttServer.ValidatingConnectionAsync += (args) =>
|
||||
{
|
||||
args.ReasonCode = true ? // Authentication logic can be added here
|
||||
MqttConnectReasonCode.Success : MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
|
||||
// You can block connections based on client ID, username, ip, etc.
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
static IHostBuilder CreateHostBuilder(string[] args)
|
||||
{
|
||||
return Host.CreateDefaultBuilder(args)
|
||||
.UseConsoleLifetime()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
services
|
||||
.AddSingleton(Console.Out);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
using var host = CreateHostBuilder(args).Build();
|
||||
await host.StartAsync();
|
||||
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
var ended = new ManualResetEventSlim();
|
||||
var starting = new ManualResetEventSlim();
|
||||
|
||||
AssemblyLoadContext.Default.Unloading += ctx =>
|
||||
{
|
||||
starting.Set();
|
||||
Log.Logger.Debug("Waiting for completion");
|
||||
ended.Wait();
|
||||
};
|
||||
|
||||
starting.Wait();
|
||||
|
||||
Log.Logger.Debug("Received signal gracefully shutting down");
|
||||
await mqttServer.StopAsync();
|
||||
Thread.Sleep(1000);
|
||||
ended.Set();
|
||||
|
||||
lifetime.StopApplication();
|
||||
await host.WaitForShutdownAsync();
|
||||
73
README.md
73
README.md
@@ -1,2 +1,71 @@
|
||||
# mqtt
|
||||
A meshtastic native packet aware MQTT broker
|
||||
# Meshtastic MQTT Broker Boilerplate
|
||||
|
||||
This project provides an MQTT broker boilerplate specifically designed for Meshtastic device networks. It handles encrypted mesh packets, validates messages, and can be configured to run with or without SSL.
|
||||
|
||||
## Features
|
||||
|
||||
- MQTT server implementation for Meshtastic devices
|
||||
- Support for encrypted mesh packet handling and validation
|
||||
- SSL support for secure MQTT connections
|
||||
- Configurable logging with Serilog
|
||||
- Packet filtering and validation logic
|
||||
|
||||
## Docker Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker installed on your system
|
||||
- Certificate file (if using SSL mode)
|
||||
|
||||
### Docker Installation
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/meshtastic/mqtt
|
||||
cd mqtt
|
||||
```
|
||||
|
||||
2. Build the Docker image:
|
||||
```bash
|
||||
docker build -t meshtastic-mqtt-broker .
|
||||
```
|
||||
|
||||
#### SSL Mode (Port 8883)
|
||||
|
||||
To run with SSL enabled:
|
||||
|
||||
1. Place your certificate file (`certificate.pfx`) in the project directory. (see [MQTTnet Server Wiki](https://github.com/dotnet/MQTTnet/wiki/Server))
|
||||
2. Run the container with the SSL environment variable:
|
||||
|
||||
```bash
|
||||
docker run -p 8883:8883 -v $(pwd)/certificate.pfx:/app/certificate.pfx meshtastic-mqtt-broker
|
||||
```
|
||||
|
||||
### Docker Compose Example
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
mqtt-broker:
|
||||
build: .
|
||||
ports:
|
||||
- "1883:1883" # Standard MQTT port
|
||||
# - "8883:8883" # SSL port (uncomment if using SSL)
|
||||
# environment:
|
||||
# - SSL=true # Uncomment to enable SSL
|
||||
# volumes:
|
||||
# - ./certificate.pfx:/app/certificate.pfx # Mount certificate if using SSL
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
- **SSL**: Set environment variable `SSL=true` to enable SSL mode
|
||||
- **Certificate**: Mount your PFX certificate file to `/app/certificate.pfx` in the container
|
||||
- **Ports**: The application uses port 1883 for standard MQTT and 8883 for SSL MQTT
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Ensure proper network access to the Docker container
|
||||
- Check that certificates are correctly formatted (for SSL mode)
|
||||
- Review logs using `docker logs [container-id]`
|
||||
|
||||
31
cert.pem
Normal file
31
cert.pem
Normal file
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
BIN
certificate.pfx
Normal file
BIN
certificate.pfx
Normal file
Binary file not shown.
54
key.pem
Normal file
54
key.pem
Normal file
@@ -0,0 +1,54 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
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
|
||||
rH1xuKTR/w3KS06Rjacob21XXWL8FRkS
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user