aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Wilk <colin.wilk@tum.de>2023-05-22 20:17:27 +0200
committerColin Wilk <colin.wilk@tum.de>2023-05-24 19:40:29 +0200
commit1f1f6eeaebc148602085515350eb12829f86c315 (patch)
tree46dd4aa80ab9125a3254e2b1a26847f41a9e79d6
downloadansible-role-borgbackup-1f1f6eeaebc148602085515350eb12829f86c315.tar.gz
ansible-role-borgbackup-1f1f6eeaebc148602085515350eb12829f86c315.zip
init
Signed-off-by: Colin Wilk <colin.wilk@tum.de>
-rw-r--r--.ansible-lint12
-rw-r--r--.pre-commit-config.yaml22
-rw-r--r--LICENSE21
-rw-r--r--README.md158
-rw-r--r--defaults/main.yml125
-rw-r--r--meta/argument_specs.yml93
-rw-r--r--meta/main.yml20
-rw-r--r--molecule/.gitignore2
-rw-r--r--molecule/default/Dockerfile.j215
-rw-r--r--molecule/default/converge.yml43
-rw-r--r--molecule/default/molecule.yml39
-rw-r--r--molecule/default/tests/test_manual_backup.py57
-rw-r--r--tasks/client_setup.yml120
-rw-r--r--tasks/installation.yml21
-rw-r--r--tasks/main.yml9
-rw-r--r--tasks/server_setup.yml22
16 files changed, 779 insertions, 0 deletions
diff --git a/.ansible-lint b/.ansible-lint
new file mode 100644
index 0000000..76a35bf
--- /dev/null
+++ b/.ansible-lint
@@ -0,0 +1,12 @@
+exclude_paths: []
+
+use_default_rules: true
+
+enable_list:
+ - fqcn-builtins
+ - yaml
+ - syntax-check[specific]
+ - name[missing]
+
+skip_list:
+ - "yaml[line-length]"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..0264f0e
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,22 @@
+default_stages: [commit, push]
+
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.0.1
+ hooks:
+ - id: check-added-large-files
+ - id: check-yaml
+ - id: trailing-whitespace
+ - id: double-quote-string-fixer
+ - id: end-of-file-fixer
+
+ - repo: https://github.com/ansible-community/ansible-lint.git
+ rev: v6.15.0
+ hooks:
+ - id: ansible-lint
+ files: \.(yaml|yml)$
+
+ - repo: https://github.com/markdownlint/markdownlint
+ rev: v0.12.0
+ hooks:
+ - id: markdownlint
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1a38188
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Colin Wilk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1e9a634
--- /dev/null
+++ b/README.md
@@ -0,0 +1,158 @@
+Ansible Role: BorgBackup
+========================
+
+An ansible role that installs and configures a cron task for BorgBackup on a
+client and (delegated) server.
+This allows you to very simply add backups to your hosts.
+
+The documentation assumes basic knowledge about BorgBackup. You can get up to
+speed with Borg by reading their excellent documentation on
+<https://borgbackup.readthedocs.io>.
+
+How it works
+------------
+
+```mermaid
+sequenceDiagram
+participant Ansible
+participant borg-client
+participant borg-server
+
+Ansible->>borg-server: Install dependencies
+Ansible->>borg-client: Install dependencies
+Ansible->>borg-server: Setup Repository
+Ansible->>borg-client: Setup Repository
+Ansible->>borg-client: Setup backup job
+borg-client->>Ansible: Store decryption keys
+borg-client-->borg-server: Do Backup
+```
+
+Installation
+------------
+
+via ansible-galaxy
+
+```sh
+ansible-galaxy collection install kliwniloc.borgbackup
+```
+
+```yaml
+# requirements.yml
+- src: kliwniloc.borgbackup
+```
+
+via git
+
+```sh
+ansible-galaxy collection install git+https://github.com/kliwniloc/ansible-role-borgbackup.git,main
+```
+
+```yaml
+# requirements.yml
+- src: https://github.com/kliwniloc/ansible-role-borgbackup
+ version: master
+ name: kliwniloc.borgbackup
+```
+
+Role Variables
+--------------
+
+For (much) more details see `defaults/main.yml`
+
+```yaml
+################################### Required ###################################
+# The borg server host to delegate the server tasks to
+borg_server_host: ""
+
+# The public ssh key of the borg server
+borg_server_host_ssh_key: ""
+
+################################### Optional ###################################
+# The borg server host to push backups to as it will be used in the backup job
+borg_server_host_url: "{{ borg_server_host }}"
+
+# The repository name that the borg repository is created
+borg_backup_name_format: "{hostname}-{now:%Y-%m-%dT%H:%M:%S}"
+
+# Location for repositories on the borg server
+borg_server_user_home: /opt/borg
+
+# Name for the repository (unique on borg server)
+borg_repo_name: "{{ inventory_hostname }}"
+
+# Borg backup name format (for cron job)
+borg_backup_name_format: "{hostname}-{now:%Y-%m-%dT%H:%M:%S}"
+
+# Should the repo be append only?
+borg_mode_append_only: false
+
+# What compression to use in the backup?
+borg_compression: zstd
+
+# Directories to include in the backup
+borg_included_dirs: []
+
+# Directories to exclude in the backup
+borg_excluded_dirs: []
+
+# Passphrase to use with borg
+borg_passphrase: ""
+
+# Where to store the decryption keys on the local machine
+borg_decryption_keys_yaml_path: "{{ inventory_dir }}/decryption_keys.yml"
+
+# Settings for the backup cron job
+borg_cron_time:
+ minute: 0
+ hour: 3
+```
+
+Example Playbooks
+-----------------
+
+Simple Playbook
+
+```yaml
+- name: >
+ Add Borg backup to the borg-client and save the
+ decryption keys alongside the playbook
+ hosts: borg-client
+ roles:
+ - role: kliwniloc.borgbackup
+ borg_server_host: borg-server
+ borg_server_host_ssh_key: ssh-rsa AAAAAAAA...
+ borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml"
+ borg_included_dirs:
+ - /home
+ - /opt/project
+```
+
+Multiple Backup servers
+
+```yaml
+- name: >
+ Add Borg backup with multiple borg servers to the borg-client and
+ save the decryption keys alongside the playbook
+ hosts: borg-client
+ vars:
+ borg_included_dirs:
+ - /home
+ - /opt/project
+ roles:
+ - role: kliwniloc.borgbackup
+ borg_server_host: borg-server1
+ borg_server_host_ssh_key: ssh-rsa AAAAAAAA...
+ - role: kliwniloc.borgbackup
+ borg_server_host: borg-server2
+ borg_server_host_ssh_key: ssh-rsa BBBBBBBB...
+```
+
+Dependencies
+------------
+
+None.
+
+License
+-------
+
+MIT
diff --git a/defaults/main.yml b/defaults/main.yml
new file mode 100644
index 0000000..95a8937
--- /dev/null
+++ b/defaults/main.yml
@@ -0,0 +1,125 @@
+---
+################################################################################
+# Borg Server Host Configuration
+################################################################################
+
+# This it the host, as specified in your ansible inventory file, that the
+# backups will be made to.
+# Ansible will delegate borg-server related tasks to that host.
+# Currently there is only a single backup host supported per role run.
+# ---
+# borg_server_host: SETME
+
+# This is the public key of the ssh server of your borg server.
+# It is used for protecting against spoofed borg servers. It is recommended you
+# set this variable as a group var in your ansible repository as it is a per
+# borg-server configuration. To get this key you can run ssh-keyscan against
+# your borg server like this:
+# ssh-keyscan -t rsa borg.example.org
+# You will need to remove the hostname from the output so that the
+# remaining key will look something like this:
+# ssh-rsa AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
+# ---
+# borg_server_host_ssh_key: SETME
+
+# This is the host url that will be used for running borg related commands on
+# the borg client. The borg server ssh port needs to be reachable from the borg
+# borg client under this host.
+# This defaults to the borg_server_host which will work as long as the inventory
+# hostnames are globally reachable.
+borg_server_host_url: '{{ borg_server_host }}'
+
+# The home directory of the borg user that is created on the borg server.
+# All borg borg client repositories will be saved in this directory on the borg
+# server. e.g. /opt/borg/client1 /opt/borg/client2
+borg_server_user_home: /opt/borg
+
+
+################################################################################
+# Borg Repository Configuration
+# See: https://borgbackup.readthedocs.io/en/stable/usage/serve.html
+################################################################################
+
+# The name of the repository (directory) created on the borg-server
+# `{{ borg_server_user_home }}/{{ borg_repo_name }}`
+# This setting is mostly relevant if you use multiple repositories per
+# borg-client in which case you have to set a custom repo names / formats to
+# avoid clashes.
+borg_repo_name: '{{ inventory_hostname }}'
+
+# Should the repo be append only? (--append-only)
+# This will deny any request to delete data from the backup repository coming
+# from the client host. This is so that an attacker would not be able to simply
+# delete the backups from a compromised client.
+# With this configuration option enabled you won't have the ability to remove
+# old backups directly from the client that pushes the backups.
+# See https://borgbackup.readthedocs.io/en/stable/usage/notes.html#append-only-mode-forbid-compaction
+borg_mode_append_only: false
+
+
+################################################################################
+# Borg Backup Configuration
+# See: https://borgbackup.readthedocs.io/en/stable/usage/create.html
+################################################################################
+
+# This is the name of the backup in the configured repository
+# The default here creates a backup with the hostname and the current time in
+# the name. It is important to dynamically generate the backup names by using
+# the placeholders so that you don't have colliding backup names.
+# Most of the time the default option is fine.
+# For more information about the borg placeholder see
+# https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-placeholders
+borg_backup_name_format: "{hostname}-{now:%Y-%m-%dT%H:%M:%S}"
+
+# Borg has a few compression modes to those from:
+# none, lz4, zstd[,L], zlib[,L], lzma[,L], auto,C[,L], obfuscate,SPEC,C[,L].
+# For more information see the borg compression page: `borg help compression` or
+# https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-compression
+borg_compression: zstd
+
+# This is a list of files and directories to be backed up in the cron job.
+# In case you leave this empty, the role will not create an automatic backup job
+borg_included_dirs: []
+
+# This is a list of files and directories that you wish to have excluded from
+# Your backups. You may need this in case you want to remove a file from a
+# folder which you want to have backed up e.g. cache directory in application.
+# If you want to backup `/application/data` and `/application/db`
+# but not `application/cache` you can add `/application` to `borg_included_dirs`
+# and add `application/cache` to `borg_excluded_dirs`.
+borg_excluded_dirs: []
+
+# By default the role is configured to only use an encryption key with no
+# passphrase. This allows it to use the borgs command on the machine without any
+# haste. If you wish to enable the borg passphrase you can do so here. Note that
+# The passphrase will be stored in plaintext inside the cron job.
+# For more information about the borg passphrase see
+# https://borgbackup.readthedocs.io/en/stable/quickstart.html#passphrase-notes
+borg_passphrase: ""
+
+# Since borg encrypts the backups on the borg-server you should save the
+# encryption keys somewhere to another machine to be able to recover the backup
+# without the keys on the backup-client.
+# While you need the decryption keys as well as actual access to the borg
+# repository to download the backup data, you should still treat the decryption
+# keys as rather sensitive information.
+# Depending on your use case it may be okay to store them in your git repository.
+# If wish to encrypt the decryption keys, you look into third party tools for
+# that such as ansible-vault, git-crypt or a completely separate secrets
+# management system.
+borg_decryption_keys_yaml_path: '{{ inventory_dir }}/decryption_keys.yml'
+
+# Define the cron values for the automatic backup job as specified in the cron
+# module.
+# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/cron_module.html
+# Values that are not specified are omitted
+# borg_cron_time:
+# minute:
+# hour:
+# weekday:
+# day:
+# month:
+# special_time:
+borg_cron_time:
+ minute: 0
+ hour: 3
diff --git a/meta/argument_specs.yml b/meta/argument_specs.yml
new file mode 100644
index 0000000..97ddd7f
--- /dev/null
+++ b/meta/argument_specs.yml
@@ -0,0 +1,93 @@
+---
+argument_specs:
+ main:
+ options:
+
+################################################################################
+# Required
+################################################################################
+ borg_server_host:
+ type: str
+ required: true
+
+ borg_server_host_ssh_key:
+ type: str
+ required: true
+
+################################################################################
+# Optional
+################################################################################
+ borg_server_host_url:
+ type: str
+ required: false
+ default: "{{ borg_server_host }}"
+
+ borg_server_user_home:
+ type: str
+ required: false
+ default: /opt/borg
+
+ borg_repo_name:
+ type: str
+ required: false
+ default: "{{ inventory_hostname }}"
+
+ borg_backup_name_format:
+ type: str
+ required: false
+ default: "{hostname}-{now:%Y-%m-%dT%H:%M:%S}"
+
+ borg_mode_append_only:
+ type: bool
+ required: false
+ default: false
+
+ borg_compression:
+ type: str
+ required: false
+ default: zstd
+
+ borg_included_dirs:
+ type: list
+ elements: str
+ required: false
+ default: []
+
+ borg_excluded_dirs:
+ type: list
+ elements: str
+ required: false
+ default: []
+
+ borg_passphrase:
+ type: str
+ required: false
+
+ borg_decryption_keys_yaml_path:
+ type: str
+ required: false
+
+ borg_cron_time:
+ type: dict
+ required: false
+ options:
+ minute:
+ type: str
+ required: false
+ default: 0
+ hour:
+ type: str
+ required: false
+ default: 3
+ weekday:
+ type: str
+ required: false
+ day:
+ type: str
+ required: false
+ month:
+ type: str
+ required: false
+ special_time:
+ type: str
+ required: false
diff --git a/meta/main.yml b/meta/main.yml
new file mode 100644
index 0000000..7af6e93
--- /dev/null
+++ b/meta/main.yml
@@ -0,0 +1,20 @@
+---
+dependencies: []
+
+galaxy_info:
+ role_name: borgbackup
+ standalone: true
+ author: kliwniloc
+ description: BorgBackup role for for deploying scheduled backups
+ license: license (MIT)
+ min_ansible_version: '2.4'
+ platforms:
+ - name: Debian
+ versions:
+ - all
+ - name: Ubuntu
+ versions:
+ - all
+ galaxy_tags:
+ - borgbackup
+ - backup
diff --git a/molecule/.gitignore b/molecule/.gitignore
new file mode 100644
index 0000000..dd2354f
--- /dev/null
+++ b/molecule/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+decryption_keys.yml
diff --git a/molecule/default/Dockerfile.j2 b/molecule/default/Dockerfile.j2
new file mode 100644
index 0000000..091ef00
--- /dev/null
+++ b/molecule/default/Dockerfile.j2
@@ -0,0 +1,15 @@
+FROM {{ item.image }}
+
+# Install dependencies.
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+ sudo wget \
+ python3-pip python3-dev python3-setuptools python3-wheel python3-apt \
+ sed ssh openssh-server \
+ && rm -rf /var/lib/apt/lists/* \
+ && rm -Rf /usr/share/doc && rm -Rf /usr/share/man \
+ && apt-get clean
+
+RUN mkdir /run/sshd
+
+ENTRYPOINT ["bash", "-c", "/usr/sbin/sshd && sleep infinity"]
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
new file mode 100644
index 0000000..9c23b5f
--- /dev/null
+++ b/molecule/default/converge.yml
@@ -0,0 +1,43 @@
+---
+- name: Converge
+ hosts: borg-client
+
+ pre_tasks:
+ # This would usually be set by the user globally on their ansible
+ # repository and can be a security risk to do automatically. We will
+ # however set the variable here in the pre_tasks since it is for testing.
+ - name: Set borg server openssh key variable
+ become: true
+ block:
+ - name: Fetch ssh_key
+ ansible.builtin.command: >
+ ssh-keyscan -t rsa
+ {{ borg_server_host }}
+ | sed "s/^[^ ]* //"
+ register: borg_server_ssh_keyscan
+ changed_when: false
+
+ - name: Set ssh_key
+ ansible.builtin.set_fact:
+ borg_server_host_ssh_key: "{{ borg_server_ssh_keyscan.stdout
+ | split(' ')
+ | reject('search', borg_server_host)
+ | join(' ') }}"
+
+ vars:
+ borg_server_host: borg-server
+ borg_server_user_home: /opt/borg
+ borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml"
+ borg_included_dirs:
+ - /etc
+ - /home
+ borg_excluded_dirs:
+ - /opt
+ - /var
+ - /reee reeee
+ borg_cron_time:
+ minute: "*"
+ hour: "*"
+
+ roles:
+ - role: kliwniloc.borgbackup
diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml
new file mode 100644
index 0000000..f37c640
--- /dev/null
+++ b/molecule/default/molecule.yml
@@ -0,0 +1,39 @@
+---
+dependency:
+ name: galaxy
+
+
+driver:
+ name: docker
+
+
+platforms:
+
+ - name: borg-client
+ image: ${MOLECULE_DISTRO_CLIENT:-debian:10}
+ dockerfile: Dockerfile.j2
+ pre_build_image: false
+ docker_networks:
+ - name: 'molecule-container-net'
+ driver_options:
+ # Setting the mtu size due to issues with docker and VPN
+ com.docker.network.driver.mtu: 1420
+ networks:
+ - name: 'molecule-container-net'
+
+ - name: borg-server
+ image: ${MOLECULE_DISTRO_SERVER:-debian:10}
+ dockerfile: Dockerfile.j2
+ pre_build_image: false
+ networks:
+ - name: 'molecule-container-net'
+
+
+provisioner:
+ name: ansible
+ playbooks:
+ converge: ${MOLECULE_PLAYBOOK:-converge.yml}
+ side_effect: ${MOLECULE_PLAYBOOK:-side_effect.yml}
+
+verifier:
+ name: testinfra
diff --git a/molecule/default/tests/test_manual_backup.py b/molecule/default/tests/test_manual_backup.py
new file mode 100644
index 0000000..4aa9f27
--- /dev/null
+++ b/molecule/default/tests/test_manual_backup.py
@@ -0,0 +1,57 @@
+import pytest
+
+testinfra_hosts = ['borg-client']
+
+
+compression_types = [
+ 'none',
+ 'lz4',
+ 'zstd',
+ 'zstd,10',
+ 'zlib',
+ 'zlib,6',
+]
+
+
+"""Creates backups with all possible combinations of compression to the backup
+host"""
+@pytest.mark.parametrize('compression', compression_types)
+def test_backup_push(host, compression):
+ c = host.run(f'borg create -C "{compression}" borg@borg-server:/opt/borg/borg-client::testinfra-{{now:%S.%f}} /etc')
+ assert c.rc == 0
+ assert c.stdout == ''
+ assert c.stderr == ''
+
+
+@pytest.mark.parametrize('compression', compression_types)
+def test_backup_restore(host, compression):
+ # Create backup
+ c = host.run(f'borg create -C "{compression}" borg@borg-server:/opt/borg/borg-client::testinfra-backup-restore-{compression} /var')
+ assert c.rc == 0
+ assert c.stdout == ''
+ assert c.stderr == ''
+
+ # Restore Backup
+ c = host.run(f'cd /mnt && borg extract borg@borg-server:/opt/borg/borg-client::testinfra-backup-restore-{compression}')
+ assert c.rc == 0
+ assert c.stdout == ''
+ assert c.stderr == ''
+
+ # Check if every file exists, content has, and permissions / metadata
+ c1 = host.run('cd /var && find /var -type f -printf "%P\n" | sort | xargs -i sh -c "echo {}; sha512sum {} | cut -d \' \' -f 1; ls -l {}; echo"')
+ c2 = host.run('cd /mnt/var && find /var -type f -printf "%P\n" | sort | xargs -i sh -c "echo {}; sha512sum {} | cut -d \' \' -f 1; ls -l {}; echo"')
+ assert c1.rc == 0 and c2.rc == 0
+ assert c1.stderr == '' and c2.stderr == ''
+ assert c1.stdout == c2.stdout
+
+ # Delete directory extract directory again for future tests
+ c = host.run('rm -rf /mnt/var')
+ assert c.rc == 0
+ assert c.stdout == ''
+ assert c.stderr == ''
+
+ # Delete backup
+ c = host.run(f'borg delete borg@borg-server:/opt/borg/borg-client::testinfra-backup-restore-{compression}')
+ assert c.rc == 0
+ assert c.stdout == ''
+ assert c.stderr == ''
diff --git a/tasks/client_setup.yml b/tasks/client_setup.yml
new file mode 100644
index 0000000..fee0b6b
--- /dev/null
+++ b/tasks/client_setup.yml
@@ -0,0 +1,120 @@
+---
+- name: Create SSH Directory
+ ansible.builtin.file:
+ path: /root/.ssh
+ owner: root
+ group: root
+ mode: '0640'
+ state: directory
+ become: true
+
+- name: Add borg server to known_hosts
+ ansible.builtin.known_hosts:
+ name: "{{ borg_server_host_url }}"
+ key: "{{ borg_server_host_url }} {{ borg_server_host_ssh_key }}"
+ path: /root/.ssh/known_hosts
+ state: present
+ become: true
+
+- name: Generate SSH keys
+ community.crypto.openssh_keypair:
+ path: /root/.ssh/id_rsa
+ owner: root
+ group: root
+ mode: '0600'
+ comment: "root@{{ inventory_hostname }}"
+ become: true
+ register: ssh_key
+
+- name: Deploy Keys to Borg server
+ 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: "{{ ssh_key.public_key }}"
+ state: present
+ become: true
+ delegate_to: "{{ borg_server_host }}"
+
+- name: Initialise Borg repository
+ ansible.builtin.command: >
+ borg init --encryption=repokey
+ borg@{{ borg_server_host_url }}:{{ borg_server_user_home }}/{{ borg_repo_name }}
+ environment:
+ BORG_PASSPHRASE: "{{ borg_passphrase }}"
+ become: true
+ register: init_borg_output
+ changed_when: init_borg_output.rc != 2
+ failed_when: >
+ init_borg_output.rc != 2 and
+ init_borg_output.rc != 0
+
+- name: Make sure key file exists
+ ansible.builtin.file:
+ path: "{{ borg_decryption_keys_yaml_path }}"
+ state: touch
+ mode: '0600'
+ access_time: preserve
+ modification_time: preserve
+ delegate_to: localhost
+ become: false
+
+- name: Read Vars file
+ ansible.builtin.include_vars:
+ file: "{{ borg_decryption_keys_yaml_path }}"
+ register: local
+
+- name: Add repository encryption keys to ansible repo
+ when: not inventory_hostname in local.ansible_facts
+ throttle: 1
+ block:
+ - name: If host new read encryption keys
+ ansible.builtin.command: >
+ borg key export --paper
+ borg@{{ borg_server_host_url }}:{{ borg_server_user_home }}/{{ borg_repo_name }}
+ become: true
+ register: borg_keys
+ changed_when: borg_keys.rc != 0
+
+ - name: If host new add encryption keys to vars
+ ansible.builtin.set_fact:
+ decryption_keys: "{{ local.ansible_facts | combine({inventory_hostname: borg_keys.stdout}) }}"
+
+- name: Update encryption vars
+ ansible.builtin.copy:
+ content: "{{ decryption_keys | to_nice_yaml(indent=2, width=2048) }}"
+ dest: "{{ borg_decryption_keys_yaml_path }}"
+ mode: '0600'
+ when: decryption_keys is defined
+ delegate_to: localhost
+ become: false
+
+- name: Set up env for cron job
+ ansible.builtin.cron:
+ name: BORG_PASSPHRASE
+ job: "{{ borg_passphrase }}"
+ state: "{{ 'present' if (borg_included_dirs | length > 0) else 'absent' }}"
+ env: true
+ user: root
+ become: true
+
+- name: Set up backup cron jobs
+ ansible.builtin.cron:
+ name: BORG (Application level backups)
+ job: >
+ borg create -C {{ borg_compression }}
+ borg@{{ borg_server_host_url }}:{{ borg_server_user_home }}/{{ borg_repo_name }}::{{ borg_backup_name_format }}
+ {{ borg_included_dirs | map('quote') | join(' ') }}
+ {% for e in (borg_excluded_dirs | map('quote')) %} --exclude {{ e }} {% endfor %}
+ user: root
+ state: "{{ 'present' if (borg_included_dirs | length > 0) else 'absent' }}"
+ minute: "{{ borg_cron_time.minute | default(omit) }}"
+ hour: "{{ borg_cron_time.hour | default(omit) }}"
+ weekday: "{{ borg_cron_time.weekday | default(omit) }}"
+ day: "{{ borg_cron_time.day | default(omit) }}"
+ month: "{{ borg_cron_time.month | default(omit) }}"
+ special_time: "{{ borg_cron_time.special_time | default(omit) }}"
+ become: true
diff --git a/tasks/installation.yml b/tasks/installation.yml
new file mode 100644
index 0000000..af379e6
--- /dev/null
+++ b/tasks/installation.yml
@@ -0,0 +1,21 @@
+---
+- name: Install Debian Server dependencies
+ ansible.builtin.apt:
+ name:
+ - borgbackup
+ state: present
+ update_cache: true
+ become: true
+ when: ansible_facts['os_family'] == "Debian"
+ delegate_to: "{{ borg_server_host }}"
+
+- name: Install Debian Client dependencies
+ ansible.builtin.apt:
+ name:
+ - borgbackup
+ - cron
+ - ssh
+ state: present
+ update_cache: true
+ become: true
+ when: ansible_facts['os_family'] == "Debian"
diff --git a/tasks/main.yml b/tasks/main.yml
new file mode 100644
index 0000000..7ffef06
--- /dev/null
+++ b/tasks/main.yml
@@ -0,0 +1,9 @@
+---
+- name: Install dependencies
+ ansible.builtin.include_tasks: installation.yml
+
+- name: Run setup on server
+ ansible.builtin.include_tasks: server_setup.yml
+
+- name: Run setup on client
+ ansible.builtin.include_tasks: client_setup.yml
diff --git a/tasks/server_setup.yml b/tasks/server_setup.yml
new file mode 100644
index 0000000..6b16e29
--- /dev/null
+++ b/tasks/server_setup.yml
@@ -0,0 +1,22 @@
+---
+- name: Create borg user
+ ansible.builtin.user:
+ name: borg
+ comment: Borgbackup user
+ create_home: true
+ home: "{{ borg_server_user_home }}"
+ 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 }}"