mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-03-28 17:42:56 +01:00
Merge pull request #48 from ipnet-mesh/feature/mqtt-tls
Added support for MQTT TLS
This commit is contained in:
@@ -52,6 +52,11 @@ MQTT_USERNAME=
|
||||
MQTT_PASSWORD=
|
||||
MQTT_PREFIX=meshcore
|
||||
|
||||
# Enable TLS/SSL for MQTT connection (default: false)
|
||||
# When enabled, uses TLS with system CA certificates (e.g., for Let's Encrypt)
|
||||
# Set to true for secure MQTT connections (port 8883)
|
||||
MQTT_TLS=false
|
||||
|
||||
# External port mappings for local MQTT broker (--profile mqtt only)
|
||||
MQTT_EXTERNAL_PORT=1883
|
||||
MQTT_WS_PORT=9001
|
||||
|
||||
@@ -47,6 +47,7 @@ services:
|
||||
- MQTT_USERNAME=${MQTT_USERNAME:-}
|
||||
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
|
||||
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
|
||||
- MQTT_TLS=${MQTT_TLS:-false}
|
||||
- SERIAL_PORT=${SERIAL_PORT:-/dev/ttyUSB0}
|
||||
- SERIAL_BAUD=${SERIAL_BAUD:-115200}
|
||||
- NODE_ADDRESS=${NODE_ADDRESS:-}
|
||||
@@ -81,6 +82,7 @@ services:
|
||||
- MQTT_USERNAME=${MQTT_USERNAME:-}
|
||||
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
|
||||
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
|
||||
- MQTT_TLS=${MQTT_TLS:-false}
|
||||
- SERIAL_PORT=${SERIAL_PORT_SENDER:-/dev/ttyUSB1}
|
||||
- SERIAL_BAUD=${SERIAL_BAUD:-115200}
|
||||
- NODE_ADDRESS=${NODE_ADDRESS_SENDER:-}
|
||||
@@ -112,6 +114,7 @@ services:
|
||||
- MQTT_USERNAME=${MQTT_USERNAME:-}
|
||||
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
|
||||
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
|
||||
- MQTT_TLS=${MQTT_TLS:-false}
|
||||
- MOCK_DEVICE=true
|
||||
- NODE_ADDRESS=${NODE_ADDRESS:-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef}
|
||||
command: ["interface", "receiver", "--mock"]
|
||||
@@ -145,6 +148,7 @@ services:
|
||||
- MQTT_USERNAME=${MQTT_USERNAME:-}
|
||||
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
|
||||
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
|
||||
- MQTT_TLS=${MQTT_TLS:-false}
|
||||
- DATA_HOME=/data
|
||||
- SEED_HOME=/seed
|
||||
# Explicitly unset to use DATA_HOME-based default path
|
||||
@@ -203,6 +207,7 @@ services:
|
||||
- MQTT_USERNAME=${MQTT_USERNAME:-}
|
||||
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
|
||||
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
|
||||
- MQTT_TLS=${MQTT_TLS:-false}
|
||||
- DATA_HOME=/data
|
||||
# Explicitly unset to use DATA_HOME-based default path
|
||||
- DATABASE_URL=
|
||||
|
||||
@@ -52,6 +52,7 @@ def create_app(
|
||||
mqtt_host: str = "localhost",
|
||||
mqtt_port: int = 1883,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
cors_origins: list[str] | None = None,
|
||||
) -> FastAPI:
|
||||
"""Create and configure the FastAPI application.
|
||||
@@ -63,6 +64,7 @@ def create_app(
|
||||
mqtt_host: MQTT broker host
|
||||
mqtt_port: MQTT broker port
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
cors_origins: Allowed CORS origins
|
||||
|
||||
Returns:
|
||||
@@ -85,6 +87,7 @@ def create_app(
|
||||
app.state.mqtt_host = mqtt_host
|
||||
app.state.mqtt_port = mqtt_port
|
||||
app.state.mqtt_prefix = mqtt_prefix
|
||||
app.state.mqtt_tls = mqtt_tls
|
||||
|
||||
# Configure CORS
|
||||
if cors_origins is None:
|
||||
|
||||
@@ -67,6 +67,13 @@ import click
|
||||
envvar="MQTT_TOPIC_PREFIX",
|
||||
help="MQTT topic prefix",
|
||||
)
|
||||
@click.option(
|
||||
"--mqtt-tls",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
envvar="MQTT_TLS",
|
||||
help="Enable TLS/SSL for MQTT connection",
|
||||
)
|
||||
@click.option(
|
||||
"--cors-origins",
|
||||
type=str,
|
||||
@@ -92,6 +99,7 @@ def api(
|
||||
mqtt_host: str,
|
||||
mqtt_port: int,
|
||||
mqtt_prefix: str,
|
||||
mqtt_tls: bool,
|
||||
cors_origins: str | None,
|
||||
reload: bool,
|
||||
) -> None:
|
||||
@@ -171,6 +179,7 @@ def api(
|
||||
mqtt_host=mqtt_host,
|
||||
mqtt_port=mqtt_port,
|
||||
mqtt_prefix=mqtt_prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
cors_origins=origins_list,
|
||||
)
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ def get_mqtt_client(request: Request) -> MQTTClient:
|
||||
mqtt_host = getattr(request.app.state, "mqtt_host", "localhost")
|
||||
mqtt_port = getattr(request.app.state, "mqtt_port", 1883)
|
||||
mqtt_prefix = getattr(request.app.state, "mqtt_prefix", "meshcore")
|
||||
mqtt_tls = getattr(request.app.state, "mqtt_tls", False)
|
||||
|
||||
# Use unique client ID to allow multiple API instances
|
||||
unique_id = uuid.uuid4().hex[:8]
|
||||
@@ -65,6 +66,7 @@ def get_mqtt_client(request: Request) -> MQTTClient:
|
||||
port=mqtt_port,
|
||||
prefix=mqtt_prefix,
|
||||
client_id=f"meshcore-api-{unique_id}",
|
||||
tls=mqtt_tls,
|
||||
)
|
||||
|
||||
client = MQTTClient(config)
|
||||
|
||||
@@ -47,6 +47,13 @@ if TYPE_CHECKING:
|
||||
envvar="MQTT_PREFIX",
|
||||
help="MQTT topic prefix",
|
||||
)
|
||||
@click.option(
|
||||
"--mqtt-tls",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
envvar="MQTT_TLS",
|
||||
help="Enable TLS/SSL for MQTT connection",
|
||||
)
|
||||
@click.option(
|
||||
"--data-home",
|
||||
type=str,
|
||||
@@ -82,6 +89,7 @@ def collector(
|
||||
mqtt_username: str | None,
|
||||
mqtt_password: str | None,
|
||||
prefix: str,
|
||||
mqtt_tls: bool,
|
||||
data_home: str | None,
|
||||
seed_home: str | None,
|
||||
database_url: str | None,
|
||||
@@ -125,6 +133,7 @@ def collector(
|
||||
ctx.obj["mqtt_username"] = mqtt_username
|
||||
ctx.obj["mqtt_password"] = mqtt_password
|
||||
ctx.obj["prefix"] = prefix
|
||||
ctx.obj["mqtt_tls"] = mqtt_tls
|
||||
ctx.obj["data_home"] = data_home or settings.data_home
|
||||
ctx.obj["seed_home"] = settings.effective_seed_home
|
||||
ctx.obj["database_url"] = effective_db_url
|
||||
@@ -139,6 +148,7 @@ def collector(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
prefix=prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
database_url=effective_db_url,
|
||||
log_level=log_level,
|
||||
data_home=data_home or settings.data_home,
|
||||
@@ -152,6 +162,7 @@ def _run_collector_service(
|
||||
mqtt_username: str | None,
|
||||
mqtt_password: str | None,
|
||||
prefix: str,
|
||||
mqtt_tls: bool,
|
||||
database_url: str,
|
||||
log_level: str,
|
||||
data_home: str,
|
||||
@@ -256,6 +267,7 @@ def _run_collector_service(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
database_url=database_url,
|
||||
webhook_dispatcher=webhook_dispatcher,
|
||||
cleanup_enabled=settings.data_retention_enabled,
|
||||
@@ -279,6 +291,7 @@ def run_cmd(ctx: click.Context) -> None:
|
||||
mqtt_username=ctx.obj["mqtt_username"],
|
||||
mqtt_password=ctx.obj["mqtt_password"],
|
||||
prefix=ctx.obj["prefix"],
|
||||
mqtt_tls=ctx.obj["mqtt_tls"],
|
||||
database_url=ctx.obj["database_url"],
|
||||
log_level=ctx.obj["log_level"],
|
||||
data_home=ctx.obj["data_home"],
|
||||
|
||||
@@ -428,6 +428,7 @@ def create_subscriber(
|
||||
mqtt_username: Optional[str] = None,
|
||||
mqtt_password: Optional[str] = None,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
database_url: str = "sqlite:///./meshcore.db",
|
||||
webhook_dispatcher: Optional["WebhookDispatcher"] = None,
|
||||
cleanup_enabled: bool = False,
|
||||
@@ -444,6 +445,7 @@ def create_subscriber(
|
||||
mqtt_username: MQTT username
|
||||
mqtt_password: MQTT password
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
database_url: Database connection URL
|
||||
webhook_dispatcher: Optional webhook dispatcher for event forwarding
|
||||
cleanup_enabled: Enable automatic event data cleanup
|
||||
@@ -464,6 +466,7 @@ def create_subscriber(
|
||||
password=mqtt_password,
|
||||
prefix=mqtt_prefix,
|
||||
client_id=f"meshcore-collector-{unique_id}",
|
||||
tls=mqtt_tls,
|
||||
)
|
||||
mqtt_client = MQTTClient(mqtt_config)
|
||||
|
||||
@@ -496,6 +499,7 @@ def run_collector(
|
||||
mqtt_username: Optional[str] = None,
|
||||
mqtt_password: Optional[str] = None,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
database_url: str = "sqlite:///./meshcore.db",
|
||||
webhook_dispatcher: Optional["WebhookDispatcher"] = None,
|
||||
cleanup_enabled: bool = False,
|
||||
@@ -512,6 +516,7 @@ def run_collector(
|
||||
mqtt_username: MQTT username
|
||||
mqtt_password: MQTT password
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
database_url: Database connection URL
|
||||
webhook_dispatcher: Optional webhook dispatcher for event forwarding
|
||||
cleanup_enabled: Enable automatic event data cleanup
|
||||
@@ -526,6 +531,7 @@ def run_collector(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=mqtt_prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
database_url=database_url,
|
||||
webhook_dispatcher=webhook_dispatcher,
|
||||
cleanup_enabled=cleanup_enabled,
|
||||
|
||||
@@ -52,6 +52,9 @@ class CommonSettings(BaseSettings):
|
||||
default=None, description="MQTT password (optional)"
|
||||
)
|
||||
mqtt_prefix: str = Field(default="meshcore", description="MQTT topic prefix")
|
||||
mqtt_tls: bool = Field(
|
||||
default=False, description="Enable TLS/SSL for MQTT connection"
|
||||
)
|
||||
|
||||
|
||||
class InterfaceSettings(CommonSettings):
|
||||
|
||||
@@ -23,6 +23,7 @@ class MQTTConfig:
|
||||
client_id: Optional[str] = None
|
||||
keepalive: int = 60
|
||||
clean_session: bool = True
|
||||
tls: bool = False
|
||||
|
||||
|
||||
class TopicBuilder:
|
||||
@@ -131,6 +132,11 @@ class MQTTClient:
|
||||
self._connected = False
|
||||
self._message_handlers: dict[str, list[MessageHandler]] = {}
|
||||
|
||||
# Set up TLS if enabled
|
||||
if config.tls:
|
||||
self._client.tls_set()
|
||||
logger.debug("TLS/SSL enabled for MQTT connection")
|
||||
|
||||
# Set up authentication if provided
|
||||
if config.username:
|
||||
self._client.username_pw_set(config.username, config.password)
|
||||
@@ -344,6 +350,7 @@ def create_mqtt_client(
|
||||
password: Optional[str] = None,
|
||||
prefix: str = "meshcore",
|
||||
client_id: Optional[str] = None,
|
||||
tls: bool = False,
|
||||
) -> MQTTClient:
|
||||
"""Create and configure an MQTT client.
|
||||
|
||||
@@ -354,6 +361,7 @@ def create_mqtt_client(
|
||||
password: MQTT password (optional)
|
||||
prefix: Topic prefix
|
||||
client_id: Client identifier (optional)
|
||||
tls: Enable TLS/SSL connection (optional)
|
||||
|
||||
Returns:
|
||||
Configured MQTTClient instance
|
||||
@@ -365,5 +373,6 @@ def create_mqtt_client(
|
||||
password=password,
|
||||
prefix=prefix,
|
||||
client_id=client_id,
|
||||
tls=tls,
|
||||
)
|
||||
return MQTTClient(config)
|
||||
|
||||
@@ -93,6 +93,13 @@ def interface() -> None:
|
||||
envvar="MQTT_PREFIX",
|
||||
help="MQTT topic prefix",
|
||||
)
|
||||
@click.option(
|
||||
"--mqtt-tls",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
envvar="MQTT_TLS",
|
||||
help="Enable TLS/SSL for MQTT connection",
|
||||
)
|
||||
@click.option(
|
||||
"--log-level",
|
||||
type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]),
|
||||
@@ -112,6 +119,7 @@ def run(
|
||||
mqtt_username: str | None,
|
||||
mqtt_password: str | None,
|
||||
prefix: str,
|
||||
mqtt_tls: bool,
|
||||
log_level: str,
|
||||
) -> None:
|
||||
"""Run the interface component.
|
||||
@@ -153,6 +161,7 @@ def run(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
)
|
||||
elif mode_upper == "SENDER":
|
||||
from meshcore_hub.interface.sender import run_sender
|
||||
@@ -168,6 +177,7 @@ def run(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
)
|
||||
else:
|
||||
click.echo(f"Unknown mode: {mode}", err=True)
|
||||
@@ -245,6 +255,13 @@ def run(
|
||||
envvar="MQTT_PREFIX",
|
||||
help="MQTT topic prefix",
|
||||
)
|
||||
@click.option(
|
||||
"--mqtt-tls",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
envvar="MQTT_TLS",
|
||||
help="Enable TLS/SSL for MQTT connection",
|
||||
)
|
||||
def receiver(
|
||||
port: str,
|
||||
baud: int,
|
||||
@@ -256,6 +273,7 @@ def receiver(
|
||||
mqtt_username: str | None,
|
||||
mqtt_password: str | None,
|
||||
prefix: str,
|
||||
mqtt_tls: bool,
|
||||
) -> None:
|
||||
"""Run interface in RECEIVER mode.
|
||||
|
||||
@@ -280,6 +298,7 @@ def receiver(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
)
|
||||
|
||||
|
||||
@@ -354,6 +373,13 @@ def receiver(
|
||||
envvar="MQTT_PREFIX",
|
||||
help="MQTT topic prefix",
|
||||
)
|
||||
@click.option(
|
||||
"--mqtt-tls",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
envvar="MQTT_TLS",
|
||||
help="Enable TLS/SSL for MQTT connection",
|
||||
)
|
||||
def sender(
|
||||
port: str,
|
||||
baud: int,
|
||||
@@ -365,6 +391,7 @@ def sender(
|
||||
mqtt_username: str | None,
|
||||
mqtt_password: str | None,
|
||||
prefix: str,
|
||||
mqtt_tls: bool,
|
||||
) -> None:
|
||||
"""Run interface in SENDER mode.
|
||||
|
||||
@@ -389,4 +416,5 @@ def sender(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
)
|
||||
|
||||
@@ -290,6 +290,7 @@ def create_receiver(
|
||||
mqtt_username: Optional[str] = None,
|
||||
mqtt_password: Optional[str] = None,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
) -> Receiver:
|
||||
"""Create a configured receiver instance.
|
||||
|
||||
@@ -304,6 +305,7 @@ def create_receiver(
|
||||
mqtt_username: MQTT username
|
||||
mqtt_password: MQTT password
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
|
||||
Returns:
|
||||
Configured Receiver instance
|
||||
@@ -324,6 +326,7 @@ def create_receiver(
|
||||
password=mqtt_password,
|
||||
prefix=mqtt_prefix,
|
||||
client_id=f"meshcore-receiver-{device.public_key[:12] if device.public_key else 'unknown'}",
|
||||
tls=mqtt_tls,
|
||||
)
|
||||
mqtt_client = MQTTClient(mqtt_config)
|
||||
|
||||
@@ -341,6 +344,7 @@ def run_receiver(
|
||||
mqtt_username: Optional[str] = None,
|
||||
mqtt_password: Optional[str] = None,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
) -> None:
|
||||
"""Run the receiver (blocking).
|
||||
|
||||
@@ -357,6 +361,7 @@ def run_receiver(
|
||||
mqtt_username: MQTT username
|
||||
mqtt_password: MQTT password
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
"""
|
||||
receiver = create_receiver(
|
||||
port=port,
|
||||
@@ -369,6 +374,7 @@ def run_receiver(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=mqtt_prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
)
|
||||
|
||||
# Set up signal handlers
|
||||
|
||||
@@ -293,6 +293,7 @@ def create_sender(
|
||||
mqtt_username: Optional[str] = None,
|
||||
mqtt_password: Optional[str] = None,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
) -> Sender:
|
||||
"""Create a configured sender instance.
|
||||
|
||||
@@ -307,6 +308,7 @@ def create_sender(
|
||||
mqtt_username: MQTT username
|
||||
mqtt_password: MQTT password
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
|
||||
Returns:
|
||||
Configured Sender instance
|
||||
@@ -327,6 +329,7 @@ def create_sender(
|
||||
password=mqtt_password,
|
||||
prefix=mqtt_prefix,
|
||||
client_id=f"meshcore-sender-{device.public_key[:12] if device.public_key else 'unknown'}",
|
||||
tls=mqtt_tls,
|
||||
)
|
||||
mqtt_client = MQTTClient(mqtt_config)
|
||||
|
||||
@@ -344,6 +347,7 @@ def run_sender(
|
||||
mqtt_username: Optional[str] = None,
|
||||
mqtt_password: Optional[str] = None,
|
||||
mqtt_prefix: str = "meshcore",
|
||||
mqtt_tls: bool = False,
|
||||
) -> None:
|
||||
"""Run the sender (blocking).
|
||||
|
||||
@@ -360,6 +364,7 @@ def run_sender(
|
||||
mqtt_username: MQTT username
|
||||
mqtt_password: MQTT password
|
||||
mqtt_prefix: MQTT topic prefix
|
||||
mqtt_tls: Enable TLS/SSL for MQTT connection
|
||||
"""
|
||||
sender = create_sender(
|
||||
port=port,
|
||||
@@ -372,6 +377,7 @@ def run_sender(
|
||||
mqtt_username=mqtt_username,
|
||||
mqtt_password=mqtt_password,
|
||||
mqtt_prefix=mqtt_prefix,
|
||||
mqtt_tls=mqtt_tls,
|
||||
)
|
||||
|
||||
# Set up signal handlers
|
||||
|
||||
Reference in New Issue
Block a user