Syncthing pour failover de mon NAS

Objectif: synchroniser les données de mon NAS (hébergé sur Turris Mox) avec d’autres machines, en pseudo-temps réel.

Syncthing est parfait pour ça. J’ai dû faire un peu de configuration pour qu’il soit capable de synchroniser des données en conservant leurs propriétaires (attribut owner), sans pour autant le faire tourner en tant que root.

Comme j’y mets les données de mon cluster Kubernetes, ce NAS devient un SPOF. Mais si les données sont synchronisées sur une autre machine, je peux facilement basculer dessus en cas de problème (ou pendant une maintenance).

Sur le routeur Turris (NAS principal)

Installer le paquet syncthing sur le Turris (via l’interface web ou en ligne de commande). La version fournie par Turris est un peu ancienne (1.4.0), mais suffisante pour l’essentiel.

Configurer /etc/config/syncthing pour l’activer (enabled à 1), et choisir un home sur le disque dur externe (pour ne pas user prématurément la carte SD du turris).

Donner les droits au user syncthing sur le répertoire partagé:

setfacl -R -m u:syncthing:rwX,d:u:syncthing:rwX /path/to/data

(édité le 26/09/2022: pour une raison que j’ignore, ces droits ne restent pas quand de nouveaux répertoires sont créés. J’ai donc mis un crontab pour qu’il se relance chaque nuit: dans /etc/crontabs/root, rajouter une ligne « @daily setfacl -R -m u:syncthing:rwX,d:u:syncthing:rwX /path/to/data »)

service syncthing restart

Pour accéder à son interface web, if faut faire un tunnel SSH:

ssh -L 9090:127.0.0.1:8384 root@ip_ou_dns_du_turris

Puis ouvrir un navigateur sur http://localhost:9090

Sur un serveur debian (failover)

