Passer au contenu principal

Borgmatic

Borgmatic est un wrapper autour de Borg qui en simplifie infiniment l’utilisation.

Installation

Il nous faut d’abord Borg (utilisez la version des backports Debian si vous êtes encore en stretch) :

apt install borgbackup

D’habitude, je préfère utiliser les paquets Debian, mais la version pip de Borgmatic apporte une option en plus que j’apprécie particulièrement.

apt install python3-pip
pip3 install borgmatic

Configuration

C’est là que Borgmatic est pratique : il permet une configuration très simple de Borg.

Générez un fichier de configuration :

generate-borgmatic-config

Cela créera le fichier /etc/borgmatic/config.yaml. Si vous souhaitez que la configuration soit dans un autre fichier :

generate-borgmatic-config -d /etc/borgmatic/autre_config.yaml

La configuration générée (exemple) est très simple à comprendre et auto-documentée, je ne vais l’expliquer, je vais me contenter d’en mettre certains points en valeur

Le dépôt

On le verra plus bas, j’utilise un serveur distant pour faire les sauvegardes. Donc :

location:
    repositories:
        - borg@server:/var/lib/borg/depot/

La passphrase

Choisissez quelque chose de costaud et sauvegardez-là quelque part !

storage:
    encryption_passphrase: "foo-bar-baz"

Utilisation d’une clé SSH particulière

Je crée plus bas une clé SSH dédiée pour Borg pour faire les sauvegardes sur le serveur distant. Il faut donc que j’indique à Borgmatic que je souhaite utiliser cette clé.

storage:
    ssh_command: ssh -i /root/.ssh/id_borgmatic

Les hooks

Situés à la fin du fichier de configuration, il s’agit d’actions qui seront effectuées avant et après la sauvegarde, ou en cas de problème lors de l’exécution.

Personnellement, j’aime bien avoir la liste de mes sauvegardes après qu’une sauvegarde soit effectuée. Je vais donc écrire :

hooks:
    after_backup:
        - /usr/local/bin/borgmatic list

Comme il est conseillé d’exporter la clé du dépôt borg, je mets aussi ceci dans les hooks :

    before_backup:
        - borg key export borg@server:/var/lib/borg/depot/ /etc/ssl/private/borg.key

Rétention et vérification du dépôt et des archives

Je fais ça sur le serveur, j’y reviendrai plus loin.

Cron

On crée un petit cron (avec l’utilisateur root) pour sauvegarder régulièrement l’ordinateur :

35 0 * * * borgmatic create -c /etc/borgmatic/mon_pc.yaml --stats 2>&1

Si ce n’est pas un serveur allumé 24h/24, il est préférable de mettre ça dans /etc/anacrontab :

@daily  15      backup borgmatic create -c /etc/borgmatic/mon_pc.yaml --stats 2>&1

Cela lancera le backup tous les jours, 15 minutes après le démarrage du pc. Ou plus tard.

Attention, sur Debian, les tâches anacron ne sont lancées que si vous êtes sur secteur ! Pour changer ça, reportez-vous au fichier /usr/share/doc/anacron/README.Debian.

Préparation du serveur du dépôt distant

Je préfère faire mes sauvegardes sur un disque distant : si mon disque lâche, j’aurai toujours mes sauvegardes.

Pour ce faire, je crée une clé dédiée sur l’ordinateur à sauvegarder, sans mot de passe vu que borgmatic va tourner automatiquement :

ssh-keygen -o -a 100 -t ed25519 -f /root/.ssh/id_borgmatic -N ''

Sur le serveur de sauvegarde, j’installe Borg et Borgmatic comme précédemment puis je crée un utilisateur borg :

adduser --system --home /var/lib/borg --shell /bin/bash --disabled-password borg
mkdir /var/lib/borg/.ssh
chmod 700 /var/lib/borg/.ssh
touch /var/lib/borg/.ssh/authorized_keys
chmod 600 /var/lib/borg/.ssh/authorized_keys
chown -R borg: /var/lib/borg/.ssh

Et je mets la clé publique créée précédemment dans /var/lib/borg/.ssh/authorized_keys, avec quelques restrictions :

command="/usr/bin/borg --umask=077 --info serve --append-only --restrict-to-repository /var/lib/borg/becky2/",restrict ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX root@mon_pc

On génère une configuration :

generate-borgmatic-config -d /etc/borgmatic/mon_pc.yaml

Et c’est dans cette configuration qu’on va configurer les durées de rétention et les vérifications du dépôt et des archives.

Rétention

