summary refs log tree commit diff
diff options
context:
space:
mode:
authorpennae <github@quasiparticle.net>2021-05-26 18:59:37 +0200
committertomberek <tomberek@users.noreply.github.com>2021-10-22 16:06:55 -0400
commitc47fcb70c6885d6df869934280ebeb715ca7e6fd (patch)
treebbfcebc59a2ed5ca209a6f5584b5a04693973862
parent56d0b5cd6a61da42cca5be52b216bad9d1ff2b59 (diff)
downloadnixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.tar
nixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.tar.gz
nixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.tar.bz2
nixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.tar.lz
nixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.tar.xz
nixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.tar.zst
nixpkgs-c47fcb70c6885d6df869934280ebeb715ca7e6fd.zip
nixos/mosquitto: rewrite the test
expand the test to check all four forms of passwords, tls certificates (both
server and client), and that acl files are formatted properly.
-rw-r--r--nixos/tests/mosquitto.nix194
1 files changed, 146 insertions, 48 deletions
diff --git a/nixos/tests/mosquitto.nix b/nixos/tests/mosquitto.nix
index 1a534184066..eb47e97ba04 100644
--- a/nixos/tests/mosquitto.nix
+++ b/nixos/tests/mosquitto.nix
@@ -2,13 +2,59 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
 
 let
   port = 1888;
-  username = "mqtt";
+  tlsPort = 1889;
   password = "VERY_secret";
+  hashedPassword = "$7$101$/WJc4Mp+I+uYE9sR$o7z9rD1EYXHPwEP5GqQj6A7k4W1yVbePlb8TqNcuOLV9WNCiDgwHOB0JHC1WCtdkssqTBduBNUnUGd6kmZvDSw==";
   topic = "test/foo";
