1 import ./make-test-python.nix ({ pkgs, lib, ... }:
7 password = "VERY_secret";
8 hashedPassword = "$7$101$/WJc4Mp+I+uYE9sR$o7z9rD1EYXHPwEP5GqQj6A7k4W1yVbePlb8TqNcuOLV9WNCiDgwHOB0JHC1WCtdkssqTBduBNUnUGd6kmZvDSw==";
11 snakeOil = pkgs.runCommand "snakeoil-certs" {
12 buildInputs = [ pkgs.gnutls.bin ];
13 caTemplate = pkgs.writeText "snakeoil-ca.template" ''
19 certTemplate = pkgs.writeText "snakeoil-cert.template" ''
26 userCertTemplate = pkgs.writeText "snakeoil-user-cert.template" ''
27 organization = snakeoil
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"
57 meta = with pkgs.lib; {
58 maintainers = with maintainers; [ peterhoeg ];
62 client = { pkgs, ... }: {
63 environment.systemPackages = with pkgs; [ mosquitto ];
66 server = { pkgs, ... }: {
67 networking.firewall.allowedTCPPorts = [ port tlsPort anonPort ];
68 networking.useNetworkd = true;
69 services.mosquitto = {
82 passwordFile = pkgs.writeText "mqtt-password" password;
85 inherit hashedPassword;
88 hashedPasswordFile = pkgs.writeText "mqtt-hashed-password" hashedPassword;
95 "read $SYS/#" # so we always have something to read
100 acl = [ "write ${topic}" ];
107 acl = [ "read $SYS/#" ];
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;
119 omitPasswordAuth = true;
120 settings.allow_anonymous = true;
121 acl = [ "pattern read #" ];
124 password = "<ignored>" + password;
125 acl = [ "write ${topic}" ];
138 def mosquitto_cmd(binary, user, topic, port):
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)
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()
163 server.wait_for_console_text(uuid)
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/#"))
181 lambda: client1.succeed(subscribe("-i 3688cdd7-aa07-42a4-be22-cb9352917e40", "reader")),
183 wait_uuid("3688cdd7-aa07-42a4-be22-cb9352917e40"),
184 client2.succeed(publish("-m test", "writer"))
188 lambda: client1.fail(subscribe("-i 24ff16a2-ae33-4a51-9098-1b417153c712", "reader")),
190 wait_uuid("24ff16a2-ae33-4a51-9098-1b417153c712"),
191 client2.succeed(publish("-m test", "reader"))
194 with subtest("check tls"):
197 "--cafile ${snakeOil}/ca.crt "
198 "--cert ${snakeOil}/client1.crt "
199 "--key ${snakeOil}/client1.key",
201 port=${toString tlsPort},
202 user="no_such_user"))
204 with subtest("check omitPasswordAuth"):
206 lambda: client1.succeed(subscribe("-i fd56032c-d9cb-4813-a3b4-6be0e04c8fc3",
207 "anonReader", port=${toString anonPort})),
209 wait_uuid("fd56032c-d9cb-4813-a3b4-6be0e04c8fc3"),
210 client2.succeed(publish("-m test", "anonWriter", port=${toString anonPort}))