diff --git a/nixos/modules/services/security/paretosecurity.nix b/nixos/modules/services/security/paretosecurity.nix index cfcb89fb8f62..9ec196eee6a0 100644 --- a/nixos/modules/services/security/paretosecurity.nix +++ b/nixos/modules/services/security/paretosecurity.nix @@ -14,38 +14,27 @@ config = lib.mkIf config.services.paretosecurity.enable { environment.systemPackages = [ config.services.paretosecurity.package ]; + systemd.packages = [ config.services.paretosecurity.package ]; - systemd.sockets."paretosecurity" = { - wantedBy = [ "sockets.target" ]; - socketConfig = { - ListenStream = "/var/run/paretosecurity.sock"; - SocketMode = "0666"; + # In traditional Linux distributions, systemd would read the [Install] section from + # unit files and automatically create the appropriate symlinks to enable services. + # However, in NixOS, due to its immutable nature and the way the Nix store works, + # the [Install] sections are not processed during system activation. Instead, we + # must explicitly tell NixOS which units to enable by specifying their target + # dependencies here. This creates the necessary symlinks in the proper locations. + systemd.sockets.paretosecurity.wantedBy = [ "sockets.target" ]; + + # Enable the tray icon and timer services if the trayIcon option is enabled + systemd.user = lib.mkIf config.services.paretosecurity.trayIcon { + services.paretosecurity-trayicon = { + wantedBy = [ "graphical-session.target" ]; + }; + services.paretosecurity-user = { + wantedBy = [ "graphical-session.target" ]; + }; + timers.paretosecurity-user = { + wantedBy = [ "timers.target" ]; }; }; - - systemd.services."paretosecurity" = { - serviceConfig = { - ExecStart = "${config.services.paretosecurity.package}/bin/paretosecurity helper"; - User = "root"; - Group = "root"; - StandardInput = "socket"; - Type = "oneshot"; - RemainAfterExit = "no"; - StartLimitInterval = "1s"; - StartLimitBurst = 100; - ProtectSystem = "full"; - ProtectHome = true; - StandardOutput = "journal"; - StandardError = "journal"; - }; - }; - - systemd.user.services."paretosecurity-trayicon" = lib.mkIf config.services.paretosecurity.trayIcon { - wantedBy = [ "graphical-session.target" ]; - serviceConfig = { - ExecStart = "${config.services.paretosecurity.package}/bin/paretosecurity trayicon"; - }; - }; - }; } diff --git a/nixos/tests/paretosecurity.nix b/nixos/tests/paretosecurity.nix index 176b6768270b..a2b9317c678f 100644 --- a/nixos/tests/paretosecurity.nix +++ b/nixos/tests/paretosecurity.nix @@ -4,11 +4,47 @@ meta.maintainers = [ lib.maintainers.zupo ]; nodes.terminal = - { config, pkgs, ... }: + { + config, + pkgs, + lib, + ... + }: + let + # Create a patched version of the package that points to the local dashboard + # for easier testing + patchedPareto = pkgs.paretosecurity.overrideAttrs (oldAttrs: { + postPatch = '' + substituteInPlace team/report.go \ + --replace-warn 'const reportURL = "https://dash.paretosecurity.com"' \ + 'const reportURL = "http://dashboard"' + ''; + }); + in { imports = [ ./common/user-account.nix ]; - services.paretosecurity.enable = true; + services.paretosecurity = { + enable = true; + package = patchedPareto; + }; + + }; + + nodes.dashboard = + { config, pkgs, ... }: + { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nginx = { + enable = true; + virtualHosts."dashboard" = { + locations."/api/v1/team/".extraConfig = '' + add_header Content-Type application/json; + return 200 '{"message": "Linked device."}'; + ''; + }; + }; }; nodes.xfce = @@ -38,8 +74,18 @@ enableOCR = true; testScript = '' + # Test setup + terminal.succeed("su - alice -c 'mkdir -p /home/alice/.config'") + for m in [terminal, dashboard]: + m.systemctl("start network-online.target") + m.wait_for_unit("network-online.target") + + # Test 1: Test the systemd socket is installed & enabled + terminal.succeed('systemctl is-enabled paretosecurity.socket') + + # Test 2: Test running checks terminal.succeed( - "su -- alice -c 'paretosecurity check" + "su - alice -c 'paretosecurity check" # Disable some checks that need intricate test setup so that this test # remains simple and fast. Tests for all checks and edge cases available # at https://github.com/ParetoSecurity/agent/tree/main/test/integration @@ -48,10 +94,29 @@ + " --skip 21830a4e-84f1-48fe-9c5b-beab436b2cdb" # Disk encryption + " --skip 44e4754a-0b42-4964-9cc2-b88b2023cb1e" # Pareto Security is up to date + " --skip f962c423-fdf5-428a-a57a-827abc9b253e" # Password manager installed + + " --skip 2e46c89a-5461-4865-a92e-3b799c12034a" # Firewall is enabled + "'" ) + # Test 3: Test linking + terminal.succeed("su - alice -c 'paretosecurity link" + + " paretosecurity://enrollTeam/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + + "eyJ0b2tlbiI6ImR1bW15LXRva2VuIiwidGVhbUlEIjoiZHVtbXktdGVhbS1pZCIsImlhdCI6" + + "MTcwMDAwMDAwMCwiZXhwIjoxOTAwMDAwMDAwfQ.WgnL6_S0EBJHwF1wEVUG8GtIcoVvK5IjWbZpUeZr4Qw'") + + config = terminal.succeed("cat /home/alice/.config/pareto.toml") + assert 'AuthToken = "dummy-token"' in config + assert 'TeamID = "dummy-team-id"' in config + + # Test 4: Test the tray icon xfce.wait_for_x() + for unit in [ + 'paretosecurity-trayicon', + 'paretosecurity-user', + 'paretosecurity-user.timer' + ]: + status, out = xfce.systemctl("is-enabled " + unit, "alice") + assert status == 0, f"Unit {unit} is not enabled (status: {status}): {out}" xfce.succeed("xdotool mousemove 850 10") xfce.wait_for_text("Pareto Security") xfce.succeed("xdotool click 1") diff --git a/pkgs/by-name/pa/paretosecurity/package.nix b/pkgs/by-name/pa/paretosecurity/package.nix index 824def6c114e..e6b5232d75cb 100644 --- a/pkgs/by-name/pa/paretosecurity/package.nix +++ b/pkgs/by-name/pa/paretosecurity/package.nix @@ -9,22 +9,18 @@ buildGoModule rec { pname = "paretosecurity"; - version = "0.0.91"; + version = "0.0.96"; src = fetchFromGitHub { owner = "ParetoSecurity"; repo = "agent"; rev = version; - hash = "sha256-/kGwV96Jp7U08jh/wPQMcoV48zQe9ixY7gpNdtFyOkk="; + hash = "sha256-SyeIGSDvrnOvyOJ0zC8CulpaMa+iZeRaMTJUSydz2tw="; }; - vendorHash = "sha256-kGrYoN0dGcSuQW47Y4LUFdHQYAoY74NOM1LLPdhmLhc="; + vendorHash = "sha256-O/OF3Y6HiiikMxf657k9eIM7UfkicIImAUxVVf/TgR8="; proxyVendor = true; - subPackages = [ - "cmd/paretosecurity" - ]; - ldflags = [ "-s" "-X=github.com/ParetoSecurity/agent/shared.Version=${version}" @@ -32,6 +28,23 @@ buildGoModule rec { "-X=github.com/ParetoSecurity/agent/shared.Date=1970-01-01T00:00:00Z" ]; + postInstall = '' + # Install global systemd files + install -Dm400 ${src}/apt/paretosecurity.socket $out/lib/systemd/system/paretosecurity.socket + install -Dm400 ${src}/apt/paretosecurity.service $out/lib/systemd/system/paretosecurity.service + substituteInPlace $out/lib/systemd/system/paretosecurity.service \ + --replace-fail "/usr/bin/paretosecurity" "$out/bin/paretosecurity" + + # Install user systemd files + install -Dm444 ${src}/apt/paretosecurity-user.timer $out/lib/systemd/user/paretosecurity-user.timer + install -Dm444 ${src}/apt/paretosecurity-user.service $out/lib/systemd/user/paretosecurity-user.service + substituteInPlace $out/lib/systemd/user/paretosecurity-user.service \ + --replace-fail "/usr/bin/paretosecurity" "$out/bin/paretosecurity" + install -Dm444 ${src}/apt/paretosecurity-trayicon.service $out/lib/systemd/user/paretosecurity-trayicon.service + substituteInPlace $out/lib/systemd/user/paretosecurity-trayicon.service \ + --replace-fail "/usr/bin/paretosecurity" "$out/bin/paretosecurity" + ''; + passthru.tests = { version = testers.testVersion { version = "${version}"; @@ -50,12 +63,13 @@ buildGoModule rec { settings such as if you have disk encryption and firewall enabled. If you use the `services.paretosecurity` NixOS module, you also get a - root helper, so that you can run the checker in userspace. Some checks + root helper that allows you to run the checker in userspace. Some checks require root permissions, and the checker asks the helper to run those. Additionally, if you enable `services.paretosecurity.trayIcon`, you get a little Vilfredo Pareto living in your systray showing your the current - status of checks. + status of checks. This will also enable a systemd timer to update the + status of checks once per hour. Finally, you can run `paretosecurity link` to configure the agent to send the status of checks to https://dash.paretosecurity.com to make