at 22.05-pre 6.6 kB view raw
1import ./make-test-python.nix ({ pkgs, lib, ... }: 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 = 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" 36 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" 46 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 ''; 54 55in { 56 name = "mosquitto"; 57 meta = with pkgs.lib; { 58 maintainers = with maintainers; [ pennae peterhoeg ]; 59 }; 60 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 services.mosquitto = { 69 enable = true; 70 settings = { 71 sys_interval = 1; 72 }; 73 listeners = [ 74 { 75 inherit port; 76 users = { 77 password_store = { 78 inherit password; 79 }; 80 password_file = { 81 passwordFile = pkgs.writeText "mqtt-password" password; 82 }; 83 hashed_store = { 84 inherit hashedPassword; 85 }; 86 hashed_file = { 87 hashedPasswordFile = pkgs.writeText "mqtt-hashed-password" hashedPassword; 88 }; 89 90 reader = { 91 inherit password; 92 acl = [ 93 "read ${topic}" 94 "read $SYS/#" # so we always have something to read 95 ]; 96 }; 97 writer = { 98 inherit password; 99 acl = [ "write ${topic}" ]; 100 }; 101 }; 102 } 103 { 104 port = tlsPort; 105 users.client1 = { 106 acl = [ "read $SYS/#" ]; 107 }; 108 settings = { 109 cafile = "${snakeOil}/ca.crt"; 110 certfile = "${snakeOil}/server.crt"; 111 keyfile = "${snakeOil}/server.key"; 112 require_certificate = true; 113 use_identity_as_username = true; 114 }; 115 } 116 { 117 port = anonPort; 118 omitPasswordAuth = true; 119 settings.allow_anonymous = true; 120 acl = [ "pattern read #" ]; 121 users = { 122 anonWriter = { 123 password = "<ignored>" + password; 124 acl = [ "write ${topic}" ]; 125 }; 126 }; 127 } 128 ]; 129 }; 130 }; 131 132 client1 = client; 133 client2 = client; 134 }; 135 136 testScript = '' 137 def mosquitto_cmd(binary, user, topic, port): 138 return ( 139 "mosquitto_{} " 140 "-V mqttv311 " 141 "-h server " 142 "-p {} " 143 "-u {} " 144 "-P '${password}' " 145 "-t '{}'" 146 ).format(binary, port, user, topic) 147 148 149 def publish(args, user, topic="${topic}", port=${toString port}): 150 return "{} {}".format(mosquitto_cmd("pub", user, topic, port), args) 151 152 def subscribe(args, user, topic="${topic}", port=${toString port}): 153 return "{} -W 5 -C 1 {}".format(mosquitto_cmd("sub", user, topic, port), args) 154 155 def parallel(*fns): 156 from threading import Thread 157 threads = [ Thread(target=fn) for fn in fns ] 158 for t in threads: t.start() 159 for t in threads: t.join() 160 161 162 start_all() 163 server.wait_for_unit("mosquitto.service") 164 165 with subtest("check passwords"): 166 client1.succeed(publish("-m test", "password_store")) 167 client1.succeed(publish("-m test", "password_file")) 168 client1.succeed(publish("-m test", "hashed_store")) 169 client1.succeed(publish("-m test", "hashed_file")) 170 171 with subtest("check acl"): 172 client1.succeed(subscribe("", "reader", topic="$SYS/#")) 173 client1.fail(subscribe("", "writer", topic="$SYS/#")) 174 175 parallel( 176 lambda: client1.succeed(subscribe("-i 3688cdd7-aa07-42a4-be22-cb9352917e40", "reader")), 177 lambda: [ 178 server.wait_for_console_text("3688cdd7-aa07-42a4-be22-cb9352917e40"), 179 client2.succeed(publish("-m test", "writer")) 180 ]) 181 182 parallel( 183 lambda: client1.fail(subscribe("-i 24ff16a2-ae33-4a51-9098-1b417153c712", "reader")), 184 lambda: [ 185 server.wait_for_console_text("24ff16a2-ae33-4a51-9098-1b417153c712"), 186 client2.succeed(publish("-m test", "reader")) 187 ]) 188 189 with subtest("check tls"): 190 client1.succeed( 191 subscribe( 192 "--cafile ${snakeOil}/ca.crt " 193 "--cert ${snakeOil}/client1.crt " 194 "--key ${snakeOil}/client1.key", 195 topic="$SYS/#", 196 port=${toString tlsPort}, 197 user="no_such_user")) 198 199 with subtest("check omitPasswordAuth"): 200 parallel( 201 lambda: client1.succeed(subscribe("-i fd56032c-d9cb-4813-a3b4-6be0e04c8fc3", 202 "anonReader", port=${toString anonPort})), 203 lambda: [ 204 server.wait_for_console_text("fd56032c-d9cb-4813-a3b4-6be0e04c8fc3"), 205 client2.succeed(publish("-m test", "anonWriter", port=${toString anonPort})) 206 ]) 207 ''; 208})