Auto-register borg repo on the controller per host
backup role now logs into borgcontroller and creates (or looks up) a
repository with alias=inventory_hostname, registering root's pubkey and
the requested storageSize. The resulting SSH URI is injected into the
borgmatic config so each host gets a remote-managed repo without manual
config.
backup_hosts entries gain a `storage_size_gb` field (stripped before
templating) and lose the manual `repositories` entry — the role fills it.
borgcontroller_{username,password} are expected from Semaphore secrets.
Also gitignores .claude/ local state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,3 +5,6 @@ venv/
|
|||||||
# MikroTik backup output
|
# MikroTik backup output
|
||||||
mikrotik/backups/
|
mikrotik/backups/
|
||||||
mikrotik/output/
|
mikrotik/output/
|
||||||
|
|
||||||
|
# Claude Code local state
|
||||||
|
.claude/
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
---
|
---
|
||||||
# Per-host borgmatic config. Keys must match inventory_hostname.
|
# Borg Controller — auto-creates a repo per host on a BorgWarehouse-backed server.
|
||||||
# Hosts not listed here are skipped by the `backup` role.
|
# borgcontroller_username / borgcontroller_password come from Semaphore secrets.
|
||||||
# The value under each host is rendered verbatim as the borgmatic
|
borgcontroller_url: https://borgcontroller.internet-master.cz
|
||||||
# config file (see https://torsion.org/borgmatic/docs/reference/configuration/).
|
|
||||||
|
# Per-host borgmatic config. Hosts not listed here are skipped by the `backup` role.
|
||||||
|
# `storage_size_gb` is stripped before rendering and used to size the controller-side
|
||||||
|
# repo. `repositories` is auto-filled from the controller — don't set it manually.
|
||||||
|
# Other keys are passed through verbatim to borgmatic, see
|
||||||
|
# https://torsion.org/borgmatic/docs/reference/configuration/
|
||||||
backup_hosts:
|
backup_hosts:
|
||||||
testipaclient:
|
testipaclient:
|
||||||
|
storage_size_gb: 10
|
||||||
source_directories:
|
source_directories:
|
||||||
- /home/jakub
|
- /home/jakub
|
||||||
repositories:
|
|
||||||
- path: /var/backups/borg/jakub-home
|
|
||||||
label: jakub-home
|
|
||||||
keep_daily: 7
|
keep_daily: 7
|
||||||
keep_weekly: 4
|
keep_weekly: 4
|
||||||
keep_monthly: 6
|
keep_monthly: 6
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
- name: Login to borg controller
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ borgcontroller_url }}/api/auth/login"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
username: "{{ borgcontroller_username }}"
|
||||||
|
password: "{{ borgcontroller_password }}"
|
||||||
|
status_code: 200
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
register: _bc_login
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Get borg server SSH endpoint
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ borgcontroller_url }}/api/config"
|
||||||
|
method: GET
|
||||||
|
headers:
|
||||||
|
Cookie: "{{ _bc_login.cookies_string }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
register: _bc_config
|
||||||
|
|
||||||
|
- name: List repositories
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ borgcontroller_url }}/api/repositories"
|
||||||
|
method: GET
|
||||||
|
headers:
|
||||||
|
Cookie: "{{ _bc_login.cookies_string }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
register: _bc_repos
|
||||||
|
|
||||||
|
- name: Find existing repository for this host
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_bc_existing: >-
|
||||||
|
{{ _bc_repos.json | selectattr('alias', 'eq', inventory_hostname) | list }}
|
||||||
|
|
||||||
|
- name: Create repository if missing
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ borgcontroller_url }}/api/repositories"
|
||||||
|
method: POST
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
alias: "{{ inventory_hostname }}"
|
||||||
|
sshPublicKey: "{{ root_ssh.ssh_public_key }}"
|
||||||
|
storageSize: "{{ backup_hosts[inventory_hostname].storage_size_gb | int }}"
|
||||||
|
headers:
|
||||||
|
Cookie: "{{ _bc_login.cookies_string }}"
|
||||||
|
status_code: [200, 201]
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when: _bc_existing | length == 0
|
||||||
|
|
||||||
|
- name: Update repository SSH key if root's key changed
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ borgcontroller_url }}/api/repositories/{{ _bc_existing[0].id }}"
|
||||||
|
method: PATCH
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
sshPublicKey: "{{ root_ssh.ssh_public_key }}"
|
||||||
|
headers:
|
||||||
|
Cookie: "{{ _bc_login.cookies_string }}"
|
||||||
|
status_code: 200
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
when:
|
||||||
|
- _bc_existing | length > 0
|
||||||
|
- _bc_existing[0].sshPublicKey != root_ssh.ssh_public_key
|
||||||
|
|
||||||
|
- name: Re-list repositories after possible create/update
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ borgcontroller_url }}/api/repositories"
|
||||||
|
method: GET
|
||||||
|
headers:
|
||||||
|
Cookie: "{{ _bc_login.cookies_string }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
register: _bc_repos_after
|
||||||
|
|
||||||
|
- name: Resolve repository for this host
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_bc_repo: >-
|
||||||
|
{{ (_bc_repos_after.json | selectattr('alias', 'eq', inventory_hostname) | list)[0] }}
|
||||||
|
|
||||||
|
- name: Build borg SSH URI
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
borgcontroller_repo_uri: "ssh://{{ _bc_repo.repository }}@{{ _bc_config.json.borgSshHost.split('@')[1] }}/./repos"
|
||||||
+27
-11
@@ -21,14 +21,6 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: '0750'
|
mode: '0750'
|
||||||
|
|
||||||
- name: Deploy borgmatic config
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: borgmatic.yaml.j2
|
|
||||||
dest: /etc/borgmatic/config.yaml
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: '0640'
|
|
||||||
|
|
||||||
- name: Ensure root has an SSH key for the borg server
|
- name: Ensure root has an SSH key for the borg server
|
||||||
ansible.builtin.user:
|
ansible.builtin.user:
|
||||||
name: root
|
name: root
|
||||||
@@ -38,9 +30,33 @@
|
|||||||
ssh_key_comment: "borgmatic@{{ inventory_hostname }}"
|
ssh_key_comment: "borgmatic@{{ inventory_hostname }}"
|
||||||
register: root_ssh
|
register: root_ssh
|
||||||
|
|
||||||
- name: Show root's SSH public key (add this to the borg server's authorized_keys)
|
- name: Register / look up repository on borg controller
|
||||||
ansible.builtin.debug:
|
ansible.builtin.include_tasks: borgcontroller.yml
|
||||||
msg: "{{ root_ssh.ssh_public_key }}"
|
when:
|
||||||
|
- borgcontroller_username is defined
|
||||||
|
- borgcontroller_password is defined
|
||||||
|
|
||||||
|
- name: Build borgmatic config (strip controller-only keys, inject repository)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_borgmatic_config: >-
|
||||||
|
{{
|
||||||
|
(backup_hosts[inventory_hostname]
|
||||||
|
| dict2items
|
||||||
|
| rejectattr('key', 'in', ['storage_size_gb'])
|
||||||
|
| items2dict)
|
||||||
|
| combine(
|
||||||
|
{'repositories': [{'path': borgcontroller_repo_uri, 'label': inventory_hostname}]}
|
||||||
|
if borgcontroller_repo_uri is defined else {}
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Deploy borgmatic config
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: borgmatic.yaml.j2
|
||||||
|
dest: /etc/borgmatic/config.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0640'
|
||||||
|
|
||||||
- name: Enable and start borgmatic timer
|
- name: Enable and start borgmatic timer
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#jinja2: trim_blocks: True, lstrip_blocks: True
|
#jinja2: trim_blocks: True, lstrip_blocks: True
|
||||||
# Managed by Ansible — do not edit by hand.
|
# Managed by Ansible — do not edit by hand.
|
||||||
{{ backup_hosts[inventory_hostname] | to_nice_yaml(indent=2, width=1000) }}
|
{{ _borgmatic_config | to_nice_yaml(indent=2, width=1000) }}
|
||||||
|
|||||||
Reference in New Issue
Block a user