Add borgmatic

This commit is contained in:
uumas
2025-03-31 03:15:14 +03:00
parent 0deed89c3f
commit 0db60e2d60
12 changed files with 344 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
---
argument_specs:
main:
short_description: Borgmatic
description:
- Installs borgmatic
options:
borgmatic_targets:
description: List of backup targets
type: list
required: true
elements: dict
options:
host:
description: Target hostname
type: str
required: true
directories:
description: Directories on the host where backup repos will be created under
type: list
required: true
elements: str

View File

@@ -0,0 +1,55 @@
---
- name: Ensure host distribution is supported
ansible.builtin.import_role:
name: compatcheck
vars:
compatcheck_supported_distributions:
- name: debian
version_min: 11
- name: ubuntu
version_min: 22
- name: Install borgmatic
ansible.builtin.apt:
name: borgmatic
- name: Disable borgmatic global timer
ansible.builtin.systemd_service:
name: borgmatic.timer
state: stopped
enabled: false
- name: Add systemd drop-in service for borgmatic
ansible.builtin.template:
src: borgmatic@.service.j2
dest: /etc/systemd/system/borgmatic@.service
mode: "0644"
- name: Create borgmatic configurations directory
ansible.builtin.file:
path: /etc/borgmatic.d
state: directory
mode: "0755"
- name: Generate ssh key for borg
community.crypto.openssh_keypair:
type: ed25519
path: "{{ ansible_user_dir }}/.ssh/id_ed25519_borg"
comment: "{{ ansible_user_id }}@{{ ansible_fqdn }} borg"
register: _borgmatic_key
- name: Setup backup targets
ansible.builtin.include_tasks:
file: target.yaml
apply:
delegate_to: "{{ target.host }}"
become: false
loop: "{{ borgmatic_targets }}"
loop_control:
loop_var: target
- name: Add borg target ssh host keys to known hosts
ansible.builtin.known_hosts:
name: "{{ item }}"
key: "{{ item }} ssh-ed25519 {{ hostvars[item].ansible_ssh_host_key_ed25519_public }}"
loop: "{{ borgmatic_targets | map(attribute='host') }}"

View File

@@ -0,0 +1,26 @@
---
- name: Gather facts
ansible.builtin.setup:
delegate_facts: true
- name: Add ssh key to authorized_keys
ansible.posix.authorized_key:
user: "{{ hostvars[target.host].ansible_user_id }}"
key: >-
{{
_borgmatic_key.public_key + ' ' + _borgmatic_key.comment
if not (ansible_check_mode and _borgmatic_key.changed)
else 'ssh-ed25519 AAAA'
}}
key_options: >-
command="borg
serve{% for directory in target.directories %}
--restrict-to-path
{{ hostvars[target.host].ansible_user_dir }}/{{ directory }}/{{ ansible_fqdn }}{%- endfor -%}",restrict
- name: Create backup directories
ansible.builtin.file:
path: "{{ hostvars[target.host].ansible_user_dir }}/{{ item }}/{{ ansible_fqdn }}"
state: directory
mode: "0700"
loop: "{{ target.directories }}"

View File

@@ -0,0 +1,63 @@
# {{ ansible_managed }}
[Unit]
Description=borgmatic backup %i
Wants=network-online.target
After=network-online.target
Documentation=https://torsion.org/borgmatic/
[Service]
Type=oneshot
RuntimeDirectory=borgmatic
StateDirectory=borgmatic
# Security settings for systemd running as root, optional but recommended to improve security. You
# can disable individual settings if they cause problems for your use case. For more details, see
# the systemd manual: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
LockPersonality=true
# Certain borgmatic features like Healthchecks integration need MemoryDenyWriteExecute to be off.
# But you can try setting it to "yes" for improved security if you don't use those features.
MemoryDenyWriteExecute=no
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
# To restrict write access further, change "ProtectSystem" to "strict" and
# uncomment "ReadWritePaths", "TemporaryFileSystem", "BindPaths" and
# "BindReadOnlyPaths". Then add any local repository paths to the list of
# "ReadWritePaths". This leaves most of the filesystem read-only to borgmatic.
ProtectSystem=full
# ReadWritePaths=-/mnt/my_backup_drive
# This will mount a tmpfs on top of /root and pass through needed paths
# TemporaryFileSystem=/root:ro
# BindPaths=-/root/.cache/borg -/root/.config/borg -/root/.borgmatic
# BindReadOnlyPaths=-/root/.ssh
# May interfere with running external programs within borgmatic hooks.
CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_NET_RAW
# Lower CPU and I/O priority.
Nice=19
CPUSchedulingPolicy=batch
IOSchedulingClass=best-effort
IOSchedulingPriority=7
IOWeight=100
Restart=no
# Prevent rate limiting of borgmatic log events. If you are using an older version of systemd that
# doesn't support this (pre-240 or so), you may have to remove this option.
LogRateLimitIntervalSec=0
ExecStart=systemd-inhibit --who="borgmatic" --what="sleep:shutdown" --why="Prevent interrupting scheduled backup" /usr/bin/borgmatic -c "/etc/borgmatic.d/%I.yaml" --verbosity -2 --syslog-verbosity 1

View File

@@ -0,0 +1,8 @@
---
borgmatic_config_backup_frequency:
unit: h
amount: 1
borgmatic_config_keep_backups_months: 6
borgmatic_config_targets: "{{ borgmatic_targets }}"

View File