C’est très simple. Pour garder un backup par mois sur les 6 derniers mois, un par semaine sur les 8 dernières semaines et un par jour sur les 14 derniers jours, il suffit de :

retention:
    keep_daily: 14
    keep_weekly: 8
    keep_monthly: 6

Comme Borg déduplique comme un monstre, cela ne prendra pas énormément de place (sauf si vous sauvegardez des trucs qui changent tout le temps)

Vérification du dépôt et des archives

Cela permet de s’assurer que votre dépôt et vos archives sont utilisables et non corrompus. Je me contente de décommenter la configuration proposée :

consistency:
   checks:
       - repository
       - archives
   check_last: 3

J’ai laissé check_last à 3 car vérifier toutes les archives peut être fort long. Donc une vérification des 3 dernières devrait faire l’affaire, surtout si on vérifie quotidiennement.

Dépôt

Attention, comme on est là sur le serveur distant, il faut lui donner un dossier local !

location:
    repositories:
        - /var/lib/borg/depot/

Script de suppression d’anciennes sauvegardes / vérification des archives

Il y a des éléments dans ce script qui prendront tout leur sens plus tard.

NOTA BENE : ce script est tout à fait adaptable à un usage à Borg sans Borgmatic.

On prépare le terrain :

apt install libcpanel-json-xs-perl
mkdir -P /opt/borgmatic-stats/tmp/ /opt/borgmatic-stats/json/
cd /opt/borgmatic-stats/
wget https://framagit.org/snippets/3716/raw -O verify-archives.pl
chmod 700 -R /opt/borgmatic-stats/
chown borg: -R /opt/borgmatic-stats/

Puis on met ceci dans /opt/borgmatic_prune_and_check.sh :

#!/bin/bash
cat <<EOF
==============================================
== mon_pc
==============================================
EOF

/usr/local/bin/borgmatic list -c /etc/borgmatic/mon_pc.yaml --json > /opt/borgmatic-stats/tmp/mon_pc.json.tmp
CONTINUE=0
if [[ -e /opt/borgmatic-stats/json/mon_pc.json ]]
then
    echo "Checking repository consistency."
    CONTINUE=$(/opt/borgmatic-stats/verify-archives.pl --old /opt/borgmatic-stats/json/mon_pc.json --new /opt/borgmatic-stats/tmp/mon_pc.json.tmp)
    echo "Repository consistency checked."
else
    CONTINUE=1
fi

if [[ $CONTINUE == '1' ]]
then
    ## Check
    /usr/local/bin/borgmatic check -c /etc/borgmatic/mon_pc.yaml 2>&1 && \
    echo "Repository checked." && \
    ## Allow pruning
    borg config /var/lib/borg/depot/ append_only 0 && \
    ## Prune
    /usr/local/bin/borgmatic prune -c /etc/borgmatic/mon_pc.yaml --stats 2>&1 && \
    echo "Repository pruned."
    ## List
    echo "Borg archives of mon_pc:"
    /usr/local/bin/borgmatic list -c /etc/borgmatic/mon_pc.yaml 2>&1

    /usr/local/bin/borgmatic list -c /etc/borgmatic/mon_pc.yaml --json > /opt/borgmatic-stats/json/mon_pc.json

    ## Disallow pruning
    borg config /var/lib/borg/depot/ append_only 1

    echo ''
else
    cat <<EOF

******************************************
* ALERT ON mon_pc! *
******************************************
All of the old archives can’t be retrieved from the repository (/var/lib/borg/depot/)!

Someone may have deleted an archive from mon_pc.

Pruning has been aborted. Please manually review the repository.

$CONTINUE
EOF
fi

On n’oublie pas de le rendre exécutable :

chmod +x /opt/borgmatic_prune_and_check.sh

Cron

Attention ! Il faut éditer la crontab de l’utilisateur borg :

crontab -e -u borg

Et son contenu :

15 3 * * * /opt/borgmatic_prune_and_check.sh

Initialisation du dépôt de sauvegarde

Sur l’ordinateur à sauvegarder :

borgmatic init -c /etc/borgmatic/mon_pc.yaml --append-only -e repokey

Le dépôt est initialisé en append-only (mais de toute façon, on le force dans le .ssh/authorized_keys du serveur distant) et avec un chiffrement par mot de passe mais avec la clé dans le dossier du dépôt distant.

Lancement d’une sauvegarde

Sur l’ordinateur à sauvegarder :

borgmatic create -c /etc/borgmatic/mon_pc.yaml --stats

Le --stats est l’option que j’affectionne et dont je parlais au début : cela affiche des statistiques sur la sauvegarde qui a été faite : sa taille, sa taille compressée, sa taille dédupliquée, le temps que ça a pris…

