{ pkgs, config, lib, name, ... }: let cfg = config.backups; secrets = config.age.secrets; postgresql = config.services.postgresql.package; additionalPackages = [ pkgs.coreutils pkgs.sudo pkgs.sqlite ] ++ lib.optionals (builtins.length cfg.postgresqlDatabases > 0) [ postgresql ]; remotes = { memoragon = { host = "memoragon.infra.federez.net"; user = "borgmatic"; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdqX4I1JyvhC6dySHLnW1IioYk1ZqltFlbDCygozrWx"; path = "./${name}"; }; harpagon = { host = "harpagon.infra.federez.net"; user = "borgmatic"; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH1qDEAEJZ0qDRUq4yeHar5LKFTtsvHJIt2a54TBB/Lz"; path = "./${name}"; }; }; in { options.backups = lib.mkOption { type = lib.types.submodule { options = { enable = lib.mkEnableOption "Sauvegardes"; directories = lib.mkOption { type = lib.types.listOf lib.types.path; default = [ ]; description = '' Répertoires à sauvegarder. ''; }; # FIXME add user ? postgresqlDatabases = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = '' Nom des bases de données PostgreSQL à sauvegarder. ''; }; sqliteDatabases = lib.mkOption { type = lib.types.attrsOf lib.types.path; default = { }; description = '' Chemins des bases de données SQLite à sauvegarder. ''; }; }; }; description = '' Configuration des sauvegardes. ''; }; config = lib.mkIf cfg.enable { age.secrets.borgmatic-passphrase = { file = ../secrets/borgmatic-passphrase.age; }; systemd.services.borgmatic = { path = additionalPackages; serviceConfig = { LoadCredential = "pass:${secrets.borgmatic-passphrase.path}"; ExecStartPre = '' ${lib.getExe' pkgs.borgmatic "borgmatic"} init --encryption repokey ''; # TODO Remove once all hosts are usign NixOS 25.05+ NoNewPrivileges = false; CapabilityBoundingSet = "CAP_DAC_READ_SEARCH CAP_NET_RAW CAP_SETUID CAP_SETGID"; }; }; environment.systemPackages = let # KillMode=process is required to allow the background SSH session # to persist when FUSE mounting a remote repository binPath = lib.makeBinPath additionalPackages; borgmaticWithCreds = pkgs.writeScriptBin "borgmatic-with-creds" '' #!${pkgs.runtimeShell} systemd-run --quiet --wait --collect --pipe --pty \ --service-type=exec \ --uid=root --gid=root \ --property=KillMode=none \ --property=LoadCredential=pass:${secrets.borgmatic-passphrase.path} \ --property=Environment=${binPath} \ -- \ ${lib.getExe' pkgs.borgmatic "borgmatic"} "$@" ''; in [ borgmaticWithCreds ]; services.openssh.knownHosts = lib.mapAttrs (_: remote: { hostNames = [ remote.host ]; publicKey = remote.publicKey; }) remotes; services.borgmatic = let version = pkgs.borgmatic.version; hasCredSupport = builtins.compareVersions version "1.9.10" >= 0; encryption = if hasCredSupport then { encryption_passphrase = "{credential systemd pass}"; } else { encryption_passcommand = "cat \${CREDENTIALS_DIRECTORY}/pass"; }; pgCommand = exe: "${lib.getExe' pkgs.sudo "sudo"} -u postgres ${lib.getExe' postgresql exe}"; in { enable = true; # $CREDENTIALS_DIRECTORY does not exist when the config check is run enableConfigCheck = hasCredSupport; settings = { source_directories = cfg.directories; repositories = lib.mapAttrsToList (name: remote: { label = name; path = "ssh://${remote.user}@${remote.host}/${remote.path}"; }) remotes; # Required for databases hooks read_special = true; # FIXME pertinent de réutiliser celle-là ? ssh_command = "ssh -i /etc/ssh/ssh_host_ed25519_key"; keep_daily = 26; keep_weekly = 20; keep_monthly = 12; # add checks postgresql_databases = map (name: { inherit name; username = "postgres"; pg_dump_command = if name == "all" then pgCommand "pg_dumpall" else pgCommand "pg_dump"; pg_restore_command = pgCommand "pg_restore"; psql_command = pgCommand "psql"; }) cfg.postgresqlDatabases; sqlite_databases = lib.mapAttrsToList (name: path: { inherit name path; }) cfg.sqliteDatabases; } // encryption; }; }; }