notes: 2.3.0 -> 2.3.1 (#352950)
[NixPkgs.git] / nixos / tests / mosquitto.nix
blobeca29292721fde0e3027c091567b969a21d2f8dd
1 import ./make-test-python.nix ({ pkgs, lib, ... }:
3 let
4   port = 1888;
5   tlsPort = 1889;
6   anonPort = 1890;
7   password = "VERY_secret";
8   hashedPassword = "$7$101$/WJc4Mp+I+uYE9sR$o7z9rD1EYXHPwEP5GqQj6A7k4W1yVbePlb8TqNcuOLV9WNCiDgwHOB0JHC1WCtdkssqTBduBNUnUGd6kmZvDSw==";
9   topic = "test/foo";
11   snakeOil = pkgs.runCommand "snakeoil-certs" {
12     buildInputs = [ pkgs.gnutls.bin ];
13     caTemplate = pkgs.writeText "snakeoil-ca.template" ''
14       cn = server
15       expiration_days = -1
16       cert_signing_key
17       ca
18     '';
19     certTemplate = pkgs.writeText "snakeoil-cert.template" ''
20       cn = server
21       expiration_days = -1
22       tls_www_server
23       encryption_key
24       signing_key
25     '';
26     userCertTemplate = pkgs.writeText "snakeoil-user-cert.template" ''
27       organization = snakeoil
28       cn = client1
29       expiration_days = -1
30       tls_www_client
31       encryption_key
32       signing_key
33     '';
34   } ''
35     mkdir "$out"
37     certtool -p --bits 2048 --outfile "$out/ca.key"
38     certtool -s --template "$caTemplate" --load-privkey "$out/ca.key" \
39                 --outfile "$out/ca.crt"
40     certtool -p --bits 2048 --outfile "$out/server.key"
41     certtool -c --template "$certTemplate" \
42                 --load-ca-privkey "$out/ca.key" \
43                 --load-ca-certificate "$out/ca.crt" \
44                 --load-privkey "$out/server.key" \
45                 --outfile "$out/server.crt"
47     certtool -p --bits 2048 --outfile "$out/client1.key"
48     certtool -c --template "$userCertTemplate" \
49                 --load-privkey "$out/client1.key" \
50                 --load-ca-privkey "$out/ca.key" \
51                 --load-ca-certificate "$out/ca.crt" \
52                 --outfile "$out/client1.crt"
53   '';
55 in {
56   name = "mosquitto";
57   meta = with pkgs.lib; {
58     maintainers = with maintainers; [ peterhoeg ];
59   };
61   nodes = let
62     client = { pkgs, ... }: {
63       environment.systemPackages = with pkgs; [ mosquitto ];
64     };
65   in {
66     server = { pkgs, ... }: {
67       networking.firewall.allowedTCPPorts = [ port tlsPort anonPort ];
68       networking.useNetworkd = true;
69       services.mosquitto = {
70         enable = true;
71         settings = {
72           sys_interval = 1;
73         };
74         listeners = [
75           {
76             inherit port;
77             users = {
78               password_store = {
79                 inherit password;
80               };
81               password_file = {
82                 passwordFile = pkgs.writeText "mqtt-password" password;
83               };
84               hashed_store = {
85                 inherit hashedPassword;
86               };
87               hashed_file = {
88                 hashedPasswordFile = pkgs.writeText "mqtt-hashed-password" hashedPassword;
89               };
91               reader = {
92                 inherit password;
93                 acl = [
94                   "read ${topic}"
95                   "read $SYS/#" # so we always have something to read
96                 ];
97               };
98               writer = {
99                 inherit password;
100                 acl = [ "write ${topic}" ];
101               };
102             };
103           }
104           {
105             port = tlsPort;
106             users.client1 = {
107               acl = [ "read $SYS/#" ];
108             };
109             settings = {
110               cafile = "${snakeOil}/ca.crt";
111               certfile = "${snakeOil}/server.crt";
112               keyfile = "${snakeOil}/server.key";
113               require_certificate = true;
114               use_identity_as_username = true;
115             };
116           }
117           {
118             port = anonPort;
119             omitPasswordAuth = true;
120             settings.allow_anonymous = true;
121             acl = [ "pattern read #" ];
122             users = {
123               anonWriter = {
124                 password = "<ignored>" + password;
125                 acl = [ "write ${topic}" ];
126               };
127             };
128           }
129         ];
130       };
131     };
133     client1 = client;
134     client2 = client;
135   };
137   testScript = ''
138     def mosquitto_cmd(binary, user, topic, port):
139         return (
140             "mosquitto_{} "
141             "-V mqttv311 "
142             "-h server "
143             "-p {} "
144             "-u {} "
145             "-P '${password}' "
146             "-t '{}'"
147         ).format(binary, port, user, topic)
150     def publish(args, user, topic="${topic}", port=${toString port}):
151         return "{} {}".format(mosquitto_cmd("pub", user, topic, port), args)
153     def subscribe(args, user, topic="${topic}", port=${toString port}):
154         return "{} -W 5 -C 1 {}".format(mosquitto_cmd("sub", user, topic, port), args)
156     def parallel(*fns):
157         from threading import Thread
158         threads = [ Thread(target=fn) for fn in fns ]
159         for t in threads: t.start()
160         for t in threads: t.join()
162     def wait_uuid(uuid):
163         server.wait_for_console_text(uuid)
164         return None
167     start_all()
168     server.wait_for_unit("mosquitto.service")
170     with subtest("check passwords"):
171         client1.succeed(publish("-m test", "password_store"))
172         client1.succeed(publish("-m test", "password_file"))
173         client1.succeed(publish("-m test", "hashed_store"))
174         client1.succeed(publish("-m test", "hashed_file"))
176     with subtest("check acl"):
177         client1.succeed(subscribe("", "reader", topic="$SYS/#"))
178         client1.fail(subscribe("", "writer", topic="$SYS/#"))
180         parallel(
181             lambda: client1.succeed(subscribe("-i 3688cdd7-aa07-42a4-be22-cb9352917e40", "reader")),
182             lambda: [
183                 wait_uuid("3688cdd7-aa07-42a4-be22-cb9352917e40"),
184                 client2.succeed(publish("-m test", "writer"))
185             ])
187         parallel(
188             lambda: client1.fail(subscribe("-i 24ff16a2-ae33-4a51-9098-1b417153c712", "reader")),
189             lambda: [
190                 wait_uuid("24ff16a2-ae33-4a51-9098-1b417153c712"),
191                 client2.succeed(publish("-m test", "reader"))
192             ])
194     with subtest("check tls"):
195         client1.succeed(
196             subscribe(
197                 "--cafile ${snakeOil}/ca.crt "
198                 "--cert ${snakeOil}/client1.crt "
199                 "--key ${snakeOil}/client1.key",
200                 topic="$SYS/#",
201                 port=${toString tlsPort},
202                 user="no_such_user"))
204     with subtest("check omitPasswordAuth"):
205         parallel(
206             lambda: client1.succeed(subscribe("-i fd56032c-d9cb-4813-a3b4-6be0e04c8fc3",
207                 "anonReader", port=${toString anonPort})),
208             lambda: [
209                 wait_uuid("fd56032c-d9cb-4813-a3b4-6be0e04c8fc3"),
210                 client2.succeed(publish("-m test", "anonWriter", port=${toString anonPort}))
211             ])
212   '';