1import ../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 })