1import ../make-test-python.nix (
2 { lib, ... }:
3 let
4 snakeoil-keys = import ./snakeoil-keys.nix;
5
6 hosts = lib.attrNames snakeoil-keys;
7
8 subnetOf =
9 name: config:
10 let
11 subnets = config.services.tinc.networks.myNetwork.hostSettings.${name}.subnets;
12 in
13 (builtins.head subnets).address;
14
15 makeTincHost =
16 name:
17 {
18 subnet,
19 extraConfig ? { },
20 }:
21 lib.mkMerge [
22 {
23 subnets = [ { address = subnet; } ];
24 settings = {
25 Ed25519PublicKey = snakeoil-keys.${name}.ed25519Public;
26 };
27 rsaPublicKey = snakeoil-keys.${name}.rsaPublic;
28 }
29 extraConfig
30 ];
31
32 makeTincNode =
33 { config, ... }:
34 name: extraConfig:
35 lib.mkMerge [
36 {
37 services.tinc.networks.myNetwork = {
38 inherit name;
39 rsaPrivateKeyFile = builtins.toFile "rsa.priv" snakeoil-keys.${name}.rsaPrivate;
40 ed25519PrivateKeyFile = builtins.toFile "ed25519.priv" snakeoil-keys.${name}.ed25519Private;
41
42 hostSettings = lib.mapAttrs makeTincHost {
43 static = {
44 subnet = "10.0.0.11";
45 # Only specify the addresses in the node's vlans, Tinc does not
46 # seem to try each one, unlike the documentation suggests...
47 extraConfig.addresses = map (vlan: {
48 address = "192.168.${toString vlan}.11";
49 port = 655;
50 }) config.virtualisation.vlans;
51 };
52 dynamic1 = {
53 subnet = "10.0.0.21";
54 };
55 dynamic2 = {
56 subnet = "10.0.0.22";
57 };
58 };
59 };
60
61 networking.useDHCP = false;
62
63 networking.interfaces."tinc.myNetwork" = {
64 virtual = true;
65 virtualType = "tun";
66 ipv4.addresses = [
67 {
68 address = subnetOf name config;
69 prefixLength = 24;
70 }
71 ];
72 };
73
74 # Prevents race condition between NixOS service and tinc creating the
75 # interface.
76 # See: https://github.com/NixOS/nixpkgs/issues/27070
77 systemd.services."tinc.myNetwork" = {
78 after = [ "network-addresses-tinc.myNetwork.service" ];
79 requires = [ "network-addresses-tinc.myNetwork.service" ];
80 };
81
82 networking.firewall.allowedTCPPorts = [ 655 ];
83 networking.firewall.allowedUDPPorts = [ 655 ];
84 }
85 extraConfig
86 ];
87
88 in
89 {
90 name = "tinc";
91 meta.maintainers = with lib.maintainers; [ minijackson ];
92
93 nodes = {
94
95 static =
96 { ... }@args:
97 makeTincNode args "static" {
98 virtualisation.vlans = [
99 1
100 2
101 ];
102
103 networking.interfaces.eth1.ipv4.addresses = [
104 {
105 address = "192.168.1.11";
106 prefixLength = 24;
107 }
108 ];
109
110 networking.interfaces.eth2.ipv4.addresses = [
111 {
112 address = "192.168.2.11";
113 prefixLength = 24;
114 }
115 ];
116 };
117
118 dynamic1 =
119 { ... }@args:
120 makeTincNode args "dynamic1" {
121 virtualisation.vlans = [ 1 ];
122 };
123
124 dynamic2 =
125 { ... }@args:
126 makeTincNode args "dynamic2" {
127 virtualisation.vlans = [ 2 ];
128 };
129
130 };
131
132 testScript = ''
133 start_all()
134
135 static.wait_for_unit("tinc.myNetwork.service")
136 dynamic1.wait_for_unit("tinc.myNetwork.service")
137 dynamic2.wait_for_unit("tinc.myNetwork.service")
138
139 # Static is accessible by the other hosts
140 dynamic1.succeed("ping -c5 192.168.1.11")
141 dynamic2.succeed("ping -c5 192.168.2.11")
142
143 # The other hosts are in separate vlans
144 dynamic1.fail("ping -c5 192.168.2.11")
145 dynamic2.fail("ping -c5 192.168.1.11")
146
147 # Each host can ping themselves through Tinc
148 static.succeed("ping -c5 10.0.0.11")
149 dynamic1.succeed("ping -c5 10.0.0.21")
150 dynamic2.succeed("ping -c5 10.0.0.22")
151
152 # Static is accessible by the other hosts through Tinc
153 dynamic1.succeed("ping -c5 10.0.0.11")
154 dynamic2.succeed("ping -c5 10.0.0.11")
155
156 # Static can access the other hosts through Tinc
157 static.succeed("ping -c5 10.0.0.21")
158 static.succeed("ping -c5 10.0.0.22")
159
160 # The other hosts in separate vlans can access each other through Tinc
161 dynamic1.succeed("ping -c5 10.0.0.22")
162 dynamic2.succeed("ping -c5 10.0.0.21")
163 '';
164 }
165)