From 54e111338dceeaf84701f508790a2ed701c9284c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1=C4=8Dek?= Date: Fri, 15 May 2026 21:58:14 +0200 Subject: [PATCH] Encrypt borg repos with repokey-blake2 + shared passphrase borg_passphrase is required (Semaphore secret, same across hosts). The role writes it to /etc/borgmatic/passphrase (0600 root) and configures borgmatic to use BORG_PASSCOMMAND=cat /etc/borgmatic/passphrase, and runs `borg init --encryption=repokey-blake2` with BORG_PASSPHRASE in the env. no_log on the tasks that touch the passphrase. Co-Authored-By: Claude Opus 4.7 (1M context) --- roles/backup/tasks/borgcontroller.yml | 5 ++++- roles/backup/tasks/main.yml | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/roles/backup/tasks/borgcontroller.yml b/roles/backup/tasks/borgcontroller.yml index a08392e..9c50e62 100644 --- a/roles/backup/tasks/borgcontroller.yml +++ b/roles/backup/tasks/borgcontroller.yml @@ -120,9 +120,12 @@ - name: Initialize borg repository (no-op if already initialized) ansible.builtin.command: - cmd: borg init --encryption=none {{ borgcontroller_repo_uri }} + cmd: borg init --encryption=repokey-blake2 {{ borgcontroller_repo_uri }} + environment: + BORG_PASSPHRASE: "{{ borg_passphrase }}" register: _borg_init changed_when: _borg_init.rc == 0 failed_when: - _borg_init.rc != 0 - "'already exists' not in (_borg_init.stderr | default(''))" + no_log: true diff --git a/roles/backup/tasks/main.yml b/roles/backup/tasks/main.yml index ddcfac3..1fa88b2 100644 --- a/roles/backup/tasks/main.yml +++ b/roles/backup/tasks/main.yml @@ -8,6 +8,13 @@ when: inventory_hostname in (backup_hosts | default({})) block: + - name: Ensure borg_passphrase is set (Semaphore secret) + ansible.builtin.assert: + that: + - borg_passphrase is defined + - borg_passphrase | length > 0 + fail_msg: "borg_passphrase must be defined (provided by Semaphore secrets)" + - name: Install borgmatic ansible.builtin.package: name: borgmatic @@ -21,6 +28,15 @@ group: root mode: '0750' + - name: Write borg passphrase file + ansible.builtin.copy: + dest: /etc/borgmatic/passphrase + content: "{{ borg_passphrase }}" + owner: root + group: root + mode: '0600' + no_log: true + - name: Ensure root has an SSH key for the borg server ansible.builtin.user: name: root @@ -36,7 +52,7 @@ - borgcontroller_username is defined - borgcontroller_password is defined - - name: Build borgmatic config (strip controller-only keys, inject repository) + - name: Build borgmatic config (strip controller-only keys, inject repository + passcommand) ansible.builtin.set_fact: _borgmatic_config: >- {{ @@ -44,6 +60,7 @@ | dict2items | rejectattr('key', 'in', ['storage_size_gb']) | items2dict) + | combine({'encryption_passcommand': 'cat /etc/borgmatic/passphrase'}) | combine( {'repositories': [{'path': borgcontroller_repo_uri, 'label': inventory_hostname}]} if borgcontroller_repo_uri is defined else {}