diff --git a/modules/services/duplicity.nix b/modules/services/duplicity.nix index 9860fc6d..b0b93e4c 100644 --- a/modules/services/duplicity.nix +++ b/modules/services/duplicity.nix @@ -1,5 +1,5 @@ # docs: https://search.nixos.org/options?channel=21.11&query=duplicity -{ config, lib, ... }: +{ config, lib, pkgs, ... }: with lib; let @@ -18,8 +18,7 @@ in sane.impermanence.service-dirs = [ "/var/lib/duplicity" ]; services.duplicity.enable = true; - services.duplicity.targetUrl = ''"$DUPLICITY_URL"''; - services.duplicity.escapeUrl = false; + services.duplicity.targetUrl = "$DUPLICITY_URL"; # format: PASSPHRASE= \n DUPLICITY_URL=b2://... # two sisters # PASSPHRASE: remote backups will be encrypted using this passphrase (using gpg) @@ -32,29 +31,28 @@ in services.duplicity.secretFile = config.sops.secrets.duplicity_passphrase.path; # NB: manually trigger with `systemctl start duplicity` services.duplicity.frequency = "daily"; - # TODO: this needs updating to handle impermanence changes - services.duplicity.exclude = [ - # impermanent/inconsequential data: - "/dev" - "/proc" - "/run" - "/sys" - "/tmp" - # bind mounted (dupes): - "/var/lib" - # other mounts - "/mnt" - # data that's not worth the cost to backup: - "/nix/persist/var/lib/uninsane/media" - "/nix/persist/home/colin/tmp" - "/nix/persist/home/colin/Videos" - "/home/colin/tmp" - "/home/colin/Videos" - ]; services.duplicity.extraFlags = [ # without --allow-source-mismatch, duplicity will abort if you change the hostname between backups "--allow-source-mismatch" + + # includes/exclude ordering matters, so we explicitly control it here. + # the first match decides a file's treatment. so here: + # - /nix/persist/home/colin/tmp is excluded + # - *other* /nix/persist/ files are included by default + # - anything else under `/` are excluded by default + "--exclude" "/nix/persist/home/colin/dev/home-logic/coremem/out" # this can reach > 1 TB + "--exclude" "/nix/persist/home/colin/use/iso" # might want to re-enable... but not critical + "--exclude" "/nix/persist/home/colin/.local/share/sublime-music" # music cache. better to just keep the HQ sources + "--exclude" "/nix/persist/home/colin/.local/share/Steam" # can just re-download games + "--exclude" "/nix/persist/home/colin/.bitmonero/lmdb" # monero blockchain + "--exclude" "/nix/persist/home/colin/.rustup" + "--exclude" "/nix/persist/home/colin/ref" # publicly available data: no point in duplicating it + "--exclude" "/nix/persist/home/colin/tmp" + "--exclude" "/nix/persist/home/colin/Videos" + "--exclude" "/nix/persist/var/lib/duplicity" # don't back up our own backup state! + "--include" "/nix/persist" + "--exclude" "/" ]; # set this for the FIRST backup, then remove it to enable incremental backups @@ -70,5 +68,26 @@ in "/dev/mmc0 5M" ]; }; + + # based on with changes: + # - remove the cleanup step: API key doesn't have delete perms + # - don't escape the targetUrl: it comes from an env var set in the secret file + systemd.services.duplicity.script = let + cfg = config.services.duplicity; + target = cfg.targetUrl; + extra = escapeShellArgs ([ "--archive-dir" "/var/lib/duplicity" ] ++ cfg.extraFlags); + dup = "${pkgs.duplicity}/bin/duplicity"; + in lib.mkForce '' + set -x + # ${dup} cleanup ${target} --force ${extra} + # ${lib.optionalString (cfg.cleanup.maxAge != null) "${dup} remove-older-than ${lib.escapeShellArg cfg.cleanup.maxAge} ${target} --force ${extra}"} + # ${lib.optionalString (cfg.cleanup.maxFull != null) "${dup} remove-all-but-n-full ${builtins.toString cfg.cleanup.maxFull} ${target} --force ${extra}"} + # ${lib.optionalString (cfg.cleanup.maxIncr != null) "${dup} remove-all-inc-of-but-n-full ${toString cfg.cleanup.maxIncr} ${target} --force ${extra}"} + exec ${dup} ${if cfg.fullIfOlderThan == "always" then "full" else "incr"} ${lib.escapeShellArg cfg.root} ${target} ${lib.escapeShellArgs ([] + ++ concatMap (p: [ "--include" p ]) cfg.include + ++ concatMap (p: [ "--exclude" p ]) cfg.exclude + ++ (lib.optionals (cfg.fullIfOlderThan != "never" && cfg.fullIfOlderThan != "always") [ "--full-if-older-than" cfg.fullIfOlderThan ]) + )} ${extra} + ''; }; } diff --git a/nixpatches/list.nix b/nixpatches/list.nix index 163c093d..d0c64f20 100644 --- a/nixpatches/list.nix +++ b/nixpatches/list.nix @@ -35,9 +35,7 @@ fetchpatch: [ # (it's a dupe of https://github.com/NixOS/nixpkgs/pull/112677 ) ./02-rpi4-uboot.patch - # TODO: upstream - # maybe convert this patch to add a `targetUrlExpr` instead of doing the `escapeShellArgs` hack - ./07-duplicity-rich-url.patch + # ./07-duplicity-rich-url.patch # enable aarch64 support for flutter's dart package # ./10-flutter-arm64.patch diff --git a/secrets/desko.yaml b/secrets/desko.yaml index 89cabd6a..5d143916 100644 --- a/secrets/desko.yaml +++ b/secrets/desko.yaml @@ -1,4 +1,4 @@ -duplicity_passphrase: ENC[AES256_GCM,data:rzUfcxe5YPloOrqgVwdCjsccexWc5RvmFf1i3Xs459iVTfWHlVJeT/IqReY6ZqdAkPJteTtrUZzak2GXyRUkE13+W0kE8isnDjPX/YDQwoK2sa+dwc4xGTekboc0gf6HH3vQpF1aiJDBfb3GtGyDVLH9MVIRPJGXSztZBduUDezA2wAx2wI=,iv:EHJg8kE/07v+ySSFDtW4FA4y1y/+fcGxfNCWoainwBI=,tag:S3ecM4DbDl8jqXLRKipZmQ==,type:str] +duplicity_passphrase: ENC[AES256_GCM,data:+UXXMiMNR3r3xvIzQVctDnFpVElx9xYOQBQsWHSZKlCDZs/Jlte48IPp3bc1u+bx1U9y5Frm5QiZYo/gAksRCjFcOTE6pc/bIREyAqB59psp5Ijhg59ToVBl3cm0II55rIDqDcBbHV2UUIvbbKn4/FBnY9y8uW8X383cHvpDPqxiPOTa,iv:eDkE+NmM2kKG4wr9sLM5IXlmlkNUaHNyE3r9rY/uayI=,tag:n9QmRFvmKv8H3gi8OAQdcw==,type:str] #ENC[AES256_GCM,data:yU9cr6MXjS4m69BeIUjUw477wt4c1djYof3Qlfr4Dytv8hWqCuqThDwQTMY5jfHdv5ipS0aEjf7GWu2M2t9W88fYdxnTN2m8IfYZp76YcjxO4fup5BXiLGIjnm+qI0g=,iv:nPo8FyGiyLRQozE4kZ6Rei6CObvbVynOs3jdMvdkpZw=,tag:+4esxPiewSsjwao6ZhAMxA==,type:comment] nix_serve_privkey: ENC[AES256_GCM,data:/Ph9J00cV7PcfpJw/NWcBpkQR+a0SQyHv1jmF4CkH+Uj8l+cRcXWynAc2APenMSfHdighXMqjsXuwRbGo0S57YuMXQjFbI8jhbXEhhAWlmET1q7uRaaZRSgq34qABw==,iv:LLYgLauPsD+3mx1GTjEUkiXgdWsnqixCJl4UfSdS5Ac=,tag:S7V6GKezS/JsbZVfq9DjjA==,type:str] colin-passwd: ENC[AES256_GCM,data:/b+l5zTlOhdoiFaMVG5HB98AOGfGZtwkH+IS/mhDgHNZ4J+t3OiEBAFPl/KPctg6ZM55QiAjNnnJ8zAsKL85om6amvrWF/Qz17qC9+pZF+6Ef8xvTQr3VPlFEYq4rGb74jQ7uyvtCjn0Ow==,iv:Z0qUimlPQMu6rsjn5b/Xfw99NzbXGS8B/hNWE+f+GoM=,tag:uGB1DZzHiLCkOtlAA58mmg==,type:str] @@ -35,8 +35,8 @@ sops: Si9kT0ZMUnJJWlhUZ3FFakZFaDlPdEEKXtWfh6wdGPin1h/UUs21cdspddpW1YDq rCKS2DI2KWdgciih9FnmWGAwGUhB3uhimUr6hgho4z+dZfLrpoP1PA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2022-10-24T08:49:49Z" - mac: ENC[AES256_GCM,data:dvxYlU/btzzH9Qor8z02kdv3S4gFUGHnEjV/XBM99+IFuAD6vuE8zFL4peGW1GiXqM2QQY0Qc9wZ+nC5/ak9ROMC8uZPXF417gs6U9yyT92FRlMSdC0AMsUhNGWjJlM733hI4YATnR+1XuwHewzzW1R3TvrouBZqSv+2rBsiZCw=,iv:A+D7IG4U+EQ6nP4xKOK1ExeZLeERpiSPzj/g87R1SdM=,tag:jSVGDO9kNxXdDSSixDrkDQ==,type:str] + lastmodified: "2022-11-16T03:02:28Z" + mac: ENC[AES256_GCM,data:EFeon4GvDEFVTJh9IR0dd8S/vVeWlMuEe9rUcL6FDYLsfm5qFb5rhsCDY/rQNanNsTcsDLK3oOXoBXP168fzwHotdjoNNyiCYAFDigVqKPt4dk9vnzH91ccyu6NUhlFlKzuDHwXkWbNJA7pNyMD3w4NKt7HbLu+r1YxOAaytWzM=,iv:xrllCUns1WY/gCuHKmZtUr5/piE4OBKHrmiewBbVBH4=,tag:8JWLLZqLFMWcDNgWwJL+Ig==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3 diff --git a/secrets/servo.yaml b/secrets/servo.yaml index 09e0f2e6..30534b93 100644 --- a/secrets/servo.yaml +++ b/secrets/servo.yaml @@ -1,4 +1,4 @@ -duplicity_passphrase: ENC[AES256_GCM,data:WAQE+xhfRg+4N9Q1P9U8Lt7sVwpcEZFPJzyHIA+FIcCcZZhv+QmvCT/eTRtAOIFvII5l9f0A4GRnSEagalyaZgTgq7t8qOhvvB+s8cIj7prM1psnKstpx3+BxsinGOsZcPqbBxph9gdGuIVP3qH7pYAT+6GMPLnxW21s0r26mZFZM8Mu15VGyuvTz2Pknw==,iv:hu+6w6TWQensA4y5wBz1vPgw8YlBk5TuxEm2rRjV6Ao=,tag:UJ2joJZNxr/+O5y0dx6q9g==,type:str] +duplicity_passphrase: ENC[AES256_GCM,data:LgPORB0HhIAfpJdQrwjS+/TWdOeddQ2YNYqfRbWhhuNlImuOlniPzrPaaFv+Mfght7OHs7rnuVr3tOHfeIEBo9S2z05ABOulttHEyeuyJZPE1/0t8IBz2gcNNWs4nhCYbVX3y/rSAG8bhz1Vdb2B/MiCicfJEZAqpXkRilQELXTR5cF5NnmEcR7zOso=,iv:NvwZhBbkYnTDt3izwwQPj4U4XAmiOD5Dv3sF50JA97o=,tag:HSJ5xr/WXn6MQdyV8QYWYw==,type:str] ddns_he: ENC[AES256_GCM,data:zAKbEAIMIsENUctG9bNAAjAty6g+w3QW5VM=,iv:ncIjblXnTiU3TQcHJutz9lCl0wBdWs+FybY0sZcnaH0=,tag:7O6EIob2/if1fcVDVEkVzQ==,type:str] #ENC[AES256_GCM,data:LMfqz2Rih6CR7RcCbA==,iv:MQ7z93Mhus2Z2q7HZMk4BzkkY/apBIR+9hIiZlknolc=,tag:HU5McecdYk12I3AcvVHEBw==,type:comment] #ENC[AES256_GCM,data:zhL2iNWZ8xPbBneffWcc93ZCW/SDv5FH,iv:P3a8+oucJRM8o7hnHUxAvefHdZEAbKJKhK2Y1+r75GA=,tag:VFvFucE5c780RmspW7p8Qg==,type:comment] @@ -56,8 +56,8 @@ sops: cWplOHBNWjlJdGI3ZWtJc0t4Mk9URG8KE+9IPGYZsIs2PaDJ2AUE4gB4QEj5zo6P aZVbubu6Tbg+tD/98RkfWAkNvoVeDYuLNPDNgqOL0UgCQiTrPPaTjw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2022-10-14T00:37:52Z" - mac: ENC[AES256_GCM,data:qKr1aKWxuJWwjUYX+JWAdwHFAwApHm9hOYBgZxAIXbXHhOo04K1MFBDTsAvtvN1a11QtCJYDNuVNpuRu3bf/5Ji5ROTaKfQCgPk+ZScJuWpLsxchYV+TnlREwQI+qgvogyMKMlPInozgd7RNnsePdg7DtYFfGMAvUtX9OidxAXI=,iv:EAkNQkIqoXtRy+uSb7ccl9T5b6hiyRll/m76nhir9AI=,tag:kCDEBJDW34VgLQPd4V+uYA==,type:str] + lastmodified: "2022-11-16T03:02:50Z" + mac: ENC[AES256_GCM,data:0/lDp6jWueeF4TPfB5rSoEzEF8QVw815DYEHRRea+SrYXGHJT80eK5sqUz00m6adG8aaSlNMAD8d2/nClar6VJKKOHdY+oN8hLztxNGQraMo107d8XOMMycj9vA4IGkCbnlng0cZTB2jsPV6Mfkmf5v+PN4N9F0bQ9W50V1Pf40=,iv:slFJjjlyCyk8aAwfRbiZ/2SKLmUGZE4OvpfrrvSJw3g=,tag:X+057fZLJnDW7CAka8+pCw==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3