nixpkgs/nixos/modules/services/security/paretosecurity.nix
Neyts Zupan 93403bdacd nixos/paretosecurity: Add support for declarative linking
Before this PR, users that wanted to link their Pareto Desktop
to Pareto Cloud had to imperatively run `paretosecurity link ...`.
This PR adds a oneshot systemd service that does the linking
so the entire Pareto app can be configured declaratively with nix.
2025-09-07 15:25:49 +00:00

134 lines
5 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.paretosecurity;
in
{
options.services.paretosecurity = {
enable = lib.mkEnableOption "[ParetoSecurity](https://paretosecurity.com) [agent](https://github.com/ParetoSecurity/agent) and its root helper";
package = lib.mkPackageOption pkgs "paretosecurity" { };
trayIcon = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Set to false to disable the tray icon and run as a CLI tool only.";
};
users = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
inviteId = lib.mkOption {
type = lib.types.str;
description = ''
A unique ID that links the agent to Pareto Cloud.
Get it from the Join Team page on `https://cloud.paretosecurity.com/team/join/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`.
In Step 2, under Linux tab, enter your email then copy it from the generated command.
'';
example = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
};
};
}
);
default = { };
description = "Per-user Pareto Security configuration.";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd.packages = [ cfg.package ];
# 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" ];
# In NixOS, systemd services are configured with minimal PATH. However,
# paretosecurity helper looks for installed software to do its job, so
# it needs the full system PATH. For example, it runs `iptables` to see if
# firewall is configured. And it looks for various password managers to see
# if one is installed.
# The `paretosecurity-user` timer service that is configured lower has
# the same need.
systemd.services = {
paretosecurity.serviceConfig.Environment = [
"PATH=${config.system.path}/bin:${config.system.path}/sbin"
];
}
// (
# Each user can set their inviteID, which creates a systemd service
# that runs `paretosecurity link ...` to link their device to Pareto Cloud.
lib.mapAttrs' (
username: userConfig:
lib.nameValuePair "paretosecurity-link-${username}" {
description = "Link Pareto Desktop to Pareto Cloud for user ${username}";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = username;
StateDirectory = "paretosecurity/${username}";
ExecStart = pkgs.writeShellScript "paretosecurity-link-${username}" ''
set -euo pipefail
INVITE_ID="${userConfig.inviteId}"
STATE_FILE="/var/lib/paretosecurity/${username}/linked-$INVITE_ID"
CONFIG_FILE="$HOME/.config/pareto.toml"
# Check if already linked with this specific invite
if [ -f "$STATE_FILE" ]; then
echo "Device already linked with invite $INVITE_ID for user ${username}"
exit 0
fi
# Ensure config directory exists
mkdir -p "$(dirname "$CONFIG_FILE")"
# Perform linking
echo "Linking device to Pareto Cloud for user ${username}..."
${cfg.package}/bin/paretosecurity link \
"paretosecurity://linkDevice/?invite_id=$INVITE_ID"
# Verify linking succeeded
if [ -f "$CONFIG_FILE" ] && grep -q "TeamID" "$CONFIG_FILE"; then
echo "Successfully linked to Pareto Cloud for user ${username}"
touch "$STATE_FILE"
else
echo "Failed to link to Pareto Cloud for user ${username}"
exit 1
fi
'';
};
wantedBy = [ "multi-user.target" ];
}
) cfg.users
);
# Enable the tray icon and timer services if the trayIcon option is enabled
systemd.user = lib.mkIf cfg.trayIcon {
services = {
paretosecurity-trayicon.wantedBy = [ "graphical-session.target" ];
paretosecurity-user = {
wantedBy = [ "graphical-session.target" ];
serviceConfig.Environment = [
"PATH=${config.system.path}/bin:${config.system.path}/sbin"
];
};
};
timers.paretosecurity-user.wantedBy = [ "timers.target" ];
};
};
}