From d045a5d76adbc273b82e46236156a613a999890c Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 10 Feb 2026 12:47:02 -0800 Subject: [PATCH] Add BorgBase offsite backup repository (#142) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Adds BorgBase as a second borgmatic repository for offsite backups (US region, append-only) - SSH key managed via 1Password, deployed to indri by Ansible - Borgmatic `ssh_command` configured to use the dedicated BorgBase key - BorgBase host key pinned in known_hosts via Ansible ## Post-merge deployment steps 1. Provision borgmatic: `mise run provision-indri -- --tags borgmatic` 2. Initialize the BorgBase repo: `ssh indri 'mise x -- borgmatic init --encryption repokey --repository borgbase-offsite'` 3. Export and store the borg repokey: `ssh indri 'borg key export ssh://k04ljcd7@k04ljcd7.repo.borgbase.com/./repo'` → save to 1Password 4. Verify first backup: `ssh indri 'mise x -- borgmatic create --repository borgbase-offsite --verbosity 1'` ## BorgBase setup (already done) - Account created, API token in 1Password (`borgbase` item in blumeops vault) - SSH keypair generated, stored in 1Password, public key uploaded to BorgBase (ID: 200815) - Repository `indri-borgmatic` created (ID: k04ljcd7, US region, append-only, 2-day alert) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/142 --- ansible/playbooks/indri.yml | 16 ++++++++++++++++ ansible/roles/borgmatic/defaults/main.yml | 9 ++++++++- ansible/roles/borgmatic/tasks/main.yml | 14 ++++++++++++++ .../roles/borgmatic/templates/borgmatic.plist.j2 | 2 +- ansible/roles/borgmatic/templates/config.yaml.j2 | 2 ++ .../feature-borgbase-offsite-backup.feature.md | 1 + 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 docs/changelog.d/feature-borgbase-offsite-backup.feature.md diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index bde198e..3042366 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -22,6 +22,22 @@ no_log: true tags: [borgmatic] + - name: Fetch BorgBase SSH private key + ansible.builtin.command: + cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/noiobufntsxyzageu7mvlp2nbe/ssh-private-key" + delegate_to: localhost + register: _borgbase_ssh_key + changed_when: false + no_log: true + check_mode: false + tags: [borgmatic] + + - name: Set BorgBase SSH key fact + ansible.builtin.set_fact: + borgbase_ssh_private_key: "{{ _borgbase_ssh_key.stdout }}" + no_log: true + tags: [borgmatic] + # Forgejo secrets - name: Fetch forgejo LFS JWT secret ansible.builtin.command: diff --git a/ansible/roles/borgmatic/defaults/main.yml b/ansible/roles/borgmatic/defaults/main.yml index 147a621..293712c 100644 --- a/ansible/roles/borgmatic/defaults/main.yml +++ b/ansible/roles/borgmatic/defaults/main.yml @@ -17,12 +17,19 @@ borgmatic_source_directories: - /Users/erichblume/.config/borgmatic - /Users/erichblume/Documents -# Backup repository +# Backup repositories borgmatic_repositories: - path: /Volumes/backups/borg/ label: sifaka-borg-backups encryption: repokey append_only: true + - path: ssh://k04ljcd7@k04ljcd7.repo.borgbase.com/./repo + label: borgbase-offsite + encryption: repokey + append_only: true + +# BorgBase SSH key (fetched from 1Password in playbook pre_tasks) +borgmatic_borgbase_ssh_key_path: /Users/erichblume/.ssh/borgbase_ed25519 # Exclude patterns borgmatic_exclude_patterns: [] diff --git a/ansible/roles/borgmatic/tasks/main.yml b/ansible/roles/borgmatic/tasks/main.yml index 99eda45..9599622 100644 --- a/ansible/roles/borgmatic/tasks/main.yml +++ b/ansible/roles/borgmatic/tasks/main.yml @@ -19,6 +19,20 @@ mode: '0600' no_log: true +# BorgBase offsite backup - SSH key and host verification +- name: Deploy BorgBase SSH private key + ansible.builtin.copy: + content: "{{ borgbase_ssh_private_key }}\n" + dest: "{{ borgmatic_borgbase_ssh_key_path }}" + mode: '0600' + no_log: true + +- name: Add BorgBase host key to known_hosts + ansible.builtin.known_hosts: + name: k04ljcd7.repo.borgbase.com + key: "k04ljcd7.repo.borgbase.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGU0mISTyHBw9tBs6SuhSq8tvNM8m9eifQxM+88TowPO" + state: present + - name: Deploy borgmatic configuration ansible.builtin.template: src: config.yaml.j2 diff --git a/ansible/roles/borgmatic/templates/borgmatic.plist.j2 b/ansible/roles/borgmatic/templates/borgmatic.plist.j2 index 75fb0c6..c7da8e8 100644 --- a/ansible/roles/borgmatic/templates/borgmatic.plist.j2 +++ b/ansible/roles/borgmatic/templates/borgmatic.plist.j2 @@ -23,7 +23,7 @@ create RunAtLoad - + StandardErrorPath {{ borgmatic_log_dir }}/mcquack.borgmatic.err.log StandardOutPath diff --git a/ansible/roles/borgmatic/templates/config.yaml.j2 b/ansible/roles/borgmatic/templates/config.yaml.j2 index 2e2bf0f..9b8da14 100644 --- a/ansible/roles/borgmatic/templates/config.yaml.j2 +++ b/ansible/roles/borgmatic/templates/config.yaml.j2 @@ -31,6 +31,8 @@ exclude_patterns: encryption_passcommand: {{ borgmatic_encryption_passcommand }} +ssh_command: ssh -o IdentitiesOnly=yes -i {{ borgmatic_borgbase_ssh_key_path }} + # Retention policy keep_daily: {{ borgmatic_keep_daily }} keep_monthly: {{ borgmatic_keep_monthly }} diff --git a/docs/changelog.d/feature-borgbase-offsite-backup.feature.md b/docs/changelog.d/feature-borgbase-offsite-backup.feature.md new file mode 100644 index 0000000..1151e3f --- /dev/null +++ b/docs/changelog.d/feature-borgbase-offsite-backup.feature.md @@ -0,0 +1 @@ +Add BorgBase offsite backup repository for 3-2-1 backup strategy