1
0
Fork 0

modules/backup: add backup module using restic

This commit is contained in:
clerie 2023-05-16 12:21:11 +02:00
parent 2ab99d861a
commit 05a2742a91
4 changed files with 136 additions and 9 deletions

View File

@ -117,6 +117,13 @@
clerie.nixfiles.enable = true;
clerie.backup = {
targets = {
cyan.serverName = "cyan.backup.clerie.de";
magenta.serverName = "magenta.backup.clerie.de";
};
};
nixpkgs.overlays = [
(import ../../pkgs/overlay.nix)
];

View File

@ -51,15 +51,14 @@ in {
};
};
services.restic.backups = {
main = {
paths = [
"/var/src"
"/var/lib"
];
initialize = true;
passwordFile = "/var/src/secrets/restic/main";
repositoryFile = "/var/src/secrets/restic/repository-cyan";
clerie.backup = {
enable = true;
jobs = {
main = {
paths = [
"/var/lib"
];
};
};
};

120
modules/backup/default.nix Normal file
View File

@ -0,0 +1,120 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.clerie.backup;
jobTargetPairs = flatten (
mapAttrsToList (jobName: jobOptions:
let
jobTargets = if jobOptions.targets == null then cfg.targets
else
filterAttrs (targetName: targetOptions:
any (map (jobOptionTarget: jobOptionTarget == targetName) jobOptions.targets)
) cfg.targets;
in mapAttrsToList (targetName: targetOptions:
{
inherit jobName jobOptions targetName targetOptions;
}
) jobTargets
) cfg.jobs
);
backupServiceUnits = listToAttrs (map ({jobName, jobOptions, targetName, targetOptions}: let
jobPasswordFile = if jobOptions.passwordFile == null then config.age.secrets."clerie-backup-job-${jobName}".path else jobOptions.passwordFile;
repoPath = if jobOptions.repoPath == null then "/${config.networking.hostName}/${jobName}" else jobOptions.repoPath;
targetPasswordFile = if targetOptions.passwordFile == null then config.age.secrets."clerie-backup-target-${targetName}".path else targetOptions.passwordFile;
targetUsername = if targetOptions.username == null then config.networking.hostName else targetOptions.username;
in
nameValuePair "clerie-backup-${jobName}-${targetName}" {
requires = [ "network.target" "local-fs.target" ];
path = [ pkgs.restic ];
serviceConfig = {
Type = "oneshot";
};
script = ''
set -euo pipefail
export RESTIC_PASSWORD_FILE=${jobPasswordFile}
export RESTIC_REPOSITORY="rest:https://${targetUsername}:$(cat ${targetPasswordFile})@${targetOptions.serverName}${repoPath}"
restic snapshots || restic init
restic backup ${escapeShellArgs jobOptions.paths}
restic check
'';
}
) jobTargetPairs);
backupServiceTimers = listToAttrs (map ({jobName, jobOptions, targetName, targetOptions}:
nameValuePair "clerie-backup-${jobName}-${targetName}" {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "hourly";
RandomizedDelaySec = "1h";
};
after = [ "network-online.target" ];
}
) jobTargetPairs);
targetOptions = { ... }: {
options = {
passwordFile = mkOption {
type = with types; nullOr str;
default = null;
};
username = mkOption {
type = with types; nullOr str;
default = null;
};
serverName = mkOption {
type = types.str;
};
};
};
jobOptions = { ... }: {
options = {
passwordFile = mkOption {
type = with types; nullOr str;
default = null;
};
repoPath = mkOption {
type = with types; nullOr str;
default = null;
};
targets = mkOption {
type = with types; nullOr (listOf str);
default = null;
};
paths = mkOption {
type = with types; listOf str;
};
};
};
in
{
options = {
clerie.backup = {
enable = mkEnableOption "clerie's Backup";
targets = mkOption {
type = with types; attrsOf (submodule targetOptions);
default = {};
};
jobs = mkOption {
type = with types; attrsOf (submodule jobOptions);
default = {};
};
};
};
config = mkIf cfg.enable {
systemd.services = backupServiceUnits;
systemd.timers = backupServiceTimers;
};
}

View File

@ -5,6 +5,7 @@
./policyrouting
./akne
./anycast_healthchecker
./backup
./clerie-firewall
./gre-tunnel
./minecraft-server