1let
2 generateNodeConf =
3 {
4 lib,
5 pkgs,
6 config,
7 privk,
8 pubk,
9 systemdCreds,
10 peerId,
11 nodeId,
12 ...
13 }:
14 {
15 imports = [ common/user-account.nix ];
16 systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
17 networking.useNetworkd = true;
18 networking.useDHCP = false;
19 networking.firewall.enable = false;
20 virtualisation.vlans = [ 1 ];
21 environment.systemPackages = with pkgs; [ wireguard-tools ];
22 environment.etc."credstore/network.wireguard.private" = lib.mkIf systemdCreds { text = privk; };
23 systemd.network = {
24 enable = true;
25 config = {
26 routeTables.custom = 23;
27 };
28 netdevs = {
29 "90-wg0" = {
30 netdevConfig = {
31 Kind = "wireguard";
32 Name = "wg0";
33 };
34 wireguardConfig = {
35 # Test storing wireguard private key using systemd credentials.
36 PrivateKey = lib.mkIf systemdCreds "@network.wireguard.private";
37
38 # NOTE: we're storing the wireguard private key in the
39 # store for this test. Do not do this in the real
40 # world. Keep in mind the nix store is
41 # world-readable.
42 PrivateKeyFile = lib.mkIf (!systemdCreds) (pkgs.writeText "wg0-priv" privk);
43 ListenPort = 51820;
44 FirewallMark = 42;
45 };
46 wireguardPeers = [
47 {
48 Endpoint = "192.168.1.${peerId}:51820";
49 PublicKey = pubk;
50 PresharedKeyFile = pkgs.writeText "psk.key" "yTL3sCOL33Wzi6yCnf9uZQl/Z8laSE+zwpqOHC4HhFU=";
51 AllowedIPs = [ "10.0.0.${peerId}/32" ];
52 PersistentKeepalive = 15;
53 }
54 ];
55 };
56 };
57 networks = {
58 "99-nope" = {
59 matchConfig.Name = "eth*";
60 linkConfig.Unmanaged = true;
61 };
62 "90-wg0" = {
63 matchConfig = {
64 Name = "wg0";
65 };
66 address = [ "10.0.0.${nodeId}/32" ];
67 routes = [
68 {
69 Gateway = "10.0.0.${nodeId}";
70 Destination = "10.0.0.0/24";
71 }
72 {
73 Gateway = "10.0.0.${nodeId}";
74 Destination = "10.0.0.0/24";
75 Table = "custom";
76 }
77 ];
78 };
79 "30-eth1" = {
80 matchConfig = {
81 Name = "eth1";
82 };
83 address = [
84 "192.168.1.${nodeId}/24"
85 "fe80::${nodeId}/64"
86 ];
87 routingPolicyRules = [
88 {
89 Table = 10;
90 IncomingInterface = "eth1";
91 Family = "both";
92 }
93 {
94 Table = 20;
95 OutgoingInterface = "eth1";
96 }
97 {
98 Table = 30;
99 From = "192.168.1.1";
100 To = "192.168.1.2";
101 SourcePort = 666;
102 DestinationPort = 667;
103 }
104 {
105 Table = 40;
106 IPProtocol = "tcp";
107 InvertRule = true;
108 }
109 {
110 Table = 50;
111 IncomingInterface = "eth1";
112 Family = "ipv4";
113 }
114 {
115 Table = 60;
116 FirewallMark = 4;
117 }
118 {
119 Table = 70;
120 FirewallMark = "16/0x1f";
121 }
122 ];
123 };
124 };
125 };
126 };
127in
128{ pkgs, ... }:
129{
130 name = "networkd";
131 meta = with pkgs.lib.maintainers; {
132 maintainers = [ picnoir ];
133 };
134 nodes = {
135 node1 =
136 { pkgs, ... }@attrs:
137 let
138 localConf = {
139 privk = "GDiXWlMQKb379XthwX0haAbK6hTdjblllpjGX0heP00=";
140 pubk = "iRxpqj42nnY0Qz8MAQbSm7bXxXP5hkPqWYIULmvW+EE=";
141 systemdCreds = false;
142 nodeId = "1";
143 peerId = "2";
144 };
145 in
146 generateNodeConf (attrs // localConf);
147
148 node2 =
149 { pkgs, ... }@attrs:
150 let
151 localConf = {
152 privk = "eHxSI2jwX/P4AOI0r8YppPw0+4NZnjOxfbS5mt06K2k=";
153 pubk = "27s0OvaBBdHoJYkH9osZpjpgSOVNw+RaKfboT/Sfq0g=";
154 systemdCreds = true;
155 nodeId = "2";
156 peerId = "1";
157 };
158 in
159 generateNodeConf (attrs // localConf);
160 };
161 testScript = ''
162 start_all()
163 node1.systemctl("start systemd-networkd-wait-online@eth1.service")
164 node1.systemctl("start systemd-networkd-wait-online.service")
165 node1.wait_for_unit("systemd-networkd-wait-online@eth1.service")
166 node1.wait_for_unit("systemd-networkd-wait-online.service")
167 node2.systemctl("start systemd-networkd-wait-online@eth1.service")
168 node2.systemctl("start systemd-networkd-wait-online.service")
169 node2.wait_for_unit("systemd-networkd-wait-online@eth1.service")
170 node2.wait_for_unit("systemd-networkd-wait-online.service")
171
172 # ================================
173 # Networkd Config
174 # ================================
175 node1.succeed("grep RouteTable=custom:23 /etc/systemd/networkd.conf")
176 node1.succeed("sudo ip route show table custom | grep '10.0.0.0/24 via 10.0.0.1 dev wg0 proto static'")
177
178 # ================================
179 # Wireguard
180 # ================================
181 node1.succeed("ping -c 5 10.0.0.2")
182 node2.succeed("ping -c 5 10.0.0.1")
183 # Is the fwmark set?
184 node2.succeed("wg | grep -q 42")
185
186 # ================================
187 # Routing Policies
188 # ================================
189 # Testing all the routingPolicyRuleConfig members:
190 # Table + IncomingInterface
191 node1.succeed("sudo ip rule | grep 'from all iif eth1 lookup 10'")
192 # OutgoingInterface
193 node1.succeed("sudo ip rule | grep 'from all oif eth1 lookup 20'")
194 # From + To + SourcePort + DestinationPort
195 node1.succeed(
196 "sudo ip rule | grep 'from 192.168.1.1 to 192.168.1.2 sport 666 dport 667 lookup 30'"
197 )
198 # IPProtocol + InvertRule
199 node1.succeed("sudo ip rule | grep 'not from all ipproto tcp lookup 40'")
200 # FirewallMark without a mask
201 node1.succeed("sudo ip rule | grep 'from all fwmark 0x4 lookup 60'")
202 # FirewallMark with a mask
203 node1.succeed("sudo ip rule | grep 'from all fwmark 0x10/0x1f lookup 70'")
204 '';
205}