Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/syncoid: automatically setup privilege delegation #79759

Merged
merged 1 commit into from Oct 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 48 additions & 4 deletions nixos/modules/services/backup/syncoid.nix
Expand Up @@ -4,6 +4,15 @@ with lib;

let
cfg = config.services.syncoid;

# Extract pool names of local datasets (ones that don't contain "@") that
# have the specified type (either "source" or "target")
getPools = type: unique (map (d: head (builtins.match "([^/]+).*" d)) (
# Filter local datasets
filter (d: !hasInfix "@" d)
# Get datasets of the specified type
(catAttrs type (attrValues cfg.commands))
));
in {

# Interface
Expand All @@ -26,14 +35,25 @@ in {

user = mkOption {
type = types.str;
default = "root";
default = "syncoid";
example = "backup";
description = ''
The user for the service. Sudo or ZFS privilege delegation must be
configured to use a user other than root.
The user for the service. ZFS privilege delegation will be
automatically configured for any local pools used by syncoid if this
option is set to a user other than root. The user will be given the
"hold" and "send" privileges on any pool that has datasets being sent
and the "create", "mount", "receive", and "rollback" privileges on
any pool that has datasets being received.
'';
};

group = mkOption {
type = types.str;
default = "syncoid";
example = "backup";
description = "The group for the service.";
};

sshKey = mkOption {
type = types.nullOr types.path;
# Prevent key from being copied to store
Expand Down Expand Up @@ -146,6 +166,18 @@ in {
# Implementation

config = mkIf cfg.enable {
users = {
users = mkIf (cfg.user == "syncoid") {
syncoid = {
group = cfg.group;
isSystemUser = true;
};
};
groups = mkIf (cfg.group == "syncoid") {
syncoid = {};
};
};

systemd.services.syncoid = {
description = "Syncoid ZFS synchronization service";
script = concatMapStringsSep "\n" (c: lib.escapeShellArgs
Expand All @@ -156,10 +188,22 @@ in {
++ c.extraArgs
++ [ "--sendoptions" c.sendOptions
"--recvoptions" c.recvOptions
"--no-privilege-elevation"
c.source c.target
])) (attrValues cfg.commands);
after = [ "zfs.target" ];
serviceConfig.User = cfg.user;
serviceConfig = {
ExecStartPre = (map (pool: lib.escapeShellArgs [
"+/run/booted-system/sw/bin/zfs" "allow"
cfg.user "hold,send" pool
]) (getPools "source")) ++
(map (pool: lib.escapeShellArgs [
"+/run/booted-system/sw/bin/zfs" "allow"
cfg.user "create,mount,receive,rollback" pool
]) (getPools "target"));
User = cfg.user;
Group = cfg.group;
};
startAt = cfg.interval;
};
};
Expand Down
9 changes: 5 additions & 4 deletions nixos/tests/sanoid.nix
Expand Up @@ -38,7 +38,7 @@ in {

services.syncoid = {
enable = true;
sshKey = "/root/.ssh/id_ecdsa";
sshKey = "/var/lib/syncoid/id_ecdsa";
commonArgs = [ "--no-sync-snap" ];
commands."pool/test".target = "root@target:pool/test";
};
Expand Down Expand Up @@ -69,11 +69,12 @@ in {
"udevadm settle",
)

source.succeed("mkdir -m 700 /root/.ssh")
source.succeed(
"cat '${snakeOilPrivateKey}' > /root/.ssh/id_ecdsa"
"mkdir -m 700 -p /var/lib/syncoid",
"cat '${snakeOilPrivateKey}' > /var/lib/syncoid/id_ecdsa",
"chmod 600 /var/lib/syncoid/id_ecdsa",
"chown -R syncoid:syncoid /var/lib/syncoid/",
)
source.succeed("chmod 600 /root/.ssh/id_ecdsa")

source.succeed("touch /tmp/mnt/test.txt")
source.systemctl("start --wait sanoid.service")
Expand Down