From 5d1703d3bc0d1c6dd7487ef4f7f39117e4db1128 Mon Sep 17 00:00:00 2001
From: Arthur BOUDREAULT <arthur@lydra.fr>
Date: Mon, 25 Jul 2022 16:00:04 +0200
Subject: [PATCH] feat: add Ansible role Restic to use S3 backups

---
 roles/ynh_backup/README-FR.md         | 72 +++++++++++++++++++--------
 roles/ynh_backup/README.md            | 69 ++++++++++++++++++-------
 roles/ynh_backup/defaults/main.yml    | 19 +++++--
 roles/ynh_backup/tasks/backup.yml     | 10 ++--
 roles/ynh_backup/tasks/borgbackup.yml | 50 +++++++++----------
 roles/ynh_backup/tasks/main.yml       | 16 ++++--
 roles/ynh_backup/tasks/restic.yml     | 39 +++++++++++++++
 roles/ynh_backup/vars/main.yml        |  1 +
 8 files changed, 197 insertions(+), 79 deletions(-)
 create mode 100644 roles/ynh_backup/tasks/restic.yml

diff --git a/roles/ynh_backup/README-FR.md b/roles/ynh_backup/README-FR.md
index 178a558..ccbc800 100644
--- a/roles/ynh_backup/README-FR.md
+++ b/roles/ynh_backup/README-FR.md
@@ -11,10 +11,11 @@ YunoHost doit déjà être installé sur votre serveur.
 ## Variables du rôle
 
 Les variables par défaut sont disponibles dans `default/main.yml` cependant il est possible de les surcharger selon vos besoins.
-Nous avons intégré deux systèmes de sauvegardes différents à ce rôle YunoHost :
+Nous avons intégré trois systèmes de sauvegardes différents à ce rôle YunoHost :
 
 - sauvegardes natives YunoHost en local
