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

nixos/tinc: rfc42 and tests
This commit is contained in:
Linus Heckemann 2020-12-14 21:52:57 +01:00 committed by GitHub
commit c40f06022a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 521 additions and 13 deletions

View file

@ -1,13 +1,156 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.tinc;
in
mkValueString = value:
if value == true then "yes"
else if value == false then "no"
else generators.mkValueStringDefault { } value;
toTincConf = generators.toKeyValue {
listsAsDuplicateKeys = true;
mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } "=";
};
tincConfType = with types;
let
valueType = oneOf [ bool str int ];
in
attrsOf (either valueType (listOf valueType));
addressSubmodule = {
options = {
address = mkOption {
type = types.str;
description = "The external IP address or hostname where the host can be reached.";
};
port = mkOption {
type = types.nullOr types.port;
default = null;
description = ''
The port where the host can be reached.
If no port is specified, the default Port is used.
'';
};
};
};
subnetSubmodule = {
options = {
address = mkOption {
type = types.str;
description = ''
The subnet of this host.
Subnets can either be single MAC, IPv4 or IPv6 addresses, in which case
a subnet consisting of only that single address is assumed, or they can
be a IPv4 or IPv6 network address with a prefix length.
IPv4 subnets are notated like 192.168.1.0/24, IPv6 subnets are notated
like fec0:0:0:1::/64. MAC addresses are notated like 0:1a:2b:3c:4d:5e.
Note that subnets like 192.168.1.1/24 are invalid.
'';
};
prefixLength = mkOption {
type = with types; nullOr (addCheck int (n: n >= 0 && n <= 128));
default = null;
description = ''
The prefix length of the subnet.
If null, a subnet consisting of only that single address is assumed.
This conforms to standard CIDR notation as described in RFC1519.
'';
};
weight = mkOption {
type = types.ints.unsigned;
default = 10;
description = ''
Indicates the priority over identical Subnets owned by different nodes.
Lower values indicate higher priority. Packets will be sent to the
node with the highest priority, unless that node is not reachable, in
which case the node with the next highest priority will be tried, and
so on.
'';
};
};
};
hostSubmodule = { config, ... }: {
options = {
addresses = mkOption {
type = types.listOf (types.submodule addressSubmodule);
default = [ ];
description = ''
The external address where the host can be reached. This will set this
host's <option>settings.Address</option> option.
This variable is only required if you want to connect to this host.
'';
};
subnets = mkOption {
type = types.listOf (types.submodule subnetSubmodule);
default = [ ];
description = ''
The subnets which this tinc daemon will serve. This will set this
host's <option>settings.Subnet</option> option.
Tinc tries to look up which other daemon it should send a packet to by
searching the appropriate subnet. If the packet matches a subnet, it
will be sent to the daemon who has this subnet in his host
configuration file.
'';
};
rsaPublicKey = mkOption {
type = types.str;
default = "";
description = ''
Legacy RSA public key of the host in PEM format, including start and
end markers.
This will be appended as-is in the host's configuration file.
The ed25519 public key can be specified using the
<option>settings.Ed25519PublicKey</option> option instead.
'';
};
settings = mkOption {
default = { };
type = types.submodule { freeformType = tincConfType; };
description = ''
Configuration for this host.
See <link xlink:href="https://tinc-vpn.org/documentation-1.1/Host-configuration-variables.html"/>
for supported values.
'';
};
};
config.settings = {
Address = mkDefault (map
(address: "${address.address} ${toString address.port}")
config.addresses);
Subnet = mkDefault (map
(subnet:
if subnet.prefixLength == null then "${subnet.address}#${toString subnet.weight}"
else "${subnet.address}/${toString subnet.prefixLength}#${toString subnet.weight}")
config.subnets);
};
};
in
{
###### interface
@ -18,7 +161,7 @@ in
networks = mkOption {
default = { };
type = with types; attrsOf (submodule {
type = with types; attrsOf (submodule ({ config, ... }: {
options = {
extraConfig = mkOption {
@ -26,6 +169,9 @@ in
type = types.lines;
description = ''
Extra lines to add to the tinc service configuration file.
Note that using the declarative <option>service.tinc.networks.&lt;name&gt;.settings</option>
option is preferred.
'';
};
@ -69,6 +215,40 @@ in
hosts = mkOption {
default = { };
type = types.attrsOf types.lines;
description = ''
The name of the host in the network as well as the configuration for that host.
This name should only contain alphanumerics and underscores.
Note that using the declarative <option>service.tinc.networks.&lt;name&gt;.hostSettings</option>
option is preferred.
'';
};
hostSettings = mkOption {
default = { };
example = literalExample ''
{
host1 = {
addresses = [
{ address = "192.168.1.42"; }
{ address = "192.168.1.42"; port = 1655; }
];
subnets = [ { address = "10.0.0.42"; } ];
rsaPublicKey = "...";
settings = {
Ed25519PublicKey = "...";
};
};
host2 = {
subnets = [ { address = "10.0.1.0"; prefixLength = 24; weight = 2; } ];
rsaPublicKey = "...";
settings = {
Compression = 10;
};
};
}
'';
type = types.attrsOf (types.submodule hostSubmodule);
description = ''
The name of the host in the network as well as the configuration for that host.
This name should only contain alphanumerics and underscores.
@ -79,7 +259,7 @@ in
default = "tun";
type = types.enum [ "tun" "tap" ];
description = ''
The type of virtual interface used for the network connection
The type of virtual interface used for the network connection.
'';
};
@ -118,8 +298,44 @@ in
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.
'';
};
settings = mkOption {
default = { };
type = types.submodule { freeformType = tincConfType; };
example = literalExample ''
{
Interface = "custom.interface";
DirectOnly = true;
Mode = "switch";
}
'';
description = ''
Configuration of the Tinc daemon for this network.
See <link xlink:href="https://tinc-vpn.org/documentation-1.1/Main-configuration-variables.html"/>
for supported values.
'';
};
};
});
config = {
hosts = mapAttrs
(hostname: host: ''
${toTincConf host.settings}
${host.rsaPublicKey}
'')
config.hostSettings;
settings = {
DeviceType = mkDefault config.interfaceType;
Name = mkDefault (if config.name == null then "$HOST" else config.name);
Ed25519PrivateKeyFile = mkIf (config.ed25519PrivateKeyFile != null) (mkDefault config.ed25519PrivateKeyFile);
PrivateKeyFile = mkIf (config.rsaPrivateKeyFile != null) (mkDefault config.rsaPrivateKeyFile);
ListenAddress = mkIf (config.listenAddress != null) (mkDefault config.listenAddress);
BindToAddress = mkIf (config.bindToAddress != null) (mkDefault config.bindToAddress);
};
};
}));
description = ''
Defines the tinc networks which will be started.
@ -144,13 +360,7 @@ in
"tinc/${network}/tinc.conf" = {
mode = "0444";
text = ''
Name = ${if data.name == null then "$HOST" else data.name}
DeviceType = ${data.interfaceType}
${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"}
${optionalString (data.rsaPrivateKeyFile != null) "PrivateKeyFile = ${data.rsaPrivateKeyFile}"}
${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"}
${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"}
Interface = tinc.${network}
${toTincConf ({ Interface = "tinc.${network}"; } // data.settings)}
${data.extraConfig}
'';
};
@ -222,4 +432,5 @@ in
};
meta.maintainers = with maintainers; [ minijackson ];
}

View file

@ -375,6 +375,7 @@ in
telegraf = handleTest ./telegraf.nix {};
tiddlywiki = handleTest ./tiddlywiki.nix {};
timezone = handleTest ./timezone.nix {};
tinc = handleTest ./tinc {};
tinydns = handleTest ./tinydns.nix {};
tor = handleTest ./tor.nix {};
# traefik test relies on docker-containers

View file

@ -0,0 +1,139 @@
import ../make-test-python.nix ({ lib, ... }:
let
snakeoil-keys = import ./snakeoil-keys.nix;
hosts = lib.attrNames snakeoil-keys;
subnetOf = name: config:
let
subnets = config.services.tinc.networks.myNetwork.hostSettings.${name}.subnets;
in
(builtins.head subnets).address;
makeTincHost = name: { subnet, extraConfig ? { } }: lib.mkMerge [
{
subnets = [{ address = subnet; }];
settings = {
Ed25519PublicKey = snakeoil-keys.${name}.ed25519Public;
};
rsaPublicKey = snakeoil-keys.${name}.rsaPublic;
}
extraConfig
];
makeTincNode = { config, ... }: name: extraConfig: lib.mkMerge [
{
services.tinc.networks.myNetwork = {
inherit name;
rsaPrivateKeyFile =
builtins.toFile "rsa.priv" snakeoil-keys.${name}.rsaPrivate;
ed25519PrivateKeyFile =
builtins.toFile "ed25519.priv" snakeoil-keys.${name}.ed25519Private;
hostSettings = lib.mapAttrs makeTincHost {
static = {
subnet = "10.0.0.11";
# Only specify the addresses in the node's vlans, Tinc does not
# seem to try each one, unlike the documentation suggests...
extraConfig.addresses = map
(vlan: { address = "192.168.${toString vlan}.11"; port = 655; })
config.virtualisation.vlans;
};
dynamic1 = { subnet = "10.0.0.21"; };
dynamic2 = { subnet = "10.0.0.22"; };
};
};
networking.useDHCP = false;
networking.interfaces."tinc.myNetwork" = {
virtual = true;
virtualType = "tun";
ipv4.addresses = [{
address = subnetOf name config;
prefixLength = 24;
}];
};
# Prevents race condition between NixOS service and tinc creating the
# interface.
# See: https://github.com/NixOS/nixpkgs/issues/27070
systemd.services."tinc.myNetwork" = {
after = [ "network-addresses-tinc.myNetwork.service" ];
requires = [ "network-addresses-tinc.myNetwork.service" ];
};
networking.firewall.allowedTCPPorts = [ 655 ];
networking.firewall.allowedUDPPorts = [ 655 ];
}
extraConfig
];
in
{
name = "tinc";
meta.maintainers = with lib.maintainers; [ minijackson ];
nodes = {
static = { ... } @ args:
makeTincNode args "static" {
virtualisation.vlans = [ 1 2 ];
networking.interfaces.eth1.ipv4.addresses = [{
address = "192.168.1.11";
prefixLength = 24;
}];
networking.interfaces.eth2.ipv4.addresses = [{
address = "192.168.2.11";
prefixLength = 24;
}];
};
dynamic1 = { ... } @ args:
makeTincNode args "dynamic1" {
virtualisation.vlans = [ 1 ];
};
dynamic2 = { ... } @ args:
makeTincNode args "dynamic2" {
virtualisation.vlans = [ 2 ];
};
};
testScript = ''
start_all()
static.wait_for_unit("tinc.myNetwork.service")
dynamic1.wait_for_unit("tinc.myNetwork.service")
dynamic2.wait_for_unit("tinc.myNetwork.service")
# Static is accessible by the other hosts
dynamic1.succeed("ping -c5 192.168.1.11")
dynamic2.succeed("ping -c5 192.168.2.11")
# The other hosts are in separate vlans
dynamic1.fail("ping -c5 192.168.2.11")
dynamic2.fail("ping -c5 192.168.1.11")
# Each host can ping themselves through Tinc
static.succeed("ping -c5 10.0.0.11")
dynamic1.succeed("ping -c5 10.0.0.21")
dynamic2.succeed("ping -c5 10.0.0.22")
# Static is accessible by the other hosts through Tinc
dynamic1.succeed("ping -c5 10.0.0.11")
dynamic2.succeed("ping -c5 10.0.0.11")
# Static can access the other hosts through Tinc
static.succeed("ping -c5 10.0.0.21")
static.succeed("ping -c5 10.0.0.22")
# The other hosts in separate vlans can access each other through Tinc
dynamic1.succeed("ping -c5 10.0.0.22")
dynamic2.succeed("ping -c5 10.0.0.21")
'';
})

View file

@ -0,0 +1,157 @@
{
static = {
ed25519Private = ''
-----BEGIN ED25519 PRIVATE KEY-----
IPR+ur5LfVdm6VlR1+FGIkbkL8Enkb9sejBa/JP6tXkg/vHoraIp70srb6jAUFm5
3YbCJiBjLW3dy16qM5PovBoWtr5hoqYYA9dFLOys8FBUFFsIGfKhnbk7g25iwxbO
-----END ED25519 PRIVATE KEY-----
'';
ed25519Public = "AqV7aeIqKGGQfXxijMLfRAVRBLixnS45G5OoduIc8mD";
rsaPrivate = ''
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxDHl0TIhhT2yH5rT+Q7MLnj+Ir8bbs3uaPqnzcxWzN1EfVP8
TWt5fSTrF2Dc78Kyu5ZNALrp7tUj0GZAegp1YeYJ28p3qTwCveywtCwbB4dI987S
yJwq95kE9aoyLa+cT99VwSTdb2YowQv2tWj/idxE3oJ+qZjy9tE5mysXm7jmTQDx
+U0XmNe6MHjKXc01Ener41u0ykJLeUfdgJ1zEyM2rQGtaHpIXfMT6kmxCaMcAMLg
YFpI38/1pQGQtROKdGOaUomx2m058bkMsJhTiBjESiLRDElRGxmMJ732crGJP0GR
ChJkaX/CnxHq7R0daZfwoTVHRu6N7WDbFQL5twIDAQABAoIBAQCM/fLTIHyYXRr5
vXFhxXGUYBz56W6UdWdEiAU5TwR92vFSQ53IIVlARtyvg0ui/b8mMcAKq0hb+03u
gN0LFyL+BKvHCLxvoRGzXTorcJrIET+t3jL6OchjANNgnDvNOytQ9wWQdKaxXLAi
8y8LdXZWozXW1d6ikKjiGL+WNCSWIcq83ktSJZcohihptU9Un16FYQzdolSC8RtI
XyT7i1ye6hW/wJTJxqZ4taX3EPat85kXS234VGSqg9bb2A1yE+U8Rq37bf8AKldJ
NUQB3JyxnkYGJcqvzDmz139+744VWxDRvXDA5vU29LC6f8bGBvwEttD98QW+pgmB
1NBU1Uo5AoGBAOzUk6k74h1RarwXaftjh/9Pures0CfNNnrkJApzFCh4bAoHNxq6
SSXqLcc/vvX2+YaZ72nn5YTo+JLQP6evM9oUaqRMAxa3nzoNCtF8U2r48UWmoUQE
aZCYbD3m7IVWFacCKRVaVTMZMTTicypSnXcbCSIEH8PRs9+L4jkHgql9AoGBANQT
TZECVhIaQnyRiKWlUE8G1QKzXIxjmfyirBe+ftlIG2XMXasAtQ4VRxpnorgqUnIH
BVrIbvRx21zlqwZbrZvyb1jHWRoyi1cqBPijpYBUm5LbV2jgHPhnfhRVqdD4CDKj
NQzIQrNymFaMWAoOQv/DE3g+Txr0fm9Ztu8ZRXZDAoGAHh3SQT0aPfwyhIS9t3gq
vS7YYa8aMVWJTgthAessbxERPB06xq1Vy/qBo8rZb9HeXV2J8n/I0iQGKDVPQvWm
tF7QSOBZrDPhjbJG4+jZesr5c5ADBfFBs1+OtDh/b11JF5nQu6RnHT5g4YbCemlT
GOhZOvgnSfGK3CyfsfzggskCgYEAmpKDK5kPUNxw70hH16v5L9Bj+zbt0qlZ+Ag8
9IV1ATuMNJNTBitay6v4iidVM3QtaUzyuytxq5s87qW7FMRHcm2ueH+70ttaMiq/
OtZT74g7aDuUpy0KEIemHn4dauENYJMSPIHOE+sHW7WpCZNBhBcUHsUTdSsU6GX0
bqr1tO8CgYBpZdR2OoX/rn8nwjmtBOH38aPnCpaAfdI2Eq2Lg6DjksP6TBt53a+R
m1lk6Kt37BPPZQ85SBr7ywvDgUzfoD7uSmHujF2JUHPsdrg9nx7pNIGlW6DlS9OU
oNXGAJ/6/y6F8uDbToUfrwFq5tKMypEEa32kFtxb9f0XQ5fSgHrBEw==
-----END RSA PRIVATE KEY-----
'';
rsaPublic = ''
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAxDHl0TIhhT2yH5rT+Q7MLnj+Ir8bbs3uaPqnzcxWzN1EfVP8TWt5
fSTrF2Dc78Kyu5ZNALrp7tUj0GZAegp1YeYJ28p3qTwCveywtCwbB4dI987SyJwq
95kE9aoyLa+cT99VwSTdb2YowQv2tWj/idxE3oJ+qZjy9tE5mysXm7jmTQDx+U0X
mNe6MHjKXc01Ener41u0ykJLeUfdgJ1zEyM2rQGtaHpIXfMT6kmxCaMcAMLgYFpI
38/1pQGQtROKdGOaUomx2m058bkMsJhTiBjESiLRDElRGxmMJ732crGJP0GRChJk
aX/CnxHq7R0daZfwoTVHRu6N7WDbFQL5twIDAQAB
-----END RSA PUBLIC KEY-----
'';
};
dynamic1 = {
ed25519Private = ''
-----BEGIN ED25519 PRIVATE KEY-----
wHNC2IMXfYtL4ehdsCX154HBvlIZYEiTOnXtckWMUtEAiX9fu7peyBkp9q+yOy9c
xsNyssLL78lt0GoweCxlu3Sza2oBQAcwb+6tuv7P/bqzcG005uCwquyCz8LVymXA
-----END ED25519 PRIVATE KEY-----
'';
ed25519Public = "t0smNaAEAH8mver77+z/m6MnBNdurAsqrswM/Sls5FA";
rsaPrivate = ''
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEApukYNGFNWvVlmx75LyOE7MEcd/ViV+yEyk+4cIBXYJ3Ouw+/
oEuh8ghQfsiUtbUPR6hPYhX2ZV8XGhuU2nAXVQV0sfZ8pdkbHQ6wHUqFcUIQAVvS
Wpm2DvZM8jkbCPP64/x5nukPwQ8VoNnb62rWGzbcj7rOeb7ndMK0TpX5Wwv8F297
nKTNCEDbK3DLTj3VD+QGnw6AoEt5i44vViAWZBXuHLHWTDC0Nq8GG+9TKODkEwt5
4dgN2X9f+WTVAYhZT3SayHLqIFIMQunN89RpWwhHSW+JIRfAfuT1TbP+wA5ptDeI
ktCkJwWyv4hK6l800BJ9GW1nbId5LPa58ipaVwIDAQABAoIBAHcw3WgKVAMwWm57
n9ZZtwKapInFYYUIEYungj5UaBFGn+pVRLJjUDJWXaUr94YK1e6F8qpIpLufPBAY
wiN7CC5exwaOzlRgxUvqwTkpjkFiu6s8tuqb+baVjD0tKnEqSW+lS/R+2hEzhG5p
JPLoSB0HAFpjPC8UdJSctcWos3if3mvOGkGCKyTkrwaJgECDfD+lZ+NBIAiYLSps
jWLE+XlY1+nfPdLUQ+TRSv3IikJ/CWbvJLl9EE1tKhkY564KytwZrkIdJlc7NyRO
HpzhyMzHu1GLsr+OsBZByNNUxEPU+bzkDQluRXUSIUs9zZoBiCQr3o04qGPTEX9n
pNU60gECgYEA3Uf+c80eqzjDxv+O0YzC+9x6A+yMrV56siGkKRPMlrSqjX7iE2Yg
tUjD25kEvtaFuB3f/7zp3h4O/VLZgXreRtXHvdrfoyyJGHvHIyCGm8sw8CEWsKo4
1LgZUzdPJRkXJq1zOgS0r1xsA1UDC4s02Ww2HwNeVWtmLUyCpA+B/ccCgYEAwRk9
tbe82eq1a85zZiPVXP2qvDH5+Vz9YiMky8xsBnoxmz2siR+NdvWBLcE2VDIY8MK1
9a1dz2a7cAHQBrtWtACFVY4zvr69DumApjbQRClDYpJ42tp2VbzlMcUDIoKudRQV
CObhrE4w4yfVizXFyH9+4Tsg5NzVYuGg9fUJ/vECgYEAoRz7KouNqfMhsLF/5hkM
Gt9zw4mm/9ALm8kcwn/U9WHD0FQy/Rbd98BsQmaOavi80cqGvqhoyz2tgkqhbUHt
tzuOPDCxphgWFcqBupTDDYoLLruYzraRvGfyoIFj0coL7jBZ9kNY31l2l5J9LhmE
OE4utbP5Kk6RTagocpWL+x8CgYB48CwcIcWf3kZeDOFtuUeqhB1o3Qwox7rSuhwT
oCaQL/vdtNTY1PAu7zhGxdoXBYFlWS3JfxlgCoGedyQo8zAscJ8RpIx4DNIwAsLW
V0I9TnKry/zxZR30OOh7MV7zQFGvdjJubtwspJQt0QcHt1f2aRO4UOYbMMxcr9+1
7BCkoQKBgQDBEtg1hx9zYGg1WN2TBSvh6NShi9S23r6IZ3Up8vz6Z2rcwB3UuhKi
xluI2ZFwM9s+7UOpaGC+hnc1aMHDEguYOPXoIzvebbYAdN4AkrsJ5d0r1GoEe64E
UXxrfuv5LeJ/vkUgWof+U3/jGOVvrjzi5y1xOC0r3kiSpMa85s1dhQ==
-----END RSA PRIVATE KEY-----
'';
rsaPublic = ''
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEApukYNGFNWvVlmx75LyOE7MEcd/ViV+yEyk+4cIBXYJ3Ouw+/oEuh
8ghQfsiUtbUPR6hPYhX2ZV8XGhuU2nAXVQV0sfZ8pdkbHQ6wHUqFcUIQAVvSWpm2
DvZM8jkbCPP64/x5nukPwQ8VoNnb62rWGzbcj7rOeb7ndMK0TpX5Wwv8F297nKTN
CEDbK3DLTj3VD+QGnw6AoEt5i44vViAWZBXuHLHWTDC0Nq8GG+9TKODkEwt54dgN
2X9f+WTVAYhZT3SayHLqIFIMQunN89RpWwhHSW+JIRfAfuT1TbP+wA5ptDeIktCk
JwWyv4hK6l800BJ9GW1nbId5LPa58ipaVwIDAQAB
-----END RSA PUBLIC KEY-----
'';
};
dynamic2 = {
ed25519Private = ''
-----BEGIN ED25519 PRIVATE KEY-----
oUx9JdIstZLMj3ZPD8mP3ITsUscCTIXhNF3VKFUVi/ma5uk50/1vrEohfDraiMxj
gAWthpkhnFzUbp+YlOHE7/Z3h1a/br2/tk8DoZ5PV6ufoV1MaBlGdu+TZgeZou0t
-----END ED25519 PRIVATE KEY-----
'';
ed25519Public = "f2dYt2/2q9fLJ/AaW+Tlu7HaVNjWQpRnr/UGoXGqLdL";
rsaPrivate = ''
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtQfijPX3BwOAs2Y0EuNjcBmsI90uYqNAonrFgTtcVwERIVE6
p6alSEakazhByujBg3jI8oPKC8eO0IJ7x/BWcgxqaw8hsPfJZFnRlwEcU5kK4c+j
UNS+hJOXp0x97T1edLpSFHDK9bZ2necblHKG5MsI4UsxEa+CZ0yoIybwWCDmYuya
PvE7CeNNa+CIOUbtPVoN4p/aBj0vZeerNBBuodNkglKRxj4l9wD9uOx4S9sdK5lu
q/rkxlViBoXRAshT+G2d/u/7/WPoiKB3QJcF33z8UfrlsTRnDDqOMSGisTPSv2LK
4QLN4hWOGXAYQqZcxTkvvjl62mCDuoy0TM+CKQIDAQABAoIBAFKpMAxXf52nPswr
/dkmFVCpmE2kADsv+iJ21tpkpYxgw1aoRZUp5cyz3P3MaVZio4IJ1A/Ql6B7Vb3l
5ulr170p6CnMdgDdlAsLbEV8T1foyOxFKHiPPBNDZXsR1WpPnGLGdRY6TqKV12HQ
lmpZRTkRcJOXBufhcTUD7r5mWFaUoZ7so6VxR4L4Tzcgv1Rl4S6jgnHOQdO6lj47
BaPjpBb+hplJ4wsRm91dQ7JApYq25XZwyxnBwQ2zAwb46wsuFxDPHlSc4wU7qTt6
x2omm33Xy2cm8L1XQhrassZzldSnAyaLBh9DC3+vFPLODDxdz5M2kpHujYYctRhv
CICMYJUCgYEA7mWVYuw0S8FNjaLx6n9Q1hr9d9vAFDd3NEaegH586xvhYNxf6n+C
2zZloVLEsX0UnBU/6ZtLAUfxUIqlvDS2r1VjSYG5SNxM6/vyGl17Niu1jC8nzf7M
V1WtDCHhT4ikZCuNkAldtgI7CXVdCVO/fTqVhjk4hDblJo7VsCZSZysCgYEAwmXp
TwlDHapDqA8UxClZuxS8k+2hthny3ihRPCuT34yqAz074zYG97ZBKwIa4Lm1vnkc
mwU7yR2aK7IYeU4ScfWm1mLjkW5iaNV/sG7iTz/RP4mBAs3KSGmuhhz8sFWcXByU
IZyvMJvC+FpgJQJn/Xc8ZmdImvXlZd6k8v4/kfsCgYEA6VzFPB2OH63slb4w42SX
o86t2dtiDigxZxnN5GhtLdSP7borpigF10JLf/y+kCOpvhRLCQk8Bdf/z+C41iAf
yEhktbrnvfvwzHxHhSmHCAMHZ19trodCTiePCrZLkQhoK6o6nAmfEyDh26NoXE3/
v71OSyLOQRZfgDwHz7PjrBsCgYAe0zojpjxWP+FqjLmmQUhROgCNFGlIDuVMBOic
uexAznVG/ja42KBSNzwuLa9FYy1Gfr3idvn78g24UA1BbvfNyj4iUJv1O6OvK+uL
dom8N0pe4NbsMuWYhel+qqoG7AxXLtDuY4IEGy7XYr1MIQ2MS5PwSQBiUguGE7/k
KBy8cQKBgQCyC9R8VWJxQLqJxZGa9Ful01bSuntB5OLRfEjFCCuGiY/3Vj+mCiQL
GOfMOi2jrcnSNgUm0uevmiFCq9m7QiPiAcSYKXPWhsz/55jJIGcZy8bwyhZ2s2Mg
BGeZgj4RFORidqkt5g/KJz0+Wp6Ks4sLoCvOzkpeXvLzFVyzGkihrw==
-----END RSA PRIVATE KEY-----
'';
rsaPublic = ''
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAtQfijPX3BwOAs2Y0EuNjcBmsI90uYqNAonrFgTtcVwERIVE6p6al
SEakazhByujBg3jI8oPKC8eO0IJ7x/BWcgxqaw8hsPfJZFnRlwEcU5kK4c+jUNS+
hJOXp0x97T1edLpSFHDK9bZ2necblHKG5MsI4UsxEa+CZ0yoIybwWCDmYuyaPvE7
CeNNa+CIOUbtPVoN4p/aBj0vZeerNBBuodNkglKRxj4l9wD9uOx4S9sdK5luq/rk
xlViBoXRAshT+G2d/u/7/WPoiKB3QJcF33z8UfrlsTRnDDqOMSGisTPSv2LK4QLN
4hWOGXAYQqZcxTkvvjl62mCDuoy0TM+CKQIDAQAB
-----END RSA PUBLIC KEY-----
'';
};
}