92 lines
2.9 KiB
Nix
92 lines
2.9 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}: let
|
|
# sops-encrypted secrets (single file holds all service secrets).
|
|
secretsFile = ../../secrets/secrets.yaml;
|
|
# Root-only runtime files restic reads from (0600 root).
|
|
runtimeDir = "/var/lib/restic";
|
|
passwordFile = "/var/lib/restic/.password";
|
|
environmentFile = "/var/lib/restic/environment";
|
|
# Backblaze B2 backend, per restic docs: b2:bucketname.
|
|
repo = "b2:hetzner-severijnse";
|
|
|
|
# Materialize the restic password and B2 credentials from sops into
|
|
# root-only files, so secrets are never world-readable in the Nix store.
|
|
writeSecrets = pkgs.writeShellScript "restic-write-secrets" ''
|
|
set -euo pipefail
|
|
mkdir -p ${runtimeDir}
|
|
${pkgs.sops}/bin/sops \
|
|
--decrypt --extract '["restic_password"]' \
|
|
--input-type yaml --output-type yaml ${secretsFile} \
|
|
| tr -d '\n' > "${passwordFile}"
|
|
chmod 0600 "${passwordFile}"
|
|
|
|
: > "${environmentFile}"
|
|
chmod 0600 "${environmentFile}"
|
|
${pkgs.sops}/bin/sops --decrypt --input-type yaml --output-type yaml ${secretsFile} |
|
|
${pkgs.gnused}/bin/sed -nE \
|
|
's/^b2_key_id: (.*)/B2_ACCOUNT_ID=\1/p; s/^b2_application_key: (.*)/B2_ACCOUNT_KEY=\1/p' \
|
|
>> "${environmentFile}"
|
|
'';
|
|
in {
|
|
systemd.services.restic-password = {
|
|
description = "Materialize restic repository password and B2 credentials from sops";
|
|
wantedBy = ["multi-user.target"];
|
|
# The age key lives in /etc/age/keys.txt; the service must know where it is
|
|
# and needs a HOME for age to report its user config directory.
|
|
environment.SOPS_AGE_KEY_FILE = "/etc/age/keys.txt";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
Environment = ["HOME=/root"];
|
|
ExecStart = "${writeSecrets}";
|
|
};
|
|
};
|
|
|
|
# B2 credentials are supplied via environmentFile (B2_ACCOUNT_ID / B2_ACCOUNT_KEY),
|
|
# matching the official module example which combines `repository` and `environmentFile`.
|
|
services.restic.backups.localbackup = {
|
|
repository = "b2:hetzner-severijnse";
|
|
environmentFile = environmentFile;
|
|
passwordFile = passwordFile;
|
|
initialize = true;
|
|
paths = [
|
|
"/home/admin"
|
|
"/var/lib/postgresql"
|
|
"/var/lib/gitea"
|
|
"/var/lib/caddy"
|
|
"/var/lib/virtualcam"
|
|
"/var/lib/coredns"
|
|
"/etc/nixos"
|
|
];
|
|
exclude = [
|
|
"/home/admin/backups"
|
|
"/home/admin/dms/mail-logs"
|
|
"/home/admin/.opencode"
|
|
"/home/admin/.local"
|
|
"/home/admin/.npm"
|
|
"/home/admin/.config"
|
|
"*.log"
|
|
"*.log.*"
|
|
"**/.cache"
|
|
];
|
|
timerConfig = {
|
|
OnCalendar = "Mon *-*-* 03:00:00";
|
|
Persistent = true;
|
|
RandomizedDelaySec = "15m";
|
|
};
|
|
pruneOpts = [
|
|
"--keep-daily 7"
|
|
"--keep-weekly 4"
|
|
"--keep-monthly 6"
|
|
];
|
|
runCheck = true;
|
|
};
|
|
|
|
# The backup must never run before the secrets exist.
|
|
systemd.services."restic-backups-localbackup" = {
|
|
requires = ["restic-password.service"];
|
|
after = ["restic-password.service"];
|
|
};
|
|
} |