-- sauvegardes à distance avec un [depot BorgBackup](https://borgbackup.readthedocs.io/en/stable/)
+- sauvegardes à distance avec un [dépôt BorgBackup](https://borgbackup.readthedocs.io/en/stable/)
+- sauvegardes à distance avec un [dépôt Restic](https://restic.readthedocs.io/en/stable/)
 
 ### Sauvegardes natives YunoHost locales
 
@@ -22,16 +23,15 @@ Nous avons intégré deux systèmes de sauvegardes différents à ce rôle YunoH
 
 ```yml
 ynh_backup:
-  scheduled: True
-  directory: "/data/backup"
-  scheduled_hour: "*"
-  scheduled_minute: "*/30"
+  scheduled:         True
+  directory:         "/data/backup"
+  scheduled_hour:    "*"
+  scheduled_minute:  "*/30"
   scheduled_weekday: "*"
-  scheduled_month: "*"
-  system: True
-  apps: True
+  scheduled_month:   "*"
+  system:            True
+  apps:              True
   number_days_to_keep: "2"
-
 ```
 
 - `ynh_backup.scheduled` : active la fonctionnalité de sauvegarde des applications YunoHost en mettant la valeur à `True`.
@@ -48,24 +48,23 @@ ynh_backup:
 
 ```yml
 ynh_borg_backup_scheduled: True
-borg_source_directories:
-  - "/data/yunohost"
-borg_repository: "/data/backup/live"
+borg_source_directories:    "{{ ynh_backup.directory }}"
+borg_repository:            "/data/backup/borg_repository"
 borg_encryption_passphrase: "PLEASECHANGEME"
-borgmatic_config_name: "borgmatic_ynh_config"
-borgmatic_cron_name: "borgmatic_ynh_cron"
+borgmatic_config_name:      "borgmatic_ynh_config"
+borgmatic_cron_name:        "borgmatic_ynh_cron"
 borg_retention_policy:
   keep_daily: "4"
 ynh_borg_backup_remote_repo: True
-borg_ssh_keys_src: "files/prd/ssh_keys/ynh_ed25519.vault"
-borg_ssh_keys_dest: "/home/debian/.ssh/ynh_ed25519"
-ynh_ssh_borg_command: "ssh_command: ssh -p 7410 -o StrictHostKeychecking=no -i {{ borg_ssh_keys_dest }}"
+borg_ssh_keys_src:          "files/prd/ssh_keys/ynh_ed25519.vault"
+borg_ssh_keys_dest:         "/home/debian/.ssh/ynh_ed25519"
+ynh_ssh_borg_command:       "ssh_command: ssh -p 7410 -o StrictHostKeychecking=no -i {{ borg_ssh_keys_dest }}"
 ```
 
 - `ynh_borg_backup_scheduled` : Active / désactive la fonctionnalité de sauvegarde avec BorgBackup.
 - `ynh_borg_backup_remote_repo` : Active / désactive la fonctionnalité de sauvegarde sur un dépôt distant BorgBackup (tâches liées à la mise en place des clés SSH). Si vous activez cette fonctionnalité, vous aurez besoin d'utiliser les variables `borg_ssh_keys_src` et `borg_ssh_keys_dest`.
-- `borg_source_directories` : Liste des dossiers source à sauvegarder. Par défaut, il s'agit du dossier contenant toutes les données YunoHost (configuration, applications).
-- `borg_repository` : Chemin complet vers le dépôt Borg. Possibilité de donner une liste de dépôts pour sauvegarder les données dans plusieurs endroits. Par défaut, il s'agit du dépôt `/data/backup/live`.
+- `borg_source_directories` : Liste des dossiers source à sauvegarder. Par défaut, il s'agit du dossier qui contient les sauvegardes faites par YunoHost.
+- `borg_repository` : Chemin complet vers le dépôt Borg. Possibilité de donner une liste de dépôts pour sauvegarder les données dans plusieurs endroits.
 - `borg_encryption_passphrase` : **Obligatoire**, mot de passe à utiliser pour la clé de chiffrement du dépôt Borg.
 - `borgmatic_config_name` : **Optionnel**, nom du fichier de configuration Borgmatic.
 - `borgmatic_cron_name` : **Optionnel**, nom du fichier de tâche cron.
@@ -76,6 +75,39 @@ ynh_ssh_borg_command: "ssh_command: ssh -p 7410 -o StrictHostKeychecking=no -i {
 
 N'hésitez pas à regarder les variables disponibles dans le [rôle](https://github.com/borgbase/ansible-role-borgbackup).
 
+### Sauvegardes distantes avec Restic
+
+- Les sauvegardes avec [Restic](https://restic.net/) : Grâce au rôle Ansible `do1jlr.restic`, nous pouvons automatiser le processus d'installation et de configuration de Restic sur un serveur YunoHost. Les sauvegardes Borg Restic accessibles sur un dépôt Restic en local ou à distance et compatible stockage objet S3. Plus d'info sur ce rôle [ici](https://github.com/roles-ansible/ansible_role_restic).
+
+⚠️ Attention, pour pouvoir utiliser le rôle Ansible `do1jlr.restic`, vous devez avoir les paquets suivants installé sur la machine qui exécute Ansible : `bzip2` (binaire disponible sur la plupart des systèmes Linux) et `jmespath` (paquet python, installable avec pip).
+
+```yml
+
+restic_repos:
+  s3_ynh_restic_repo:
+    location:              "s3:s3.fr-par.scw.cloud/dummy_bucket_name"
+    password:              "dummy_restic_repo_password"
+    aws_access_key:        "dummy_access_key"
+    aws_secret_access_key: "dummy_secret_access_key"
+    aws_default_region:    "fr-par"
+    init: true
+
+restic_backups:
+  YunoHost_remote:
+    name:        "remote_ynh_restic"
+    repo:        "s3_ynh_restic_repo"
+    src:         "{{ ynh_backup.directory }}"
+    tags:
+      - yunohost
+      - remote
+    keep_within: "{{ restic_keep_time }}"
+    scheduled: true
+    schedule_hour: 1
+    schedule_minute: 0
+```
+
+N'hésitez pas à regarder les variables disponibles dans le [rôle](https://github.com/borgbase/ansible-role-borgbackup).
+
 ## Dépendances
 
 Le rôle `m3nu.ansible_role_borgbackup` sera installé sur la machine exécutant Ansible pour que les tâches liées à Borg fonctionnent. Un fichier `requirements.yml` est à la racine du rôle et va télécharger le rôle (par défaut vers `~/.ansible/roles`).
diff --git a/roles/ynh_backup/README.md b/roles/ynh_backup/README.md
index 59ba516..d6dcd11 100644
--- a/roles/ynh_backup/README.md
+++ b/roles/ynh_backup/README.md
@@ -11,10 +11,11 @@ YunoHost needs to be installed on your server.
 ## Role Variables
 
 The default variables are available in `default/main.yml` however it is possible to override them according to your needs.
-We have integrated two different backup systems to this YunoHost role:
+We have integrated three different backup systems to this YunoHost role:
 
 - YunoHost native local backups
 - Remote backups with a [BorgBackup repository](https://borgbackup.readthedocs.io/en/stable/)
+- Remote backups with a [Restic repository](https://restic.readthedocs.io/en/stable/)
 
 ### YunoHost native local backups
 
@@ -22,16 +23,15 @@ YunoHost provides its own native backup system. It is able to back up YunoHost c
 
 ```yml
 ynh_backup:
-  scheduled: True
-  directory: "/data/backup"
-  scheduled_hour: "*"
-  scheduled_minute: "*/30"
+  scheduled:         True
+  directory:         "/data/backup"
+  scheduled_hour:    "*"
+  scheduled_minute:  "*/30"
   scheduled_weekday: "*"
-  scheduled_month: "*"
-  system: True
-  apps: True
-  src_script: "templates/ynh_backup.sh.j2"
-  dest_script: "/usr/bin"
+  scheduled_month:   "*"
+  system:            True
+  apps:              True
+  number_days_to_keep: "2"
 ```
 
 - `ynh_backup.scheduled`: Enable the YunoHost applications backup feature by setting the value to `True`.
@@ -39,7 +39,7 @@ ynh_backup:
 - `ynh_backup.scheduled_[hour|minute|weekday|month]`: modifies the scheduling of the cron task. By default, it will run every day of the year at 3am. For more information about cron time settings, this tool can be useful: <https://crontab.guru/>.
 - `ynh_backup.system`: Disable YunoHost system backup by setting the value to `False`, the default value is `True`.
 - `ynh_backup.apps`: Disable backup of YunoHost applications by setting the value to `False`, the default is `True`.
-- `ynh_backup.number_days_to_keep` : Determines the number of days to keep for the purging system, the default is 2.
+- `ynh_backup.number_days_to_keep`: Determines the number of days to keep for the purging system, the default is 2.
 - ⚠️ Beware, once you enable the local backup feature `ynh_backup.scheduled`, you cannot disable system **and** application backups. If you set `ynh_backup.system` **and** `ynh_backup.apps` to `False`, the role will fail.
 
 ### remote backups with YunoHost BorgBackup
@@ -48,22 +48,22 @@ ynh_backup:
 
 ```yml
 ynh_borg_backup_scheduled: True
-borg_source_directories:
-  - "/data/yunohost"
-borg_repository: "/data/backup/live"
+borg_source_directories:    "{{ ynh_backup.directory }}"
+borg_repository:            "/data/backup/borg_repository"
 borg_encryption_passphrase: "PLEASECHANGEME"
-borgmatic_config_name: "borgmatic_ynh_config"
-borgmatic_cron_name: "borgmatic_ynh_cron"
+borgmatic_config_name:      "borgmatic_ynh_config"
+borgmatic_cron_name:        "borgmatic_ynh_cron"
 borg_retention_policy:
   keep_daily: "4"
 ynh_borg_backup_remote_repo: True
-borg_ssh_keys_src: "files/prd/ssh_keys/ynh_ed25519.vault"
-borg_ssh_keys_dest: "/home/debian/.ssh/ynh_ed25519"
+borg_ssh_keys_src:          "files/prd/ssh_keys/ynh_ed25519.vault"
+borg_ssh_keys_dest:         "/home/debian/.ssh/ynh_ed25519"
+ynh_ssh_borg_command:       "ssh_command: ssh -p 7410 -o StrictHostKeychecking=no -i {{ borg_ssh_keys_dest }}"
 ```
 
 - `ynh_borg_backup_scheduled`: Enable / disable the backup feature with BorgBackup.
 - `ynh_borg_backup_remote_repo`: Enable / disable the backup functionality on a BorgBackup remote repository (tasks related to SSH keys setup). If you enable this feature, then you will need to use `borg_ssh_keys_src` and `borg_ssh_keys_dest` variables. 
-- `borg_source_directories`: List of source folders to back up. By default, this is the folder containing all YunoHost data (configuration, applications).
+- `borg_source_directories`: List of source folders to back up. By default, this is the folder in which YunoHost local backups are located.
 - `borg_repository`: Full path to the Borg repository. Possibility to give a list of repositories to save data in several places.
 - `borg_encryption_passphrase` : **Mandatory**, password to use for the Borg repository encryption key.
 - `borgmatic_config_name`: **Optional**, name of the Borgmatic configuration file.
@@ -75,6 +75,37 @@ borg_ssh_keys_dest: "/home/debian/.ssh/ynh_ed25519"
 
 Feel free to look at the variables available in the [role](https://github.com/borgbase/ansible-role-borgbackup).
 
+### remote backups with YunoHost Restic
+
+- Backups with [Restic](https://restic.net/): Thanks to the Ansible role `do1jlr.restic` we can automate the installation and configuration process of Restic on a YunoHost server. Restic backups are accessible on a local or a remote Restic repository and compatible with S3 object storage. More info about this role [here](https://github.com/roles-ansible/ansible_role_restic).
+
+```yml
+
+restic_repos:
+  s3_ynh_restic_repo:
+    location:              "s3:s3.fr-par.scw.cloud/dummy_bucket_name"
+    password:              "dummy_restic_repo_password"
+    aws_access_key:        "dummy_access_key"
+    aws_secret_access_key: "dummy_secret_access_key"
+    aws_default_region:    "fr-par"
+    init: true
+
+restic_backups:
+  YunoHost_remote:
+    name:        "remote_ynh_restic"
+    repo:        "s3_ynh_restic_repo"
+    src:         "{{ ynh_backup.directory }}"
+    tags:
+      - yunohost
+      - remote
+    keep_within: "{{ restic_keep_time }}"
+    scheduled: true
+    schedule_hour: 1
+    schedule_minute: 0
+```
+
+Feel free to look at the variables available in the [role](https://github.com/roles-ansible/ansible_role_restic).
+
 ## Dependencies
 
 The `m3nu.ansible_role_borgbackup` role will be installed on the machine running Ansible for Borg-related tasks to work. A `requirements.yml` file is in the root of the role and will download the role (by default to `~/.ansible/roles`).
diff --git a/roles/ynh_backup/defaults/main.yml b/roles/ynh_backup/defaults/main.yml
index 7b616ac..b6423c7 100644
--- a/roles/ynh_backup/defaults/main.yml
+++ b/roles/ynh_backup/defaults/main.yml
@@ -23,10 +23,19 @@ ynh_backup:
   scheduled: False
 
 # Variables for YunoHost BorgBackup
-ynh_borg_backup_scheduled: False
+ynh_borg_backup_scheduled:   False
 borg_source_directories:
-  - "/data/yunohost"
-borg_repository: "/data/backup/live"
-borg_init_command: "borgmatic init -c /etc/borgmatic/{{ borgmatic_config_name }} -e repokey --syslog-verbosity 1"
-borg_archive_name_format: "'{hostname}-yunohost-live-data-{now:%Y-%m-%d-%H%M%S}'"
+  - "/home/yunohost.backup"
+borg_repository:             "/data/backup/borg_repository"
+borg_init_command:           "borgmatic init -c /etc/borgmatic/{{ borgmatic_config_name }} -e repokey --syslog-verbosity 1"
+borg_archive_name_format:    "'{hostname}-yunohost-live-data-{now:%Y-%m-%d-%H%M%S}'"
 ynh_borg_backup_remote_repo: False
+
+# Variables for YunoHost Restic
+# https://github.com/roles-ansible/ansible_role_restic
+ynh_restic_backup_scheduled: False
+restic_version:              "0.12.1"
+restic_schedule_type:        "cronjob"
+do1jlr_restic_version:       "v0.7.1"
+
+restic_keep_time:            "0y1m0d0h"
diff --git a/roles/ynh_backup/tasks/backup.yml b/roles/ynh_backup/tasks/backup.yml
index 94ebd0b..7e6d6a3 100644
--- a/roles/ynh_backup/tasks/backup.yml
+++ b/roles/ynh_backup/tasks/backup.yml
@@ -25,19 +25,19 @@
 
 - name: Create backup folder if doesn't already exist
   ansible.builtin.file:
-    path: "{{ ynh_backup.directory }}"
+    path:  "{{ ynh_backup.directory }}"
     state: directory
-    mode: '0750'
+    mode:  '0750'
   when: ynh_backup.directory is defined
   tags: backup
 
 - name: Create backup script
   ansible.builtin.template:
-    src: "{{ ynh_backup_src_script }}"
-    dest: "{{ ynh_backup_dest_script }}"
+    src:   "{{ ynh_backup_src_script }}"
+    dest:  "{{ ynh_backup_dest_script }}"
     owner: root
     group: root
-    mode: '0740'
+    mode:  '0740'
   tags: backup
 
 - name: Create cron task to schedule YNH backup script
diff --git a/roles/ynh_backup/tasks/borgbackup.yml b/roles/ynh_backup/tasks/borgbackup.yml
index 9cb46ee..2dbfc28 100644
--- a/roles/ynh_backup/tasks/borgbackup.yml
+++ b/roles/ynh_backup/tasks/borgbackup.yml
@@ -18,12 +18,12 @@
 #                                                                             #
 #-----------------------------------------------------------------------------#
 - name: Download BorgBackup role on localhost
-  ansible.builtin.command: ansible-galaxy install m3nu.ansible_role_borgbackup,v0.9.0 -p ~/.ansible/roles
+  ansible.builtin.command: ansible-galaxy install m3nu.ansible_role_borgbackup,v0.9.0 -p "{{ _ansible_role_directory }}"
   delegate_to: localhost
   become: False
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
 
 - name: Gather facts for BorgBackup role
   ansible.builtin.setup:
@@ -35,8 +35,8 @@
   ansible.builtin.import_role:
     name: m3nu.ansible_role_borgbackup
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
 
 - name: Create backup folder for BorgBackup repository
   ansible.builtin.file:
@@ -44,29 +44,29 @@
     state: directory
     mode: '0750'
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
 
 - name: Configure host for Borg Remote repository
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
   block:
     - name: deploy ssh public key for BorgBackup
       ansible.builtin.copy:
-        src: "{{ borg_ssh_keys_src }}.pub"
-        dest: "{{ borg_ssh_keys_dest }}.pub"
+        src:   "{{ borg_ssh_keys_src }}.pub"
+        dest:  "{{ borg_ssh_keys_dest }}.pub"
         owner: "root"
         group: "root"
-        mode: 0600
+        mode:  0600
 
     - name: deploy ssh private key for BorgBackup
       ansible.builtin.copy:
-        src: "{{ borg_ssh_keys_src }}.vault"
-        dest: "{{ borg_ssh_keys_dest }}"
+        src:   "{{ borg_ssh_keys_src }}.vault"
+        dest:  "{{ borg_ssh_keys_dest }}"
         owner: "root"
         group: "root"
-        mode: 0600
+        mode:  0600
   when: ynh_borg_backup_remote_repo
 
 - name: change SSH command in "/etc/borgmatic/{{ borgmatic_config_name }}"
@@ -77,19 +77,19 @@
     state: present
   when: ynh_ssh_borg_command is defined
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
 
 
 - name: change archive name in "/etc/borgmatic/{{ borgmatic_config_name }}"
   ansible.builtin.lineinfile:
-    path: "/etc/borgmatic/{{ borgmatic_config_name }}"
+    path:   "/etc/borgmatic/{{ borgmatic_config_name }}"
     regexp: "archive_name_format:"
     line: "    archive_name_format: {{ borg_archive_name_format }}"
     state: present
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
 
 - name: Create borg launch script in /usr/local/bin
   ansible.builtin.copy:
@@ -97,16 +97,16 @@
       #!/bin/bash
       . /opt/borgmatic/bin/activate
       borg "$@"
-    dest: /usr/local/bin/borg
+    dest:  /usr/local/bin/borg
     owner: root
     group: root
     mode: "0755"
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
 
 - name: Initialize a new Borg repository
   ansible.builtin.command: "{{ borg_init_command }}"
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
diff --git a/roles/ynh_backup/tasks/main.yml b/roles/ynh_backup/tasks/main.yml
index ac16ba4..0ffd2f0 100644
--- a/roles/ynh_backup/tasks/main.yml
+++ b/roles/ynh_backup/tasks/main.yml
@@ -17,15 +17,21 @@
 # along with this program. If not, see <http://www.gnu.org/licenses/>.        #
 #                                                                             #
 #-----------------------------------------------------------------------------#
-
-- name: Enable Yunohost apps backup
+- name: Enable Yunohost local backups
   ansible.builtin.include_tasks: backup.yml
   when: ynh_backup.scheduled
   tags: backup
 
-- name: Use Borg Backup with YunoHost
+- name: Use BorgBackup with YunoHost
   ansible.builtin.include_tasks: borgbackup.yml
   when: ynh_borg_backup_scheduled
   tags:
-  - backup
-  - borg
+    - backup
+    - borg
+
+- name: Use Restic with YunoHost
+  ansible.builtin.include_tasks: restic.yml
+  when: ynh_restic_backup_scheduled
+  tags:
+    - backup
+    - restic
diff --git a/roles/ynh_backup/tasks/restic.yml b/roles/ynh_backup/tasks/restic.yml
new file mode 100644
index 0000000..05467fd
--- /dev/null
+++ b/roles/ynh_backup/tasks/restic.yml
@@ -0,0 +1,39 @@
+---
+#-----------------------------------------------------------------------------#
+# ansible-yunohost allows to deploy Yunohost using Ansible                    #
+# Copyright 2021-present Lydra https://www.lydra.fr/                          #
+#                                                                             #
+# this program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# this program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program. If not, see <http://www.gnu.org/licenses/>.        #
+#                                                                             #
+#-----------------------------------------------------------------------------#
+- name: Download Restic role on localhost
+  ansible.builtin.command: "ansible-galaxy install do1jlr.restic,{{ do1jlr_restic_version }} -p {{ _ansible_role_directory }}"
+  delegate_to: localhost
+  become:      False
+  tags:
+    - backup
+    - restic
+
+- name: Gather facts for Restic role
+  ansible.builtin.setup:
+  tags:
+    - backup
+    - restic
+
+- name: Run Restic role
+  ansible.builtin.import_role:
+    name: do1jlr.restic
+  tags:
+    - backup
+    - restic
diff --git a/roles/ynh_backup/vars/main.yml b/roles/ynh_backup/vars/main.yml
index 2b41c9e..4b5fa40 100644
--- a/roles/ynh_backup/vars/main.yml
+++ b/roles/ynh_backup/vars/main.yml
@@ -22,3 +22,4 @@
 ynh_backup_src_script: "templates/ynh_backup.sh.j2"
 ynh_backup_dest_script: "/usr/local/bin/ynh_backup.sh"
 _ynh_backup_directory: "/home/yunohost.backup/archives"
+_ansible_role_directory: "~/.ansible/roles"
-- 
GitLab