Installer le package syncthing (version fournie par la distribution, ou une version plus récente depuis https://apt.syncthing.net/)

Créer un user syncthing:

sudo useradd syncthing
sudo mkdir /home/syncthing
sudo chown syncthing:syncthing /home/syncthing

Créer un répertoire /etc/systemd/system/syncthing@syncthing.service.d/ puis un fichier /etc/systemd/system/syncthing@syncthing.service.d/override.conf pour surcharger la configuration systemd de syncthing: y choisir un répertoire « home » différent si besoin (sur un disque externe plutôt que sur la mémoire flash, dans mon cas de figure), et autoriser cet exécutable à faire des chown/chmod et à modifier les fichiers qui ne lui appartiennent pas:

[Service]
# We have to clear this setting before setting a new value
ExecStart=
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart --logflags=0 --home=/mnt/hdd/syncthing
AmbientCapabilities=CAP_CHOWN CAP_FOWNER CAP_DAC_OVERRIDE

Si le répertoire /mnt/hdd/syncthing n’existe pas, il faut le créer (avec syncthing comme owner/group). Ensuite on peut créer et démarrer le service:

sudo systemctl daemon-reload
sudo systemctl start syncthing@syncthing.service
sudo systemctl enable syncthing@syncthing.service

La configuration de syncthing se trouve alors dans /mnt/hdd/syncthing/config.xml

Se connecter à l’interface web (avec un tunnel SSH, comme ci-dessus), connecter les 2 machines (au sens de syncthing), puis partager le répertoire du NAS du Turris avec cette machine. Par précaution, je configure dans les options avancées un type de partage « Réception seulement » (c’est-à-dire de l’actif/passif. A voir par la suite si j’ose passer en actif/actif)

Pour que le owner des fichiers ne soit pas perdu, il y a une option à activer : https://docs.syncthing.net/users/config.html?highlight=owner#config-option-folder.copyownershipfromparent . On peut le faire dans le fichier /mnt/hdd/syncthing/config.xml (dans le noeud XML correspondant au partage concerné):

<copyOwnershipFromParent>true</copyOwnershipFromParent>

(puis faire un « sudo systemctl restart syncthing@syncthing.service »)

Pour que ce « copyOwnershipFromParent » fonctionne, la solution simple aurait été de faire tourner syncthing sous le user root. Mais ce n’est vraiment pas recommandé en termes de sécurité: j’ai préféré le faire tourner sous le compte syncthing. C’est pour cela que j’ai dû ajouter des « AmbientCapabilities » au service systemd.

NB: il aurait été encore plus sécurisant de donner des droits uniquement sur le répertoire des données, avec des commandes setfacl. Mais je n’ai pas réussi à le faire fonctionner, à cause d’un mask qui revient à r-x et qui empêche le user syncthing de créer des répertoires

Puis on change manuellement le owner des sous-répertoires que l’on souhaite:

sudo chown -R xxx:yyy repertoire_du_nas_synchronisé

et syncthing conservera ce owner pour tous les fichiers en-dessous (nouveaux ou pas). Ce n’est pas parfait: si le owner n’est pas toujours le même dans un répertoire, syncthing ne saura pas faire. Mais ça suffit dans la grande majorité de mes cas.

En configurant un serveur NFS sur la machine où les données sont répliquées, elle peut servir de failover en cas d’indispo du NAS principal.

Sur un PC Ubuntu (sauvegarde)

Cela permet d’avoir une sauvegarde toujours à jour, en local.

La version de syncthing de la distribution est en général suffisante, et on peut la faire tourner avec le user courant.

Si besoin, on peut y appliquer la même technique pour garder les owners.

Sur une machine distante (pour un futur PRA)

On peut faire la même procédure pour une machine qui n’est pas sur le réseau local.

Si on ne fait rien de particulier elles peuvent communiquer entre elles via les serveurs relais de Syncthing. Mais on peut aussi passer par un tunnel SSH: https://docs.syncthing.net/users/tunneling.

J’ai mis ça en place dans l’objectif de mettre en place un PRA: si je fais le nécessaire pour savoir relancer mes pods sur le site distant, leurs données seront déjà là.

Monitoring

J’ai mis en place un monitoring simple de la synchro Syncthing, avec un cron quotidien qui vérifie que tout est OK, via son API REST: https://docs.syncthing.net/rest/folder-errors-get.html et https://docs.syncthing.net/rest/system-error-get.html. J’ai simplement créé un fichier /etc/cron.daily/check-syncthing exécutable, avec ce contenu:

#!/bin/bash
# Checks that Syncthing is OK

# API key found in the web interface, in advanced settings
API_KEY="xxx"
# Folder ids separated by spaces
FOLDER_IDS="yyy"

# Check errors for each folder
for FOLDER_ID in ${FOLDER_IDS}
do
        HTTP_STATUS=$(curl -SsL --write-out "%{http_code}" --header "X-API-Key:${API_KEY}" http://localhost:8384/rest/folder/errors?folder=${FOLDER_ID} -o /dev/null)
        HTTP_BODY=$(curl -SsL --header "X-API-Key:${API_KEY}" http://localhost:8384/rest/folder/errors?folder=${FOLDER_ID})
        if [ ${HTTP_STATUS} -gt 400 ]; then
                echo "Error using REST API for folder ${FOLDER_ID}. Status ${HTTP_STATUS}: ${HTTP_BODY}"
        else
                FOLDER_ERRORS_COUNT=$(echo "${HTTP_BODY}" | jq ".errors | length")
                if [ ${FOLDER_ERRORS_COUNT} -gt 0 ]; then
                        echo "${FOLDER_ERRORS_COUNT} folder error(s) found for folder ${FOLDER_ID}:"
                        echo "${HTTP_BODY}" | jq ".errors"
                fi
        fi
done

# Check global errors
HTTP_STATUS=$(curl -SsL --write-out "%{http_code}" --header "X-API-Key:${API_KEY}" http://localhost:8384/rest/system/error -o /dev/null)
HTTP_BODY=$(curl -SsL --header "X-API-Key:${API_KEY}" http://localhost:8384/rest/system/error)
if [ ${HTTP_STATUS} -gt 400 ]; then
        echo "Error using REST API for glabal errors. Status ${HTTP_STATUS}: ${GLOBAL_ERRORS}"
else
        GLOBAL_ERRORS_COUNT=$(echo "${HTTP_BODY}" | jq ".errors | length")
        if [ ${GLOBAL_ERRORS_COUNT} -gt 0 ]; then
                echo "${GLOBAL_ERRORS_COUNT} global error(s) found:"
                echo "${HTTP_BODY}" | jq ".errors"
        fi
fi

(il faut remplacer au début du fichier l’API-Key et les folder ids de votre instance)

Améliorations à envisager

Il serait probablement bon d’avoir plusieurs répertoires distincts sur le NAS, correspondant à différentes storageClasses k8s: celle par défaut avec une synchronisation toutes les 4h (par exemple), et une autre avec une synchronisation temps réel (pour ce qui est plus important).

Je verrai aussi à mettre en place un cron pour faire des .tar.gz quotidiens du contenu du répertoire (+ purge des plus anciens), sur la machine distante.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *