aboutsummaryrefslogtreecommitdiffstats
path: root/tasks
diff options
context:
space:
mode:
authorColin Wilk <colin@wilk.cx>2026-06-27 15:22:45 +0200
committerColin Wilk <colin@wilk.cx>2026-06-27 16:55:53 +0200
commita22ff185f9836023817f9d4f8df3157b948f8cf2 (patch)
treeb51ce1fff12bc245ba024ab9db793bbdd8a8acae /tasks
parent2ff66fc090110dcf384e74bc26e73481eb780253 (diff)
downloadansible-role-borgbackup-a22ff185f9836023817f9d4f8df3157b948f8cf2.tar.gz
ansible-role-borgbackup-a22ff185f9836023817f9d4f8df3157b948f8cf2.zip
borg!: support multiple backups to same target
This brings support for multiple backups pointing to the same borg server, different repositories, from the same client. We support this by parsing the SSH authorized key file first and appending allowed repositories to allow independent definitions of the borg targets. So that the decryption_keys do not clash we include the repository name as well as the host in the key. This will lead to new keys being created for existing hosts in the new format. Old Format: {{ combine({inventory_hostname: borg_keys.stdout}) }} New Format: {{ combine({(inventory_hostname ~ '_' ~ borg_repo_name): borg_keys.stdout}) }} If upgrading from an older version that used just the hostname as the key, your existing `decryption_keys.yml` can be manually removed once the new format is also added.
Diffstat (limited to 'tasks')
-rw-r--r--tasks/client_setup.yml70
-rw-r--r--tasks/server_setup.yml12
2 files changed, 61 insertions, 21 deletions
diff --git a/tasks/client_setup.yml b/tasks/client_setup.yml
index 1eae105..8769852 100644
--- a/tasks/client_setup.yml
+++ b/tasks/client_setup.yml
@@ -26,15 +26,67 @@
become: true
register: ssh_key
-- name: Deploy Keys to Borg server
+- name: Ensure authorized_keys file exists on borg server
+ ansible.builtin.file:
+ path: "{{ borg_server_user_home }}/.ssh/authorized_keys"
+ state: touch
+ owner: borg
+ group: borg
+ mode: "0600"
+ access_time: preserve
+ modification_time: preserve
+ become: true
+ delegate_to: "{{ borg_server_host }}"
+
+- name: Read existing authorized_keys content
+ ansible.builtin.slurp:
+ src: "{{ borg_server_user_home }}/.ssh/authorized_keys"
+ become: true
+ delegate_to: "{{ borg_server_host }}"
+ register: auth_keys_content
+
+- name: Find existing authorized_keys line for this host
+ ansible.builtin.set_fact:
+ existing_line: >-
+ {{
+ (auth_keys_content.content | b64decode).splitlines()
+ | select("search", ssh_key.public_key | trim | regex_escape)
+ | first
+ | default("")
+ }}
+
+- name: Detect --append-only setting from existing entry
+ ansible.builtin.set_fact:
+ existing_append_only: "{{ existing_line is search('--append-only') }}"
+ when: existing_line | length > 0
+
+- name: Fail if --append-only setting differs from existing entry
+ ansible.builtin.fail:
+ msg: |
+ Inconsistent --append-only setting for host {{ inventory_hostname }}.
+ Existing entry has --append-only={{ existing_append_only }}, but current invocation uses --append-only={{ borg_mode_append_only }}.
+ All repositories for a host must have the same --append-only setting.
+ when:
+ - existing_line | length > 0
+ - existing_append_only != borg_mode_append_only
+
+- name: Compute all repos for this host
+ ansible.builtin.set_fact:
+ all_repos: >-
+ {{
+ (
+ (existing_line | regex_findall('--restrict-to-repository ([^\s"]+)'))
+ + [borg_server_user_home + '/' + borg_repo_name]
+ ) | unique | sort }}
+
+- name: Update authorized_keys entry for this host
ansible.builtin.lineinfile:
path: "{{ borg_server_user_home }}/.ssh/authorized_keys"
- line: >
- restrict,command="borg serve
- {{ "--append-only" if borg_mode_append_only }}
- --restrict-to-repository {{ borg_repo_name }}"
- {{ ssh_key.public_key }} root@{{ inventory_hostname }}
- search_string: --restrict-to-repository {{ borg_repo_name }}" {{ ssh_key.public_key }}
+ search_string: "{{ ssh_key.public_key | trim }}"
+ line: >-
+ restrict,command="borg serve{{ " --append-only" if borg_mode_append_only }}
+ {{ all_repos | map('regex_replace', '^', '--restrict-to-repository ') | join(' ') }}"
+ {{ ssh_key.public_key | trim }} root@{{ inventory_hostname }}
state: present
become: true
delegate_to: "{{ borg_server_host }}"
@@ -68,7 +120,7 @@
register: local
- name: Add repository encryption keys to ansible repo
- when: not inventory_hostname in local.ansible_facts
+ when: not (inventory_hostname ~ '_' ~ borg_repo_name) in local.ansible_facts
throttle: 1
block:
- name: If host new read encryption keys
@@ -81,7 +133,7 @@
- name: If host new add encryption keys to vars
ansible.builtin.set_fact:
- decryption_keys: "{{ local.ansible_facts | combine({inventory_hostname: borg_keys.stdout}) }}"
+ decryption_keys: "{{ local.ansible_facts | combine({(inventory_hostname ~ '_' ~ borg_repo_name): borg_keys.stdout}) }}"
- name: Update encryption vars
ansible.builtin.copy:
diff --git a/tasks/server_setup.yml b/tasks/server_setup.yml
index 603dd3d..e47d012 100644
--- a/tasks/server_setup.yml
+++ b/tasks/server_setup.yml
@@ -8,15 +8,3 @@
generate_ssh_key: true
become: true
delegate_to: "{{ borg_server_host }}"
-
-- name: Make sure authorized keys exists
- ansible.builtin.file:
- path: "{{ borg_server_user_home }}/.ssh/authorized_keys"
- state: touch
- owner: borg
- group: borg
- mode: "644"
- access_time: preserve
- modification_time: preserve
- become: true
- delegate_to: "{{ borg_server_host }}"