Afficher la liste des sauvegardes

borgmatic list -c /etc/borgmatic/mon_pc.yaml 

Restauration de sauvegardes

Voir la section kivabien de l’article sur Borg.

Explication de l’option --append-only

Contrairement à ce qu’on pourrait croire, --append-only n’empêche pas de supprimer des sauvegardes (Ce ticket Github m’en a fait prendre conscience). Ainsi, un attaquant ayant pris le contrôle de l’ordinateur à sauvegarder pourra supprimer des sauvegardes. Sauf qu’elle ne seront pas vraiment supprimées : l’attaquant aura créé des transactions qui indiquent la suppression des sauvegardes mais ces transactions ne seront rééllement appliquées que lors d’une action comme prune depuis un ordinateur qui aura le droit de faire sauter le append-only. C’est comme un BEGIN TRANSACTION; sans COMMIT; en SQL 🙂

C’est le cas du script /opt/borgmatic_prune_and_check.sh qui fait effectivement sauter (et le remet après) le verrou avec :

borg config /var/lib/borg/depot/ append_only 0

Comme on automatise la suppression des anciennes sauvegardes avec cron, il nous faut un moyen de repérer de tels changements. C’est tout le but de /opt/borgmatic-stats/verify-archives.pl qui compare la liste des archives du dépôt avec la liste créée la dernière fois que /opt/borgmatic_prune_and_check.sh a supprimé les anciennes archives. Comme le serveur distant est le seul à normalement pouvoir supprimer des archives, il est logique de retrouver toutes les anciennes archives au lancement suivant.

Ainsi, en cas d’incohérence, la suppression des anciennes archives ne s’effectue pas.

NOTA BENE : ceci n’empêchera pas un attaquant de changer la configuration de borgmatic pour lui faire sauvegarder des trucs inutiles plutôt que ce qui vous intéresse. Ou de couper les sauvegardes. C’est un inconvénient des sauvegardes en push plutôt qu’en pull. Je n’ai pas encore trouvé de solution à ça. Peut-être un script qui analyserait la sortie de borgmatic create --stats --jon qui me permettrait de repérer des changements importants dans la taille de la sauvegarde, le temps de sauvegarde ou le ratio de déduplication ?

En cas d’incohérence : restaurer des sauvegardes supprimées par un attaquant

Sur le serveur distant :

su - borg
cd /var/lib/borg/depot/

Regarder le log des transactions pour trouver les n° des transactions suspectes : cat transactions

Ce qui nous donne un truc du genre :

transaction 23, UTC time 2019-08-01T13:40:34.435019
transaction 25, UTC time 2019-08-01T13:42:05.662188
transaction 27, UTC time 2019-08-01T13:43:18.403771
transaction 29, UTC time 2019-08-01T13:43:53.306636
transaction 31, UTC time 2019-08-01T13:44:51.831937
transaction 33, UTC time 2019-08-01T13:45:17.786886
transaction 35, UTC time 2019-08-01T22:17:12.044179
transaction 37, UTC time 2019-08-02T08:02:55.005430
transaction 43, UTC time 2019-08-02T22:17:10.068843
transaction 45, UTC time 2019-08-03T22:17:09.625745
transaction 47, UTC time 2019-08-04T22:17:09.673447
transaction 49, UTC time 2019-08-05T22:17:13.709208
transaction 51, UTC time 2019-08-06T10:11:26.301496
transaction 53, UTC time 2019-08-06T10:20:46.178014
transaction 55, UTC time 2019-08-06T10:26:47.940003

Là, il faut savoir quand a eu lieu le problème. Ici, c'était le 6 août à partir de 10h. Donc les transactions 51 à 55. Mais attention ! Il s'agit en fait des transactions 50 à 55 : le numéro indiqué dans le fichier est celui du dernier fichier modifié par la transaction. Il faut donc partir du n+1 de la dernière transaction correcte.

Après, c'est facile :

rm hints.* index.* integrity.*
rm data/**/{50..55}

On finit par rafraîchir le cache du dépôt :

borg delete --cache-only /var/lib/borg/depot/

Sur l’ordinateur sauvegardé, par contre, je n’arrivais pas à supprimer le cache, j’avais ce message :

Cache, or information obtained from the security directory is newer than repository - this is either an attack or unsafe (multiple repos with same ID)

Ceci m'a réglé le problème :

rm -rf ~/.cache/borg/le_dossier_avec_un_nom_monstrueux
rm -rf ~/.config/borg/security/un_autre_dossier_avec_un_nom_monstrueux