diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml index 550dddfaf352..864b6e47db26 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml @@ -341,6 +341,15 @@ Add udev rules for the Teensy family of microcontrollers. + + + The pass-secret-service package now + includes systemd units from upstream, so adding it to the + NixOS services.dbus.packages option will + make it start automatically as a systemd user service when an + application tries to talk to the libsecret D-Bus API. + + There is a new module for the thunar diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md index b652419bcf16..d4059e739322 100644 --- a/nixos/doc/manual/release-notes/rl-2211.section.md +++ b/nixos/doc/manual/release-notes/rl-2211.section.md @@ -128,6 +128,8 @@ Use `configure.packages` instead. - Add udev rules for the Teensy family of microcontrollers. +- The `pass-secret-service` package now includes systemd units from upstream, so adding it to the NixOS `services.dbus.packages` option will make it start automatically as a systemd user service when an application tries to talk to the libsecret D-Bus API. + - There is a new module for the `thunar` program (the Xfce file manager), which depends on the `xfconf` dbus service, and also has a dbus service and a systemd unit. The option `services.xserver.desktopManager.xfce.thunarPlugins` has been renamed to `programs.thunar.plugins`, and in a future release it may be removed. - There is a new module for the `xfconf` program (the Xfce configuration storage system), which has a dbus service. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 268ebbf18dd7..5f454a61acef 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1002,6 +1002,7 @@ ./services/security/oauth2_proxy.nix ./services/security/oauth2_proxy_nginx.nix ./services/security/opensnitch.nix + ./services/security/pass-secret-service.nix ./services/security/privacyidea.nix ./services/security/physlock.nix ./services/security/shibboleth-sp.nix diff --git a/nixos/modules/services/security/pass-secret-service.nix b/nixos/modules/services/security/pass-secret-service.nix new file mode 100644 index 000000000000..6ae190eb95e6 --- /dev/null +++ b/nixos/modules/services/security/pass-secret-service.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.passSecretService; +in +{ + options.services.passSecretService = { + enable = mkEnableOption "pass secret service"; + + package = mkOption { + type = types.package; + default = pkgs.pass-secret-service; + defaultText = literalExpression "pkgs.pass-secret-service"; + description = "Which pass-secret-service package to use."; + example = literalExpression "pkgs.pass-secret-service.override { python3 = pkgs.python310 }"; + }; + }; + + config = mkIf cfg.enable { + systemd.packages = [ cfg.package ]; + services.dbus.packages = [ cfg.package ]; + }; + + meta.maintainers = with maintainers; [ aidalgol ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3253a3c750be..11189e77bd96 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -413,6 +413,7 @@ in { pam-oath-login = handleTest ./pam/pam-oath-login.nix {}; pam-u2f = handleTest ./pam/pam-u2f.nix {}; pam-ussh = handleTest ./pam/pam-ussh.nix {}; + pass-secret-service = handleTest ./pass-secret-service.nix {}; pantalaimon = handleTest ./matrix/pantalaimon.nix {}; pantheon = handleTest ./pantheon.nix {}; paperless = handleTest ./paperless.nix {}; diff --git a/nixos/tests/pass-secret-service.nix b/nixos/tests/pass-secret-service.nix new file mode 100644 index 000000000000..a85a508bfe16 --- /dev/null +++ b/nixos/tests/pass-secret-service.nix @@ -0,0 +1,69 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "pass-secret-service"; + meta.maintainers = with lib; [ aidalgol ]; + + nodes.machine = { nodes, pkgs, ... }: + { + imports = [ ./common/user-account.nix ]; + + services.passSecretService.enable = true; + + environment.systemPackages = [ + # Create a script that tries to make a request to the D-Bus secrets API. + (pkgs.writers.writePython3Bin "secrets-dbus-init" + { + libraries = [ pkgs.python3Packages.secretstorage ]; + } '' + import secretstorage + print("Initializing dbus connection...") + connection = secretstorage.dbus_init() + print("Requesting default collection...") + collection = secretstorage.get_default_collection(connection) + print("Done! dbus-org.freedesktop.secrets should now be active.") + '') + pkgs.pass + ]; + + programs.gnupg = { + agent.enable = true; + agent.pinentryFlavor = "tty"; + dirmngr.enable = true; + }; + }; + + # Some of the commands are run via a virtual console because they need to be + # run under a real login session, with D-Bus running in the environment. + testScript = { nodes, ... }: + let + user = nodes.machine.config.users.users.alice; + gpg-uid = "alice@example.net"; + gpg-pw = "foobar9000"; + ready-file = "/tmp/secrets-dbus-init.done"; + in + '' + # Initialise the pass(1) storage. + machine.succeed(""" + sudo -u alice gpg --pinentry-mode loopback --batch --passphrase ${gpg-pw} \ + --quick-gen-key ${gpg-uid} \ + """) + machine.succeed("sudo -u alice pass init ${gpg-uid}") + + with subtest("Service is not running on login"): + machine.wait_until_tty_matches("1", "login: ") + machine.send_chars("alice\n") + machine.wait_until_tty_matches("1", "login: alice") + machine.wait_until_succeeds("pgrep login") + machine.wait_until_tty_matches("1", "Password: ") + machine.send_chars("${user.password}\n") + machine.wait_until_succeeds("pgrep -u alice bash") + + _, output = machine.systemctl("status dbus-org.freedesktop.secrets --no-pager", "alice") + assert "Active: inactive (dead)" in output + + with subtest("Service starts after a client tries to talk to the D-Bus API"): + machine.send_chars("secrets-dbus-init; touch ${ready-file}\n") + machine.wait_for_file("${ready-file}") + _, output = machine.systemctl("status dbus-org.freedesktop.secrets --no-pager", "alice") + assert "Active: active (running)" in output + ''; +}) diff --git a/pkgs/applications/misc/pass-secret-service/default.nix b/pkgs/applications/misc/pass-secret-service/default.nix index 12f8935797c8..6a57c15b742b 100644 --- a/pkgs/applications/misc/pass-secret-service/default.nix +++ b/pkgs/applications/misc/pass-secret-service/default.nix @@ -1,26 +1,50 @@ -{ lib, fetchFromGitHub, python3, dbus, gnupg }: +{ lib +, fetchFromGitHub +, python3 +, dbus +, gnupg +, coreutils +, nixosTests +}: python3.pkgs.buildPythonApplication rec { pname = "pass-secret-service"; # PyPI has old alpha version. Since then the project has switched from using a # seemingly abandoned D-Bus package pydbus and started using maintained # dbus-next. So let's use latest from GitHub. - version = "unstable-2020-04-12"; + version = "unstable-2022-03-21"; src = fetchFromGitHub { owner = "mdellweg"; repo = "pass_secret_service"; - rev = "f6fbca6ac3ccd16bfec407d845ed9257adf74dfa"; - sha256 = "0rm4pbx1fiwds1v7f99khhh7x3inv9yniclwd95mrbgljk3cc6a4"; + rev = "149f8557e07098eee2f46561eea61e83255ac59b"; + sha256 = "sha256-+/pFi6+K8rl0Ihm6cp/emUQVtau6+Apl8/VEr9AI0Xs="; }; + patches = [ + # Only needed until https://github.com/mdellweg/pass_secret_service/pull/30 + # is merged. + ./int_from_bytes-deprecation-fix.patch + ]; # Need to specify session.conf file for tests because it won't be found under # /etc/ in check phase. postPatch = '' substituteInPlace Makefile \ --replace "dbus-run-session" "dbus-run-session --config-file=${dbus}/share/dbus-1/session.conf" \ - --replace '-p $(relpassstore)' '-p $(PASSWORD_STORE_DIR)' + --replace '-p $(relpassstore)' '-p $(PASSWORD_STORE_DIR)' \ + --replace 'pytest-3' 'pytest' + + substituteInPlace systemd/org.freedesktop.secrets.service \ + --replace "/bin/false" "${coreutils}/bin/false" + substituteInPlace systemd/dbus-org.freedesktop.secrets.service \ + --replace "/usr/local" "$out" + ''; + + postInstall = '' + mkdir -p "$out/share/dbus-1/services/" "$out/lib/systemd/user/" + cp systemd/org.freedesktop.secrets.service "$out/share/dbus-1/services/" + cp systemd/dbus-org.freedesktop.secrets.service "$out/lib/systemd/user/" ''; propagatedBuildInputs = with python3.pkgs; [ @@ -44,17 +68,15 @@ python3.pkgs.buildPythonApplication rec { ps.pypass ]; - checkPhase = '' - runHook preCheck - make test - runHook postCheck - ''; + checkTarget = "test"; + + passthru.tests.pass-secret-service = nixosTests.pass-secret-service; meta = { description = "Libsecret D-Bus API with pass as the backend"; homepage = "https://github.com/mdellweg/pass_secret_service/"; license = lib.licenses.gpl3Only; platforms = lib.platforms.all; - maintainers = with lib.maintainers; [ jluttine ]; + maintainers = with lib.maintainers; [ jluttine aidalgol ]; }; } diff --git a/pkgs/applications/misc/pass-secret-service/int_from_bytes-deprecation-fix.patch b/pkgs/applications/misc/pass-secret-service/int_from_bytes-deprecation-fix.patch new file mode 100644 index 000000000000..b7e78e7b7915 --- /dev/null +++ b/pkgs/applications/misc/pass-secret-service/int_from_bytes-deprecation-fix.patch @@ -0,0 +1,22 @@ +--- a/pass_secret_service/interfaces/session.py ++++ b/pass_secret_service/interfaces/session.py +@@ -4,7 +4,6 @@ + import os + import hmac + from hashlib import sha256 +-from cryptography.utils import int_from_bytes + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives.ciphers import Cipher + from cryptography.hazmat.primitives.ciphers.modes import CBC +@@ -27,9 +26,9 @@ class Session(ServiceInterface, SerialMixin): + @classmethod + @run_in_executor + def _create_dh(cls, input): +- priv_key = int_from_bytes(os.urandom(0x80), "big") ++ priv_key = int.from_bytes(os.urandom(0x80), "big") + pub_key = pow(2, priv_key, dh_prime) +- shared_secret = pow(int_from_bytes(input, "big"), priv_key, dh_prime) ++ shared_secret = pow(int.from_bytes(input, "big"), priv_key, dh_prime) + salt = b"\x00" * 0x20 + shared_key = hmac.new(salt, shared_secret.to_bytes(0x80, "big"), sha256).digest() + aes_key = hmac.new(shared_key, b"\x01", sha256).digest()[:0x10]