From a6e4beefb3264348b856ff4b5da0806d0bdd3230 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 10 Jan 2024 13:51:51 +0100 Subject: [PATCH] Integrate restic backup system --- roles/lander/files/lander.backup.sh | 12 ++++++++++ roles/lander/tasks/main.yml | 15 +++++++++++- roles/restic-rest/tasks/main.yml | 6 ++++- roles/restic/files/restic_backups_passwd | 7 ++++++ roles/restic/tasks/main.yml | 29 ++++++++++++++++++++++++ roles/restic/templates/backup-all.sh.j2 | 17 ++++++++++++++ 6 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 roles/lander/files/lander.backup.sh create mode 100644 roles/restic/files/restic_backups_passwd create mode 100644 roles/restic/templates/backup-all.sh.j2 diff --git a/roles/lander/files/lander.backup.sh b/roles/lander/files/lander.backup.sh new file mode 100644 index 0000000..9b9ecb1 --- /dev/null +++ b/roles/lander/files/lander.backup.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +data_dir='/mnt/data1/lander' +snapshot_dir="${data_dir}.snapshot" + +# Read-only snapshot for atomic backup +btrfs subvolume snapshot -r "$data_dir" "$snapshot_dir" || exit $? + +restic backup "$snapshot_dir" + +# Always remove snapshot subvolume, even if restic fails +btrfs subvolume delete "$snapshot_dir" diff --git a/roles/lander/tasks/main.yml b/roles/lander/tasks/main.yml index f64ed46..8efb4b9 100644 --- a/roles/lander/tasks/main.yml +++ b/roles/lander/tasks/main.yml @@ -23,7 +23,12 @@ system: true create_home: false -- name: Ensure data directory is present +# Only one BTRFS file system should be mounted, so this will match that one +- name: Ensure data subvolume is present + community.general.btrfs_subvolume: + name: '/lander' + +- name: Ensure data subvolume permissions are correct ansible.builtin.file: path: '/mnt/data1/lander' state: directory @@ -48,6 +53,14 @@ mode: '0644' notify: lander-restart +- name: Ensure backup script is present + ansible.builtin.copy: + src: 'lander.backup.sh' + dest: '/etc/backups/lander.backup.sh' + owner: 'root' + group: 'root' + mode: '0644' + - name: Ensure service file is present ansible.builtin.copy: src: 'lander.service' diff --git a/roles/restic-rest/tasks/main.yml b/roles/restic-rest/tasks/main.yml index 9f1ea7d..f326006 100644 --- a/roles/restic-rest/tasks/main.yml +++ b/roles/restic-rest/tasks/main.yml @@ -40,7 +40,11 @@ system: true create_home: false -- name: Ensure data directory is present +- name: Ensure data subvolume is present + community.general.btrfs_subvolume: + name: '/restic-rest' + +- name: Ensure data subvolume permissions are correct ansible.builtin.file: path: '/mnt/data1/restic-rest' state: directory diff --git a/roles/restic/files/restic_backups_passwd b/roles/restic/files/restic_backups_passwd new file mode 100644 index 0000000..005fb46 --- /dev/null +++ b/roles/restic/files/restic_backups_passwd @@ -0,0 +1,7 @@ +$ANSIBLE_VAULT;1.1;AES256 +33666438313237356564363136333933633035303531653464643766373434623834663736386463 +3464643731366237633334616536613864396162353264360a316130333032316437393333396466 +34356638393834316235633062646330336438376135346666663064303831666632353834663465 +6636663930356138640a323433613263393939303833616637336436366630386133386338613736 +34353433643539306238663638656539373731616238656635353561356632366332623532396465 +3936373534643966616131616161633234663430633233653435 diff --git a/roles/restic/tasks/main.yml b/roles/restic/tasks/main.yml index cf22626..6d2e722 100644 --- a/roles/restic/tasks/main.yml +++ b/roles/restic/tasks/main.yml @@ -25,3 +25,32 @@ group: 'root' mode: '0755' when: 'res.changed' + +- name: Ensure backup scripts directory is present + ansible.builtin.file: + path: '/etc/backups' + state: directory + mode: '0755' + +- name: Ensure Restic backups password file is present + ansible.builtin.copy: + src: 'restic_backups_passwd' + dest: '/etc/backups/restic_backups_passwd' + owner: root + group: root + mode: '0600' + +- name: Ensure backup-all script is present + ansible.builtin.template: + src: "backup-all.sh.j2" + dest: '/etc/backups/backup-all.sh' + owner: root + group: root + mode: '0644' + +- name: Ensure backup cronjob is enabled + ansible.builtin.cron: + name: 'Perform nightly backups' + minute: '0' + hour: '2' + job: 'bash /etc/backups/backup-all.sh' diff --git a/roles/restic/templates/backup-all.sh.j2 b/roles/restic/templates/backup-all.sh.j2 new file mode 100644 index 0000000..5200aaa --- /dev/null +++ b/roles/restic/templates/backup-all.sh.j2 @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# This script sequentially executes all shell scripts matching +# /etc/backups/*.backup.sh, with environment variables configured to publish +# backups to the local Restic REST server. + +# Get passed along to subcalls to bash +export RESTIC_REPOSITORY='rest:http://{{ groups['nas'][0] }}:8000/backups' +export RESTIC_PASSWORD_FILE='/etc/backups/restic_backups_passwd' + +for script in $(find /etc/backups -name '*.backup.sh'); do + bash "$script" +done + +# Prune older backups +restic forget --keep-last 7 && \ + restic prune