diff --git a/.sops.yaml b/.sops.yaml index fc9da64..bc78ba5 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -59,3 +59,9 @@ creation_rules: - *emily age: - *florp + - path_regex: secrets/restic/zh3485s1.yaml + key_groups: + - pgp: + - *emily + age: + - *florp diff --git a/config/hosts/florp/configuration.nix b/config/hosts/florp/configuration.nix index 4a7e62f..365cfd5 100644 --- a/config/hosts/florp/configuration.nix +++ b/config/hosts/florp/configuration.nix @@ -12,6 +12,15 @@ domain = lib.mkForce "social"; }; kyouma.nginx.defaultForbidden = "florp.social"; + + kyouma.restic = { + enable = true; + remoteUser = "zh3485s1"; + paths = [ + "/var/lib/akkoma" + "/var/lib/postgresql" + ]; + }; systemd.network.networks."98-eth-default" = { address = [ "2a0f:be01:0:100::171/128" @@ -19,7 +28,7 @@ }; services.postgresql.settings = { - max_connections = 30; + max_connections = 128; shared_buffers = "4GB"; effective_cache_size = "12GB"; maintenance_work_mem = "1GB"; diff --git a/modules/restic/default.nix b/modules/restic/default.nix new file mode 100644 index 0000000..0160ef2 --- /dev/null +++ b/modules/restic/default.nix @@ -0,0 +1,65 @@ +{ config, lib, pkgs, ... }: let + cfg = config.kyouma.restic; +in { + options.kyouma.restic = let + inherit (lib) mkOption types; + in { + enable = lib.mkEnableOption "Enable restic backup"; + paths = mkOption { + description = "paths to backup"; + type = with types; listOf path; + default = []; + }; + pruneOpts = mkOption { + description = "paths to backup"; + type = with types; listOf str; + default = [ + "--keep-hourly 24" + "--keep-daily 14" + "--keep-weekly 8" + "--keep-monthly 12" + ]; + }; + remote = mkOption { + description = "restic remote to use"; + type = types.nonEmptyStr; + default = "zh3485.rsync.net"; + }; + remoteUser = mkOption { + description = "remote ssh user"; + type = types.nonEmptyStr; + default = ""; + }; + user = mkOption { + description = "user who runs the backup job"; + type = types.nonEmptyStr; + default = "root"; + }; + repo = mkOption { + description = "restic repo"; + type = types.nonEmptyStr; + default = "${config.networking.hostName}-backup"; + }; + }; + config = lib.mkIf cfg.enable { + sops.secrets."restic/${cfg.remoteUser}/password" = { + sopsFile = ../../secrets/restic/${cfg.remoteUser}.yaml; + }; + sops.secrets."restic/${cfg.remoteUser}/id_ed25519" = { + sopsFile = ../../secrets/restic/${cfg.remoteUser}.yaml; + }; + + services.restic.backups."${config.networking.hostName}-${cfg.remote}" = { + inherit (cfg) paths user pruneOpts; + initialize = true; + repository = "sftp:${cfg.remoteUser}@${cfg.remote}:${cfg.repo}"; + passwordFile = config.sops.secrets."restic/${cfg.remoteUser}".path; + extraOptions = let + knownHost = pkgs.writeText "${cfg.remote}-known-host" (builtins.readFile ./${cfg.remote}/ssh_host_ed25519_key.pub); + sshKey = config.sops.secrets."restic/${cfg.remoteUser}/id_ed25519".path; + in [ + "sftp.command='ssh ${cfg.remoteUser}@${cfg.remote} -i ${sshKey} -o UserKnownHostsFile=${knownHost} -s sftp'" + ]; + }; + }; +} diff --git a/modules/restic/zh3485.rsync.net/ssh_host_ed25519_key.pub b/modules/restic/zh3485.rsync.net/ssh_host_ed25519_key.pub new file mode 100644 index 0000000..a4fe26e --- /dev/null +++ b/modules/restic/zh3485.rsync.net/ssh_host_ed25519_key.pub @@ -0,0 +1 @@ +zh3485.rsync.net ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJtclizeBy1Uo3D86HpgD3LONGVH0CJ0NT+YfZlldAJd diff --git a/secrets/restic/zh3485s1.yaml b/secrets/restic/zh3485s1.yaml new file mode 100644 index 0000000..7ec39ce --- /dev/null +++ b/secrets/restic/zh3485s1.yaml @@ -0,0 +1,35 @@ +restic: + zh3485s1: + password: ENC[AES256_GCM,data:lDDSSqUH3pewpMA+6SNwGwRz95MBjeaD6I3RWUQNBFXsw/W9RoIY85AcRXxCl7CW,iv:NFF6uCs2FolMe9cgPkoAFmbWdXG2SuVRtoOyQXouEAU=,tag:UeC49xFwFkMh0Wi8p9reFw==,type:str] + id_ed25519: ENC[AES256_GCM,data:fe2CAKWSrEOvEPZgGhbigw+DEnDUGtTXEj7nuGaH5enMGDvd7QtRlDYLkM+g9zKrRJ46e2nM6btUZVqqx4rJiUbjJ5B/cBzb259CTxKGgHeMj/cYXPypamIEKFwUrloxzrgxH5JIOoUvj9Ny1P1UPIB//B5Z1Uunqmdqd2XoiAtDwZ/hvyuOfdFyUkKmOnCdF4pheMRXZ6Z51N4f09OIwuZ/xC4LQYAVB2lyycOgrvefPA5YYabMd23yEXn6v/BiP1TWbSInTHvz6Rii2WYqYO3ORCfi1pvWe15kSrTsT9zYRzLvZi5TD+4FMhLmIttZB2OXK7X+h6cHtej7X+v6HKhMKHNJhukRUP7DpcZ0+ArBEdw2j+H7C4q8NdR9rk4we7WpdQlY7tGpJvEzSis+P/Jph/tkzx6xXpKiOeOAXgQUS41qcAy7gmZj7CdsulerUHcfDy4a0y1OEuDmoYW420cGGYOCB9BlvYQbaDhyv5AMSL8sMZfm7mX5qXliE7ZQc20ggKoY2MKH6RjnT8pQhMBWo/NW13PPBDIL,iv:1+aopW183ir5XHMKcDons24A/E61mLuyJGrQTRpPXdE=,tag:s1w+HZdktM0H9FUrz097Cw==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age18vc8rcmczlt3r0ee7jr9s8l3yrkthu8wtypt08eh0eskpkw3dg6qxs7t3t + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQeWpKYktzVE1tMkpGU1c5 + akZZdlBkeFQyUmUrOHZxTGE2V1FUVmV3cG5VCmZvTG1JTS9SUTk1aVl6TnBPQ1Fh + clRDTmQzQUJxWlYyV2dmVXNyTDJ2K0kKLS0tIHA3S0dsQzRxRWF4RFdSSzh1aXI5 + ZFQvWFhZTndubkxaRVh3YXl0V25ZcUEK0/wV9i01kRkphrseSBqAL9f8tUlUtJDO + PUZL2Em/QjNEnXJaxxR612ONA94ptK9bsqzRJV5RtGqDwd+oAnr13Q== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-11-08T13:41:02Z" + mac: ENC[AES256_GCM,data:tMatUcv/jbvQ1URp6DrUyuiB9+rgCCdOxEVcM0NBiV5P9DGWE1hWytky4yPE9nFUOWLI7m4nTSEXHuT4yT3LkBd1Ndzhm5wQ0NEAVnZ6Sj7YOQI5CS1q95sviJBv57PBkaajHDNeSJX2hEQeR4qJFUR4fu0hIwadyzeunP/kfKE=,iv:gXRAg4cN43ocQMZm0lL8AnrbDtK+TKGchWpd/TYhnjA=,tag:+HqYuDWjoTdv+CWrJmuwxA==,type:str] + pgp: + - created_at: "2024-11-08T13:31:55Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hF4D1GtNSlou/HkSAQdALVqRZ2qzjR86mEE/MHAR5H3gmIukchY/NSvGg1Ggfmsw + uZhnl5puGOO579ItHXbk+BYwBS2koL7jyhnX8E9zmM3d3SZHwzx0mk79fr2jLFj6 + 0l4BLrhhcpUtzfje4/SeTgWFRIA68ON/PUTmW2Lgclh9OpQfbbousFS/JMvvdHaT + /3uJEww5MKMPlqWqK7w7z6iwIITRKH0vzQoIZ3hVcDKtKOJrJ/1bWcJorFsazxvT + =KZPf + -----END PGP MESSAGE----- + fp: B04F01A7A98A13020C39B4A68AB7B773A214ACE5 + unencrypted_suffix: _unencrypted + version: 3.9.1