diff --git a/hosts/sire/guests/paperless.nix b/hosts/sire/guests/paperless.nix index 83c4988..d399398 100644 --- a/hosts/sire/guests/paperless.nix +++ b/hosts/sire/guests/paperless.nix @@ -1,10 +1,12 @@ { config, + lib, nodes, ... }: let sentinelCfg = nodes.sentinel.config; paperlessDomain = "paperless.${sentinelCfg.repo.secrets.local.personalDomain}"; + paperlessBackupDir = "/var/cache/paperless-backup"; in { microvm.mem = 1024 * 6; microvm.vcpu = 8; @@ -92,4 +94,29 @@ in { }; systemd.services.paperless.serviceConfig.RestartSec = "600"; # Retry every 10 minutes + + systemd.tmpfiles.settings."10-paperless".${paperlessBackupDir}.d = { + inherit (config.services.paperless) user; + mode = "0700"; + }; + + systemd.services.paperless-backup = let + cfg = config.systemd.services.paperless-consumer; + in { + description = "Paperless documents backup"; + serviceConfig = lib.recursiveUpdate cfg.serviceConfig { + ExecStart = "${config.services.paperless.package}/bin/paperless-ngx document_exporter -na -nt -f -d ${paperlessBackupDir}"; + ReadWritePaths = cfg.serviceConfig.ReadWritePaths ++ [paperlessBackupDir]; + Restart = "no"; + Type = "oneshot"; + }; + inherit (cfg) environment; + requiredBy = ["restic-backups-storage-box-dusk.service"]; + }; + + backups.storageBoxes.dusk = { + subuser = "paperless"; + user = "paperless"; + paths = [paperlessBackupDir]; + }; } diff --git a/hosts/sire/guests/samba.nix b/hosts/sire/guests/samba.nix index e790548..9a618e3 100644 --- a/hosts/sire/guests/samba.nix +++ b/hosts/sire/guests/samba.nix @@ -246,7 +246,7 @@ in { "/shares/users/${user}-paperless".d = { user = "paperless"; group = "paperless"; - mode = "0750"; + mode = "0550"; }; "/paperless/consume/${user}".d = { user = "paperless"; @@ -347,37 +347,9 @@ in { } // lib.mapAttrs (_: cfg: {gid = cfg.id;}) (smbUsers // smbGroups); - # Backups - # ======================================================================== - - age.secrets.restic-encryption-password.generator.script = "alnum"; - age.secrets.restic-ssh-privkey.generator.script = "ssh-ed25519"; - - services.restic.backups.main = { - hetznerStorageBox = let - box = config.repo.secrets.global.hetzner.storageboxes.dusk; - in { - enable = true; - inherit (box) mainUser; - inherit (box.users.samba) subUid path; - sshAgeSecret = "restic-ssh-privkey"; - }; - - # We need to backup stuff from other users, so run as root. + backups.storageBoxes.dusk = { + subuser = "samba"; user = "root"; - timerConfig = { - OnCalendar = "06:15"; - RandomizedDelaySec = "3h"; - Persistent = true; - }; - initialize = true; - passwordFile = config.age.secrets.restic-encryption-password.path; paths = ["/bunker"]; - pruneOpts = [ - "--keep-daily 14" - "--keep-weekly 7" - "--keep-monthly 12" - "--keep-yearly 75" - ]; }; } diff --git a/hosts/ward/guests/radicale.nix b/hosts/ward/guests/radicale.nix index 20c9e2f..889e998 100644 --- a/hosts/ward/guests/radicale.nix +++ b/hosts/ward/guests/radicale.nix @@ -82,4 +82,10 @@ in { }; systemd.services.radicale.serviceConfig.RestartSec = "600"; # Retry every 10 minutes + + backups.storageBoxes.dusk = { + subuser = "radicale"; + user = "radicale"; + paths = ["/var/lib/radicale"]; + }; } diff --git a/hosts/ward/guests/vaultwarden.nix b/hosts/ward/guests/vaultwarden.nix index 5c04682..bfefd13 100644 --- a/hosts/ward/guests/vaultwarden.nix +++ b/hosts/ward/guests/vaultwarden.nix @@ -74,7 +74,6 @@ in { smtpSecurity = "force_tls"; smtpPort = 465; }; - #backupDir = "/data/backup"; environmentFile = config.age.secrets.vaultwarden-env.path; }; @@ -85,36 +84,9 @@ in { RestartSec = "600"; # Retry every 10 minutes }; - # Backups - # ======================================================================== - - age.secrets.restic-encryption-password.generator.script = "alnum"; - age.secrets.restic-ssh-privkey.generator.script = "ssh-ed25519"; - - services.restic.backups.main = { - hetznerStorageBox = let - box = config.repo.secrets.global.hetzner.storageboxes.dusk; - in { - enable = true; - inherit (box) mainUser; - inherit (box.users.vaultwarden) subUid path; - sshAgeSecret = "restic-ssh-privkey"; - }; - + backups.storageBoxes.dusk = { + subuser = "vaultwarden"; user = "vaultwarden"; - timerConfig = { - OnCalendar = "06:15"; - RandomizedDelaySec = "3h"; - Persistent = true; - }; - initialize = true; - passwordFile = config.age.secrets.restic-encryption-password.path; paths = [config.services.vaultwarden.backupDir]; - pruneOpts = [ - "--keep-daily 14" - "--keep-weekly 7" - "--keep-monthly 12" - "--keep-yearly 75" - ]; }; } diff --git a/hosts/ward/net.nix b/hosts/ward/net.nix index 45034dd..86f6fbc 100644 --- a/hosts/ward/net.nix +++ b/hosts/ward/net.nix @@ -104,12 +104,6 @@ in { masquerade = true; }; - # Rule needed to allow local-vms wireguard traffic - lan-to-local = { - from = ["lan"]; - to = ["local"]; - }; - outbound = { from = ["lan"]; to = ["lan" "untrusted"]; diff --git a/modules/backups.nix b/modules/backups.nix new file mode 100644 index 0000000..3d8d7ef --- /dev/null +++ b/modules/backups.nix @@ -0,0 +1,79 @@ +{ + config, + lib, + ... +}: let + inherit + (lib) + attrValues + flip + mkIf + mkMerge + mkOption + types + ; +in { + options.backups.storageBoxes = mkOption { + description = "Backups to Hetzner Storage Boxes using restic"; + default = {}; + type = types.attrsOf (types.submodule (submod: { + options = { + name = mkOption { + description = "The name of the storage box to backup to. The box must be defined in the global secrets. Defaults to the attribute name."; + default = submod.config._module.args.name; + type = types.str; + }; + + subuser = mkOption { + description = "The name of the storage box subuser as defined in the global secrets, mapping this user to a subuser id."; + type = types.str; + }; + + user = mkOption { + description = "The user as which restic should run."; + type = types.str; + }; + + paths = mkOption { + description = "The paths to backup."; + type = types.listOf types.path; + }; + }; + })); + }; + + config = mkIf (config.backups.storageBoxes != {}) { + age.secrets.restic-encryption-password.generator.script = "alnum"; + age.secrets.restic-ssh-privkey.generator.script = "ssh-ed25519"; + + services.restic.backups = mkMerge (flip map (attrValues config.backups.storageBoxes) + (boxCfg: { + "storage-box-${boxCfg.name}" = { + hetznerStorageBox = let + box = config.repo.secrets.global.hetzner.storageboxes.${boxCfg.name}; + in { + enable = true; + inherit (box) mainUser; + inherit (box.users.${boxCfg.subuser}) subUid path; + sshAgeSecret = "restic-ssh-privkey"; + }; + + # We need to backup stuff from other users, so run as root. + inherit (boxCfg) user paths; + timerConfig = { + OnCalendar = "06:15"; + RandomizedDelaySec = "3h"; + Persistent = true; + }; + initialize = true; + passwordFile = config.age.secrets.restic-encryption-password.path; + pruneOpts = [ + "--keep-daily 14" + "--keep-weekly 7" + "--keep-monthly 12" + "--keep-yearly 75" + ]; + }; + })); + }; +} diff --git a/modules/default.nix b/modules/default.nix index 337663b..46bf981 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -27,6 +27,7 @@ ./config/users.nix ./acme-wildcard.nix + ./backups.nix ./deterministic-ids.nix ./distributed-config.nix ./kanidm.nix diff --git a/secrets/generated/sire-paperless/restic-encryption-password.age b/secrets/generated/sire-paperless/restic-encryption-password.age new file mode 100644 index 0000000..2abdffe --- /dev/null +++ b/secrets/generated/sire-paperless/restic-encryption-password.age @@ -0,0 +1,10 @@ +age-encryption.org/v1 +-> X25519 yVph6Vasomk5FHVnvYCP9EIWNTDUV9VLjT3tLWO6V2U +jiNBCi4nwRT5BraQtwlF1NLEC8ed6kEvg1D6XKT808g +-> piv-p256 xqSe8Q Aj4vwj2hYJUA4EhprNegsyBlBERNaC4JOQSYFDTA4JPL +EEqBpXe1tPZfngPeuZ4I/cUkFhoG5Z4P2bjUQ8xIRoM +-> n-grease g)hu +9qNhz-| +oXZBv+XwPtRbS7tlKyekxGJAhn1Qje/y2nAjjFg+8Ap+oxwpdMwJ4iFG2yxsWdew +XcdgOj/61A +--- ZSNOvD2AbvfGqKJ+bGh4n6EZOuiIs0KsB+CDGjmojJY +œ(™ŽôÄI .MþülB‹'ùбð¶B½ˆ<±`¿ºE¯iÐ[¾&%†„Kø‘@"?ÿ±§§ˆAà¼ÁëXn"£!ñ üTØÅ \ No newline at end of file diff --git a/secrets/generated/sire-paperless/restic-ssh-privkey.age b/secrets/generated/sire-paperless/restic-ssh-privkey.age new file mode 100644 index 0000000..5cc9c46 Binary files /dev/null and b/secrets/generated/sire-paperless/restic-ssh-privkey.age differ diff --git a/secrets/generated/ward-radicale/restic-encryption-password.age b/secrets/generated/ward-radicale/restic-encryption-password.age new file mode 100644 index 0000000..2e10b9d Binary files /dev/null and b/secrets/generated/ward-radicale/restic-encryption-password.age differ diff --git a/secrets/generated/ward-radicale/restic-ssh-privkey.age b/secrets/generated/ward-radicale/restic-ssh-privkey.age new file mode 100644 index 0000000..2b8522b Binary files /dev/null and b/secrets/generated/ward-radicale/restic-ssh-privkey.age differ diff --git a/secrets/global.nix.age b/secrets/global.nix.age index 5adc6c0..4cc52dd 100644 Binary files a/secrets/global.nix.age and b/secrets/global.nix.age differ