+
+  snakeOil = pkgs.runCommand "snakeoil-certs" {
+    buildInputs = [ pkgs.gnutls.bin ];
+    caTemplate = pkgs.writeText "snakeoil-ca.template" ''
+      cn = server
+      expiration_days = -1
+      cert_signing_key
+      ca
+    '';
+    certTemplate = pkgs.writeText "snakeoil-cert.template" ''
+      cn = server
+      expiration_days = -1
+      tls_www_server
+      encryption_key
+      signing_key
+    '';
+    userCertTemplate = pkgs.writeText "snakeoil-user-cert.template" ''
+      organization = snakeoil
+      cn = client1
+      expiration_days = -1
+      tls_www_client
+      encryption_key
+      signing_key
+    '';
+  } ''
+    mkdir "$out"
+
+    certtool -p --bits 2048 --outfile "$out/ca.key"
+    certtool -s --template "$caTemplate" --load-privkey "$out/ca.key" \
+                --outfile "$out/ca.crt"
+    certtool -p --bits 2048 --outfile "$out/server.key"
+    certtool -c --template "$certTemplate" \
+                --load-ca-privkey "$out/ca.key" \
+                --load-ca-certificate "$out/ca.crt" \
+                --load-privkey "$out/server.key" \
+                --outfile "$out/server.crt"
+
+    certtool -p --bits 2048 --outfile "$out/client1.key"
+    certtool -c --template "$userCertTemplate" \
+                --load-privkey "$out/client1.key" \
+                --load-ca-privkey "$out/ca.key" \
+                --load-ca-certificate "$out/ca.crt" \
+                --outfile "$out/client1.crt"
+  '';
+
 in {
   name = "mosquitto";
   meta = with pkgs.lib; {
-    maintainers = with maintainers; [ peterhoeg ];
+    maintainers = with maintainers; [ pennae peterhoeg ];
   };
 
   nodes = let
@@ -17,79 +63,131 @@ in {
     };
   in {
     server = { pkgs, ... }: {
-      networking.firewall.allowedTCPPorts = [ port ];
+      networking.firewall.allowedTCPPorts = [ port tlsPort ];
       services.mosquitto = {
         enable = true;
+        settings = {
+          sys_interval = 1;
+        };
         listeners = [
           {
             inherit port;
-            users.${username} = {
-              inherit password;
-              acl = [
-                "readwrite ${topic}"
-              ];
+            users = {
+              password_store = {
+                inherit password;
+              };
+              password_file = {
+                passwordFile = pkgs.writeText "mqtt-password" password;
+              };
+              hashed_store = {
+                inherit hashedPassword;
+              };
+              hashed_file = {
+                hashedPasswordFile = pkgs.writeText "mqtt-hashed-password" hashedPassword;
+              };
+
+              reader = {
+                inherit password;
+                acl = [
+                  "read ${topic}"
+                  "read $SYS/#" # so we always have something to read
+                ];
+              };
+              writer = {
+                inherit password;
+                acl = [ "write ${topic}" ];
+              };
+            };
+          }
+          {
+            port = tlsPort;
+            users.client1 = {
+              acl = [ "read $SYS/#" ];
+            };
+            settings = {
+              cafile = "${snakeOil}/ca.crt";
+              certfile = "${snakeOil}/server.crt";
+              keyfile = "${snakeOil}/server.key";
+              require_certificate = true;
+              use_identity_as_username = true;
             };
           }
         ];
       };
-
-      # disable private /tmp for this test
-      systemd.services.mosquitto.serviceConfig.PrivateTmp = lib.mkForce false;
     };
 
     client1 = client;
     client2 = client;
   };
 
-  testScript = let
-    file = "/tmp/msg";
-  in ''
-    def mosquitto_cmd(binary):
+  testScript = ''
+    def mosquitto_cmd(binary, user, topic, port):
         return (
-            "${pkgs.mosquitto}/bin/mosquitto_{} "
+            "mosquitto_{} "
             "-V mqttv311 "
             "-h server "
-            "-p ${toString port} "
-            "-u ${username} "
+            "-p {} "
+            "-u {} "
             "-P '${password}' "
-            "-t ${topic}"
-        ).format(binary)
+            "-t '{}'"
+        ).format(binary, port, user, topic)
 
 
-    def publish(args):
-        return "{} {}".format(mosquitto_cmd("pub"), args)
+    def publish(args, user, topic="${topic}", port=${toString port}):
+        return "{} {}".format(mosquitto_cmd("pub", user, topic, port), args)
 
 
-    def subscribe(args):
-        return "({} -C 1 {} | tee ${file} &)".format(mosquitto_cmd("sub"), args)
+    def subscribe(args, user, topic="${topic}", port=${toString port}):
+        return "{} -C 1 {}".format(mosquitto_cmd("sub", user, topic, port), args)
+
+    def parallel(*fns):
+        from threading import Thread
+        threads = [ Thread(target=fn) for fn in fns ]
+        for t in threads: t.start()
+        for t in threads: t.join()
 
 
     start_all()
     server.wait_for_unit("mosquitto.service")
 
-    for machine in server, client1, client2:
-        machine.fail("test -f ${file}")
-
-    # QoS = 0, so only one subscribers should get it
-    server.execute(subscribe("-q 0"))
-
-    # we need to give the subscribers some time to connect
-    client2.execute("sleep 5")
-    client2.succeed(publish("-m FOO -q 0"))
-
-    server.wait_until_succeeds("grep -q FOO ${file}")
-    server.execute("rm ${file}")
-
-    # QoS = 1, so both subscribers should get it
-    server.execute(subscribe("-q 1"))
-    client1.execute(subscribe("-q 1"))
-
-    # we need to give the subscribers some time to connect
-    client2.execute("sleep 5")
-    client2.succeed(publish("-m BAR -q 1"))
-
-    for machine in server, client1:
-        machine.wait_until_succeeds("grep -q BAR ${file}")
-        machine.execute("rm ${file}")
+    def check_passwords():
+        client1.succeed(publish("-m test", "password_store"))
+        client1.succeed(publish("-m test", "password_file"))
+        client1.succeed(publish("-m test", "hashed_store"))
+        client1.succeed(publish("-m test", "hashed_file"))
+
+    check_passwords()
+
+    def check_acl():
+        client1.succeed(subscribe("", "reader", topic="$SYS/#"))
+        client1.fail(subscribe("-W 5", "writer", topic="$SYS/#"))
+
+        parallel(
+            lambda: client1.succeed(subscribe("-i 3688cdd7-aa07-42a4-be22-cb9352917e40", "reader")),
+            lambda: [
+                server.wait_for_console_text("3688cdd7-aa07-42a4-be22-cb9352917e40"),
+                client2.succeed(publish("-m test", "writer"))
+            ])
+
+        parallel(
+            lambda: client1.fail(subscribe("-W 5 -i 24ff16a2-ae33-4a51-9098-1b417153c712", "reader")),
+            lambda: [
+                server.wait_for_console_text("24ff16a2-ae33-4a51-9098-1b417153c712"),
+                client2.succeed(publish("-m test", "reader"))
+            ])
+
+    check_acl()
+
+    def check_tls():
+        client1.succeed(
+            subscribe(
+                "--cafile ${snakeOil}/ca.crt "
+                "--cert ${snakeOil}/client1.crt "
+                "--key ${snakeOil}/client1.key",
+                topic="$SYS/#",
+                port=${toString tlsPort},
+                user="no_such_user"))
+
+    check_tls()
   '';
 })