From c161eeb056a6c91d226b5be7adef617625eb4f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Mon, 18 Mar 2024 05:05:54 +0100 Subject: [PATCH] backups: Switch to hetzner storage box --- configuration/services/backups.nix | 76 +++++++++++++++++++----------- configuration/sops.nix | 10 ++++ keys/production.yaml | 6 ++- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/configuration/services/backups.nix b/configuration/services/backups.nix index 3635a83..98aa473 100644 --- a/configuration/services/backups.nix +++ b/configuration/services/backups.nix @@ -24,6 +24,24 @@ inherit name text; runtimeInputs = packages; }); + + # *NOT* a TOML file, for some reason quotes are interpreted + # *literally + rcloneConfig = pkgs.writeText "rclone.conf" '' + [storagebox] + type = sftp + user = u395933 + host = u395933.your-storagebox.de + port = 23 + key_file = ${config.sops.secrets."restic/storagebox-ssh-key".path} + shell_type = unix + ''; + + resticEnv = { + RESTIC_PASSWORD_FILE = config.sops.secrets."restic/storagebox-backups".path; + RESTIC_REPOSITORY = "rclone:storagebox:backups"; + RCLONE_CONFIG = rcloneConfig; + }; in { options = { services.backups = lib.mkOption { @@ -120,13 +138,11 @@ in { # Doesn't hurt to finish the ongoing prune restartIfChanged = false; - environment = { - RESTIC_PASSWORD_FILE = config.sops.secrets."restic/local-backups".path; - RESTIC_REPOSITORY = "/var/lib/backups/"; - RESTIC_CACHE_DIR = "%C/restic-prune"; - }; + environment = resticEnv; path = with pkgs; [ + openssh + rclone restic ]; @@ -145,11 +161,6 @@ in { CacheDirectory = "restic-prune"; CacheDirectoryMode = "0700"; - ReadWritePaths = "/var/lib/backups/"; - - # Ensure we don't leave behind any files with the - # temporary UID of this service. - ExecStopPost = "+${pkgs.coreutils}/bin/chown -R root:backup /var/lib/backups/"; }; }; } @@ -158,17 +169,24 @@ in { # Don't want to restart mid-backup restartIfChanged = false; - environment = { - RESTIC_CACHE_DIR = "%C/backup-${name}"; - RESTIC_PASSWORD_FILE = config.sops.secrets."restic/local-backups".path; - # TODO(tlater): If I ever add more than one repo, service - # shutdown/restarting will potentially break if multiple - # backups for the same service overlap. A more clever - # sentinel file with reference counts would probably solve - # this. - RESTIC_REPOSITORY = "/var/lib/backups/"; - }; + environment = + resticEnv + // { + RESTIC_CACHE_DIR = "%C/backup-${name}"; + }; + path = with pkgs; [ + coreutils + openssh + rclone + restic + ]; + + # TODO(tlater): If I ever add more than one repo, service + # shutdown/restarting will potentially break if multiple + # backups for the same service overlap. A more clever + # sentinel file with reference counts would probably solve + # this. serviceConfig = { User = backup.user; Group = "backup"; @@ -183,8 +201,8 @@ in { ExecStartPre = map (service: "+${mkShutdownScript service}") backup.pauseServices - ++ singleton (writeScript "backup-${name}-repo-init" [pkgs.restic pkgs.coreutils] '' - restic snapshots || (restic init && chmod -R g+rwx "$RESTIC_REPOSITORY"/*) + ++ singleton (writeScript "backup-${name}-repo-init" [] '' + restic snapshots || restic init '') ++ optional (backup.preparation.text != null) (writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text); @@ -220,10 +238,14 @@ in { }) config.services.backups; - users.groups.backup = {}; - - systemd.tmpfiles.rules = [ - "d /var/lib/backups/ 0770 root backup" - ]; + users = { + # This user is only used to own the ssh key, because apparently + # the ssh client checks file permissions and is stuck in 1980. + users.backup = { + group = "backup"; + isSystemUser = true; + }; + groups.backup = {}; + }; }; } diff --git a/configuration/sops.nix b/configuration/sops.nix index c7cb1f0..dc9fcb5 100644 --- a/configuration/sops.nix +++ b/configuration/sops.nix @@ -36,6 +36,16 @@ group = "backup"; mode = "0440"; }; + "restic/storagebox-backups" = { + owner = "root"; + group = "backup"; + mode = "0440"; + }; + "restic/storagebox-ssh-key" = { + owner = "backup"; + group = "backup"; + mode = "0040"; + }; # Steam "steam/tlater" = {}; diff --git a/keys/production.yaml b/keys/production.yaml index 80172e2..aa25072 100644 --- a/keys/production.yaml +++ b/keys/production.yaml @@ -14,6 +14,8 @@ wireguard: server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str] restic: local-backups: ENC[AES256_GCM,data:NLNVlR9G9bLSZOkMoPvkbBbAZlKkmiUbdWHOFDnaefuy9wNLH53ctOIyS0rSsQLaJCSBTpgPSWIIXUSuzoK/eA==,iv:DzuujmyJJP4GiE5z7KOOGUEzUgOwmtf/7UYhwkyLe9g=,tag:cElFhpVC7S6HYlB6UyN7PQ==,type:str] + storagebox-backups: ENC[AES256_GCM,data:UyT8jCkKlfYJXjWLI9MbYfeVhY5d89N3aj1Olj54/aBOP3gwcrx6gU56Pwa1xKZ3lR13AVs/b4wF9sbvP7Kqqg==,iv:0HM+DgH4iCiWpjRvAYCFQGEy4xIBQwAM+PkkzOsizw0=,tag:jbrqo1In2O4jVM5e7fjOzg==,type:str] + storagebox-ssh-key: ENC[AES256_GCM,data:7aYlKX7I8Bsur3nm4nV9eSW3lmIxBCeCUMbPX3qgcotPbyPYaUqD3MOCnFRepajYkFXAgMX4jknqLfoO9xYc4bavDFjOY8Ww/KmLay7ces6tDnkK6tTRxcNRPUBqEzaFiPNsZc2UwHnmHOF0rKvQusvhCOplYao3xxz5McTHC7IEriUApSNudWCg3qGbyAmxkGEw7tRfh6IiUXEOFeaXDZd78dWZlWIIeospmA1hcVhkLGrjMmoikt/YANHUpWPbd+B9E6x+s2eIzFdvztRjarBluWPZuX981b+hcOm1/+HY/tJ/jzgyVbX1rjmdgZ9jZqdKO/vkOkijHWXlwpQ0QJ2s8p5MURPGRsC7W5jfmGbVKrubxfQC2mSJRJgBaj1wX3yI4GbfCXNdbpseAMy7t8OmN/iMN57lGnD3uX8CWWD327PIWp3SgwMDIZtJRlMBu3vMUrBdNnrrlYoLgf821tX7JWW6L5g1EK/bcBZqZZ/6rE+Q9fLiJHTsj2lyTzQZRLKsn0YePlcIMOWHO2CG/aWrfycdSKKjaKGG,iv:OVnEIMFB4h/EQ8zV3XOpVXLDrV5t4roNYDFQz99m4sQ=,tag:mKWF12uD1TLla/MoJs2zNg==,type:str] turn: env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str] secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str] @@ -26,8 +28,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-03-18T04:04:56Z" - mac: ENC[AES256_GCM,data:qIbgeaaFQXYacURO9EVfvtvvlUP0j7FMJuh9CIRbzQCyoSedibt1yhGIMQk2ERUliPb8OEuG4QPZ8rled/DmP1BHrUNTYFnRcagtPOnIE+0b9TuAVrj+vTWVl4MvQKMt9i/DQJsWAZVuaP8isDuZ77mVnlj1V8F+1MvXtL0+ZVM=,iv:0mKgiXjWrmNmuXLEsPYBMWSZvD9qrHDHEkSPAm9GCY4=,tag:ZM32r6kbsi4ERGFERzTRpA==,type:str] + lastmodified: "2024-03-18T04:05:08Z" + mac: ENC[AES256_GCM,data:/v/XRBizRUn8UR1HOIaKrY11/5I8RmsctmxXg0wcCXO983YgcGYTEqfXOSRJzkh/MOjB0c8jpdfE7tyqYgB2iYjm+HmRFVsGNB484e+3ukAMsxz4CTz26RWC/LEemQeBOa5RB3rrPXXo1LrJCBN9Y+T/PVgO2xshUSxyhRFQmMA=,iv:km1m8k/vrCVT8ugvqAZNORLu0NSW66B58btH5k4e//Y=,tag:RZXviobM03IQXN1FwDUlVA==,type:str] pgp: - created_at: "2024-03-18T04:02:00Z" enc: |-