From 74026bb1014460d511534da8a955bee6948716ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 20 Jan 2023 14:43:26 +0100 Subject: tests: Move NixOS tests to tests/nixos This will allow contributors to find them more easily. --- tests/containers.nix | 68 ---------- tests/github-flakes.nix | 210 ------------------------------ tests/id-test.nix | 8 -- tests/nix-copy-closure.nix | 77 ----------- tests/nixos/containers/containers.nix | 68 ++++++++++ tests/nixos/containers/id-test.nix | 8 ++ tests/nixos/containers/systemd-nspawn.nix | 78 +++++++++++ tests/nixos/github-flakes.nix | 210 ++++++++++++++++++++++++++++++ tests/nixos/nix-copy-closure.nix | 77 +++++++++++ tests/nixos/nss-preload.nix | 125 ++++++++++++++++++ tests/nixos/remote-builds.nix | 112 ++++++++++++++++ tests/nixos/setuid.nix | 126 ++++++++++++++++++ tests/nixos/sourcehut-flakes.nix | 167 ++++++++++++++++++++++++ tests/nss-preload.nix | 125 ------------------ tests/remote-builds.nix | 112 ---------------- tests/setuid.nix | 126 ------------------ tests/sourcehut-flakes.nix | 167 ------------------------ tests/systemd-nspawn.nix | 78 ----------- 18 files changed, 971 insertions(+), 971 deletions(-) delete mode 100644 tests/containers.nix delete mode 100644 tests/github-flakes.nix delete mode 100644 tests/id-test.nix delete mode 100644 tests/nix-copy-closure.nix create mode 100644 tests/nixos/containers/containers.nix create mode 100644 tests/nixos/containers/id-test.nix create mode 100644 tests/nixos/containers/systemd-nspawn.nix create mode 100644 tests/nixos/github-flakes.nix create mode 100644 tests/nixos/nix-copy-closure.nix create mode 100644 tests/nixos/nss-preload.nix create mode 100644 tests/nixos/remote-builds.nix create mode 100644 tests/nixos/setuid.nix create mode 100644 tests/nixos/sourcehut-flakes.nix delete mode 100644 tests/nss-preload.nix delete mode 100644 tests/remote-builds.nix delete mode 100644 tests/setuid.nix delete mode 100644 tests/sourcehut-flakes.nix delete mode 100644 tests/systemd-nspawn.nix (limited to 'tests') diff --git a/tests/containers.nix b/tests/containers.nix deleted file mode 100644 index a4856b2df..000000000 --- a/tests/containers.nix +++ /dev/null @@ -1,68 +0,0 @@ -# Test whether we can run a NixOS container inside a Nix build using systemd-nspawn. -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") { - inherit system; - extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; -}; - -makeTest ({ - name = "containers"; - - nodes = - { - host = - { config, lib, pkgs, nodes, ... }: - { virtualisation.writableStore = true; - virtualisation.diskSize = 2048; - virtualisation.additionalPaths = - [ pkgs.stdenvNoCC - (import ./systemd-nspawn.nix { inherit nixpkgs; }).toplevel - ]; - virtualisation.memorySize = 4096; - nix.settings.substituters = lib.mkForce [ ]; - nix.extraOptions = - '' - extra-experimental-features = nix-command auto-allocate-uids cgroups - extra-system-features = uid-range - ''; - nix.nixPath = [ "nixpkgs=${nixpkgs}" ]; - }; - }; - - testScript = { nodes }: '' - start_all() - - host.succeed("nix --version >&2") - - # Test that 'id' gives the expected result in various configurations. - - # Existing UIDs, sandbox. - host.succeed("nix build -v --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1") - host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") - - # Existing UIDs, no sandbox. - host.succeed("nix build -v --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2") - host.succeed("[[ $(cat ./result) = 'uid=30001(nixbld1) gid=30000(nixbld) groups=30000(nixbld)' ]]") - - # Auto-allocated UIDs, sandbox. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3") - host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") - - # Auto-allocated UIDs, no sandbox. - host.succeed("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4") - host.succeed("[[ $(cat ./result) = 'uid=872415232 gid=30000(nixbld) groups=30000(nixbld)' ]]") - - # Auto-allocated UIDs, UID range, sandbox. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true") - host.succeed("[[ $(cat ./result) = 'uid=0(root) gid=0(root) groups=0(root)' ]]") - - # Auto-allocated UIDs, UID range, no sandbox. - host.fail("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true") - - # Run systemd-nspawn in a Nix build. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}") - host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]") - ''; - -}) diff --git a/tests/github-flakes.nix b/tests/github-flakes.nix deleted file mode 100644 index a8b036b17..000000000 --- a/tests/github-flakes.nix +++ /dev/null @@ -1,210 +0,0 @@ -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") { - inherit system; - extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; -}; - -let - - # Generate a fake root CA and a fake api.github.com / github.com / channels.nixos.org certificate. - cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; } - '' - mkdir -p $out - - openssl genrsa -out ca.key 2048 - openssl req -new -x509 -days 36500 -key ca.key \ - -subj "/C=NL/ST=Denial/L=Springfield/O=Dis/CN=Root CA" -out $out/ca.crt - - openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \ - -subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=github.com" -out server.csr - openssl x509 -req -extfile <(printf "subjectAltName=DNS:api.github.com,DNS:github.com,DNS:channels.nixos.org") \ - -days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt - ''; - - registry = pkgs.writeTextFile { - name = "registry"; - text = '' - { - "flakes": [ - { - "from": { - "type": "indirect", - "id": "nixpkgs" - }, - "to": { - "type": "github", - "owner": "NixOS", - "repo": "nixpkgs" - } - }, - { - "from": { - "type": "indirect", - "id": "private-flake" - }, - "to": { - "type": "github", - "owner": "fancy-enterprise", - "repo": "private-flake" - } - } - ], - "version": 2 - } - ''; - destination = "/flake-registry.json"; - }; - - private-flake-rev = "9f1dd0df5b54a7dc75b618034482ed42ce34383d"; - - private-flake-api = pkgs.runCommand "private-flake" {} - '' - mkdir -p $out/{commits,tarball} - - # Setup https://docs.github.com/en/rest/commits/commits#get-a-commit - echo '{"sha": "${private-flake-rev}"}' > $out/commits/HEAD - - # Setup tarball download via API - dir=private-flake - mkdir $dir - echo '{ outputs = {...}: {}; }' > $dir/flake.nix - tar cfz $out/tarball/${private-flake-rev} $dir --hard-dereference - ''; - - nixpkgs-api = pkgs.runCommand "nixpkgs-flake" {} - '' - mkdir -p $out/commits - - # Setup https://docs.github.com/en/rest/commits/commits#get-a-commit - echo '{"sha": "${nixpkgs.rev}"}' > $out/commits/HEAD - ''; - - archive = pkgs.runCommand "nixpkgs-flake" {} - '' - mkdir -p $out/archive - - dir=NixOS-nixpkgs-${nixpkgs.shortRev} - cp -prd ${nixpkgs} $dir - # Set the correct timestamp in the tarball. - find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} -- - tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference - ''; -in - -makeTest ( - -{ - name = "github-flakes"; - - nodes = - { - github = - { config, pkgs, ... }: - { networking.firewall.allowedTCPPorts = [ 80 443 ]; - - services.httpd.enable = true; - services.httpd.adminAddr = "foo@example.org"; - services.httpd.extraConfig = '' - ErrorLog syslog:local6 - ''; - services.httpd.virtualHosts."channels.nixos.org" = - { forceSSL = true; - sslServerKey = "${cert}/server.key"; - sslServerCert = "${cert}/server.crt"; - servedDirs = - [ { urlPath = "/"; - dir = registry; - } - ]; - }; - services.httpd.virtualHosts."api.github.com" = - { forceSSL = true; - sslServerKey = "${cert}/server.key"; - sslServerCert = "${cert}/server.crt"; - servedDirs = - [ { urlPath = "/repos/NixOS/nixpkgs"; - dir = nixpkgs-api; - } - { urlPath = "/repos/fancy-enterprise/private-flake"; - dir = private-flake-api; - } - ]; - }; - services.httpd.virtualHosts."github.com" = - { forceSSL = true; - sslServerKey = "${cert}/server.key"; - sslServerCert = "${cert}/server.crt"; - servedDirs = - [ { urlPath = "/NixOS/nixpkgs"; - dir = archive; - } - ]; - }; - }; - - client = - { config, lib, pkgs, nodes, ... }: - { virtualisation.writableStore = true; - virtualisation.diskSize = 2048; - virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ]; - virtualisation.memorySize = 4096; - nix.settings.substituters = lib.mkForce [ ]; - nix.extraOptions = "experimental-features = nix-command flakes"; - networking.hosts.${(builtins.head nodes.github.config.networking.interfaces.eth1.ipv4.addresses).address} = - [ "channels.nixos.org" "api.github.com" "github.com" ]; - security.pki.certificateFiles = [ "${cert}/ca.crt" ]; - }; - }; - - testScript = { nodes }: '' - # fmt: off - import json - import time - - start_all() - - def cat_log(): - github.succeed("cat /var/log/httpd/*.log >&2") - - github.wait_for_unit("httpd.service") - - client.succeed("curl -v https://github.com/ >&2") - out = client.succeed("nix registry list") - print(out) - assert "github:NixOS/nixpkgs" in out, "nixpkgs flake not found" - assert "github:fancy-enterprise/private-flake" in out, "private flake not found" - cat_log() - - # If no github access token is provided, nix should use the public archive url... - out = client.succeed("nix flake metadata nixpkgs --json") - print(out) - info = json.loads(out) - assert info["revision"] == "${nixpkgs.rev}", f"revision mismatch: {info['revision']} != ${nixpkgs.rev}" - cat_log() - - # ... otherwise it should use the API - out = client.succeed("nix flake metadata private-flake --json --access-tokens github.com=ghp_000000000000000000000000000000000000 --tarball-ttl 0") - print(out) - info = json.loads(out) - assert info["revision"] == "${private-flake-rev}", f"revision mismatch: {info['revision']} != ${private-flake-rev}" - cat_log() - - client.succeed("nix registry pin nixpkgs") - client.succeed("nix flake metadata nixpkgs --tarball-ttl 0 >&2") - - # Shut down the web server. The flake should be cached on the client. - github.succeed("systemctl stop httpd.service") - - info = json.loads(client.succeed("nix flake metadata nixpkgs --json")) - date = time.strftime("%Y%m%d%H%M%S", time.gmtime(info['lastModified'])) - assert date == "${nixpkgs.lastModifiedDate}", "time mismatch" - - client.succeed("nix build nixpkgs#hello") - - # The build shouldn't fail even with --tarball-ttl 0 (the server - # being down should not be a fatal error). - client.succeed("nix build nixpkgs#fuse --tarball-ttl 0") - ''; - -}) diff --git a/tests/id-test.nix b/tests/id-test.nix deleted file mode 100644 index 8eb9d38f9..000000000 --- a/tests/id-test.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ name, uidRange ? false }: - -with import {}; - -runCommand name - { requiredSystemFeatures = if uidRange then ["uid-range"] else []; - } - "id; id > $out" diff --git a/tests/nix-copy-closure.nix b/tests/nix-copy-closure.nix deleted file mode 100644 index 2dc164ae4..000000000 --- a/tests/nix-copy-closure.nix +++ /dev/null @@ -1,77 +0,0 @@ -# Test ‘nix-copy-closure’. - -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") { - inherit system; - extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; -}; - -makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; pkgD = pkgs.tmux; in { - name = "nix-copy-closure"; - - nodes = - { client = - { config, lib, pkgs, ... }: - { virtualisation.writableStore = true; - virtualisation.additionalPaths = [ pkgA pkgD.drvPath ]; - nix.settings.substituters = lib.mkForce [ ]; - }; - - server = - { config, pkgs, ... }: - { services.openssh.enable = true; - virtualisation.writableStore = true; - virtualisation.additionalPaths = [ pkgB pkgC ]; - }; - }; - - testScript = { nodes }: '' - # fmt: off - import subprocess - - start_all() - - # Create an SSH key on the client. - subprocess.run([ - "${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", "" - ], capture_output=True, check=True) - - client.succeed("mkdir -m 700 /root/.ssh") - client.copy_from_host("key", "/root/.ssh/id_ed25519") - client.succeed("chmod 600 /root/.ssh/id_ed25519") - - # Install the SSH key on the server. - server.succeed("mkdir -m 700 /root/.ssh") - server.copy_from_host("key.pub", "/root/.ssh/authorized_keys") - server.wait_for_unit("sshd") - client.wait_for_unit("network.target") - client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'") - - # Copy the closure of package A from the client to the server. - server.fail("nix-store --check-validity ${pkgA}") - client.succeed("nix-copy-closure --to server --gzip ${pkgA} >&2") - server.succeed("nix-store --check-validity ${pkgA}") - - # Copy the closure of package B from the server to the client. - client.fail("nix-store --check-validity ${pkgB}") - client.succeed("nix-copy-closure --from server --gzip ${pkgB} >&2") - client.succeed("nix-store --check-validity ${pkgB}") - - # Copy the closure of package C via the SSH substituter. - client.fail("nix-store -r ${pkgC}") - - # Copy the derivation of package D's derivation from the client to the server. - server.fail("nix-store --check-validity ${pkgD.drvPath}") - client.succeed("nix-copy-closure --to server --gzip ${pkgD.drvPath} >&2") - server.succeed("nix-store --check-validity ${pkgD.drvPath}") - - # FIXME - # client.succeed( - # "nix-store --option use-ssh-substituter true" - # " --option ssh-substituter-hosts root\@server" - # " -r ${pkgC} >&2" - # ) - # client.succeed("nix-store --check-validity ${pkgC}") - ''; -}) diff --git a/tests/nixos/containers/containers.nix b/tests/nixos/containers/containers.nix new file mode 100644 index 000000000..a4856b2df --- /dev/null +++ b/tests/nixos/containers/containers.nix @@ -0,0 +1,68 @@ +# Test whether we can run a NixOS container inside a Nix build using systemd-nspawn. +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system; + extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; +}; + +makeTest ({ + name = "containers"; + + nodes = + { + host = + { config, lib, pkgs, nodes, ... }: + { virtualisation.writableStore = true; + virtualisation.diskSize = 2048; + virtualisation.additionalPaths = + [ pkgs.stdenvNoCC + (import ./systemd-nspawn.nix { inherit nixpkgs; }).toplevel + ]; + virtualisation.memorySize = 4096; + nix.settings.substituters = lib.mkForce [ ]; + nix.extraOptions = + '' + extra-experimental-features = nix-command auto-allocate-uids cgroups + extra-system-features = uid-range + ''; + nix.nixPath = [ "nixpkgs=${nixpkgs}" ]; + }; + }; + + testScript = { nodes }: '' + start_all() + + host.succeed("nix --version >&2") + + # Test that 'id' gives the expected result in various configurations. + + # Existing UIDs, sandbox. + host.succeed("nix build -v --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1") + host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") + + # Existing UIDs, no sandbox. + host.succeed("nix build -v --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2") + host.succeed("[[ $(cat ./result) = 'uid=30001(nixbld1) gid=30000(nixbld) groups=30000(nixbld)' ]]") + + # Auto-allocated UIDs, sandbox. + host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3") + host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") + + # Auto-allocated UIDs, no sandbox. + host.succeed("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4") + host.succeed("[[ $(cat ./result) = 'uid=872415232 gid=30000(nixbld) groups=30000(nixbld)' ]]") + + # Auto-allocated UIDs, UID range, sandbox. + host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true") + host.succeed("[[ $(cat ./result) = 'uid=0(root) gid=0(root) groups=0(root)' ]]") + + # Auto-allocated UIDs, UID range, no sandbox. + host.fail("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true") + + # Run systemd-nspawn in a Nix build. + host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}") + host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]") + ''; + +}) diff --git a/tests/nixos/containers/id-test.nix b/tests/nixos/containers/id-test.nix new file mode 100644 index 000000000..8eb9d38f9 --- /dev/null +++ b/tests/nixos/containers/id-test.nix @@ -0,0 +1,8 @@ +{ name, uidRange ? false }: + +with import {}; + +runCommand name + { requiredSystemFeatures = if uidRange then ["uid-range"] else []; + } + "id; id > $out" diff --git a/tests/nixos/containers/systemd-nspawn.nix b/tests/nixos/containers/systemd-nspawn.nix new file mode 100644 index 000000000..424436b3f --- /dev/null +++ b/tests/nixos/containers/systemd-nspawn.nix @@ -0,0 +1,78 @@ +{ nixpkgs }: + +let + + machine = { config, pkgs, ... }: + { + system.stateVersion = "22.05"; + boot.isContainer = true; + systemd.services.console-getty.enable = false; + networking.dhcpcd.enable = false; + + services.httpd = { + enable = true; + adminAddr = "nixos@example.org"; + }; + + systemd.services.test = { + wantedBy = [ "multi-user.target" ]; + after = [ "httpd.service" ]; + script = '' + source /.env + echo "Hello World" > $out/msg + ls -lR /dev > $out/dev + ${pkgs.curl}/bin/curl -sS --fail http://localhost/ > $out/page.html + ''; + unitConfig = { + FailureAction = "exit-force"; + FailureActionExitStatus = 42; + SuccessAction = "exit-force"; + }; + }; + }; + + cfg = (import (nixpkgs + "/nixos/lib/eval-config.nix") { + modules = [ machine ]; + system = "x86_64-linux"; + }); + + config = cfg.config; + +in + +with cfg._module.args.pkgs; + +runCommand "test" + { buildInputs = [ config.system.path ]; + requiredSystemFeatures = [ "uid-range" ]; + toplevel = config.system.build.toplevel; + } + '' + root=$(pwd)/root + mkdir -p $root $root/etc + + export > $root/.env + + # Make /run a tmpfs to shut up a systemd warning. + mkdir /run + mount -t tmpfs none /run + chmod 0700 /run + + mount -t cgroup2 none /sys/fs/cgroup + + mkdir -p $out + + touch /etc/os-release + echo a5ea3f98dedc0278b6f3cc8c37eeaeac > /etc/machine-id + + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1 \ + ${config.systemd.package}/bin/systemd-nspawn \ + --keep-unit \ + -M ${config.networking.hostName} -D "$root" \ + --register=no \ + --resolv-conf=off \ + --bind-ro=/nix/store \ + --bind=$out \ + --private-network \ + $toplevel/init + '' diff --git a/tests/nixos/github-flakes.nix b/tests/nixos/github-flakes.nix new file mode 100644 index 000000000..a8b036b17 --- /dev/null +++ b/tests/nixos/github-flakes.nix @@ -0,0 +1,210 @@ +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system; + extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; +}; + +let + + # Generate a fake root CA and a fake api.github.com / github.com / channels.nixos.org certificate. + cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; } + '' + mkdir -p $out + + openssl genrsa -out ca.key 2048 + openssl req -new -x509 -days 36500 -key ca.key \ + -subj "/C=NL/ST=Denial/L=Springfield/O=Dis/CN=Root CA" -out $out/ca.crt + + openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \ + -subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=github.com" -out server.csr + openssl x509 -req -extfile <(printf "subjectAltName=DNS:api.github.com,DNS:github.com,DNS:channels.nixos.org") \ + -days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt + ''; + + registry = pkgs.writeTextFile { + name = "registry"; + text = '' + { + "flakes": [ + { + "from": { + "type": "indirect", + "id": "nixpkgs" + }, + "to": { + "type": "github", + "owner": "NixOS", + "repo": "nixpkgs" + } + }, + { + "from": { + "type": "indirect", + "id": "private-flake" + }, + "to": { + "type": "github", + "owner": "fancy-enterprise", + "repo": "private-flake" + } + } + ], + "version": 2 + } + ''; + destination = "/flake-registry.json"; + }; + + private-flake-rev = "9f1dd0df5b54a7dc75b618034482ed42ce34383d"; + + private-flake-api = pkgs.runCommand "private-flake" {} + '' + mkdir -p $out/{commits,tarball} + + # Setup https://docs.github.com/en/rest/commits/commits#get-a-commit + echo '{"sha": "${private-flake-rev}"}' > $out/commits/HEAD + + # Setup tarball download via API + dir=private-flake + mkdir $dir + echo '{ outputs = {...}: {}; }' > $dir/flake.nix + tar cfz $out/tarball/${private-flake-rev} $dir --hard-dereference + ''; + + nixpkgs-api = pkgs.runCommand "nixpkgs-flake" {} + '' + mkdir -p $out/commits + + # Setup https://docs.github.com/en/rest/commits/commits#get-a-commit + echo '{"sha": "${nixpkgs.rev}"}' > $out/commits/HEAD + ''; + + archive = pkgs.runCommand "nixpkgs-flake" {} + '' + mkdir -p $out/archive + + dir=NixOS-nixpkgs-${nixpkgs.shortRev} + cp -prd ${nixpkgs} $dir + # Set the correct timestamp in the tarball. + find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} -- + tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference + ''; +in + +makeTest ( + +{ + name = "github-flakes"; + + nodes = + { + github = + { config, pkgs, ... }: + { networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.extraConfig = '' + ErrorLog syslog:local6 + ''; + services.httpd.virtualHosts."channels.nixos.org" = + { forceSSL = true; + sslServerKey = "${cert}/server.key"; + sslServerCert = "${cert}/server.crt"; + servedDirs = + [ { urlPath = "/"; + dir = registry; + } + ]; + }; + services.httpd.virtualHosts."api.github.com" = + { forceSSL = true; + sslServerKey = "${cert}/server.key"; + sslServerCert = "${cert}/server.crt"; + servedDirs = + [ { urlPath = "/repos/NixOS/nixpkgs"; + dir = nixpkgs-api; + } + { urlPath = "/repos/fancy-enterprise/private-flake"; + dir = private-flake-api; + } + ]; + }; + services.httpd.virtualHosts."github.com" = + { forceSSL = true; + sslServerKey = "${cert}/server.key"; + sslServerCert = "${cert}/server.crt"; + servedDirs = + [ { urlPath = "/NixOS/nixpkgs"; + dir = archive; + } + ]; + }; + }; + + client = + { config, lib, pkgs, nodes, ... }: + { virtualisation.writableStore = true; + virtualisation.diskSize = 2048; + virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ]; + virtualisation.memorySize = 4096; + nix.settings.substituters = lib.mkForce [ ]; + nix.extraOptions = "experimental-features = nix-command flakes"; + networking.hosts.${(builtins.head nodes.github.config.networking.interfaces.eth1.ipv4.addresses).address} = + [ "channels.nixos.org" "api.github.com" "github.com" ]; + security.pki.certificateFiles = [ "${cert}/ca.crt" ]; + }; + }; + + testScript = { nodes }: '' + # fmt: off + import json + import time + + start_all() + + def cat_log(): + github.succeed("cat /var/log/httpd/*.log >&2") + + github.wait_for_unit("httpd.service") + + client.succeed("curl -v https://github.com/ >&2") + out = client.succeed("nix registry list") + print(out) + assert "github:NixOS/nixpkgs" in out, "nixpkgs flake not found" + assert "github:fancy-enterprise/private-flake" in out, "private flake not found" + cat_log() + + # If no github access token is provided, nix should use the public archive url... + out = client.succeed("nix flake metadata nixpkgs --json") + print(out) + info = json.loads(out) + assert info["revision"] == "${nixpkgs.rev}", f"revision mismatch: {info['revision']} != ${nixpkgs.rev}" + cat_log() + + # ... otherwise it should use the API + out = client.succeed("nix flake metadata private-flake --json --access-tokens github.com=ghp_000000000000000000000000000000000000 --tarball-ttl 0") + print(out) + info = json.loads(out) + assert info["revision"] == "${private-flake-rev}", f"revision mismatch: {info['revision']} != ${private-flake-rev}" + cat_log() + + client.succeed("nix registry pin nixpkgs") + client.succeed("nix flake metadata nixpkgs --tarball-ttl 0 >&2") + + # Shut down the web server. The flake should be cached on the client. + github.succeed("systemctl stop httpd.service") + + info = json.loads(client.succeed("nix flake metadata nixpkgs --json")) + date = time.strftime("%Y%m%d%H%M%S", time.gmtime(info['lastModified'])) + assert date == "${nixpkgs.lastModifiedDate}", "time mismatch" + + client.succeed("nix build nixpkgs#hello") + + # The build shouldn't fail even with --tarball-ttl 0 (the server + # being down should not be a fatal error). + client.succeed("nix build nixpkgs#fuse --tarball-ttl 0") + ''; + +}) diff --git a/tests/nixos/nix-copy-closure.nix b/tests/nixos/nix-copy-closure.nix new file mode 100644 index 000000000..2dc164ae4 --- /dev/null +++ b/tests/nixos/nix-copy-closure.nix @@ -0,0 +1,77 @@ +# Test ‘nix-copy-closure’. + +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system; + extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; +}; + +makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; pkgD = pkgs.tmux; in { + name = "nix-copy-closure"; + + nodes = + { client = + { config, lib, pkgs, ... }: + { virtualisation.writableStore = true; + virtualisation.additionalPaths = [ pkgA pkgD.drvPath ]; + nix.settings.substituters = lib.mkForce [ ]; + }; + + server = + { config, pkgs, ... }: + { services.openssh.enable = true; + virtualisation.writableStore = true; + virtualisation.additionalPaths = [ pkgB pkgC ]; + }; + }; + + testScript = { nodes }: '' + # fmt: off + import subprocess + + start_all() + + # Create an SSH key on the client. + subprocess.run([ + "${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", "" + ], capture_output=True, check=True) + + client.succeed("mkdir -m 700 /root/.ssh") + client.copy_from_host("key", "/root/.ssh/id_ed25519") + client.succeed("chmod 600 /root/.ssh/id_ed25519") + + # Install the SSH key on the server. + server.succeed("mkdir -m 700 /root/.ssh") + server.copy_from_host("key.pub", "/root/.ssh/authorized_keys") + server.wait_for_unit("sshd") + client.wait_for_unit("network.target") + client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'") + + # Copy the closure of package A from the client to the server. + server.fail("nix-store --check-validity ${pkgA}") + client.succeed("nix-copy-closure --to server --gzip ${pkgA} >&2") + server.succeed("nix-store --check-validity ${pkgA}") + + # Copy the closure of package B from the server to the client. + client.fail("nix-store --check-validity ${pkgB}") + client.succeed("nix-copy-closure --from server --gzip ${pkgB} >&2") + client.succeed("nix-store --check-validity ${pkgB}") + + # Copy the closure of package C via the SSH substituter. + client.fail("nix-store -r ${pkgC}") + + # Copy the derivation of package D's derivation from the client to the server. + server.fail("nix-store --check-validity ${pkgD.drvPath}") + client.succeed("nix-copy-closure --to server --gzip ${pkgD.drvPath} >&2") + server.succeed("nix-store --check-validity ${pkgD.drvPath}") + + # FIXME + # client.succeed( + # "nix-store --option use-ssh-substituter true" + # " --option ssh-substituter-hosts root\@server" + # " -r ${pkgC} >&2" + # ) + # client.succeed("nix-store --check-validity ${pkgC}") + ''; +}) diff --git a/tests/nixos/nss-preload.nix b/tests/nixos/nss-preload.nix new file mode 100644 index 000000000..5a6ff3f68 --- /dev/null +++ b/tests/nixos/nss-preload.nix @@ -0,0 +1,125 @@ +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system; + extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; +}; + +let + nix-fetch = pkgs.writeText "fetch.nix" '' + derivation { + # This derivation is an copy from what is available over at + # nix.git:corepkgs/fetchurl.nix + builder = "builtin:fetchurl"; + + # We're going to fetch data from the http_dns instance created before + # we expect the content to be the same as the content available there. + # ``` + # $ nix-hash --type sha256 --to-base32 $(echo "hello world" | sha256sum | cut -d " " -f 1) + # 0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59 + # ``` + outputHash = "0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59"; + outputHashAlgo = "sha256"; + outputHashMode = "flat"; + + name = "example.com"; + url = "http://example.com"; + + unpack = false; + executable = false; + + system = "builtin"; + + preferLocalBuild = true; + + impureEnvVars = [ + "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" + ]; + + urls = [ "http://example.com" ]; + } + ''; +in + +makeTest ( + +rec { + name = "nss-preload"; + + nodes = { + http_dns = { lib, pkgs, config, ... }: { + networking.firewall.enable = false; + networking.interfaces.eth1.ipv6.addresses = lib.mkForce [ + { address = "fd21::1"; prefixLength = 64; } + ]; + networking.interfaces.eth1.ipv4.addresses = lib.mkForce [ + { address = "192.168.0.1"; prefixLength = 24; } + ]; + + services.unbound = { + enable = true; + enableRootTrustAnchor = false; + settings = { + server = { + interface = [ "192.168.0.1" "fd21::1" "::1" "127.0.0.1" ]; + access-control = [ "192.168.0.0/24 allow" "fd21::/64 allow" "::1 allow" "127.0.0.0/8 allow" ]; + local-data = [ + ''"example.com. IN A 192.168.0.1"'' + ''"example.com. IN AAAA fd21::1"'' + ''"tarballs.nixos.org. IN A 192.168.0.1"'' + ''"tarballs.nixos.org. IN AAAA fd21::1"'' + ]; + }; + }; + }; + + services.nginx = { + enable = true; + virtualHosts."example.com" = { + root = pkgs.runCommand "testdir" {} '' + mkdir "$out" + echo hello world > "$out/index.html" + ''; + }; + }; + }; + + # client consumes a remote resolver + client = { lib, nodes, pkgs, ... }: { + networking.useDHCP = false; + networking.nameservers = [ + (lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv6.addresses).address + (lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv4.addresses).address + ]; + networking.interfaces.eth1.ipv6.addresses = [ + { address = "fd21::10"; prefixLength = 64; } + ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.10"; prefixLength = 24; } + ]; + + nix.settings.extra-sandbox-paths = lib.mkForce []; + nix.settings.substituters = lib.mkForce []; + nix.settings.sandbox = lib.mkForce true; + }; + }; + + testScript = { nodes, ... }: '' + http_dns.wait_for_unit("nginx") + http_dns.wait_for_open_port(80) + http_dns.wait_for_unit("unbound") + http_dns.wait_for_open_port(53) + + client.start() + client.wait_for_unit('multi-user.target') + + with subtest("can fetch data from a remote server outside sandbox"): + client.succeed("nix --version >&2") + client.succeed("curl -vvv http://example.com/index.html >&2") + + with subtest("nix-build can lookup dns and fetch data"): + client.succeed(""" + nix-build ${nix-fetch} >&2 + """) + ''; +}) diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix new file mode 100644 index 000000000..9f88217fe --- /dev/null +++ b/tests/nixos/remote-builds.nix @@ -0,0 +1,112 @@ +# Test Nix's remote build feature. + +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system; + extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; +}; + +makeTest ( + +let + + # The configuration of the remote builders. + builder = + { config, pkgs, ... }: + { services.openssh.enable = true; + virtualisation.writableStore = true; + nix.settings.sandbox = true; + }; + + # Trivial Nix expression to build remotely. + expr = config: nr: pkgs.writeText "expr.nix" + '' + let utils = builtins.storePath ${config.system.build.extraUtils}; in + derivation { + name = "hello-${toString nr}"; + system = "i686-linux"; + PATH = "''${utils}/bin"; + builder = "''${utils}/bin/sh"; + args = [ "-c" "if [ ${toString nr} = 5 ]; then echo FAIL; exit 1; fi; echo Hello; mkdir $out $foo; cat /proc/sys/kernel/hostname > $out/host; ln -s $out $foo/bar; sleep 10" ]; + outputs = [ "out" "foo" ]; + } + ''; + +in + +{ + name = "remote-builds"; + + nodes = + { builder1 = builder; + builder2 = builder; + + client = + { config, lib, pkgs, ... }: + { nix.settings.max-jobs = 0; # force remote building + nix.distributedBuilds = true; + nix.buildMachines = + [ { hostName = "builder1"; + sshUser = "root"; + sshKey = "/root/.ssh/id_ed25519"; + system = "i686-linux"; + maxJobs = 1; + } + { hostName = "builder2"; + sshUser = "root"; + sshKey = "/root/.ssh/id_ed25519"; + system = "i686-linux"; + maxJobs = 1; + } + ]; + virtualisation.writableStore = true; + virtualisation.additionalPaths = [ config.system.build.extraUtils ]; + nix.settings.substituters = lib.mkForce [ ]; + programs.ssh.extraConfig = "ConnectTimeout 30"; + }; + }; + + testScript = { nodes }: '' + # fmt: off + import subprocess + + start_all() + + # Create an SSH key on the client. + subprocess.run([ + "${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", "" + ], capture_output=True, check=True) + client.succeed("mkdir -p -m 700 /root/.ssh") + client.copy_from_host("key", "/root/.ssh/id_ed25519") + client.succeed("chmod 600 /root/.ssh/id_ed25519") + + # Install the SSH key on the builders. + client.wait_for_unit("network.target") + for builder in [builder1, builder2]: + builder.succeed("mkdir -p -m 700 /root/.ssh") + builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys") + builder.wait_for_unit("sshd") + client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'") + + # Perform a build and check that it was performed on the builder. + out = client.succeed( + "nix-build ${expr nodes.client.config 1} 2> build-output", + "grep -q Hello build-output" + ) + builder1.succeed(f"test -e {out}") + + # And a parallel build. + paths = client.succeed(r'nix-store -r $(nix-instantiate ${expr nodes.client.config 2})\!out $(nix-instantiate ${expr nodes.client.config 3})\!out') + out1, out2 = paths.split() + builder1.succeed(f"test -e {out1} -o -e {out2}") + builder2.succeed(f"test -e {out1} -o -e {out2}") + + # And a failing build. + client.fail("nix-build ${expr nodes.client.config 5}") + + # Test whether the build hook automatically skips unavailable builders. + builder1.block() + client.succeed("nix-build ${expr nodes.client.config 4}") + ''; +}) diff --git a/tests/nixos/setuid.nix b/tests/nixos/setuid.nix new file mode 100644 index 000000000..6784615e4 --- /dev/null +++ b/tests/nixos/setuid.nix @@ -0,0 +1,126 @@ +# Verify that Linux builds cannot create setuid or setgid binaries. + +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system; + extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; +}; + +makeTest { + name = "setuid"; + + nodes.machine = + { config, lib, pkgs, ... }: + { virtualisation.writableStore = true; + nix.settings.substituters = lib.mkForce [ ]; + nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ]; + virtualisation.additionalPaths = [ pkgs.stdenvNoCC pkgs.pkgsi686Linux.stdenvNoCC ]; + }; + + testScript = { nodes }: '' + # fmt: off + start_all() + + # Copying to /tmp should succeed. + machine.succeed(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" {} " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') + + machine.succeed("rm /tmp/id") + + # Creating a setuid binary should fail. + machine.fail(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" {} " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + chmod 4755 /tmp/id + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') + + machine.succeed("rm /tmp/id") + + # Creating a setgid binary should fail. + machine.fail(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" {} " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + chmod 2755 /tmp/id + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') + + machine.succeed("rm /tmp/id") + + # The checks should also work on 32-bit binaries. + machine.fail(r""" + nix-build --no-sandbox -E '(with import { system = "i686-linux"; }; runCommand "foo" {} " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + chmod 2755 /tmp/id + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') + + machine.succeed("rm /tmp/id") + + # The tests above use fchmodat(). Test chmod() as well. + machine.succeed(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + perl -e \"chmod 0666, qw(/tmp/id) or die\" + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 666 ]]') + + machine.succeed("rm /tmp/id") + + machine.fail(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + perl -e \"chmod 04755, qw(/tmp/id) or die\" + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') + + machine.succeed("rm /tmp/id") + + # And test fchmod(). + machine.succeed(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 01750, \\\$x or die\" + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 1750 ]]') + + machine.succeed("rm /tmp/id") + + machine.fail(r""" + nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " + mkdir -p $out + cp ${pkgs.coreutils}/bin/id /tmp/id + perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 04777, \\\$x or die\" + ")' + """.strip()) + + machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') + + machine.succeed("rm /tmp/id") + ''; +} diff --git a/tests/nixos/sourcehut-flakes.nix b/tests/nixos/sourcehut-flakes.nix new file mode 100644 index 000000000..b77496ab6 --- /dev/null +++ b/tests/nixos/sourcehut-flakes.nix @@ -0,0 +1,167 @@ +{ nixpkgs, system, overlay }: + +with import (nixpkgs + "/nixos/lib/testing-python.nix") +{ + inherit system; + extraConfigurations = [{ nixpkgs.overlays = [ overlay ]; }]; +}; + +let + # Generate a fake root CA and a fake git.sr.ht certificate. + cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; } + '' + mkdir -p $out + + openssl genrsa -out ca.key 2048 + openssl req -new -x509 -days 36500 -key ca.key \ + -subj "/C=NL/ST=Denial/L=Springfield/O=Dis/CN=Root CA" -out $out/ca.crt + + openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \ + -subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=git.sr.ht" -out server.csr + openssl x509 -req -extfile <(printf "subjectAltName=DNS:git.sr.ht") \ + -days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt + ''; + + registry = pkgs.writeTextFile { + name = "registry"; + text = '' + { + "flakes": [ + { + "from": { + "type": "indirect", + "id": "nixpkgs" + }, + "to": { + "type": "sourcehut", + "owner": "~NixOS", + "repo": "nixpkgs" + } + } + ], + "version": 2 + } + ''; + destination = "/flake-registry.json"; + }; + + nixpkgs-repo = pkgs.runCommand "nixpkgs-flake" { } + '' + dir=NixOS-nixpkgs-${nixpkgs.shortRev} + cp -prd ${nixpkgs} $dir + + # Set the correct timestamp in the tarball. + find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} -- + + mkdir -p $out/archive + tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference + + echo 'ref: refs/heads/master' > $out/HEAD + + mkdir -p $out/info + echo -e '${nixpkgs.rev}\trefs/heads/master\n${nixpkgs.rev}\trefs/tags/foo-bar' > $out/info/refs + ''; + +in + +makeTest ( + + { + name = "sourcehut-flakes"; + + nodes = + { + # Impersonate git.sr.ht + sourcehut = + { config, pkgs, ... }: + { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.extraConfig = '' + ErrorLog syslog:local6 + ''; + services.httpd.virtualHosts."git.sr.ht" = + { + forceSSL = true; + sslServerKey = "${cert}/server.key"; + sslServerCert = "${cert}/server.crt"; + servedDirs = + [ + { + urlPath = "/~NixOS/nixpkgs"; + dir = nixpkgs-repo; + } + { + urlPath = "/~NixOS/flake-registry/blob/master"; + dir = registry; + } + ]; + }; + }; + + client = + { config, lib, pkgs, nodes, ... }: + { + virtualisation.writableStore = true; + virtualisation.diskSize = 2048; + virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ]; + virtualisation.memorySize = 4096; + nix.settings.substituters = lib.mkForce [ ]; + nix.extraOptions = '' + experimental-features = nix-command flakes + flake-registry = https://git.sr.ht/~NixOS/flake-registry/blob/master/flake-registry.json + ''; + environment.systemPackages = [ pkgs.jq ]; + networking.hosts.${(builtins.head nodes.sourcehut.config.networking.interfaces.eth1.ipv4.addresses).address} = + [ "git.sr.ht" ]; + security.pki.certificateFiles = [ "${cert}/ca.crt" ]; + }; + }; + + testScript = { nodes }: '' + # fmt: off + import json + import time + + start_all() + + sourcehut.wait_for_unit("httpd.service") + + client.succeed("curl -v https://git.sr.ht/ >&2") + client.succeed("nix registry list | grep nixpkgs") + + # Test that it resolves HEAD + rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs --json | jq -r .revision") + assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" + # Test that it resolves branches + rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/master --json | jq -r .revision") + assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" + # Test that it resolves tags + rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/foo-bar --json | jq -r .revision") + assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" + + # Registry and pinning test + rev = client.succeed("nix flake info nixpkgs --json | jq -r .revision") + assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" + + client.succeed("nix registry pin nixpkgs") + + client.succeed("nix flake info nixpkgs --tarball-ttl 0 >&2") + + # Shut down the web server. The flake should be cached on the client. + sourcehut.succeed("systemctl stop httpd.service") + + info = json.loads(client.succeed("nix flake info nixpkgs --json")) + date = time.strftime("%Y%m%d%H%M%S", time.gmtime(info['lastModified'])) + assert date == "${nixpkgs.lastModifiedDate}", "time mismatch" + + client.succeed("nix build nixpkgs#hello") + + # The build shouldn't fail even with --tarball-ttl 0 (the server + # being down should not be a fatal error). + client.succeed("nix build nixpkgs#fuse --tarball-ttl 0") + ''; + + }) diff --git a/tests/nss-preload.nix b/tests/nss-preload.nix deleted file mode 100644 index 5a6ff3f68..000000000 --- a/tests/nss-preload.nix +++ /dev/null @@ -1,125 +0,0 @@ -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") { - inherit system; - extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; -}; - -let - nix-fetch = pkgs.writeText "fetch.nix" '' - derivation { - # This derivation is an copy from what is available over at - # nix.git:corepkgs/fetchurl.nix - builder = "builtin:fetchurl"; - - # We're going to fetch data from the http_dns instance created before - # we expect the content to be the same as the content available there. - # ``` - # $ nix-hash --type sha256 --to-base32 $(echo "hello world" | sha256sum | cut -d " " -f 1) - # 0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59 - # ``` - outputHash = "0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59"; - outputHashAlgo = "sha256"; - outputHashMode = "flat"; - - name = "example.com"; - url = "http://example.com"; - - unpack = false; - executable = false; - - system = "builtin"; - - preferLocalBuild = true; - - impureEnvVars = [ - "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" - ]; - - urls = [ "http://example.com" ]; - } - ''; -in - -makeTest ( - -rec { - name = "nss-preload"; - - nodes = { - http_dns = { lib, pkgs, config, ... }: { - networking.firewall.enable = false; - networking.interfaces.eth1.ipv6.addresses = lib.mkForce [ - { address = "fd21::1"; prefixLength = 64; } - ]; - networking.interfaces.eth1.ipv4.addresses = lib.mkForce [ - { address = "192.168.0.1"; prefixLength = 24; } - ]; - - services.unbound = { - enable = true; - enableRootTrustAnchor = false; - settings = { - server = { - interface = [ "192.168.0.1" "fd21::1" "::1" "127.0.0.1" ]; - access-control = [ "192.168.0.0/24 allow" "fd21::/64 allow" "::1 allow" "127.0.0.0/8 allow" ]; - local-data = [ - ''"example.com. IN A 192.168.0.1"'' - ''"example.com. IN AAAA fd21::1"'' - ''"tarballs.nixos.org. IN A 192.168.0.1"'' - ''"tarballs.nixos.org. IN AAAA fd21::1"'' - ]; - }; - }; - }; - - services.nginx = { - enable = true; - virtualHosts."example.com" = { - root = pkgs.runCommand "testdir" {} '' - mkdir "$out" - echo hello world > "$out/index.html" - ''; - }; - }; - }; - - # client consumes a remote resolver - client = { lib, nodes, pkgs, ... }: { - networking.useDHCP = false; - networking.nameservers = [ - (lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv6.addresses).address - (lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv4.addresses).address - ]; - networking.interfaces.eth1.ipv6.addresses = [ - { address = "fd21::10"; prefixLength = 64; } - ]; - networking.interfaces.eth1.ipv4.addresses = [ - { address = "192.168.0.10"; prefixLength = 24; } - ]; - - nix.settings.extra-sandbox-paths = lib.mkForce []; - nix.settings.substituters = lib.mkForce []; - nix.settings.sandbox = lib.mkForce true; - }; - }; - - testScript = { nodes, ... }: '' - http_dns.wait_for_unit("nginx") - http_dns.wait_for_open_port(80) - http_dns.wait_for_unit("unbound") - http_dns.wait_for_open_port(53) - - client.start() - client.wait_for_unit('multi-user.target') - - with subtest("can fetch data from a remote server outside sandbox"): - client.succeed("nix --version >&2") - client.succeed("curl -vvv http://example.com/index.html >&2") - - with subtest("nix-build can lookup dns and fetch data"): - client.succeed(""" - nix-build ${nix-fetch} >&2 - """) - ''; -}) diff --git a/tests/remote-builds.nix b/tests/remote-builds.nix deleted file mode 100644 index 9f88217fe..000000000 --- a/tests/remote-builds.nix +++ /dev/null @@ -1,112 +0,0 @@ -# Test Nix's remote build feature. - -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") { - inherit system; - extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; -}; - -makeTest ( - -let - - # The configuration of the remote builders. - builder = - { config, pkgs, ... }: - { services.openssh.enable = true; - virtualisation.writableStore = true; - nix.settings.sandbox = true; - }; - - # Trivial Nix expression to build remotely. - expr = config: nr: pkgs.writeText "expr.nix" - '' - let utils = builtins.storePath ${config.system.build.extraUtils}; in - derivation { - name = "hello-${toString nr}"; - system = "i686-linux"; - PATH = "''${utils}/bin"; - builder = "''${utils}/bin/sh"; - args = [ "-c" "if [ ${toString nr} = 5 ]; then echo FAIL; exit 1; fi; echo Hello; mkdir $out $foo; cat /proc/sys/kernel/hostname > $out/host; ln -s $out $foo/bar; sleep 10" ]; - outputs = [ "out" "foo" ]; - } - ''; - -in - -{ - name = "remote-builds"; - - nodes = - { builder1 = builder; - builder2 = builder; - - client = - { config, lib, pkgs, ... }: - { nix.settings.max-jobs = 0; # force remote building - nix.distributedBuilds = true; - nix.buildMachines = - [ { hostName = "builder1"; - sshUser = "root"; - sshKey = "/root/.ssh/id_ed25519"; - system = "i686-linux"; - maxJobs = 1; - } - { hostName = "builder2"; - sshUser = "root"; - sshKey = "/root/.ssh/id_ed25519"; - system = "i686-linux"; - maxJobs = 1; - } - ]; - virtualisation.writableStore = true; - virtualisation.additionalPaths = [ config.system.build.extraUtils ]; - nix.settings.substituters = lib.mkForce [ ]; - programs.ssh.extraConfig = "ConnectTimeout 30"; - }; - }; - - testScript = { nodes }: '' - # fmt: off - import subprocess - - start_all() - - # Create an SSH key on the client. - subprocess.run([ - "${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", "" - ], capture_output=True, check=True) - client.succeed("mkdir -p -m 700 /root/.ssh") - client.copy_from_host("key", "/root/.ssh/id_ed25519") - client.succeed("chmod 600 /root/.ssh/id_ed25519") - - # Install the SSH key on the builders. - client.wait_for_unit("network.target") - for builder in [builder1, builder2]: - builder.succeed("mkdir -p -m 700 /root/.ssh") - builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys") - builder.wait_for_unit("sshd") - client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'") - - # Perform a build and check that it was performed on the builder. - out = client.succeed( - "nix-build ${expr nodes.client.config 1} 2> build-output", - "grep -q Hello build-output" - ) - builder1.succeed(f"test -e {out}") - - # And a parallel build. - paths = client.succeed(r'nix-store -r $(nix-instantiate ${expr nodes.client.config 2})\!out $(nix-instantiate ${expr nodes.client.config 3})\!out') - out1, out2 = paths.split() - builder1.succeed(f"test -e {out1} -o -e {out2}") - builder2.succeed(f"test -e {out1} -o -e {out2}") - - # And a failing build. - client.fail("nix-build ${expr nodes.client.config 5}") - - # Test whether the build hook automatically skips unavailable builders. - builder1.block() - client.succeed("nix-build ${expr nodes.client.config 4}") - ''; -}) diff --git a/tests/setuid.nix b/tests/setuid.nix deleted file mode 100644 index 6784615e4..000000000 --- a/tests/setuid.nix +++ /dev/null @@ -1,126 +0,0 @@ -# Verify that Linux builds cannot create setuid or setgid binaries. - -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") { - inherit system; - extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ]; -}; - -makeTest { - name = "setuid"; - - nodes.machine = - { config, lib, pkgs, ... }: - { virtualisation.writableStore = true; - nix.settings.substituters = lib.mkForce [ ]; - nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ]; - virtualisation.additionalPaths = [ pkgs.stdenvNoCC pkgs.pkgsi686Linux.stdenvNoCC ]; - }; - - testScript = { nodes }: '' - # fmt: off - start_all() - - # Copying to /tmp should succeed. - machine.succeed(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" {} " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') - - machine.succeed("rm /tmp/id") - - # Creating a setuid binary should fail. - machine.fail(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" {} " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - chmod 4755 /tmp/id - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') - - machine.succeed("rm /tmp/id") - - # Creating a setgid binary should fail. - machine.fail(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" {} " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - chmod 2755 /tmp/id - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') - - machine.succeed("rm /tmp/id") - - # The checks should also work on 32-bit binaries. - machine.fail(r""" - nix-build --no-sandbox -E '(with import { system = "i686-linux"; }; runCommand "foo" {} " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - chmod 2755 /tmp/id - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') - - machine.succeed("rm /tmp/id") - - # The tests above use fchmodat(). Test chmod() as well. - machine.succeed(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - perl -e \"chmod 0666, qw(/tmp/id) or die\" - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 666 ]]') - - machine.succeed("rm /tmp/id") - - machine.fail(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - perl -e \"chmod 04755, qw(/tmp/id) or die\" - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') - - machine.succeed("rm /tmp/id") - - # And test fchmod(). - machine.succeed(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 01750, \\\$x or die\" - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 1750 ]]') - - machine.succeed("rm /tmp/id") - - machine.fail(r""" - nix-build --no-sandbox -E '(with import {}; runCommand "foo" { buildInputs = [ perl ]; } " - mkdir -p $out - cp ${pkgs.coreutils}/bin/id /tmp/id - perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 04777, \\\$x or die\" - ")' - """.strip()) - - machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]') - - machine.succeed("rm /tmp/id") - ''; -} diff --git a/tests/sourcehut-flakes.nix b/tests/sourcehut-flakes.nix deleted file mode 100644 index b77496ab6..000000000 --- a/tests/sourcehut-flakes.nix +++ /dev/null @@ -1,167 +0,0 @@ -{ nixpkgs, system, overlay }: - -with import (nixpkgs + "/nixos/lib/testing-python.nix") -{ - inherit system; - extraConfigurations = [{ nixpkgs.overlays = [ overlay ]; }]; -}; - -let - # Generate a fake root CA and a fake git.sr.ht certificate. - cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; } - '' - mkdir -p $out - - openssl genrsa -out ca.key 2048 - openssl req -new -x509 -days 36500 -key ca.key \ - -subj "/C=NL/ST=Denial/L=Springfield/O=Dis/CN=Root CA" -out $out/ca.crt - - openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \ - -subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=git.sr.ht" -out server.csr - openssl x509 -req -extfile <(printf "subjectAltName=DNS:git.sr.ht") \ - -days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt - ''; - - registry = pkgs.writeTextFile { - name = "registry"; - text = '' - { - "flakes": [ - { - "from": { - "type": "indirect", - "id": "nixpkgs" - }, - "to": { - "type": "sourcehut", - "owner": "~NixOS", - "repo": "nixpkgs" - } - } - ], - "version": 2 - } - ''; - destination = "/flake-registry.json"; - }; - - nixpkgs-repo = pkgs.runCommand "nixpkgs-flake" { } - '' - dir=NixOS-nixpkgs-${nixpkgs.shortRev} - cp -prd ${nixpkgs} $dir - - # Set the correct timestamp in the tarball. - find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} -- - - mkdir -p $out/archive - tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference - - echo 'ref: refs/heads/master' > $out/HEAD - - mkdir -p $out/info - echo -e '${nixpkgs.rev}\trefs/heads/master\n${nixpkgs.rev}\trefs/tags/foo-bar' > $out/info/refs - ''; - -in - -makeTest ( - - { - name = "sourcehut-flakes"; - - nodes = - { - # Impersonate git.sr.ht - sourcehut = - { config, pkgs, ... }: - { - networking.firewall.allowedTCPPorts = [ 80 443 ]; - - services.httpd.enable = true; - services.httpd.adminAddr = "foo@example.org"; - services.httpd.extraConfig = '' - ErrorLog syslog:local6 - ''; - services.httpd.virtualHosts."git.sr.ht" = - { - forceSSL = true; - sslServerKey = "${cert}/server.key"; - sslServerCert = "${cert}/server.crt"; - servedDirs = - [ - { - urlPath = "/~NixOS/nixpkgs"; - dir = nixpkgs-repo; - } - { - urlPath = "/~NixOS/flake-registry/blob/master"; - dir = registry; - } - ]; - }; - }; - - client = - { config, lib, pkgs, nodes, ... }: - { - virtualisation.writableStore = true; - virtualisation.diskSize = 2048; - virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ]; - virtualisation.memorySize = 4096; - nix.settings.substituters = lib.mkForce [ ]; - nix.extraOptions = '' - experimental-features = nix-command flakes - flake-registry = https://git.sr.ht/~NixOS/flake-registry/blob/master/flake-registry.json - ''; - environment.systemPackages = [ pkgs.jq ]; - networking.hosts.${(builtins.head nodes.sourcehut.config.networking.interfaces.eth1.ipv4.addresses).address} = - [ "git.sr.ht" ]; - security.pki.certificateFiles = [ "${cert}/ca.crt" ]; - }; - }; - - testScript = { nodes }: '' - # fmt: off - import json - import time - - start_all() - - sourcehut.wait_for_unit("httpd.service") - - client.succeed("curl -v https://git.sr.ht/ >&2") - client.succeed("nix registry list | grep nixpkgs") - - # Test that it resolves HEAD - rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs --json | jq -r .revision") - assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" - # Test that it resolves branches - rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/master --json | jq -r .revision") - assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" - # Test that it resolves tags - rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/foo-bar --json | jq -r .revision") - assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" - - # Registry and pinning test - rev = client.succeed("nix flake info nixpkgs --json | jq -r .revision") - assert rev.strip() == "${nixpkgs.rev}", "revision mismatch" - - client.succeed("nix registry pin nixpkgs") - - client.succeed("nix flake info nixpkgs --tarball-ttl 0 >&2") - - # Shut down the web server. The flake should be cached on the client. - sourcehut.succeed("systemctl stop httpd.service") - - info = json.loads(client.succeed("nix flake info nixpkgs --json")) - date = time.strftime("%Y%m%d%H%M%S", time.gmtime(info['lastModified'])) - assert date == "${nixpkgs.lastModifiedDate}", "time mismatch" - - client.succeed("nix build nixpkgs#hello") - - # The build shouldn't fail even with --tarball-ttl 0 (the server - # being down should not be a fatal error). - client.succeed("nix build nixpkgs#fuse --tarball-ttl 0") - ''; - - }) diff --git a/tests/systemd-nspawn.nix b/tests/systemd-nspawn.nix deleted file mode 100644 index 424436b3f..000000000 --- a/tests/systemd-nspawn.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ nixpkgs }: - -let - - machine = { config, pkgs, ... }: - { - system.stateVersion = "22.05"; - boot.isContainer = true; - systemd.services.console-getty.enable = false; - networking.dhcpcd.enable = false; - - services.httpd = { - enable = true; - adminAddr = "nixos@example.org"; - }; - - systemd.services.test = { - wantedBy = [ "multi-user.target" ]; - after = [ "httpd.service" ]; - script = '' - source /.env - echo "Hello World" > $out/msg - ls -lR /dev > $out/dev - ${pkgs.curl}/bin/curl -sS --fail http://localhost/ > $out/page.html - ''; - unitConfig = { - FailureAction = "exit-force"; - FailureActionExitStatus = 42; - SuccessAction = "exit-force"; - }; - }; - }; - - cfg = (import (nixpkgs + "/nixos/lib/eval-config.nix") { - modules = [ machine ]; - system = "x86_64-linux"; - }); - - config = cfg.config; - -in - -with cfg._module.args.pkgs; - -runCommand "test" - { buildInputs = [ config.system.path ]; - requiredSystemFeatures = [ "uid-range" ]; - toplevel = config.system.build.toplevel; - } - '' - root=$(pwd)/root - mkdir -p $root $root/etc - - export > $root/.env - - # Make /run a tmpfs to shut up a systemd warning. - mkdir /run - mount -t tmpfs none /run - chmod 0700 /run - - mount -t cgroup2 none /sys/fs/cgroup - - mkdir -p $out - - touch /etc/os-release - echo a5ea3f98dedc0278b6f3cc8c37eeaeac > /etc/machine-id - - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1 \ - ${config.systemd.package}/bin/systemd-nspawn \ - --keep-unit \ - -M ${config.networking.hostName} -D "$root" \ - --register=no \ - --resolv-conf=off \ - --bind-ro=/nix/store \ - --bind=$out \ - --private-network \ - $toplevel/init - '' -- cgit v1.2.3