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.
This commit is contained in:
@@ -17,6 +17,25 @@ in
|
||||
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 {
|
||||
@@ -38,9 +57,64 @@ in
|
||||
# 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"
|
||||
];
|
||||
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 {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
imports = [ ./common/user-account.nix ];
|
||||
|
||||
services.paretosecurity.enable = true;
|
||||
|
||||
services.paretosecurity.users.alice.inviteId = "test-invite-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||||
};
|
||||
|
||||
nodes.xfce =
|
||||
@@ -46,46 +46,52 @@
|
||||
terminal.systemctl("start network-online.target")
|
||||
terminal.wait_for_unit("network-online.target")
|
||||
|
||||
# Test 1: Test the systemd socket is installed & enabled
|
||||
terminal.succeed('systemctl is-enabled paretosecurity.socket')
|
||||
with subtest("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"
|
||||
# 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
|
||||
+ " --skip c96524f2-850b-4bb9-abc7-517051b6c14e" # SecureBoot
|
||||
+ " --skip 37dee029-605b-4aab-96b9-5438e5aa44d8" # Screen lock
|
||||
+ " --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
|
||||
+ "'"
|
||||
)
|
||||
with subtest("Test running checks"):
|
||||
terminal.succeed(
|
||||
"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
|
||||
+ " --skip c96524f2-850b-4bb9-abc7-517051b6c14e" # SecureBoot
|
||||
+ " --skip 37dee029-605b-4aab-96b9-5438e5aa44d8" # Screen lock
|
||||
+ " --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
|
||||
+ "'"
|
||||
)
|
||||
|
||||
# Test 3: 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 460 10")
|
||||
xfce.wait_for_text("Pareto Security")
|
||||
xfce.succeed("xdotool click 1")
|
||||
xfce.wait_for_text("Run Checks")
|
||||
with subtest("Test linking to Pareto Cloud"):
|
||||
# The linking service will fail because there is no Internet,
|
||||
# but we can check that it tried
|
||||
terminal.succeed('systemctl list-units --type=service | grep paretosecurity-link-alice')
|
||||
terminal.succeed('journalctl -u paretosecurity-link-alice.service | grep "Linking device to Pareto Cloud for user alice"')
|
||||
|
||||
# Test 4: Desktop entry
|
||||
xfce.succeed("xdotool mousemove 10 10")
|
||||
xfce.succeed("xdotool click 1") # hide the tray icon window
|
||||
xfce.succeed("xdotool click 1") # show the Applications menu
|
||||
xfce.succeed("xdotool mousemove 10 200")
|
||||
xfce.succeed("xdotool click 1")
|
||||
xfce.wait_for_text("Pareto Security")
|
||||
with subtest("Test 3: 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 460 10")
|
||||
xfce.wait_for_text("Pareto Security")
|
||||
xfce.succeed("xdotool click 1")
|
||||
xfce.wait_for_text("Run Checks")
|
||||
|
||||
# Test 5: paretosecurity:// URL handler is registered
|
||||
xfce.succeed("su - alice -c 'xdg-open paretosecurity://foo'")
|
||||
with subtest("Test 4: Desktop entry"):
|
||||
xfce.succeed("xdotool mousemove 10 10")
|
||||
xfce.succeed("xdotool click 1") # hide the tray icon window
|
||||
xfce.succeed("xdotool click 1") # show the Applications menu
|
||||
xfce.succeed("xdotool mousemove 10 200")
|
||||
xfce.succeed("xdotool click 1")
|
||||
xfce.wait_for_text("Pareto Security")
|
||||
|
||||
with subtest("Test 5: paretosecurity:// URL handler is registered"):
|
||||
xfce.succeed("su - alice -c 'xdg-open paretosecurity://foo'")
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -78,13 +78,15 @@ buildGoModule (finalAttrs: {
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Agent that makes sure your laptop is correctly configured for security";
|
||||
description = "A simple trayicon app that makes sure your laptop is correctly configured for security";
|
||||
longDescription = ''
|
||||
The Pareto Security agent is a free and open source app to help you make
|
||||
sure that your laptop is configured for security.
|
||||
[Pareto Desktop](https://paretosecurity.com/linux) is a free and open
|
||||
source trayicon app to help you configure your laptop for security. It
|
||||
nudges you to take care of 20% of security-related tasks that bring 80% of
|
||||
protection.
|
||||
|
||||
By default, it's a CLI command that prints out a report on basic security
|
||||
settings such as if you have disk encryption and firewall enabled.
|
||||
In it's simplest form, it's a CLI command that prints out a report on basic
|
||||
security 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 that allows you to run the checker in userspace. Some checks
|
||||
@@ -96,9 +98,11 @@ buildGoModule (finalAttrs: {
|
||||
once per hour. If you want to use just the CLI mode, set
|
||||
`services.paretosecurity.trayIcon` to `false`.
|
||||
|
||||
Finally, you can run `paretosecurity link` to configure the agent
|
||||
to send the status of checks to https://dash.paretosecurity.com to make
|
||||
compliance people happy. No sending happens until your device is linked.
|
||||
Finally, if you set `users.users.alice.paretosecurity.inviteId = "..."`
|
||||
to the `inviteId` you get on [Pareto Cloud](https://cloud.paretosecurity.com/),
|
||||
then Pareto Desktop acts as a read-only and push-only agent that sends the
|
||||
status of checks to https://cloud.paretosecurity.com which makes
|
||||
compliance people happy and your privacy intact.
|
||||
'';
|
||||
homepage = "https://github.com/ParetoSecurity/agent";
|
||||
license = lib.licenses.gpl3Only;
|
||||
|
||||
Reference in New Issue
Block a user