diff --git a/flake.nix b/flake.nix index b5473e13..69a2186a 100644 --- a/flake.nix +++ b/flake.nix @@ -65,6 +65,7 @@ system, pkgs, config, + lib, ... }: { @@ -77,6 +78,27 @@ unstable = import inputs.unstable { inherit system; }; master = import inputs.master { inherit system; }; }; + + checks = + let + nixos-lib = import (inputs.nixpkgs + "/nixos/lib") { }; + testDir = builtins.attrNames (builtins.readDir ./tests); + testFiles = builtins.filter (n: builtins.match "^.*.nix$" n != null) testDir; + in + builtins.listToAttrs ( + map (x: { + name = "test-${lib.strings.removeSuffix ".nix" x}"; + value = nixos-lib.runTest ( + import (./tests + "/${x}") { + inherit self; + inherit pkgs; + inherit lib; + inherit config; + } + ); + }) testFiles + ); + devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ deploy-rs @@ -95,6 +117,7 @@ jq ]; }; + devShells.ci = pkgs.mkShell { buildInputs = with pkgs; [ nodejs ]; }; }; diff --git a/modules/core/nix.nix b/modules/core/nix.nix index 338cdd18..ece11ae6 100644 --- a/modules/core/nix.nix +++ b/modules/core/nix.nix @@ -6,7 +6,7 @@ ... }: { - nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ ]; + nixpkgs.config = lib.mkDefault { allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ ]; }; nix = { # Use default version alias for nix package diff --git a/tests/keycloak.nix b/tests/keycloak.nix new file mode 100644 index 00000000..5e735fd6 --- /dev/null +++ b/tests/keycloak.nix @@ -0,0 +1,92 @@ +{ + self, + pkgs, + lib, + config, + ... +}: +let +in +{ + name = "keycloak"; + + hostPkgs = pkgs; + + node.pkgs = pkgs; + node.specialArgs = self.outputs.nixosConfigurations.nachtigall._module.specialArgs; + + nodes = { + acme-server = { + imports = [ + self.nixosModules.home-manager + self.nixosModules.core + ./support/ca.nix + ]; + }; + + client = { + imports = [ + self.nixosModules.home-manager + self.nixosModules.core + ./support/client.nix + ]; + }; + + nachtigall = { + imports = [ + self.inputs.agenix.nixosModules.default + self.nixosModules.home-manager + self.nixosModules.core + self.nixosModules.backups + self.nixosModules.nginx + self.nixosModules.keycloak + self.nixosModules.postgresql + ./support/global.nix + ]; + + systemd.tmpfiles.rules = [ "f /tmp/dbf 1777 root root 10d password" ]; + + virtualisation.memorySize = 4096; + + pub-solar-os.auth = { + enable = true; + database-password-file = "/tmp/dbf"; + }; + services.keycloak.database.createLocally = true; + + networking.interfaces.eth0.ipv4.addresses = [ + { + address = "192.168.1.3"; + prefixLength = 32; + } + ]; + }; + }; + + testScript = + { nodes, ... }: + let + user = nodes.client.users.users.${nodes.client.pub-solar-os.authentication.username}; + #uid = toString user.uid; + bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u ${user.name})/bus"; + gdbus = "${bus} gdbus"; + su = command: "su - ${user.name} -c '${command}'"; + gseval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval"; + wmClass = su "${gdbus} ${gseval} global.display.focus_window.wm_class"; + in + '' + start_all() + + nachtigall.wait_for_unit("system.slice") + nachtigall.succeed("ping 127.0.0.1 -c 2") + nachtigall.wait_for_unit("nginx.service") + nachtigall.wait_for_unit("keycloak.service") + nachtigall.wait_until_succeeds("curl http://127.0.0.1:8080/") + nachtigall.wait_until_succeeds("curl https://auth.test.pub.solar/") + + client.wait_for_unit("system.slice") + client.sleep(30) + # client.wait_until_succeeds("${wmClass} | grep -q 'firefox'") + client.screenshot("screen") + ''; +} diff --git a/tests/support/ca.nix b/tests/support/ca.nix new file mode 100644 index 00000000..632c610c --- /dev/null +++ b/tests/support/ca.nix @@ -0,0 +1,47 @@ +{ + pkgs, + lib, + config, + ... +}: +{ + imports = [ ./global.nix ]; + + systemd.tmpfiles.rules = [ "f /tmp/step-ca-intermediate-pw 1777 root root 10d password" ]; + + networking.interfaces.eth0.ipv4.addresses = [ + { + address = "192.168.1.1"; + prefixLength = 32; + } + ]; + + services.step-ca = + let + certificates = pkgs.stdenv.mkDerivation { + name = "certificates"; + src = ./step; + installPhase = '' + mkdir -p $out; + cp -r certs $out/ + cp -r secrets $out/ + ''; + }; + in + { + enable = true; + openFirewall = true; + intermediatePasswordFile = "/tmp/step-ca-intermediate-pw"; + port = 443; + address = "0.0.0.0"; + settings = (builtins.fromJSON (builtins.readFile ./step/config/ca.json)) // { + root = "${certificates}/certs/root_ca.crt"; + crt = "${certificates}/certs/intermediate_ca.crt"; + key = "${certificates}/secrets/intermediate_ca_key"; + db = { + type = "badgerv2"; + dataSource = "/var/lib/step-ca/db"; + }; + }; + }; +} diff --git a/tests/support/client.nix b/tests/support/client.nix new file mode 100644 index 00000000..41e97f09 --- /dev/null +++ b/tests/support/client.nix @@ -0,0 +1,35 @@ +{ + pkgs, + lib, + config, + ... +}: +{ + imports = [ ./global.nix ]; + + services.xserver.enable = true; + services.xserver.displayManager.gdm.enable = true; + services.xserver.desktopManager.gnome.enable = true; + services.xserver.displayManager.autoLogin.enable = true; + services.xserver.displayManager.autoLogin.user = config.pub-solar-os.authentication.username; + + systemd.user.services = { + "org.gnome.Shell@wayland" = { + serviceConfig = { + ExecStart = [ + # Clear the list before overriding it. + "" + # Eval API is now internal so Shell needs to run in unsafe mode. + "${pkgs.gnome.gnome-shell}/bin/gnome-shell --unsafe-mode" + ]; + }; + }; + }; + + networking.interfaces.eth0.ipv4.addresses = [ + { + address = "192.168.1.2"; + prefixLength = 32; + } + ]; +} diff --git a/tests/support/global.nix b/tests/support/global.nix new file mode 100644 index 00000000..f5e68c91 --- /dev/null +++ b/tests/support/global.nix @@ -0,0 +1,50 @@ +{ + pkgs, + lib, + config, + ... +}: +{ + pub-solar-os.networking.domain = "test.pub.solar"; + + security.acme.defaults.server = "https://ca.${config.pub-solar-os.networking.domain}/acme/acme/directory"; + + security.pki.certificates = [ (builtins.readFile ./step/certs/root_ca.crt) ]; + + services.openssh = { + enable = true; + openFirewall = true; + settings = { + PermitRootLogin = lib.mkForce "yes"; + PermitEmptyPasswords = lib.mkForce "yes"; + PasswordAuthentication = lib.mkForce true; + }; + }; + + security.pam.services.sshd.allowNullPassword = true; + + virtualisation.forwardPorts = + let + address = (builtins.elemAt config.networking.interfaces.eth0.ipv4.addresses 0).address; + lastAddressPart = builtins.elemAt (lib.strings.splitString "." address) 3; + in + [ + { + from = "host"; + host.port = 2000 + (lib.strings.toInt lastAddressPart); + guest.port = 22; + } + ]; + + networking.interfaces.eth0.useDHCP = false; + + networking.hosts = { + "192.168.1.1" = [ "ca.${config.pub-solar-os.networking.domain}" ]; + "192.168.1.2" = [ "client.${config.pub-solar-os.networking.domain}" ]; + "192.168.1.3" = [ + "${config.pub-solar-os.networking.domain}" + "www.${config.pub-solar-os.networking.domain}" + "auth.${config.pub-solar-os.networking.domain}" + ]; + }; +} diff --git a/tests/support/step/certs/intermediate_ca.crt b/tests/support/step/certs/intermediate_ca.crt new file mode 100644 index 00000000..32208384 --- /dev/null +++ b/tests/support/step/certs/intermediate_ca.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB4DCCAYagAwIBAgIQVR/3c0swvc/ifeYqLQn3HTAKBggqhkjOPQQDAjA6MRcw +FQYDVQQKEw5wdWIuc29sYXItdGVzdDEfMB0GA1UEAxMWcHViLnNvbGFyLXRlc3Qg +Um9vdCBDQTAeFw0yNDA4MjQwMTI3MTBaFw0zNDA4MjIwMTI3MTBaMEIxFzAVBgNV +BAoTDnB1Yi5zb2xhci10ZXN0MScwJQYDVQQDEx5wdWIuc29sYXItdGVzdCBJbnRl +cm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATpCjy3PAiawAeb +47ZZ9kPXuuV0EavOfFlgnlZBkOc2AXY0R6P1jK06US0SiPo17rqyNgUWH0oV4v8i +/HbZYNXYo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAd +BgNVHQ4EFgQU1hueYsLAH6+wxjArqCM3IHFqnIEwHwYDVR0jBBgwFoAUxg/BmKK7 +9Zs+b1bvlpYwggy5lnswCgYIKoZIzj0EAwIDSAAwRQIgfxkjyC4HHADRmNDLqZ5L +0po+JD5/9b1L//JoXG+vgXECIQDgkRe8r8/0Ep/NWgBtbkA3oTYq8vCwo1FewBZZ +43fo5w== +-----END CERTIFICATE----- diff --git a/tests/support/step/certs/root_ca.crt b/tests/support/step/certs/root_ca.crt new file mode 100644 index 00000000..71f5cea0 --- /dev/null +++ b/tests/support/step/certs/root_ca.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuDCCAV2gAwIBAgIQMXg7xoEIrVjgvKcrRaxo0DAKBggqhkjOPQQDAjA6MRcw +FQYDVQQKEw5wdWIuc29sYXItdGVzdDEfMB0GA1UEAxMWcHViLnNvbGFyLXRlc3Qg +Um9vdCBDQTAeFw0yNDA4MjQwMTI3MDlaFw0zNDA4MjIwMTI3MDlaMDoxFzAVBgNV +BAoTDnB1Yi5zb2xhci10ZXN0MR8wHQYDVQQDExZwdWIuc29sYXItdGVzdCBSb290 +IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYNxMcHclQP/zv2y6LJIGx9pg +Q2337Zb8TuPY+DnL1MjuCMoeTaMwngzjU/DSbKL0Vx/y+I+PBjhHmPrYcGDcSKNF +MEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE +FMYPwZiiu/WbPm9W75aWMIIMuZZ7MAoGCCqGSM49BAMCA0kAMEYCIQDcgr9WyR1C +806aEQ38alYgGg3PhQdT14q47tWIUOnpygIhAM0x/QK/mm7VvQxBLAA4DT6X730m +k/tBvh9SHNbwPxCt +-----END CERTIFICATE----- diff --git a/tests/support/step/config/ca.json b/tests/support/step/config/ca.json new file mode 100644 index 00000000..6735a002 --- /dev/null +++ b/tests/support/step/config/ca.json @@ -0,0 +1,44 @@ +{ + "federatedRoots": null, + "address": ":443", + "insecureAddress": "", + "dnsNames": ["ca.test.pub.solar"], + "logger": { + "format": "text" + }, + "db": { + "type": "badgerv2", + "badgerFileLoadingMode": "" + }, + "authority": { + "provisioners": [ + { + "name": "acme", + "type": "ACME" + }, + { + "type": "JWK", + "name": "test.pub.solar", + "key": { + "use": "sig", + "kty": "EC", + "kid": "lM-BJXRwwQcdgxLqAS4Za23A2YatZpwXx-PP5NIt8JM", + "crv": "P-256", + "alg": "ES256", + "x": "ouB2mP04Kt8rDa10C8ZzYyzA36rrz-k0c4_ud1hVjyg", + "y": "RbXKcudQRPEFqjG_5AxuqCQXn7pyRToQCwC4MrwLVUQ" + }, + "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiNWR5T2puR2Y5aFFNRlc1U25fRWhzUSJ9.a3xtSBuMmzZCMsdfHAXMgFpe9bq8A6bGGOoW9F2Gw7AhxL4bG-AlgA.IA68rSJSGTAKnaVS.XDQc4da-8D9Ykfw-8S4uphsauq5gsEm4qp7zKQUIvcjUlnPAtiHP3xiiBie29ncdg8rKmyzprEEOpTNvXtQl7LsPsHXyKV3SqsTnJecvim9YXGDneAHyWe-XF6hyCZAfSoFbFMgLDKR6d44hMht3ueazL_TPlkFUBLrJbsW782MfdfF3nzcaDf_JDuhKsKHDmKqZyNXDzwf6rINe8adrf5gqaLM2_sGhk7i3XyXygn8HHVw1Dj_w2gPOVm4MS7CO_NgikPqAtGuXDhpWZfXte-FlnMO6d9xQF67b0cwB8kmColPSp1zRiCKPAk9vof8Nn-gGE_aw8zxPi0CJkoY.xbuqSSspgLc_Uw17uiRF7Q" + } + ] + }, + "tls": { + "cipherSuites": [ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + ], + "minVersion": 1.2, + "maxVersion": 1.3, + "renegotiation": false + } +} diff --git a/tests/support/step/config/defaults.json b/tests/support/step/config/defaults.json new file mode 100644 index 00000000..40f76069 --- /dev/null +++ b/tests/support/step/config/defaults.json @@ -0,0 +1,6 @@ +{ + "ca-url": "https://ca.test.pub.solar", + "ca-config": "/home/b12f/.step/config/ca.json", + "fingerprint": "4d6a1a918355380acbd0256a2203d0a0da8436bb788e8f19326589045c3cd842", + "root": "/home/b12f/.step/certs/root_ca.crt" +} diff --git a/tests/support/step/secrets/intermediate_ca_key b/tests/support/step/secrets/intermediate_ca_key new file mode 100644 index 00000000..a8eb8b27 --- /dev/null +++ b/tests/support/step/secrets/intermediate_ca_key @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,0b34c00cb76ffc16441f5fe762d8d915 + +xJQ5r5kGiaG6rCsmVnONxm99sqceb62dO8/YvgdZ/ouHAxz1OlXYpTJNd2GvezAc +XA6Zx6eGzNCOyhgMNJTXEn8QmcJcMd6OjVLxQ9Tr2Mi3LShcBzMPs30/X2XYsM22 +5G4fRhQD0L4nQ08B3GG6FjPe/HYmkRNZmAeDc2wE5Fg= +-----END EC PRIVATE KEY----- diff --git a/tests/support/step/secrets/root_ca_key b/tests/support/step/secrets/root_ca_key new file mode 100644 index 00000000..19605f93 --- /dev/null +++ b/tests/support/step/secrets/root_ca_key @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,48f59a57e5a2b81359e0a3668161b61e + +jMZbpiHSFa74ns30QrAnIlcguqWp+FE20cXbiIVPpLAJpzGskc3k5vRFTpPM8geg +sZ6bVvq3APbKmkopxZHWpd4ly6uHkolbtR1NFxTNKymaJZuSuKspUmDohkIyZN6c +KG0upERMZIOg6Ky1JiM5pLJMHBTsCmzJBmdFCW7GSww= +-----END EC PRIVATE KEY----- diff --git a/tests/website.nix b/tests/website.nix index dc33aff0..452262b1 100644 --- a/tests/website.nix +++ b/tests/website.nix @@ -8,17 +8,52 @@ { name = "website"; - nodes.nachtigall-test = self.nixosConfigurations.nachtigall-test; - - node.specialArgs = self.outputs.nixosConfigurations.nachtigall._module.specialArgs; hostPkgs = pkgs; - enableOCR = true; + node.pkgs = pkgs; + node.specialArgs = self.outputs.nixosConfigurations.nachtigall._module.specialArgs; + + nodes = { + acme-server = { + imports = [ + self.nixosModules.home-manager + self.nixosModules.core + ./support/ca.nix + ]; + }; + + nachtigall = { + imports = [ + self.nixosModules.home-manager + self.nixosModules.core + self.nixosModules.nginx + self.nixosModules.nginx-website + ./support/global.nix + ]; + + virtualisation.memorySize = 4096; + + networking.interfaces.eth0.ipv4.addresses = [ + { + address = "192.168.1.3"; + prefixLength = 32; + } + ]; + }; + }; testScript = '' - machine.wait_for_unit("system.slice") - machine.succeed("ping 127.0.0.1 -c 2") - machine.wait_for_unit("nginx.service") - machine.succeed("curl -H 'Host:pub.solar' http://127.0.0.1/") + start_all() + + acme_server.wait_for_unit("system.slice") + acme_server.wait_for_unit("step-ca.service") + acme_server.succeed("ping ca.test.pub.solar -c 2") + acme_server.wait_until_succeeds("curl 127.0.0.1:443") + + nachtigall.wait_for_unit("system.slice") + nachtigall.succeed("ping test.pub.solar -c 2") + nachtigall.succeed("ping ca.test.pub.solar -c 2") + nachtigall.wait_for_unit("nginx.service") + nachtigall.wait_until_succeeds("curl https://test.pub.solar/") ''; }