Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
nixos-cfg/modules/restic_separate-user.nix
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
88 lines (83 sloc)
3.44 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ pkgs, lib, config, options, ... }: | |
with lib; | |
let | |
dot = f: g: x: f (g x); | |
compose = foldr dot id; | |
withUser = n: v: { user = "restic-${n}"; }; | |
cacheDir = n: "/var/tmp/restic-${n}"; | |
withCache = n: v: { extraBackupArgs = v.extraBackupArgs ++ [ "--cache-dir ${cacheDir n}" ]; }; | |
extendAttrs = f: mapAttrs (n: v: v // (f n v)); | |
rename = mapAttrs' (n: v: nameValuePair "generated-${n}" v); | |
augmentedBackups = compose ((map extendAttrs [ withUser withCache ]) ++ [ rename ]) config.resticSeparateUser.backups; | |
setfacl = "${pkgs.acl.bin}/bin/setfacl"; | |
in | |
{ | |
options.resticSeparateUser.backups = mkOption { | |
description = '' | |
Periodic backups to create with Restic employing autogenerated users. | |
''; | |
type = with types; attrsOf | |
(submodule ({name, ...}: | |
mapAttrs (n: v: | |
if n == "options" then | |
filterAttrs (n: v: n != "user") v | |
else | |
v | |
) ((head options.services.restic.backups.type.getSubModules).submodule { name = name; }))); | |
default = options.services.restic.backups.default; | |
example = options.services.restic.backups.example; | |
}; | |
config = { | |
services.restic.backups = augmentedBackups; | |
environment.systemPackages = [ pkgs.restic ]; | |
# create users | |
users.users = mapAttrs' (n: v: nameValuePair v.user { isSystemUser = true; }) augmentedBackups; | |
# create and set permissions for cache directory | |
system.activationScripts = mapAttrs' (n: v: nameValuePair | |
"restic-backups-${n}-cachedir" | |
{ | |
text = '' | |
${pkgs.coreutils}/bin/mkdir -pm 0700 ${cacheDir n} | |
${pkgs.coreutils}/bin/chown ${v.user} ${cacheDir n} | |
${setfacl} -k ${cacheDir n} | |
''; | |
deps = []; | |
}) augmentedBackups; | |
# add permissions for paths | |
# necessary to do as prestart because a default set with X would make newly created files executable | |
# and because some applications change the ACL mask upon saving | |
systemd.services = | |
let | |
# make sure to run before any other setfacl calls as they are destructive | |
makeAccessible = entity: target: '' | |
current=${target} | |
while current="$(dirname "$current")"; do | |
${setfacl} --mask -m ${entity}:x "$current" || break | |
if [ "$current" = "/" ]; then | |
break | |
fi | |
done | |
''; | |
in | |
mapAttrs' (n: v: | |
nameValuePair "restic-backups-${n}" { | |
# deprecated in 19.09, see nixos/nixpkgs#53852 | |
serviceConfig.PermissionsStartOnly = true; | |
preStart = '' | |
${makeAccessible "u:${v.user}" v.passwordFile} | |
${setfacl} --mask -m u:${v.user}:r ${v.passwordFile} | |
${optionalString (!(isNull v.s3CredentialsFile)) '' | |
${makeAccessible "u:${v.user}" v.s3CredentialsFile} | |
${setfacl} --mask -m u:${v.user}:r ${v.s3CredentialsFile} | |
''} | |
${setfacl} --mask -Rm u:${v.user}:rX ${concatStringsSep " " v.paths} | |
''; | |
postStart = '' | |
# supressing errors from the rmeoval of proc directories that races with their permissions being removed | |
stderr=$(${setfacl} --mask -Rx u:s${v.user} / 2>&1 >/dev/null) | |
status=$? | |
[[ $status -eq 0 ]] || ${pkgs.gnugrep}/bin/grep "No such file or directory" <<< "$stderr" || ${pkgs.coreutils}/bin/echo "$stderr" && exit $status | |
''; | |
} ) augmentedBackups; | |
}; | |
} |