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.

authored by

pennae and committed by
tomberek
c47fcb70 56d0b5cd

+140 -42
+140 -42
nixos/tests/mosquitto.nix
··· 2 3 let 4 port = 1888; 5 - username = "mqtt"; 6 password = "VERY_secret"; 7 topic = "test/foo"; 8 in { 9 name = "mosquitto"; 10 meta = with pkgs.lib; { 11 - maintainers = with maintainers; [ peterhoeg ]; 12 }; 13 14 nodes = let ··· 17 }; 18 in { 19 server = { pkgs, ... }: { 20 - networking.firewall.allowedTCPPorts = [ port ]; 21 services.mosquitto = { 22 enable = true; 23 listeners = [ 24 { 25 inherit port; 26 - users.${username} = { 27 - inherit password; 28 - acl = [ 29 - "readwrite ${topic}" 30 - ]; 31 }; 32 } 33 ]; 34 }; 35 - 36 - # disable private /tmp for this test 37 - systemd.services.mosquitto.serviceConfig.PrivateTmp = lib.mkForce false; 38 }; 39 40 client1 = client; 41 client2 = client; 42 }; 43 44 - testScript = let 45 - file = "/tmp/msg"; 46 - in '' 47 - def mosquitto_cmd(binary): 48 return ( 49 - "${pkgs.mosquitto}/bin/mosquitto_{} " 50 "-V mqttv311 " 51 "-h server " 52 - "-p ${toString port} " 53 - "-u ${username} " 54 "-P '${password}' " 55 - "-t ${topic}" 56 - ).format(binary) 57 58 59 - def publish(args): 60 - return "{} {}".format(mosquitto_cmd("pub"), args) 61 62 63 - def subscribe(args): 64 - return "({} -C 1 {} | tee ${file} &)".format(mosquitto_cmd("sub"), args) 65 66 67 start_all() 68 server.wait_for_unit("mosquitto.service") 69 70 - for machine in server, client1, client2: 71 - machine.fail("test -f ${file}") 72 73 - # QoS = 0, so only one subscribers should get it 74 - server.execute(subscribe("-q 0")) 75 76 - # we need to give the subscribers some time to connect 77 - client2.execute("sleep 5") 78 - client2.succeed(publish("-m FOO -q 0")) 79 80 - server.wait_until_succeeds("grep -q FOO ${file}") 81 - server.execute("rm ${file}") 82 83 - # QoS = 1, so both subscribers should get it 84 - server.execute(subscribe("-q 1")) 85 - client1.execute(subscribe("-q 1")) 86 87 - # we need to give the subscribers some time to connect 88 - client2.execute("sleep 5") 89 - client2.succeed(publish("-m BAR -q 1")) 90 91 - for machine in server, client1: 92 - machine.wait_until_succeeds("grep -q BAR ${file}") 93 - machine.execute("rm ${file}") 94 ''; 95 })
··· 2 3 let 4 port = 1888; 5 + tlsPort = 1889; 6 password = "VERY_secret"; 7 + hashedPassword = "$7$101$/WJc4Mp+I+uYE9sR$o7z9rD1EYXHPwEP5GqQj6A7k4W1yVbePlb8TqNcuOLV9WNCiDgwHOB0JHC1WCtdkssqTBduBNUnUGd6kmZvDSw=="; 8 topic = "test/foo"; 9 + 10 + snakeOil = pkgs.runCommand "snakeoil-certs" { 11 + buildInputs = [ pkgs.gnutls.bin ]; 12 + caTemplate = pkgs.writeText "snakeoil-ca.template" '' 13 + cn = server 14 + expiration_days = -1 15 + cert_signing_key 16 + ca 17 + ''; 18 + certTemplate = pkgs.writeText "snakeoil-cert.template" '' 19 + cn = server 20 + expiration_days = -1 21 + tls_www_server 22 + encryption_key 23 + signing_key 24 + ''; 25 + userCertTemplate = pkgs.writeText "snakeoil-user-cert.template" '' 26 + organization = snakeoil 27 + cn = client1 28 + expiration_days = -1 29 + tls_www_client 30 + encryption_key 31 + signing_key 32 + ''; 33 + } '' 34 + mkdir "$out" 35 + 36 + certtool -p --bits 2048 --outfile "$out/ca.key" 37 + certtool -s --template "$caTemplate" --load-privkey "$out/ca.key" \ 38 + --outfile "$out/ca.crt" 39 + certtool -p --bits 2048 --outfile "$out/server.key" 40 + certtool -c --template "$certTemplate" \ 41 + --load-ca-privkey "$out/ca.key" \ 42 + --load-ca-certificate "$out/ca.crt" \ 43 + --load-privkey "$out/server.key" \ 44 + --outfile "$out/server.crt" 45 + 46 + certtool -p --bits 2048 --outfile "$out/client1.key" 47 + certtool -c --template "$userCertTemplate" \ 48 + --load-privkey "$out/client1.key" \ 49 + --load-ca-privkey "$out/ca.key" \ 50 + --load-ca-certificate "$out/ca.crt" \ 51 + --outfile "$out/client1.crt" 52 + ''; 53 + 54 in { 55 name = "mosquitto"; 56 meta = with pkgs.lib; { 57 + maintainers = with maintainers; [ pennae peterhoeg ]; 58 }; 59 60 nodes = let ··· 63 }; 64 in { 65 server = { pkgs, ... }: { 66 + networking.firewall.allowedTCPPorts = [ port tlsPort ]; 67 services.mosquitto = { 68 enable = true; 69 + settings = { 70 + sys_interval = 1; 71 + }; 72 listeners = [ 73 { 74 inherit port; 75 + users = { 76 + password_store = { 77 + inherit password; 78 + }; 79 + password_file = { 80 + passwordFile = pkgs.writeText "mqtt-password" password; 81 + }; 82 + hashed_store = { 83 + inherit hashedPassword; 84 + }; 85 + hashed_file = { 86 + hashedPasswordFile = pkgs.writeText "mqtt-hashed-password" hashedPassword; 87 + }; 88 + 89 + reader = { 90 + inherit password; 91 + acl = [ 92 + "read ${topic}" 93 + "read $SYS/#" # so we always have something to read 94 + ]; 95 + }; 96 + writer = { 97 + inherit password; 98 + acl = [ "write ${topic}" ]; 99 + }; 100 + }; 101 + } 102 + { 103 + port = tlsPort; 104 + users.client1 = { 105 + acl = [ "read $SYS/#" ]; 106 + }; 107 + settings = { 108 + cafile = "${snakeOil}/ca.crt"; 109 + certfile = "${snakeOil}/server.crt"; 110 + keyfile = "${snakeOil}/server.key"; 111 + require_certificate = true; 112 + use_identity_as_username = true; 113 }; 114 } 115 ]; 116 }; 117 }; 118 119 client1 = client; 120 client2 = client; 121 }; 122 123 + testScript = '' 124 + def mosquitto_cmd(binary, user, topic, port): 125 return ( 126 + "mosquitto_{} " 127 "-V mqttv311 " 128 "-h server " 129 + "-p {} " 130 + "-u {} " 131 "-P '${password}' " 132 + "-t '{}'" 133 + ).format(binary, port, user, topic) 134 135 136 + def publish(args, user, topic="${topic}", port=${toString port}): 137 + return "{} {}".format(mosquitto_cmd("pub", user, topic, port), args) 138 139 140 + def subscribe(args, user, topic="${topic}", port=${toString port}): 141 + return "{} -C 1 {}".format(mosquitto_cmd("sub", user, topic, port), args) 142 + 143 + def parallel(*fns): 144 + from threading import Thread 145 + threads = [ Thread(target=fn) for fn in fns ] 146 + for t in threads: t.start() 147 + for t in threads: t.join() 148 149 150 start_all() 151 server.wait_for_unit("mosquitto.service") 152 153 + def check_passwords(): 154 + client1.succeed(publish("-m test", "password_store")) 155 + client1.succeed(publish("-m test", "password_file")) 156 + client1.succeed(publish("-m test", "hashed_store")) 157 + client1.succeed(publish("-m test", "hashed_file")) 158 + 159 + check_passwords() 160 161 + def check_acl(): 162 + client1.succeed(subscribe("", "reader", topic="$SYS/#")) 163 + client1.fail(subscribe("-W 5", "writer", topic="$SYS/#")) 164 165 + parallel( 166 + lambda: client1.succeed(subscribe("-i 3688cdd7-aa07-42a4-be22-cb9352917e40", "reader")), 167 + lambda: [ 168 + server.wait_for_console_text("3688cdd7-aa07-42a4-be22-cb9352917e40"), 169 + client2.succeed(publish("-m test", "writer")) 170 + ]) 171 172 + parallel( 173 + lambda: client1.fail(subscribe("-W 5 -i 24ff16a2-ae33-4a51-9098-1b417153c712", "reader")), 174 + lambda: [ 175 + server.wait_for_console_text("24ff16a2-ae33-4a51-9098-1b417153c712"), 176 + client2.succeed(publish("-m test", "reader")) 177 + ]) 178 179 + check_acl() 180 181 + def check_tls(): 182 + client1.succeed( 183 + subscribe( 184 + "--cafile ${snakeOil}/ca.crt " 185 + "--cert ${snakeOil}/client1.crt " 186 + "--key ${snakeOil}/client1.key", 187 + topic="$SYS/#", 188 + port=${toString tlsPort}, 189 + user="no_such_user")) 190 191 + check_tls() 192 ''; 193 })