vuls: init at 0.27.0
[NixPkgs.git] / nixos / tests / rosenpass.nix
blob5ef6e55f537461f7fa443c550183cd73d8d2ca87
1 import ./make-test-python.nix ({ pkgs, ... }:
2 let
3   deviceName = "rp0";
5   server = {
6     ip = "fe80::1";
7     wg = {
8       public = "mQufmDFeQQuU/fIaB2hHgluhjjm1ypK4hJr1cW3WqAw=";
9       secret = "4N5Y1dldqrpsbaEiY8O0XBUGUFf8vkvtBtm8AoOX7Eo=";
10       listen = 10000;
11     };
12   };
13   client = {
14     ip = "fe80::2";
15     wg = {
16       public = "Mb3GOlT7oS+F3JntVKiaD7SpHxLxNdtEmWz/9FMnRFU=";
17       secret = "uC5dfGMv7Oxf5UDfdPkj6rZiRZT2dRWp5x8IQxrNcUE=";
18     };
19   };
22   name = "rosenpass";
24   nodes =
25     let
26       shared = peer: { config, modulesPath, ... }: {
27         imports = [ "${modulesPath}/services/networking/rosenpass.nix" ];
29         boot.kernelModules = [ "wireguard" ];
31         services.rosenpass = {
32           enable = true;
33           defaultDevice = deviceName;
34           settings = {
35             verbosity = "Verbose";
36             public_key = "/etc/rosenpass/pqpk";
37             secret_key = "/etc/rosenpass/pqsk";
38           };
39         };
41         networking.firewall.allowedUDPPorts = [ 9999 ];
43         systemd.network = {
44           enable = true;
45           networks."rosenpass" = {
46             matchConfig.Name = deviceName;
47             networkConfig.IPv4Forwarding = true;
48             networkConfig.IPv6Forwarding = true;
49             address = [ "${peer.ip}/64" ];
50           };
52           netdevs."10-rp0" = {
53             netdevConfig = {
54               Kind = "wireguard";
55               Name = deviceName;
56             };
57             wireguardConfig.PrivateKeyFile = "/etc/wireguard/wgsk";
58           };
59         };
61         environment.etc."wireguard/wgsk" = {
62           text = peer.wg.secret;
63           user = "systemd-network";
64           group = "systemd-network";
65         };
66       };
67     in
68     {
69       server = {
70         imports = [ (shared server) ];
72         networking.firewall.allowedUDPPorts = [ server.wg.listen ];
74         systemd.network.netdevs."10-${deviceName}" = {
75           wireguardConfig.ListenPort = server.wg.listen;
76           wireguardPeers = [
77             {
78               AllowedIPs = [ "::/0" ];
79               PublicKey = client.wg.public;
80             }
81           ];
82         };
84         services.rosenpass.settings = {
85           listen = [ "0.0.0.0:9999" ];
86           peers = [
87             {
88               public_key = "/etc/rosenpass/peers/client/pqpk";
89               peer = client.wg.public;
90             }
91           ];
92         };
93       };
94       client = {
95         imports = [ (shared client) ];
97         systemd.network.netdevs."10-${deviceName}".wireguardPeers = [
98           {
99             AllowedIPs = [ "::/0" ];
100             PublicKey = server.wg.public;
101             Endpoint = "server:${builtins.toString server.wg.listen}";
102           }
103         ];
105         services.rosenpass.settings.peers = [
106           {
107             public_key = "/etc/rosenpass/peers/server/pqpk";
108             endpoint = "server:9999";
109             peer = server.wg.public;
110           }
111         ];
112       };
113     };
115   testScript = { ... }: ''
116     from os import system
118     # Full path to rosenpass in the store, to avoid fiddling with `$PATH`.
119     rosenpass = "${pkgs.rosenpass}/bin/rosenpass"
121     # Path in `/etc` where keys will be placed.
122     etc = "/etc/rosenpass"
124     start_all()
126     for machine in [server, client]:
127         machine.wait_for_unit("multi-user.target")
129     # Gently stop Rosenpass to avoid crashes during key generation/distribution.
130     for machine in [server, client]:
131         machine.execute("systemctl stop rosenpass.service")
133     for (name, machine, remote) in [("server", server, client), ("client", client, server)]:
134         pk, sk = f"{name}.pqpk", f"{name}.pqsk"
135         system(f"{rosenpass} gen-keys --force --secret-key {sk} --public-key {pk}")
136         machine.copy_from_host(sk, f"{etc}/pqsk")
137         machine.copy_from_host(pk, f"{etc}/pqpk")
138         remote.copy_from_host(pk, f"{etc}/peers/{name}/pqpk")
140     for machine in [server, client]:
141         machine.execute("systemctl start rosenpass.service")
143     for machine in [server, client]:
144         machine.wait_for_unit("rosenpass.service")
146     with subtest("ping"):
147         client.succeed("ping -c 2 -i 0.5 ${server.ip}%${deviceName}")
149     with subtest("preshared-keys"):
150         # Rosenpass works by setting the WireGuard preshared key at regular intervals.
151         # Thus, if it is not active, then no key will be set, and the output of `wg show` will contain "none".
152         # Otherwise, if it is active, then the key will be set and "none" will not be found in the output of `wg show`.
153         for machine in [server, client]:
154             machine.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5)
155   '';
157   # NOTE: Below configuration is for "interactive" (=developing/debugging) only.
158   interactive.nodes =
159     let
160       inherit (import ./ssh-keys.nix pkgs) snakeOilPublicKey snakeOilPrivateKey;
162       sshAndKeyGeneration = {
163         services.openssh.enable = true;
164         users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
165         environment.systemPackages = [
166           (pkgs.writeShellApplication {
167             name = "gen-keys";
168             runtimeInputs = [ pkgs.rosenpass ];
169             text = ''
170               HOST="$(hostname)"
171               if [ "$HOST" == "server" ]
172               then
173                 PEER="client"
174               else
175                 PEER="server"
176               fi
178               # Generate keypair.
179               mkdir -vp /etc/rosenpass/peers/$PEER
180               rosenpass gen-keys --force --secret-key /etc/rosenpass/pqsk --public-key /etc/rosenpass/pqpk
182               # Set up SSH key.
183               mkdir -p /root/.ssh
184               cp ${snakeOilPrivateKey} /root/.ssh/id_ecdsa
185               chmod 0400 /root/.ssh/id_ecdsa
187               # Copy public key to other peer.
188               # shellcheck disable=SC2029
189               ssh -o StrictHostKeyChecking=no $PEER "mkdir -pv /etc/rosenpass/peers/$HOST"
190               scp /etc/rosenpass/pqpk "$PEER:/etc/rosenpass/peers/$HOST/pqpk"
191             '';
192           })
193         ];
194       };
196       # Use kmscon <https://www.freedesktop.org/wiki/Software/kmscon/>
197       # to provide a slightly nicer console, and while we're at it,
198       # also use a nice font.
199       # With kmscon, we can for example zoom in/out using [Ctrl] + [+]
200       # and [Ctrl] + [-]
201       niceConsoleAndAutologin.services.kmscon = {
202         enable = true;
203         autologinUser = "root";
204         fonts = [{
205           name = "Fira Code";
206           package = pkgs.fira-code;
207         }];
208       };
209     in
210     {
211       server = sshAndKeyGeneration // niceConsoleAndAutologin;
212       client = sshAndKeyGeneration // niceConsoleAndAutologin;
213     };