Commit ccd0705
Changed files (14)
.secrets
cache
cyrene
modules
core
services
.secrets/cache/cyrene/90368eb6941a8944e0fc24966be3ddb20d484915de5a62ab2a94f00d5a169045
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 7JvPpg vtJRmGRwgyG9QjPgkPBWspvPGe+mymm8Rdq8W64qCDg
+XXg9k8KwZqO3r3gSAuuMCvDELuXwWTAk13O8TKP3FfA
+-> z?/g3/}-grease eKsO)d]
+eZa83HBk3yiQ1rvunAHVzw0SrVVo1G26z3zUoELIjTmy6n74c8lhpx5RbFA4uFqn
+CA
+--- P1mwhVKWk94xM3SfIE/NctqmIsv6AMJ8//tbs/XOLpY
+�
+�
�v�_�-%l��;�8=ņ�g]���l��������!��%�����gN�_K�N3a����
\ No newline at end of file
.secrets/cache/cyrene/e0e0e5e5363924900f12a6c553c8b891f51b8f9be9f0f4a384a4a3fa687c8bad
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 7JvPpg be38WCi3U6iK0CKufOr7LA+VPOaf1T7IbEswOFv4Owg
+bxC3a0Mfkbg44uDT5ihf6cK9Mgfhkk1AclOqbHtDN9c
+-> sG_f]_b-grease h,IlbYe# "E ALJ.',B
+HZzcdenfgVknnsnLbDnjEfAs/4BAU8INEe+BmxC6vDiF9aTzTA
+--- 4fSfOZ9rNAr4dSuUfqJEf2ilLdFewqQNALVKEgKFXzw
+L�n������L�z�6���0e5���dE����X�����O�z��K^,��1ĠbȮ���v���J�W �f� Z%���&�/���C�ȪeG����
\ No newline at end of file
.secrets/cache/cyrene/fe55ab39098c279df452ce8ba0de2943572a59e97f577bf48976821ba60a8cf9
Binary file
.secrets/cache/mobius/2acb558122acbeff93466f6f6c4d55e8ee77d5a5f30856e38a803e4690bd02ea
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 qzANJQ 2uWq50dxyjZtUC/XyOGX3d9DzSy7G1M+MuI8aLitOE0
+YhsyeNfk34kUL/ADnVS651KZMLlXEFoAnlETFOJ+HIE
+-> _~Mg$oL-grease
+WPHRQHpqqoYqq+7sKA1luLH5T1QTs0hL15ClC16gVhixrCsdSx3YUc9A9m0FCzjV
+KBYEUOJJiijms+q1otvMtKSFExJjcJ4
+--- j58vfyrC2ig5spGn2bkdFnvRjA0k5xs/FwtQxF/pKdA
+�H�햠eG��[Gdّ��s�A�E������v�9�q����>�ܾ�C�_��<qk�?9�ƈ
\ No newline at end of file
.secrets/cache/mobius/70f424563d1267ee11ee7df7c226d5ffc0d055a0bb41234da301c79a3ca62307
Binary file
.secrets/cache/mobius/c109f6ea9904d52dc5e9260e099b761b6c4e088bba4f6a0b4057b26d5a13eadd
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 qzANJQ 7u7ES7FTd0L9y+oGw5cArYrKYQVi6YkW0dy/tUzAXlc
+uq7IxydM2INTnFnr09521WiTVdmMXEbAhOesOBuLaGE
+-> Y[5n-grease V=r9]#, ?E5 IB
+NQjiZCxrD9qqaA7L8WU35uZShu0
+--- BVrANARdteLiJOj1YjG6oo/7iMRnZgxZPAnnUB/7SnQ
+��N1�L��wr�������z�`u/Y)���t*_�35�E9���;�r~�;܍����b�_11
�i�����[����m9��kS���1��Q�
\ No newline at end of file
modules/core/backups/default.nix
@@ -0,0 +1,74 @@
+{den, ...}: {
+ den.quirks.backup.description = ''
+ Restic backup entries collected from aspects (host)
+
+ Each aspect contributes a name -> services.restic.backups options map:
+ backup = {
+ foo = {
+ paths = ["/var/lib/foo"];
+ backupPrepareCommand = "…";
+ };
+ };
+
+ Names must be unique across aspects.
+ '';
+
+ den.aspects.core.includes = [den.aspects.core.backups];
+ den.aspects.core.backups = {
+ nixos = {
+ host,
+ backup,
+ lib,
+ config,
+ ...
+ }: let
+ mergeBackups = backups:
+ lib.foldl' (
+ acc: entry: let
+ duplicates = lib.intersectAttrs entry acc;
+ in
+ if duplicates != {}
+ then
+ throw ("core.backups: duplicate backup names collected from aspects:"
+ + "${lib.concatStringsSep ", " (lib.attrNames duplicates)}")
+ else acc // entry
+ ) {}
+ backups;
+
+ allBackups = mergeBackups backup;
+ in
+ lib.mkIf (allBackups != {}) {
+ services.restic.backups = lib.mapAttrs (name: value:
+ {
+ user = "root";
+ repository = "s3:https://584bf08b6f2fa0a8918cae5af0ee45bc.r2.cloudflarestorage.com/backups/${host.name}/${name}";
+ initialize = true;
+ environmentFile = config.vaultix.templates.restic-backup-env.path;
+ passwordFile = config.vaultix.secrets.restic-backup-password.path;
+ timerConfig = {
+ OnCalendar = "04:00";
+ RandomizedDelaySec = "1h";
+ Persistent = true;
+ };
+ pruneOpts = [
+ "--keep-daily 7"
+ "--keep-weekly 4"
+ "--keep-monthly 3"
+ ];
+ }
+ // value)
+ allBackups;
+
+ vaultix.secrets = {
+ restic-backup-r2-id.file = ./r2-id.age;
+ restic-backup-r2-key.file = ./r2-key.age;
+ restic-backup-password.file = ./password.age;
+ };
+
+ vaultix.templates.restic-backup-env.content = ''
+ AWS_ACCESS_KEY_ID=${config.vaultix.placeholder.restic-backup-r2-id}
+ AWS_SECRET_ACCESS_KEY=${config.vaultix.placeholder.restic-backup-r2-key}
+ '';
+ };
+ };
+}
modules/core/backups/password.age
Binary file
modules/core/backups/r2-id.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> piv-p256 xCEwtQ A7PYfRdi+SPgO7EMEeMJ154LYhWZKDZ+0eB9iR+YjiM0
+xaT2Op2qG8ibtNmIzJ29US42qI8yQi2V0JaVOZlq0XE
+-> "P{^>-grease `= .c
+kkkA1fbaScW0SNW7yNdLzCmreuypkEHxG/pYDmWxbZ0K6ZZgABAWnuG/U+PcbSFF
+whbXUgnOvtHjrcc
+--- O6yuX0BAVkg2z0l6PS6OXOevgOqwPxFpu9lBxiLOrG8
+�iH���In`ߋ�E{*�]A��Pvf�=:����H���A$=C<3����2W!U�"��(��
\ No newline at end of file
modules/core/backups/r2-key.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> piv-p256 xCEwtQ AhHLKGZshKfDALRyrRV04TE+yQSZB1rOyqXoE0e4izwL
+k42kRO4QR1kJjyTHbcGhvn3g2fToUV7VYgKvcehBwL8
+-> h.r.QMtj-grease 3{m -KtBe -]Xr`yV (Q9+/P*f
+p7bvAoEcW/lhcvDax8uVQ+esucOktPskLheaopOFoKLY0B/ArnlmYe1EtXk
+--- 5aK3NPrIR0OFamf5mE6v0meDH4P5hK8QeC5mjyJhj4o
+I�v�8p���C�Mڈ�ih�N
��9��^*�)�b*+�f��p�is�������ֵ�T��3�{�.r��X�ew1B1�K�������)r�A�2k`G\
\ No newline at end of file
modules/services/forgejo/server.nix
@@ -33,6 +33,45 @@
];
};
+ backup = {
+ pkgs,
+ lib,
+ config,
+ ...
+ }: {
+ forgejo = let
+ backupDir = "/tmp/backups/forgejo";
+ in {
+ paths = [backupDir];
+
+ backupPrepareCommand = lib.getExe (pkgs.writeShellApplication {
+ name = "backup-vaultwarden-prepare";
+ runtimeInputs = [
+ config.services.forgejo.package
+ pkgs.sudo
+ ];
+ text = ''
+ set -euo pipefail
+
+ DATA_DIR="${config.services.forgejo.stateDir}"
+ FORGEJO_USER="${config.services.forgejo.user}"
+
+ mkdir -p "${backupDir}"
+ chown "$FORGEJO_USER" "${backupDir}"
+
+ sudo -u "$FORGEJO_USER" forgejo dump \
+ --work-path "$DATA_DIR" \
+ --type "zip" \
+ --file "${backupDir}/forgejo-dump.zip" \
+ --verbose
+ '';
+ });
+ backupCleanupCommand = ''
+ rm -r "${backupDir}"
+ '';
+ };
+ };
+
nixos = {
host,
pkgs,
modules/services/grafana/default.nix
@@ -42,6 +42,42 @@
};
};
+ backup = {
+ pkgs,
+ lib,
+ ...
+ }: {
+ grafana = let
+ backupDir = "/tmp/backups/grafana";
+ in {
+ paths = [backupDir];
+
+ # See https://grafana.com/docs/grafana/latest/administration/back-up-grafana/
+ backupPrepareCommand = lib.getExe (pkgs.writeShellApplication {
+ name = "backup-grafana-prepare";
+ runtimeInputs = [
+ pkgs.sqlite
+ ];
+ text = ''
+ set -euo pipefail
+
+ DATA_DIR="/var/lib/grafana/data"
+
+ mkdir -p "${backupDir}"
+
+ sqlite3 "$DATA_DIR/grafana.db" ".backup '${backupDir}/grafana.db'"
+
+ if [ -d "$DATA_DIR/plugins" ]; then
+ cp -a "$DATA_DIR/plugins" "${backupDir}/"
+ fi
+ '';
+ });
+ backupCleanupCommand = ''
+ rm -r "${backupDir}"
+ '';
+ };
+ };
+
nixos = {
host,
config,
modules/services/vaultwarden.nix
@@ -33,6 +33,42 @@
};
};
+ backup = {
+ pkgs,
+ lib,
+ ...
+ }: {
+ vaultwarden = let
+ backupDir = "/tmp/backups/vaultwarden";
+ in {
+ paths = [backupDir];
+
+ # See https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault
+ backupPrepareCommand = lib.getExe (pkgs.writeShellApplication {
+ name = "backup-vaultwarden-prepare";
+ runtimeInputs = [
+ pkgs.sqlite
+ ];
+ text = ''
+ set -euo pipefail
+
+ DATA_DIR="/var/lib/vaultwarden"
+
+ mkdir -p "${backupDir}"
+
+ sqlite3 "$DATA_DIR/db.sqlite3" ".backup '${backupDir}/db.sqlite3'"
+
+ if [ -d "$DATA_DIR/attachments" ]; then
+ cp -a "$DATA_DIR/attachments" "${backupDir}/"
+ fi
+ '';
+ });
+ backupCleanupCommand = ''
+ rm -r "${backupDir}"
+ '';
+ };
+ };
+
nixos = {host, ...}: let
cfg = host.settings.services.vaultwarden;
in {
modules/services/wakapi.nix
@@ -42,6 +42,12 @@
};
};
+ backup = {
+ wakapi = {
+ paths = ["/var/lib/private/wakapi"];
+ };
+ };
+
nixos = {
host,
config,