Merge pull request #106073 from minijackson/tinc-rfc42-and-tests

nixos/tinc: rfc42 and tests

authored by Linus Heckemann and committed by GitHub c40f0602 7a6203ff

+521 -13
+224 -13
nixos/modules/services/networking/tinc.nix
··· 1 { config, lib, pkgs, ... }: 2 3 with lib; 4 5 - let 6 7 - cfg = config.services.tinc; 8 9 - in 10 11 { 12 13 ###### interface ··· 18 19 networks = mkOption { 20 default = { }; 21 - type = with types; attrsOf (submodule { 22 options = { 23 24 extraConfig = mkOption { ··· 26 type = types.lines; 27 description = '' 28 Extra lines to add to the tinc service configuration file. 29 ''; 30 }; 31 ··· 72 description = '' 73 The name of the host in the network as well as the configuration for that host. 74 This name should only contain alphanumerics and underscores. 75 ''; 76 }; 77 ··· 79 default = "tun"; 80 type = types.enum [ "tun" "tap" ]; 81 description = '' 82 - The type of virtual interface used for the network connection 83 ''; 84 }; 85 ··· 116 The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. 117 118 Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. 119 ''; 120 }; 121 }; 122 - }); 123 124 description = '' 125 Defines the tinc networks which will be started. ··· 144 "tinc/${network}/tinc.conf" = { 145 mode = "0444"; 146 text = '' 147 - Name = ${if data.name == null then "$HOST" else data.name} 148 - DeviceType = ${data.interfaceType} 149 - ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"} 150 - ${optionalString (data.rsaPrivateKeyFile != null) "PrivateKeyFile = ${data.rsaPrivateKeyFile}"} 151 - ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"} 152 - ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"} 153 - Interface = tinc.${network} 154 ${data.extraConfig} 155 ''; 156 }; ··· 222 223 }; 224 225 }
··· 1 { config, lib, pkgs, ... }: 2 3 with lib; 4 + let 5 + cfg = config.services.tinc; 6 7 + mkValueString = value: 8 + if value == true then "yes" 9 + else if value == false then "no" 10 + else generators.mkValueStringDefault { } value; 11 + 12 + toTincConf = generators.toKeyValue { 13 + listsAsDuplicateKeys = true; 14 + mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } "="; 15 + }; 16 + 17 + tincConfType = with types; 18 + let 19 + valueType = oneOf [ bool str int ]; 20 + in 21 + attrsOf (either valueType (listOf valueType)); 22 + 23 + addressSubmodule = { 24 + options = { 25 + address = mkOption { 26 + type = types.str; 27 + description = "The external IP address or hostname where the host can be reached."; 28 + }; 29 + 30 + port = mkOption { 31 + type = types.nullOr types.port; 32 + default = null; 33 + description = '' 34 + The port where the host can be reached. 35 + 36 + If no port is specified, the default Port is used. 37 + ''; 38 + }; 39 + }; 40 + }; 41 + 42 + subnetSubmodule = { 43 + options = { 44 + address = mkOption { 45 + type = types.str; 46 + description = '' 47 + The subnet of this host. 48 + 49 + Subnets can either be single MAC, IPv4 or IPv6 addresses, in which case 50 + a subnet consisting of only that single address is assumed, or they can 51 + be a IPv4 or IPv6 network address with a prefix length. 52 + 53 + IPv4 subnets are notated like 192.168.1.0/24, IPv6 subnets are notated 54 + like fec0:0:0:1::/64. MAC addresses are notated like 0:1a:2b:3c:4d:5e. 55 + 56 + Note that subnets like 192.168.1.1/24 are invalid. 57 + ''; 58 + }; 59 + 60 + prefixLength = mkOption { 61 + type = with types; nullOr (addCheck int (n: n >= 0 && n <= 128)); 62 + default = null; 63 + description = '' 64 + The prefix length of the subnet. 65 + 66 + If null, a subnet consisting of only that single address is assumed. 67 + 68 + This conforms to standard CIDR notation as described in RFC1519. 69 + ''; 70 + }; 71 + 72 + weight = mkOption { 73 + type = types.ints.unsigned; 74 + default = 10; 75 + description = '' 76 + Indicates the priority over identical Subnets owned by different nodes. 77 + 78 + Lower values indicate higher priority. Packets will be sent to the 79 + node with the highest priority, unless that node is not reachable, in 80 + which case the node with the next highest priority will be tried, and 81 + so on. 82 + ''; 83 + }; 84 + }; 85 + }; 86 + 87 + hostSubmodule = { config, ... }: { 88 + options = { 89 + addresses = mkOption { 90 + type = types.listOf (types.submodule addressSubmodule); 91 + default = [ ]; 92 + description = '' 93 + The external address where the host can be reached. This will set this 94 + host's <option>settings.Address</option> option. 95 + 96 + This variable is only required if you want to connect to this host. 97 + ''; 98 + }; 99 + 100 + subnets = mkOption { 101 + type = types.listOf (types.submodule subnetSubmodule); 102 + default = [ ]; 103 + description = '' 104 + The subnets which this tinc daemon will serve. This will set this 105 + host's <option>settings.Subnet</option> option. 106 + 107 + Tinc tries to look up which other daemon it should send a packet to by 108 + searching the appropriate subnet. If the packet matches a subnet, it 109 + will be sent to the daemon who has this subnet in his host 110 + configuration file. 111 + ''; 112 + }; 113 + 114 + rsaPublicKey = mkOption { 115 + type = types.str; 116 + default = ""; 117 + description = '' 118 + Legacy RSA public key of the host in PEM format, including start and 119 + end markers. 120 + 121 + This will be appended as-is in the host's configuration file. 122 + 123 + The ed25519 public key can be specified using the 124 + <option>settings.Ed25519PublicKey</option> option instead. 125 + ''; 126 + }; 127 128 + settings = mkOption { 129 + default = { }; 130 + type = types.submodule { freeformType = tincConfType; }; 131 + description = '' 132 + Configuration for this host. 133 134 + See <link xlink:href="https://tinc-vpn.org/documentation-1.1/Host-configuration-variables.html"/> 135 + for supported values. 136 + ''; 137 + }; 138 + }; 139 140 + config.settings = { 141 + Address = mkDefault (map 142 + (address: "${address.address} ${toString address.port}") 143 + config.addresses); 144 + 145 + Subnet = mkDefault (map 146 + (subnet: 147 + if subnet.prefixLength == null then "${subnet.address}#${toString subnet.weight}" 148 + else "${subnet.address}/${toString subnet.prefixLength}#${toString subnet.weight}") 149 + config.subnets); 150 + }; 151 + }; 152 + 153 + in 154 { 155 156 ###### interface ··· 161 162 networks = mkOption { 163 default = { }; 164 + type = with types; attrsOf (submodule ({ config, ... }: { 165 options = { 166 167 extraConfig = mkOption { ··· 169 type = types.lines; 170 description = '' 171 Extra lines to add to the tinc service configuration file. 172 + 173 + Note that using the declarative <option>service.tinc.networks.&lt;name&gt;.settings</option> 174 + option is preferred. 175 ''; 176 }; 177 ··· 218 description = '' 219 The name of the host in the network as well as the configuration for that host. 220 This name should only contain alphanumerics and underscores. 221 + 222 + Note that using the declarative <option>service.tinc.networks.&lt;name&gt;.hostSettings</option> 223 + option is preferred. 224 + ''; 225 + }; 226 + 227 + hostSettings = mkOption { 228 + default = { }; 229 + example = literalExample '' 230 + { 231 + host1 = { 232 + addresses = [ 233 + { address = "192.168.1.42"; } 234 + { address = "192.168.1.42"; port = 1655; } 235 + ]; 236 + subnets = [ { address = "10.0.0.42"; } ]; 237 + rsaPublicKey = "..."; 238 + settings = { 239 + Ed25519PublicKey = "..."; 240 + }; 241 + }; 242 + host2 = { 243 + subnets = [ { address = "10.0.1.0"; prefixLength = 24; weight = 2; } ]; 244 + rsaPublicKey = "..."; 245 + settings = { 246 + Compression = 10; 247 + }; 248 + }; 249 + } 250 + ''; 251 + type = types.attrsOf (types.submodule hostSubmodule); 252 + description = '' 253 + The name of the host in the network as well as the configuration for that host. 254 + This name should only contain alphanumerics and underscores. 255 ''; 256 }; 257 ··· 259 default = "tun"; 260 type = types.enum [ "tun" "tap" ]; 261 description = '' 262 + The type of virtual interface used for the network connection. 263 ''; 264 }; 265 ··· 296 The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. 297 298 Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. 299 + ''; 300 + }; 301 + 302 + settings = mkOption { 303 + default = { }; 304 + type = types.submodule { freeformType = tincConfType; }; 305 + example = literalExample '' 306 + { 307 + Interface = "custom.interface"; 308 + DirectOnly = true; 309 + Mode = "switch"; 310 + } 311 + ''; 312 + description = '' 313 + Configuration of the Tinc daemon for this network. 314 + 315 + See <link xlink:href="https://tinc-vpn.org/documentation-1.1/Main-configuration-variables.html"/> 316 + for supported values. 317 ''; 318 }; 319 }; 320 + 321 + config = { 322 + hosts = mapAttrs 323 + (hostname: host: '' 324 + ${toTincConf host.settings} 325 + ${host.rsaPublicKey} 326 + '') 327 + config.hostSettings; 328 + 329 + settings = { 330 + DeviceType = mkDefault config.interfaceType; 331 + Name = mkDefault (if config.name == null then "$HOST" else config.name); 332 + Ed25519PrivateKeyFile = mkIf (config.ed25519PrivateKeyFile != null) (mkDefault config.ed25519PrivateKeyFile); 333 + PrivateKeyFile = mkIf (config.rsaPrivateKeyFile != null) (mkDefault config.rsaPrivateKeyFile); 334 + ListenAddress = mkIf (config.listenAddress != null) (mkDefault config.listenAddress); 335 + BindToAddress = mkIf (config.bindToAddress != null) (mkDefault config.bindToAddress); 336 + }; 337 + }; 338 + })); 339 340 description = '' 341 Defines the tinc networks which will be started. ··· 360 "tinc/${network}/tinc.conf" = { 361 mode = "0444"; 362 text = '' 363 + ${toTincConf ({ Interface = "tinc.${network}"; } // data.settings)} 364 ${data.extraConfig} 365 ''; 366 }; ··· 432 433 }; 434 435 + meta.maintainers = with maintainers; [ minijackson ]; 436 }
+1
nixos/tests/all-tests.nix
··· 375 telegraf = handleTest ./telegraf.nix {}; 376 tiddlywiki = handleTest ./tiddlywiki.nix {}; 377 timezone = handleTest ./timezone.nix {}; 378 tinydns = handleTest ./tinydns.nix {}; 379 tor = handleTest ./tor.nix {}; 380 # traefik test relies on docker-containers
··· 375 telegraf = handleTest ./telegraf.nix {}; 376 tiddlywiki = handleTest ./tiddlywiki.nix {}; 377 timezone = handleTest ./timezone.nix {}; 378 + tinc = handleTest ./tinc {}; 379 tinydns = handleTest ./tinydns.nix {}; 380 tor = handleTest ./tor.nix {}; 381 # traefik test relies on docker-containers
+139
nixos/tests/tinc/default.nix
···
··· 1 + import ../make-test-python.nix ({ lib, ... }: 2 + let 3 + snakeoil-keys = import ./snakeoil-keys.nix; 4 + 5 + hosts = lib.attrNames snakeoil-keys; 6 + 7 + subnetOf = name: config: 8 + let 9 + subnets = config.services.tinc.networks.myNetwork.hostSettings.${name}.subnets; 10 + in 11 + (builtins.head subnets).address; 12 + 13 + makeTincHost = name: { subnet, extraConfig ? { } }: lib.mkMerge [ 14 + { 15 + subnets = [{ address = subnet; }]; 16 + settings = { 17 + Ed25519PublicKey = snakeoil-keys.${name}.ed25519Public; 18 + }; 19 + rsaPublicKey = snakeoil-keys.${name}.rsaPublic; 20 + } 21 + extraConfig 22 + ]; 23 + 24 + makeTincNode = { config, ... }: name: extraConfig: lib.mkMerge [ 25 + { 26 + services.tinc.networks.myNetwork = { 27 + inherit name; 28 + rsaPrivateKeyFile = 29 + builtins.toFile "rsa.priv" snakeoil-keys.${name}.rsaPrivate; 30 + ed25519PrivateKeyFile = 31 + builtins.toFile "ed25519.priv" snakeoil-keys.${name}.ed25519Private; 32 + 33 + hostSettings = lib.mapAttrs makeTincHost { 34 + static = { 35 + subnet = "10.0.0.11"; 36 + # Only specify the addresses in the node's vlans, Tinc does not 37 + # seem to try each one, unlike the documentation suggests... 38 + extraConfig.addresses = map 39 + (vlan: { address = "192.168.${toString vlan}.11"; port = 655; }) 40 + config.virtualisation.vlans; 41 + }; 42 + dynamic1 = { subnet = "10.0.0.21"; }; 43 + dynamic2 = { subnet = "10.0.0.22"; }; 44 + }; 45 + }; 46 + 47 + networking.useDHCP = false; 48 + 49 + networking.interfaces."tinc.myNetwork" = { 50 + virtual = true; 51 + virtualType = "tun"; 52 + ipv4.addresses = [{ 53 + address = subnetOf name config; 54 + prefixLength = 24; 55 + }]; 56 + }; 57 + 58 + # Prevents race condition between NixOS service and tinc creating the 59 + # interface. 60 + # See: https://github.com/NixOS/nixpkgs/issues/27070 61 + systemd.services."tinc.myNetwork" = { 62 + after = [ "network-addresses-tinc.myNetwork.service" ]; 63 + requires = [ "network-addresses-tinc.myNetwork.service" ]; 64 + }; 65 + 66 + networking.firewall.allowedTCPPorts = [ 655 ]; 67 + networking.firewall.allowedUDPPorts = [ 655 ]; 68 + } 69 + extraConfig 70 + ]; 71 + 72 + in 73 + { 74 + name = "tinc"; 75 + meta.maintainers = with lib.maintainers; [ minijackson ]; 76 + 77 + nodes = { 78 + 79 + static = { ... } @ args: 80 + makeTincNode args "static" { 81 + virtualisation.vlans = [ 1 2 ]; 82 + 83 + networking.interfaces.eth1.ipv4.addresses = [{ 84 + address = "192.168.1.11"; 85 + prefixLength = 24; 86 + }]; 87 + 88 + networking.interfaces.eth2.ipv4.addresses = [{ 89 + address = "192.168.2.11"; 90 + prefixLength = 24; 91 + }]; 92 + }; 93 + 94 + 95 + dynamic1 = { ... } @ args: 96 + makeTincNode args "dynamic1" { 97 + virtualisation.vlans = [ 1 ]; 98 + }; 99 + 100 + dynamic2 = { ... } @ args: 101 + makeTincNode args "dynamic2" { 102 + virtualisation.vlans = [ 2 ]; 103 + }; 104 + 105 + }; 106 + 107 + testScript = '' 108 + start_all() 109 + 110 + static.wait_for_unit("tinc.myNetwork.service") 111 + dynamic1.wait_for_unit("tinc.myNetwork.service") 112 + dynamic2.wait_for_unit("tinc.myNetwork.service") 113 + 114 + # Static is accessible by the other hosts 115 + dynamic1.succeed("ping -c5 192.168.1.11") 116 + dynamic2.succeed("ping -c5 192.168.2.11") 117 + 118 + # The other hosts are in separate vlans 119 + dynamic1.fail("ping -c5 192.168.2.11") 120 + dynamic2.fail("ping -c5 192.168.1.11") 121 + 122 + # Each host can ping themselves through Tinc 123 + static.succeed("ping -c5 10.0.0.11") 124 + dynamic1.succeed("ping -c5 10.0.0.21") 125 + dynamic2.succeed("ping -c5 10.0.0.22") 126 + 127 + # Static is accessible by the other hosts through Tinc 128 + dynamic1.succeed("ping -c5 10.0.0.11") 129 + dynamic2.succeed("ping -c5 10.0.0.11") 130 + 131 + # Static can access the other hosts through Tinc 132 + static.succeed("ping -c5 10.0.0.21") 133 + static.succeed("ping -c5 10.0.0.22") 134 + 135 + # The other hosts in separate vlans can access each other through Tinc 136 + dynamic1.succeed("ping -c5 10.0.0.22") 137 + dynamic2.succeed("ping -c5 10.0.0.21") 138 + ''; 139 + })
+157
nixos/tests/tinc/snakeoil-keys.nix
···
··· 1 + { 2 + static = { 3 + ed25519Private = '' 4 + -----BEGIN ED25519 PRIVATE KEY----- 5 + IPR+ur5LfVdm6VlR1+FGIkbkL8Enkb9sejBa/JP6tXkg/vHoraIp70srb6jAUFm5 6 + 3YbCJiBjLW3dy16qM5PovBoWtr5hoqYYA9dFLOys8FBUFFsIGfKhnbk7g25iwxbO 7 + -----END ED25519 PRIVATE KEY----- 8 + ''; 9 + 10 + ed25519Public = "AqV7aeIqKGGQfXxijMLfRAVRBLixnS45G5OoduIc8mD"; 11 + 12 + rsaPrivate = '' 13 + -----BEGIN RSA PRIVATE KEY----- 14 + MIIEpAIBAAKCAQEAxDHl0TIhhT2yH5rT+Q7MLnj+Ir8bbs3uaPqnzcxWzN1EfVP8 15 + TWt5fSTrF2Dc78Kyu5ZNALrp7tUj0GZAegp1YeYJ28p3qTwCveywtCwbB4dI987S 16 + yJwq95kE9aoyLa+cT99VwSTdb2YowQv2tWj/idxE3oJ+qZjy9tE5mysXm7jmTQDx 17 + +U0XmNe6MHjKXc01Ener41u0ykJLeUfdgJ1zEyM2rQGtaHpIXfMT6kmxCaMcAMLg 18 + YFpI38/1pQGQtROKdGOaUomx2m058bkMsJhTiBjESiLRDElRGxmMJ732crGJP0GR 19 + ChJkaX/CnxHq7R0daZfwoTVHRu6N7WDbFQL5twIDAQABAoIBAQCM/fLTIHyYXRr5 20 + vXFhxXGUYBz56W6UdWdEiAU5TwR92vFSQ53IIVlARtyvg0ui/b8mMcAKq0hb+03u 21 + gN0LFyL+BKvHCLxvoRGzXTorcJrIET+t3jL6OchjANNgnDvNOytQ9wWQdKaxXLAi 22 + 8y8LdXZWozXW1d6ikKjiGL+WNCSWIcq83ktSJZcohihptU9Un16FYQzdolSC8RtI 23 + XyT7i1ye6hW/wJTJxqZ4taX3EPat85kXS234VGSqg9bb2A1yE+U8Rq37bf8AKldJ 24 + NUQB3JyxnkYGJcqvzDmz139+744VWxDRvXDA5vU29LC6f8bGBvwEttD98QW+pgmB 25 + 1NBU1Uo5AoGBAOzUk6k74h1RarwXaftjh/9Pures0CfNNnrkJApzFCh4bAoHNxq6 26 + SSXqLcc/vvX2+YaZ72nn5YTo+JLQP6evM9oUaqRMAxa3nzoNCtF8U2r48UWmoUQE 27 + aZCYbD3m7IVWFacCKRVaVTMZMTTicypSnXcbCSIEH8PRs9+L4jkHgql9AoGBANQT 28 + TZECVhIaQnyRiKWlUE8G1QKzXIxjmfyirBe+ftlIG2XMXasAtQ4VRxpnorgqUnIH 29 + BVrIbvRx21zlqwZbrZvyb1jHWRoyi1cqBPijpYBUm5LbV2jgHPhnfhRVqdD4CDKj 30 + NQzIQrNymFaMWAoOQv/DE3g+Txr0fm9Ztu8ZRXZDAoGAHh3SQT0aPfwyhIS9t3gq 31 + vS7YYa8aMVWJTgthAessbxERPB06xq1Vy/qBo8rZb9HeXV2J8n/I0iQGKDVPQvWm 32 + tF7QSOBZrDPhjbJG4+jZesr5c5ADBfFBs1+OtDh/b11JF5nQu6RnHT5g4YbCemlT 33 + GOhZOvgnSfGK3CyfsfzggskCgYEAmpKDK5kPUNxw70hH16v5L9Bj+zbt0qlZ+Ag8 34 + 9IV1ATuMNJNTBitay6v4iidVM3QtaUzyuytxq5s87qW7FMRHcm2ueH+70ttaMiq/ 35 + OtZT74g7aDuUpy0KEIemHn4dauENYJMSPIHOE+sHW7WpCZNBhBcUHsUTdSsU6GX0 36 + bqr1tO8CgYBpZdR2OoX/rn8nwjmtBOH38aPnCpaAfdI2Eq2Lg6DjksP6TBt53a+R 37 + m1lk6Kt37BPPZQ85SBr7ywvDgUzfoD7uSmHujF2JUHPsdrg9nx7pNIGlW6DlS9OU 38 + oNXGAJ/6/y6F8uDbToUfrwFq5tKMypEEa32kFtxb9f0XQ5fSgHrBEw== 39 + -----END RSA PRIVATE KEY----- 40 + ''; 41 + 42 + rsaPublic = '' 43 + -----BEGIN RSA PUBLIC KEY----- 44 + MIIBCgKCAQEAxDHl0TIhhT2yH5rT+Q7MLnj+Ir8bbs3uaPqnzcxWzN1EfVP8TWt5 45 + fSTrF2Dc78Kyu5ZNALrp7tUj0GZAegp1YeYJ28p3qTwCveywtCwbB4dI987SyJwq 46 + 95kE9aoyLa+cT99VwSTdb2YowQv2tWj/idxE3oJ+qZjy9tE5mysXm7jmTQDx+U0X 47 + mNe6MHjKXc01Ener41u0ykJLeUfdgJ1zEyM2rQGtaHpIXfMT6kmxCaMcAMLgYFpI 48 + 38/1pQGQtROKdGOaUomx2m058bkMsJhTiBjESiLRDElRGxmMJ732crGJP0GRChJk 49 + aX/CnxHq7R0daZfwoTVHRu6N7WDbFQL5twIDAQAB 50 + -----END RSA PUBLIC KEY----- 51 + ''; 52 + }; 53 + 54 + dynamic1 = { 55 + ed25519Private = '' 56 + -----BEGIN ED25519 PRIVATE KEY----- 57 + wHNC2IMXfYtL4ehdsCX154HBvlIZYEiTOnXtckWMUtEAiX9fu7peyBkp9q+yOy9c 58 + xsNyssLL78lt0GoweCxlu3Sza2oBQAcwb+6tuv7P/bqzcG005uCwquyCz8LVymXA 59 + -----END ED25519 PRIVATE KEY----- 60 + ''; 61 + 62 + ed25519Public = "t0smNaAEAH8mver77+z/m6MnBNdurAsqrswM/Sls5FA"; 63 + 64 + rsaPrivate = '' 65 + -----BEGIN RSA PRIVATE KEY----- 66 + MIIEpAIBAAKCAQEApukYNGFNWvVlmx75LyOE7MEcd/ViV+yEyk+4cIBXYJ3Ouw+/ 67 + oEuh8ghQfsiUtbUPR6hPYhX2ZV8XGhuU2nAXVQV0sfZ8pdkbHQ6wHUqFcUIQAVvS 68 + Wpm2DvZM8jkbCPP64/x5nukPwQ8VoNnb62rWGzbcj7rOeb7ndMK0TpX5Wwv8F297 69 + nKTNCEDbK3DLTj3VD+QGnw6AoEt5i44vViAWZBXuHLHWTDC0Nq8GG+9TKODkEwt5 70 + 4dgN2X9f+WTVAYhZT3SayHLqIFIMQunN89RpWwhHSW+JIRfAfuT1TbP+wA5ptDeI 71 + ktCkJwWyv4hK6l800BJ9GW1nbId5LPa58ipaVwIDAQABAoIBAHcw3WgKVAMwWm57 72 + n9ZZtwKapInFYYUIEYungj5UaBFGn+pVRLJjUDJWXaUr94YK1e6F8qpIpLufPBAY 73 + wiN7CC5exwaOzlRgxUvqwTkpjkFiu6s8tuqb+baVjD0tKnEqSW+lS/R+2hEzhG5p 74 + JPLoSB0HAFpjPC8UdJSctcWos3if3mvOGkGCKyTkrwaJgECDfD+lZ+NBIAiYLSps 75 + jWLE+XlY1+nfPdLUQ+TRSv3IikJ/CWbvJLl9EE1tKhkY564KytwZrkIdJlc7NyRO 76 + HpzhyMzHu1GLsr+OsBZByNNUxEPU+bzkDQluRXUSIUs9zZoBiCQr3o04qGPTEX9n 77 + pNU60gECgYEA3Uf+c80eqzjDxv+O0YzC+9x6A+yMrV56siGkKRPMlrSqjX7iE2Yg 78 + tUjD25kEvtaFuB3f/7zp3h4O/VLZgXreRtXHvdrfoyyJGHvHIyCGm8sw8CEWsKo4 79 + 1LgZUzdPJRkXJq1zOgS0r1xsA1UDC4s02Ww2HwNeVWtmLUyCpA+B/ccCgYEAwRk9 80 + tbe82eq1a85zZiPVXP2qvDH5+Vz9YiMky8xsBnoxmz2siR+NdvWBLcE2VDIY8MK1 81 + 9a1dz2a7cAHQBrtWtACFVY4zvr69DumApjbQRClDYpJ42tp2VbzlMcUDIoKudRQV 82 + CObhrE4w4yfVizXFyH9+4Tsg5NzVYuGg9fUJ/vECgYEAoRz7KouNqfMhsLF/5hkM 83 + Gt9zw4mm/9ALm8kcwn/U9WHD0FQy/Rbd98BsQmaOavi80cqGvqhoyz2tgkqhbUHt 84 + tzuOPDCxphgWFcqBupTDDYoLLruYzraRvGfyoIFj0coL7jBZ9kNY31l2l5J9LhmE 85 + OE4utbP5Kk6RTagocpWL+x8CgYB48CwcIcWf3kZeDOFtuUeqhB1o3Qwox7rSuhwT 86 + oCaQL/vdtNTY1PAu7zhGxdoXBYFlWS3JfxlgCoGedyQo8zAscJ8RpIx4DNIwAsLW 87 + V0I9TnKry/zxZR30OOh7MV7zQFGvdjJubtwspJQt0QcHt1f2aRO4UOYbMMxcr9+1 88 + 7BCkoQKBgQDBEtg1hx9zYGg1WN2TBSvh6NShi9S23r6IZ3Up8vz6Z2rcwB3UuhKi 89 + xluI2ZFwM9s+7UOpaGC+hnc1aMHDEguYOPXoIzvebbYAdN4AkrsJ5d0r1GoEe64E 90 + UXxrfuv5LeJ/vkUgWof+U3/jGOVvrjzi5y1xOC0r3kiSpMa85s1dhQ== 91 + -----END RSA PRIVATE KEY----- 92 + ''; 93 + 94 + rsaPublic = '' 95 + -----BEGIN RSA PUBLIC KEY----- 96 + MIIBCgKCAQEApukYNGFNWvVlmx75LyOE7MEcd/ViV+yEyk+4cIBXYJ3Ouw+/oEuh 97 + 8ghQfsiUtbUPR6hPYhX2ZV8XGhuU2nAXVQV0sfZ8pdkbHQ6wHUqFcUIQAVvSWpm2 98 + DvZM8jkbCPP64/x5nukPwQ8VoNnb62rWGzbcj7rOeb7ndMK0TpX5Wwv8F297nKTN 99 + CEDbK3DLTj3VD+QGnw6AoEt5i44vViAWZBXuHLHWTDC0Nq8GG+9TKODkEwt54dgN 100 + 2X9f+WTVAYhZT3SayHLqIFIMQunN89RpWwhHSW+JIRfAfuT1TbP+wA5ptDeIktCk 101 + JwWyv4hK6l800BJ9GW1nbId5LPa58ipaVwIDAQAB 102 + -----END RSA PUBLIC KEY----- 103 + ''; 104 + }; 105 + 106 + dynamic2 = { 107 + ed25519Private = '' 108 + -----BEGIN ED25519 PRIVATE KEY----- 109 + oUx9JdIstZLMj3ZPD8mP3ITsUscCTIXhNF3VKFUVi/ma5uk50/1vrEohfDraiMxj 110 + gAWthpkhnFzUbp+YlOHE7/Z3h1a/br2/tk8DoZ5PV6ufoV1MaBlGdu+TZgeZou0t 111 + -----END ED25519 PRIVATE KEY----- 112 + ''; 113 + 114 + ed25519Public = "f2dYt2/2q9fLJ/AaW+Tlu7HaVNjWQpRnr/UGoXGqLdL"; 115 + 116 + rsaPrivate = '' 117 + -----BEGIN RSA PRIVATE KEY----- 118 + MIIEpAIBAAKCAQEAtQfijPX3BwOAs2Y0EuNjcBmsI90uYqNAonrFgTtcVwERIVE6 119 + p6alSEakazhByujBg3jI8oPKC8eO0IJ7x/BWcgxqaw8hsPfJZFnRlwEcU5kK4c+j 120 + UNS+hJOXp0x97T1edLpSFHDK9bZ2necblHKG5MsI4UsxEa+CZ0yoIybwWCDmYuya 121 + PvE7CeNNa+CIOUbtPVoN4p/aBj0vZeerNBBuodNkglKRxj4l9wD9uOx4S9sdK5lu 122 + q/rkxlViBoXRAshT+G2d/u/7/WPoiKB3QJcF33z8UfrlsTRnDDqOMSGisTPSv2LK 123 + 4QLN4hWOGXAYQqZcxTkvvjl62mCDuoy0TM+CKQIDAQABAoIBAFKpMAxXf52nPswr 124 + /dkmFVCpmE2kADsv+iJ21tpkpYxgw1aoRZUp5cyz3P3MaVZio4IJ1A/Ql6B7Vb3l 125 + 5ulr170p6CnMdgDdlAsLbEV8T1foyOxFKHiPPBNDZXsR1WpPnGLGdRY6TqKV12HQ 126 + lmpZRTkRcJOXBufhcTUD7r5mWFaUoZ7so6VxR4L4Tzcgv1Rl4S6jgnHOQdO6lj47 127 + BaPjpBb+hplJ4wsRm91dQ7JApYq25XZwyxnBwQ2zAwb46wsuFxDPHlSc4wU7qTt6 128 + x2omm33Xy2cm8L1XQhrassZzldSnAyaLBh9DC3+vFPLODDxdz5M2kpHujYYctRhv 129 + CICMYJUCgYEA7mWVYuw0S8FNjaLx6n9Q1hr9d9vAFDd3NEaegH586xvhYNxf6n+C 130 + 2zZloVLEsX0UnBU/6ZtLAUfxUIqlvDS2r1VjSYG5SNxM6/vyGl17Niu1jC8nzf7M 131 + V1WtDCHhT4ikZCuNkAldtgI7CXVdCVO/fTqVhjk4hDblJo7VsCZSZysCgYEAwmXp 132 + TwlDHapDqA8UxClZuxS8k+2hthny3ihRPCuT34yqAz074zYG97ZBKwIa4Lm1vnkc 133 + mwU7yR2aK7IYeU4ScfWm1mLjkW5iaNV/sG7iTz/RP4mBAs3KSGmuhhz8sFWcXByU 134 + IZyvMJvC+FpgJQJn/Xc8ZmdImvXlZd6k8v4/kfsCgYEA6VzFPB2OH63slb4w42SX 135 + o86t2dtiDigxZxnN5GhtLdSP7borpigF10JLf/y+kCOpvhRLCQk8Bdf/z+C41iAf 136 + yEhktbrnvfvwzHxHhSmHCAMHZ19trodCTiePCrZLkQhoK6o6nAmfEyDh26NoXE3/ 137 + v71OSyLOQRZfgDwHz7PjrBsCgYAe0zojpjxWP+FqjLmmQUhROgCNFGlIDuVMBOic 138 + uexAznVG/ja42KBSNzwuLa9FYy1Gfr3idvn78g24UA1BbvfNyj4iUJv1O6OvK+uL 139 + dom8N0pe4NbsMuWYhel+qqoG7AxXLtDuY4IEGy7XYr1MIQ2MS5PwSQBiUguGE7/k 140 + KBy8cQKBgQCyC9R8VWJxQLqJxZGa9Ful01bSuntB5OLRfEjFCCuGiY/3Vj+mCiQL 141 + GOfMOi2jrcnSNgUm0uevmiFCq9m7QiPiAcSYKXPWhsz/55jJIGcZy8bwyhZ2s2Mg 142 + BGeZgj4RFORidqkt5g/KJz0+Wp6Ks4sLoCvOzkpeXvLzFVyzGkihrw== 143 + -----END RSA PRIVATE KEY----- 144 + ''; 145 + 146 + rsaPublic = '' 147 + -----BEGIN RSA PUBLIC KEY----- 148 + MIIBCgKCAQEAtQfijPX3BwOAs2Y0EuNjcBmsI90uYqNAonrFgTtcVwERIVE6p6al 149 + SEakazhByujBg3jI8oPKC8eO0IJ7x/BWcgxqaw8hsPfJZFnRlwEcU5kK4c+jUNS+ 150 + hJOXp0x97T1edLpSFHDK9bZ2necblHKG5MsI4UsxEa+CZ0yoIybwWCDmYuyaPvE7 151 + CeNNa+CIOUbtPVoN4p/aBj0vZeerNBBuodNkglKRxj4l9wD9uOx4S9sdK5luq/rk 152 + xlViBoXRAshT+G2d/u/7/WPoiKB3QJcF33z8UfrlsTRnDDqOMSGisTPSv2LK4QLN 153 + 4hWOGXAYQqZcxTkvvjl62mCDuoy0TM+CKQIDAQAB 154 + -----END RSA PUBLIC KEY----- 155 + ''; 156 + }; 157 + }