From dece72548ee0fee77eda54f3a8a958c86f460191 Mon Sep 17 00:00:00 2001 From: Guillaume Girol Date: Wed, 29 Nov 2023 12:00:00 +0000 Subject: [PATCH] nixseparatedebuginfod: add module and nixos test --- nixos/modules/module-list.nix | 1 + .../development/nixseparatedebuginfod.nix | 105 ++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/nixseparatedebuginfod.nix | 80 +++++++++++++ .../ni/nixseparatedebuginfod/package.nix | 7 ++ 5 files changed, 194 insertions(+) create mode 100644 nixos/modules/services/development/nixseparatedebuginfod.nix create mode 100644 nixos/tests/nixseparatedebuginfod.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index cafde7f9efdf..708085a2dd85 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -489,6 +489,7 @@ ./services/development/jupyterhub/default.nix ./services/development/livebook.nix ./services/development/lorri.nix + ./services/development/nixseparatedebuginfod.nix ./services/development/rstudio-server/default.nix ./services/development/zammad.nix ./services/display-managers/greetd.nix diff --git a/nixos/modules/services/development/nixseparatedebuginfod.nix b/nixos/modules/services/development/nixseparatedebuginfod.nix new file mode 100644 index 000000000000..daf85153d339 --- /dev/null +++ b/nixos/modules/services/development/nixseparatedebuginfod.nix @@ -0,0 +1,105 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.services.nixseparatedebuginfod; + url = "127.0.0.1:${toString cfg.port}"; +in +{ + options = { + services.nixseparatedebuginfod = { + enable = lib.mkEnableOption "separatedebuginfod, a debuginfod server providing source and debuginfo for nix packages"; + port = lib.mkOption { + description = "port to listen"; + default = 1949; + type = lib.types.port; + }; + nixPackage = lib.mkOption { + type = lib.types.package; + default = pkgs.nix; + defaultText = lib.literalExpression "pkgs.nix"; + description = '' + The version of nix that nixseparatedebuginfod should use as client for the nix daemon. It is strongly advised to use nix version >= 2.18, otherwise some debug info may go missing. + ''; + }; + allowOldNix = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Do not fail evaluation when {option}`services.nixseparatedebuginfod.nixPackage` is older than nix 2.18. + ''; + }; + }; + }; + config = lib.mkIf cfg.enable { + assertions = [ { + assertion = cfg.allowOldNix || (lib.versionAtLeast cfg.nixPackage.version "2.18"); + message = "nixseparatedebuginfod works better when `services.nixseparatedebuginfod.nixPackage` is set to nix >= 2.18 (instead of ${cfg.nixPackage.name}). Set `services.nixseparatedebuginfod.allowOldNix` to bypass."; + } ]; + + systemd.services.nixseparatedebuginfod = { + wantedBy = [ "multi-user.target" ]; + wants = [ "nix-daemon.service" ]; + after = [ "nix-daemon.service" ]; + path = [ cfg.nixPackage ]; + serviceConfig = { + ExecStart = [ "${pkgs.nixseparatedebuginfod}/bin/nixseparatedebuginfod -l ${url}" ]; + Restart = "on-failure"; + CacheDirectory = "nixseparatedebuginfod"; + # nix does not like DynamicUsers in allowed-users + User = "nixseparatedebuginfod"; + Group = "nixseparatedebuginfod"; + + # hardening + # Filesystem stuff + ProtectSystem = "strict"; # Prevent writing to most of / + ProtectHome = true; # Prevent accessing /home and /root + PrivateTmp = true; # Give an own directory under /tmp + PrivateDevices = true; # Deny access to most of /dev + ProtectKernelTunables = true; # Protect some parts of /sys + ProtectControlGroups = true; # Remount cgroups read-only + RestrictSUIDSGID = true; # Prevent creating SETUID/SETGID files + PrivateMounts = true; # Give an own mount namespace + RemoveIPC = true; + UMask = "0077"; + + # Capabilities + CapabilityBoundingSet = ""; # Allow no capabilities at all + NoNewPrivileges = true; # Disallow getting more capabilities. This is also implied by other options. + + # Kernel stuff + ProtectKernelModules = true; # Prevent loading of kernel modules + SystemCallArchitectures = "native"; # Usually no need to disable this + ProtectKernelLogs = true; # Prevent access to kernel logs + ProtectClock = true; # Prevent setting the RTC + + # Networking + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + + # Misc + LockPersonality = true; # Prevent change of the personality + ProtectHostname = true; # Give an own UTS namespace + RestrictRealtime = true; # Prevent switching to RT scheduling + MemoryDenyWriteExecute = true; # Maybe disable this for interpreters like python + RestrictNamespaces = true; + }; + }; + + users.users.nixseparatedebuginfod = { + isSystemUser = true; + group = "nixseparatedebuginfod"; + }; + + users.groups.nixseparatedebuginfod = { }; + + nix.settings.extra-allowed-users = [ "nixseparatedebuginfod" ]; + + environment.variables.DEBUGINFOD_URLS = "http://${url}"; + + environment.systemPackages = [ + # valgrind support requires debuginfod-find on PATH + (lib.getBin pkgs.elfutils) + ]; + + environment.etc."gdb/gdbinit.d/nixseparatedebuginfod.gdb".text = "set debuginfod enabled on"; + + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3b0871e36a77..5bb48abe2fc8 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -592,6 +592,7 @@ in { nixos-rebuild-install-bootloader = handleTestOn ["x86_64-linux"] ./nixos-rebuild-install-bootloader.nix {}; nixos-rebuild-specialisations = handleTestOn ["x86_64-linux"] ./nixos-rebuild-specialisations.nix {}; nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; }; + nixseparatedebuginfod = handleTest ./nixseparatedebuginfod.nix {}; node-red = handleTest ./node-red.nix {}; nomad = handleTest ./nomad.nix {}; non-default-filesystems = handleTest ./non-default-filesystems.nix {}; diff --git a/nixos/tests/nixseparatedebuginfod.nix b/nixos/tests/nixseparatedebuginfod.nix new file mode 100644 index 000000000000..7c192a73c706 --- /dev/null +++ b/nixos/tests/nixseparatedebuginfod.nix @@ -0,0 +1,80 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + secret-key = "key-name:/COlMSRbehSh6YSruJWjL+R0JXQUKuPEn96fIb+pLokEJUjcK/2Gv8Ai96D7JGay5gDeUTx5wdpPgNvum9YtwA=="; + public-key = "key-name:BCVI3Cv9hr/AIveg+yRmsuYA3lE8ecHaT4Db7pvWLcA="; +in +{ + name = "nixseparatedebuginfod"; + /* A binary cache with debug info and source for nix */ + nodes.cache = { pkgs, ... }: { + services.nix-serve = { + enable = true; + secretKeyFile = builtins.toFile "secret-key" secret-key; + openFirewall = true; + }; + system.extraDependencies = [ + pkgs.nix.debug + pkgs.nix.src + pkgs.sl + ]; + }; + /* the machine where we need the debuginfo */ + nodes.machine = { + imports = [ + ../modules/installer/cd-dvd/channel.nix + ]; + services.nixseparatedebuginfod.enable = true; + nix.settings = { + substituters = lib.mkForce [ "http://cache:5000" ]; + trusted-public-keys = [ public-key ]; + }; + environment.systemPackages = [ + pkgs.valgrind + pkgs.gdb + (pkgs.writeShellScriptBin "wait_for_indexation" '' + set -x + while debuginfod-find debuginfo /run/current-system/sw/bin/nix |& grep 'File too large'; do + sleep 1; + done + '') + ]; + }; + testScript = '' + start_all() + cache.wait_for_unit("nix-serve.service") + cache.wait_for_open_port(5000) + machine.wait_for_unit("nixseparatedebuginfod.service") + machine.wait_for_open_port(1949) + + with subtest("show the config to debug the test"): + machine.succeed("nix --extra-experimental-features nix-command show-config |& logger") + machine.succeed("cat /etc/nix/nix.conf |& logger") + with subtest("check that the binary cache works"): + machine.succeed("nix-store -r ${pkgs.sl}") + + # nixseparatedebuginfod needs .drv to associate executable -> source + # on regular systems this would be provided by nixos-rebuild + machine.succeed("nix-instantiate '' -A nix") + + machine.succeed("timeout 600 wait_for_indexation") + + # test debuginfod-find + machine.succeed("debuginfod-find debuginfo /run/current-system/sw/bin/nix") + + # test that gdb can fetch source + out = machine.succeed("gdb /run/current-system/sw/bin/nix --batch -x ${builtins.toFile "commands" '' + start + l + ''}") + print(out) + assert 'int main(' in out + + # test that valgrind can display location information + # this relies on the fact that valgrind complains about nix + # libgc helps in this regard, and we also ask valgrind to show leak kinds + # which are usually false positives. + out = machine.succeed("valgrind --leak-check=full --show-leak-kinds=all nix-env --version 2>&1") + print(out) + assert 'main.cc' in out + ''; +}) diff --git a/pkgs/by-name/ni/nixseparatedebuginfod/package.nix b/pkgs/by-name/ni/nixseparatedebuginfod/package.nix index 85e1175d4512..faacd2330d3f 100644 --- a/pkgs/by-name/ni/nixseparatedebuginfod/package.nix +++ b/pkgs/by-name/ni/nixseparatedebuginfod/package.nix @@ -5,6 +5,7 @@ , openssl , sqlite , pkg-config +, nixosTests }: rustPlatform.buildRustPackage rec { @@ -31,6 +32,12 @@ rustPlatform.buildRustPackage rec { nativeBuildInputs = [ pkg-config ]; + passthru = { + tests = { + inherit (nixosTests) nixseparatedebuginfod; + }; + }; + meta = with lib; { description = "Downloads and provides debug symbols and source code for nix derivations to gdb and other debuginfod-capable debuggers as needed"; homepage = "https://github.com/symphorien/nixseparatedebuginfod";