Commit 13f4fd43467e

Vincent Demeester <vincent@sbr.pm>
2025-11-28 14:00:54
feat: Add MQTT infrastructure with Mosquitto and Traefik
- Enable MQTT connectivity for IoT devices and Home Assistant - Provide encrypted MQTT access via Traefik with Let's Encrypt - Route MQTT traffic from rhea to demeter's Mosquitto broker 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 7a7ee84
secrets/demeter/mosquitto-homeassistant-password.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> piv-p256 ItIHHA A+jErFG84aEN4mrqsGXJtfPeloLDE+8u5PdhJNHAYHTl
+3gBSkbqr1KCLTJjXYhB5SrWa+8Yg+oAOVrPE8fXEBjQ
+-> piv-p256 ViCCtQ ArUm1Vex/CxRwYOfpUYZT+mxMjTLYytzrk5549+jMA1b
+ymzK34rGQnRC5fx1Bb/aLXUMu9oPA0nULYdaDDuDlS0
+-> ssh-ed25519 gecXFg QWSZRBQXgetnnhVIy9WL2LxWmBxNqiUim1cnmOXvr20
+ED5VczxRHyzagxCUadUURBbZjO5CuXOh8f9wIElp2kw
+--- RYlrvU8+If/DhD22OAkMySoUlYgyzMjJ3xJZIud/+I4
+�y �Ôc��$c'A_�Ql��N���e;�^�l���!sw�;�譛
\ No newline at end of file
systems/demeter/extra.nix
@@ -1,6 +1,7 @@
 {
   libx,
   globals,
+  config,
   ...
 }:
 {
@@ -15,6 +16,27 @@
   # TODO make it an option ? (otherwise I'll add it for all)
   users.users.vincent.linger = true;
   services = {
+    mosquitto = {
+      enable = true;
+      listeners = [
+        {
+          address = "0.0.0.0";
+          port = 1883;
+          omitPasswordAuth = false;
+          settings = {
+            allow_anonymous = false;
+          };
+          acl = [ "topic readwrite #" ];
+          users = {
+            homeassistant = {
+              acl = [ "readwrite #" ];
+              hashedPasswordFile = config.age.secrets."mosquitto-homeassistant-password".path;
+            };
+          };
+        }
+      ];
+    };
+
     wireguard = {
       enable = true;
       ips = libx.wg-ips globals.machines.demeter.net.vpn.ips;
@@ -22,4 +44,11 @@
       endpointPublicKey = "${globals.machines.kerkouane.net.vpn.pubkey}";
     };
   };
+
+  age.secrets."mosquitto-homeassistant-password" = {
+    file = ../../secrets/demeter/mosquitto-homeassistant-password.age;
+    mode = "400";
+    owner = "mosquitto";
+    group = "mosquitto";
+  };
 }
systems/rhea/extra.nix
@@ -33,6 +33,12 @@
           websecure = {
             address = ":443";
           };
+          mqtt = {
+            address = ":1883";
+          };
+          mqtts = {
+            address = ":8883";
+          };
         };
 
         # Certificate resolver using Gandi DNS
@@ -224,6 +230,32 @@
             };
             middlewares = syncthingMiddlewares;
           };
+          tcp = {
+            routers = {
+              mqtt = {
+                rule = "HostSNI(`*`)";
+                service = "mqtt";
+                entryPoints = [ "mqtt" ];
+              };
+              mqtts = {
+                rule = "HostSNI(`mqtt.sbr.pm`)";
+                service = "mqtt";
+                entryPoints = [ "mqtts" ];
+                tls = {
+                  certResolver = "letsencrypt";
+                };
+              };
+            };
+            services = {
+              mqtt = {
+                loadBalancer = {
+                  servers = [
+                    { address = "${builtins.head globals.machines.demeter.net.vpn.ips}:1883"; }
+                  ];
+                };
+              };
+            };
+          };
         };
     };
 
@@ -430,6 +462,8 @@
   networking.firewall.allowedTCPPorts = [
     80
     443
+    1883 # MQTT
+    8883 # MQTTS
   ];
 
   # Environment file for Gandi API key (managed by agenix)
globals.nix
@@ -574,5 +574,7 @@ _: {
       host = "rhea";
       aliases = [ "s" ];
     };
+    # MQTT on demeter
+    mqtt.host = "demeter";
   };
 }
secrets.nix
@@ -93,4 +93,5 @@ in
   "secrets/minica.pem.age".publicKeys = users ++ systems;
   "secrets/shikoku/aria2rpcsecret.age".publicKeys = users ++ [ shikoku ];
   "secrets/rhea/gandi.env.age".publicKeys = users ++ [ rhea ];
+  "secrets/demeter/mosquitto-homeassistant-password.age".publicKeys = users ++ [ demeter ];
 }