nixos-config/modules/restic/default.nix

140 lines
4.2 KiB
Nix

# SPDX-FileCopyrightText: 2020-2024 Simon Bruder <simon@sbruder.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
{ config, lib, pkgs, ... }:
let
cfg = config.sbruder.restic;
sftpTarget = "u313368-sub4@u313368-sub4.your-storagebox.de";
sftpPort = 23;
repository = "sftp://${sftpTarget}:${toString sftpPort}/personal";
mkPruneConfig = { tag, timerConfig, opts }: {
inherit repository timerConfig;
passwordFile = config.sops.secrets.restic-password.path;
paths = [ ];
extraOptions = [
"-o"
"sftp.command='ssh -i ${config.sops.secrets.restic-ssh-key.path} -p ${toString sftpPort} ${sftpTarget} -s sftp'"
];
pruneOpts = [
"--compression auto"
"--tag ${tag}"
"--verbose"
] ++ opts;
};
in
{
imports = [
./system.nix
./vm-image.nix
];
options.sbruder.restic = {
enable = lib.mkEnableOption "restic";
authScript.enable = (lib.mkEnableOption "script to use restic as user without dealing with authentication") // {
default = cfg.enable && config.sbruder.gui.enable;
};
prune.enable = lib.mkEnableOption "pruning";
mirror.backblaze.enable = lib.mkEnableOption "mirroring to Backblaze B2";
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
sops.secrets = {
restic-password = { };
restic-repository = { };
};
}
(lib.mkIf cfg.authScript.enable {
environment.systemPackages = [
(pkgs.writeShellScriptBin "restic-auth" ''
${pkgs.restic}/bin/restic \
--password-command="pass data/backup/restic-nixos" \
--repo "${repository}" \
$@
'')
];
})
(lib.mkIf cfg.prune.enable {
sops.secrets.restic-ssh-key = {
sopsFile = ../../machines/${config.networking.hostName}/secrets.yaml;
};
services.restic.backups = {
system-prune = mkPruneConfig {
tag = "system";
timerConfig = {
OnCalendar = "*-1/2-07 03:00:00";
RandomizedDelaySec = "4h";
};
opts = [
"--keep-daily 7"
"--keep-monthly 12"
"--keep-weekly 5"
"--keep-yearly 10"
];
};
vm-image-prune = mkPruneConfig {
tag = "vm-image";
timerConfig = {
OnCalendar = "06:00";
RandomizedDelaySec = "1h";
};
opts = [
"--keep-last 1"
];
};
};
})
(lib.mkIf cfg.mirror.backblaze.enable {
sops.secrets = {
restic-ssh-key.sopsFile = ../../machines/${config.networking.hostName}/secrets.yaml;
restic-mirror-backblaze-env.sopsFile = ../../machines/${config.networking.hostName}/secrets.yaml;
};
systemd.services.restic-mirror-backblaze = {
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
serviceConfig = {
ExecStart = "${pkgs.rclone}/bin/rclone --config /dev/null sync :sftp,user=u313368-sub4,host=u313368-sub4.your-storagebox.de,port=23,key_file=$CREDENTIALS_DIRECTORY/ssh-key: :b2:sbruder-restic";
EnvironmentFile = config.sops.secrets.restic-mirror-backblaze-env.path;
LoadCredential = "ssh-key:${config.sops.secrets.restic-ssh-key.path}";
DynamicUser = true;
CapabilityBoundingSet = null;
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "noaccess";
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = "@system-service";
};
};
systemd.timers.restic-mirror-backblaze = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "hourly";
RandomizedDelaySec = "15min";
};
};
})
]);
}