# The generic installer: router-setup is answered on its console (DHCP uplink, # LAN address, web UI password), builds the router offline and installs it. # The installed box must come up with those settings, serve the web UI with # that password, and rebuild to exactly itself. { self, pkgs }: let common = import ./common.nix; # Only so the test driver can talk to the installed system. local = pkgs.writeText "local.nix" '' { lib, modulesPath, ... }: { imports = [ (modulesPath + "/testing/test-instrumentation.nix") ]; # The instrumentation empties root's password; keep router-setup's. users.users.root.hashedPasswordFile = lib.mkForce "/var/lib/router/root-password"; } ''; sharedDisk = { virtualisation.diskImage = "./target.qcow2"; virtualisation.diskSize = 8192; # Evaluating and building a NixOS system takes memory. virtualisation.memorySize = 3072; }; answers = pkgs.writeText "answers" '' /dev/vda eth1 eth2 dhcp gw 192.168.10.1 hunter2hunter2 hunter2hunter2 /dev/vda ''; in pkgs.testers.runNixOSTest { name = "router-setup"; nodes = { installer = { lib, ... }: { imports = [ sharedDisk (self.lib.setupModule { inherit local; }) common.tools ]; environment.etc."router-test-local.nix".source = local; # The interfaces router-setup offers. virtualisation.vlans = [ 1 2 ]; virtualisation.fileSystems."/".autoFormat = true; virtualisation.emptyDiskImages = [ 1024 ]; virtualisation.rootDevice = "/dev/vdb"; nix.settings.substituters = lib.mkForce [ ]; }; target = { imports = [ sharedDisk ]; virtualisation.vlans = [ 1 2 ]; virtualisation.useBootLoader = true; virtualisation.useEFIBoot = true; virtualisation.useDefaultFilesystems = false; virtualisation.efi.keepVariables = false; virtualisation.fileSystems."/" = { device = "/dev/disk/by-label/this-is-not-real-and-will-never-be-used"; fsType = "ext4"; }; }; }; testScript = '' installer.start() installer.wait_for_unit("multi-user.target") with subtest("router-setup refuses to install without the disk name"): installer.fail("printf '/dev/vda\\neth1\\neth2\\ndhcp\\n\\n\\nhunter2hunter2\\nhunter2hunter2\\n\\nno\\n' | router-setup") installer.fail("blkid /dev/vda1") with subtest("router-setup builds the answered configuration offline and installs it"): installer.succeed("router-setup --local /etc/router-test-local.nix < ${answers} >&2") installer.shutdown() target.state_dir = installer.state_dir target.start() target.wait_for_unit("multi-user.target") with subtest("the installed router has the answered settings"): assert target.succeed("hostname").strip() == "gw" target.wait_until_succeeds("ip -4 addr show dev eth2 | grep -q 192.168.10.1/24") target.succeed("grep -q '192.168.10.100 - 192.168.10.200' /etc/kea/dhcp4-server.conf") settings = target.succeed("cat /var/lib/router/config/settings.json") assert '"eth1"' in settings and '"dhcp"' in settings, settings assert target.succeed("stat -c %a /var/lib/router/webui-password").strip() == "600" with subtest("the web UI accepts the password given to router-setup"): target.wait_for_unit("router-webui.service") target.wait_until_succeeds("curl -sk -o /dev/null https://127.0.0.1/login") code = target.succeed("curl -sk -o /dev/null -w '%{http_code}' -d password=hunter2hunter2 https://127.0.0.1/login") assert code == "303", code with subtest("root logs in on the console with the same password"): target.wait_until_tty_matches("1", "login: ") target.send_chars("root\n") target.wait_until_tty_matches("1", "Password: ") target.send_chars("hunter2hunter2\n") target.wait_until_succeeds("pgrep -u root -t tty1 bash") assert target.succeed("stat -c %a /var/lib/router/root-password").strip() == "600" with subtest("the box rebuilds to exactly the installed system"): out = target.succeed("router-apply 2>&1") assert "no changes" in out, out ''; }