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