@@ -0,0 +1,6 @@
---
- name: Restart borgmatic timer {{ borgmatic_config_name }}
ansible.builtin.systemd_service:
name: "borgmatic@{{ borgmatic_config_name }}.timer"
state: restarted
daemon_reload: true

View File

@@ -0,0 +1,65 @@
---
argument_specs:
main:
short_description: Borgmatic config
description:
- Creates a bormatic configuration in /etc/borgmatic.d/ and creates the repos
options:
borgmatic_config_name:
description:
- Name of the borgmatic config.
- Must be unique within the (source) host.
type: str
required: true
borgmatic_config_directories:
description: Directories to backup
type: list
required: true
elements: str
borgmatic_config_encryption_passphrase:
description: Passphrase for borg repo encryption
type: str
required: true
borgmatic_config_targets:
description:
- List of backup targets for this config.
- All backup targets and directories must be listed in borgmatic_targets.
- Defaults to all defined in borgmatic_targets.
type: list
required: false
elements: dict
options:
host:
description: Target hostname
type: str
required: true
directories:
description: Directories on the host where backup repos will be created under
type: list
required: true
elements: str
borgmatic_config_backup_frequency:
description: How often to take backups. Defaults to once per hour.
type: dict
required: false
default:
unit: h
amount: 1
options:
unit:
description: Time unit
type: str
required: true
choices:
- min
- h
- d
amount:
description: Every how many time units to take backpus
type: int
required: true
borgmatic_config_keep_backups_months:
description: How many months to keep backups for
type: int
required: false
default: 6

View File

@@ -0,0 +1,3 @@
---
dependencies:
- borgmatic

View File

@@ -0,0 +1,35 @@
---
- name: Fail if hosts or directories listed in borgmatic_config_targets not in borgmatic_targets.
ansible.builtin.fail:
msg: All backup targets and directories must be listed in borgmatic_targets.
when: >-
borgmatic_config_targets
| items2dict(key_name='host', value_name='directories')
| ansible.builtin.combine(
borgmatic_targets | items2dict(key_name='host', value_name='directories'), list_merge='prepend_rp'
)
| dict2items(key_name='host', value_name='directories')
| difference(borgmatic_targets)
| length != 0
- name: Add borgmatic configuration for {{ borgmatic_config_name }}
ansible.builtin.template:
src: borgmatic.yaml.j2
dest: /etc/borgmatic.d/{{ borgmatic_config_name }}.yaml
mode: "0600"
no_log: true
- name: Add systemd timer for borgmatic {{ borgmatic_config_name }}
ansible.builtin.template:
src: borgmatic@.timer.j2
dest: /etc/systemd/system/borgmatic@{{ borgmatic_config_name }}.timer
mode: "0644"
register: _borgmatic_config_systemd_timer
notify: Restart borgmatic timer {{ borgmatic_config_name }}
- name: Enable systemd timer for borgmatic {{ borgmatic_config_name }}
ansible.builtin.systemd_service:
name: borgmatic@{{ borgmatic_config_name }}.timer
state: started
enabled: true
when: "not (ansible_check_mode and _borgmatic_config_systemd_timer.changed and _borgmatic_config_systemd_timer.diff.before == '')"

View File

@@ -0,0 +1,35 @@
# {{ ansible_managed }}
# vim:ft=yaml
source_directories:
{{ borgmatic_config_directories | to_nice_yaml }}
repositories:
{% for target in borgmatic_config_targets %}
{% for directory in target.directories %}
- path: "ssh://{{ hostvars[target.host].ansible_user_id }}@{{ target.host }}/{{ directory }}/{{ ansible_fqdn }}/{{ borgmatic_config_name }}"
{% endfor %}
{% endfor %}
working_directory: "~"
one_file_system: true
exclude_patterns:
- /var/cache
exclude_caches: true
exclude_if_present:
- .nobackup
keep_exclude_tags: true
source_directories_must_exist: true
compression: zstd
encryption_passphrase: "{{ borgmatic_config_encryption_passphrase }}"
ssh_command: ssh -i ~/.ssh/id_ed25519_borg
keep_within: 48H
keep_hourly: 168
keep_daily: 30
keep_weekly: 26
keep_monthly: {{ (borgmatic_config_keep_backups_months / 2) | round(0, 'ceil') | int }}
keep_yearly: {{ (borgmatic_config_keep_backups_months / 12) | round(0, 'ceil') | int }}

View File

@@ -0,0 +1,21 @@
# {{ ansible_managed }}
[Unit]
Description=Run borgmatic backup
[Timer]
{% if borgmatic_config_backup_frequency.unit == "min" %}
OnCalendar=*:0/{{ borgmatic_config_backup_frequency.amount }}
{% elif borgmatic_config_backup_frequency.unit == "h" %}
OnCalendar=0/{{ borgmatic_config_backup_frequency.amount }}:30
{% elif borgmatic_config_backup_frequency.unit == "d" %}
OnCalendar=*-1/{{ borgmatic_config_backup_frequency.amount }} 22:00
{% else %}
{{ dafuq }}
{% endif %}
Persistent=true
RandomizedDelaySec={{ 10 * borgmatic_config_backup_frequency.amount }}{{ _borgmatic_config_previous_time_unit[borgmatic_config_backup_frequency.unit] }}
FixedRandomDelay=true
[Install]
WantedBy=timers.target

View File

@@ -0,0 +1,5 @@
---
_borgmatic_config_previous_time_unit:
min: s
h: min
d: h