Sauvegarde distante dans le Cloud IBM avec restic

Bien que n’ayant subi aucune perte de données, à proprement parler (grâce à la sauvegarde, le serveur étant tout de même parti en fumé), dans l’incendie du datacenter OVH de Strasbourg, je me suis tout de même interrogé sur la pertinence du processus en place. Le système en place est-il suffisant ? Quelles sont ses limites ? Ses faiblesses ? Quels scénarios pourrait mettre en échec toute la politique de sauvegarde: corruption des données, échec silencieux du processus, problème technique chez les deux hébergeurs, etc ?

Au détour d’une lecture, je découvre peu après l’événement, l’existence d’un cloud IBM et surtout, son plafond d’utilisation gratuite pour la partie Object Storage, à savoir 25Go. Parfait pour mettre en place un deuxième point de sauvegarde distant et découvrir la plateforme !

Je liste à la suite les étapes qui m’ont permis de configurer un espace de stockage et d’obtenir les identifiants nécessaires à un accès distant, en espérant ne pas en avoir oublié (et que l’interface n’ai pas changé entre le moment d’écriture de ces lignes et le moment où vous les lisez).

Étapes de mise en place

  • Créer un compte sur https://cloud.ibm.com/registration.
  • Cliquer sur « Créer une ressource ».
  • Chercher et cliquer sur Object Storage.
  • Créer l’objet en lui donnant un nom de service.
  • Créer un compartiment (= bucket).
  • Donnez-lui un nom (correspondra à BUCKET_NAME).
  • Choisissez une classe de stockage (Standard par exemple).
  • Aller dans « Configuration » pour trouver les informations de points d’extrémité.
  • C’est le point d’extrémité public qui nous intéresse, s3.eu-de.cloud-object-storage.appdomain.cloud dans mon cas. C’est notre PUBLIC_ENDPOINT_LOCATION .
  • Ensuite, direction « Données d’identification pour le service ».
  • Cliquer sur « Nouvelles données d’identification ».
  • Activer l’option « Inclure un identifiant HMAC ».
  • Cliquer sur « Ajouter ».
  • Copier les données d’identification ainsi créées.
  • Parmi ces données, celles qui nous intéressent sont access_key_id et secret_access_key.

Configuration restic

Pour la configuration de restic, je reste bref et vous renvoi à la documentation. J’utilise cet outil de sauvegarde depuis au moins deux années sans aucun problème et je me rends compte que je ne lui ai jamais consacré d’article, ce qu’il faudra que je corrige. Pour faire simple, je configure les paramètres de connexion comme variables d’environnement. Ensuite, j’initialise le dépôt distant avec la commande d’initialisation restic, afin de pouvoir ensuite y envoyer des données. L’URL du stockage distant est de la forme: s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET_NAME.

$ export AWS_ACCESS_KEY_ID=access_key_id
$ export AWS_SECRET_ACCESS_KEY=secret_access_key
restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET_NAME init

Conclusion

Après quelques heures de tests et de configuration, je dispose maintenant d’un deuxième emplacement de sauvegarde distant, sans coûts supplémentaires autre que le temps de mise en place. Avec déjà quelques semaines d’utilisation, je n’ai pas rencontré d’erreurs lors de l’exécution de mes sauvegardes journalières, ce qui est toujours agréable. Un petit bémol tout de même, il ne semble pas possible de créer plusieurs buckets différents en version gratuite. Tous mes essais se sont soldés par une erreur dans l’interface de création, malheureusement sans information sur la raison précise de l’erreur. N’hésitez donc pas à refaire une tentative pour vérifier si le problème était temporaire ou non. De mon côté, je me suis accommodé d’un bucket unique.

Cette deuxième sauvegarde accroît encore davantage mon niveau de sérénité et ma certitude d’avoir peu de risque de subir une perte de données. Il me reste encore à trouver une bonne solution pour effectuer périodiquement un déploiement local à partir de la sauvegarde et ainsi vérifier son intégrité.

Inspirations

Synapse: Procédure de mise à jour

J’effectue quelques essais autour de l’hébergement d’un serveur synapse pour le système de messagerie matrix. Petit pense-bête pour retrouver facilement la procédure de mise à jour du serveur synapse, sans avoir à la (re)chercher à chaque fois dans la documentation.

$ sudo systemctl stop matrix-synapse.service
$ sudo su synapse
$ cd ~/synapse
$ source env/bin/activate
$ pip install --upgrade matrix-synapse
$ pip install --upgrade pip
$ exit
$ sudo systemctl start matrix-synapse.service

Chiffrement d’une image de container

Le chiffrement d’une image de container est un sujet assez peu évoqué. Les premiers exemples de solution n’apparaissant qu’à partir de 2019, poussé en particulier par Brandon Lum de IBM.

Le support de ces fonctionnalités ne semble pas très répandu pour le moment. Du côté des outils, nous parlons actuellement de buildah et skopeo pour la partie build, containerd et cri-o pour la partie runtime et enfin docker distribution pour la partie registry. A priori, il ne semble pas y avoir de support dans docker, mais un commentaire au détour d’une issue GitHub semblait indiquer que cela sera le cas. Je n’ai en revanche pas trouvé d’annonce ou de roadmap permettant de valider ce point.

Voici à la suite, les étapes de mon test de chiffrement d’une image de container, réalisé sous ArchLinux, mais transposable à toute autre distribution, à condition de pouvoir installer l’ensemble de dépendances.

Préparation

Installation des Dépendances

Sous ArchLinux, installation des composants:

$ sudo pacman -S docker containerd buildah podman minikube kubectl helm
$ sudo systemctl start docker
$ sudo gpasswd -a <user> docker

Registre d’image local

Démarrage d’un registre local pour y stocker l’image chiffrée que je vais générer.

$ sudo systemctl start docker  
$ sudo systemctl status docker
$ sudo docker run -d -p 5000:5000 --restart=always --name registry registry:2

Génération du couple de clé

$ openssl genrsa -out testKey.pem 2048
$ openssl rsa -in testKey.pem -pubout -out testKey.pub.pem

Création d’une image basique

$ mkdir app
$ cd app
$ nano Dockerfile
$ nano secret-file

Avec pour contenu du Dockerfile:

FROM nginx:latest
COPY secret-file /secret-file

Et secret-file, un simple fichier texte contenant une chaîne de caractère aléatoire.

Construction de l’image

$ sudo buildah bud -t encrypted-test .

Export

Export vers le registre local.

$ sudo buildah push --tls-verify=false --encryption-key jwe:../testKey.pub.pem encrypted-test localhost:5000/vvision/encrypted-test:latest

Export local dans une archive oci.

$ sudo buildah push --encryption-key jwe:../testKey.pub.pem  encrypted-test oci-archive:encrypted-test:latest

Nettoyage

Suppression de toutes les images locales.

$ sudo buildah rmi --all

Récupération de l’image

$ sudo buildah pull --tls-verify=false --decryption-key keys/testKey.pem localhost:5000/vvision/encrypted-test

Exécution

$ sudo podman run -it localhost:5000/vvision/encrypted-test
/bin/bashroot@3e3c1ccde93c:/# cat secret-file
ASecretSecret

Vérification

Tentative de récupération de l’image depuis le registre local, sans préciser la clef.

$ sudo buildah pull --tls-verify=false localhost:5000/vvision/encrypted-test Getting image source signatures
Copying blob 302a0c0a162e [--------------------------------------] 0.0b / 914.0b
Copying blob 83e2b8dcdf4b [--------------------------------------] 0.0b / 27.1MiB
Copying blob 9b30e9f5d77e [--------------------------------------] 0.0b / 617.0b
Copying blob 0f9f80250abf [--------------------------------------] 0.0b / 134.0b
Copying blob 7c9b3bc4d85b [--------------------------------------] 0.0b / 26.3MiB
Copying blob 538a8875d492 [--------------------------------------] 0.0b / 678.0b
Error decrypting layer sha256:83e2b8dcdf4bfca8bea0b23e771e84074e4cc308bf892bd6d63b3a0c9dab0564: missing private key needed for decryption

Utilisation dans Kubernetes

Démarrage d’un cluster Kubernetes avec minikube.

$ minikube start --network-plugin=cni --enable-default-cni --container-runtime=cri-o --bootstrapper=kubeadm --insecure-registry="<local_ip_addr>:5000"
$ minikube dashboard

Mécanisme de synchronisation des clefs

Installation du composant responsable déploiement de la clef de déchiffrement.

$ git clone https://github.com/IBM/k8s-enc-image-operator.git
$ cd k8s-enc-image-operator
$ kubectl create namespace enc-key-sync
$ helm install --namespace=enc-key-sync k8s-enc-image-operator ./helm-operator/helm-charts/enckeysync/

Ajout de la clé dans Kubernetes Secret

$ kubectl create -n enc-key-sync secret generic --type=key --from-file=testKey.pem test-decryption-key

Déploiement du container

$ kubectl run test-enc --image=localhost:5000/vvision/encrypted-test

Conclusion

Au moment de mes premiers tests, je ne crois pas avoir rencontré de difficulté à récupérer l’image dans le registre local. Néanmoins, lors de tests plus récents, je n’ai pas réussi à reproduire ce fonctionnement sur d’autres plateformes. En revanche, une récupération depuis le registre docker, ou celui proposé dans la Google Cloud Plateform fonctionne.

Il me reste maintenant à reproduire ce mécanisme sur un cas réel complet, c’est-à-dire, de l’intégration au processus et aux outils de CI/CD, à la configuration du cluster Kubernetes cible, avec pour objectif un déploiement de la solution en production.

Sources

RackPi : Mise en place

Ayant fait l’acquisition d’un rack de taille moyenne l’an dernier, dans le but d’organiser et de ranger correctement tous mes appareils informatiques: switch, NAS, routeur, Raspberry Pi, onduleur, je cherchais une bonne manière d’organiser les Raspberry Pi. Après quelques recherches parmi les modèles 3D existants sur Thingiverse et Prusa Printers, j’ai trouvé le projet RackPi: nombreuses photos, documentation plutôt complètes, étapes d’assemblage claires. Il ne restait plus qu’à me lancer dans la construction de mon exemplaire.

Assemblage

Quelques notes concernant l’assemblage. Premièrement, les résistances, celle de 1k ohm est à utiliser avec la led et celle de 10k ohm sert pour le bouton poussoir. Pour les vis, j’utilise du côté des headers, une M2.5 de 14mm de longueur, afin de pouvoir sécuriser par la suite un PoE hat. De l’autre côté, j’ai fait le choix d’une M2.5 de longueur 10mm. Les différents composants sont maintenus en place à coup de pistolet à colle. Étape incontournable avant l’encollage: tester, tester et tester ! C’est du bon sens, mais cela évite de devoir tout démonter après coup. Illustration: dans le deuxième exemplaire que j’ai assemblé, ma partie bouton ne fonctionnait pas et j’ai donc pu en refaire une nouvelle avant de procéder à l’assemblage.

Gros plan sur le circuit du bouton poussoir.

Câblage

En suivant le câblage et en m’aidant du document de référence concernant les GPIO du Raspberry Pi, j’obtiens la configuration suivante.

LED

(+) -> Fil jaune -> Pin 16 -> GPIO 23
(-) -> Fil orange -> Pin 14 -> GROUND

Bouton marche/arrêt

Fil violet -> Pin 39 -> GROUND
Fil bleu -> GLOBAL_EN

Bouton poussoir

Fil violet -> Pin 17 -> POWER 3V3
Fil gris -> Pin 38 -> GPIO 20
Fil blanc -> Pin 25 -> GROUND

Écran

SDA -> Fil vert -> Pin 3 -> GPIO 2
SCL -> Fil jaune -> Pin 5 -> GPIO 3
VCC -> Fil rouge -> Pin 1 -> POWER 3V3
GND -> Fil noir -> Pin 6 -> GROUND

Câblé et prêt à coller.

Configuration du pi

Activation de I2C et SPI via raspi-config:

$ sudo raspi-config
-> Interfacing Options
-> SPI
-> I2C

Installation des dépendances nécessaires:

$ sudo apt-get install python3-pip
$ sudo pip3 install adafruit-circuitpython-ssd1306
$ sudo apt-get install python3-pil
$ sudo apt-get install -y python-smbus
$ sudo apt-get install -y i2c-tools
$ sudo pip3 install psutil

Vérification du câblage de l’écran. Celui-ci doit apparaître à l’adresse 3c.

sudo i2cdetect -y 1

Ensuite, récupération du script d’affichage des informations sur l’écran. Deux URL possibles, celle du fichier sur Thingiverse, ou celle pointant vers le même script dans un dépôt sur Github que je contrôle. De cette manière, je peux m’assurer que le code exécuté et bien celui que j’ai relu et qu’il n’a pas été modifié ultérieurement sur Thingiverse, dans le cas d’une installation ou d’une réinstallation future.

# Utilisation du fichier "officiel".
$ wget https://www.thingiverse.com/download:8343823 -O infoscreen.py
# Utilisation d'une copie du fichier dans un dépôt que je contrôle.
$ wget https://raw.githubusercontent.com/vvision/rack-pi/main/infoscreen.py
$ sudo chmod 755 infoscreen.py

Exécution du script pour vérifier son fonctionnement.

$ sudo python3 infoscreen.py

Afin de s’assurer que le script est lancé automatiquement au démarrage, on modifie rc.local en ajoutant la ligne suivante, juste avant la ligne exit 0, via sudo nano /etc/rc.local et en modifiant le chemin vers l’emplacement du script sur le disque si besoin.

sudo python3 /home/pi/infoscreen.py &

Côté contrôle, un appui simple sur le bouton permet d’allumer l’écran. Un appui d’environ 8s déclenche un redémarrage et au-dessus de 12s, le pi déclenchera un shutdown. En cas d’arrêt, si vous avez câblez le bouton slider sur le pin central EN/GLOBAL_EN de votre pi 3+ ou de votre pi 4, il est possible de couper l’alimentation du pi grâce à ce bouton une fois le pi éteint.

Conclusion

Quelques mots pour terminer. Le résultat est extrêmement satisfaisant, mon Raspberry Pi est maintenant correctement installé dans le rack (ce sera encore mieux dès que j’aurais trouvé des vis auto-taraudantes correctes). L’écran est utile pour avoir l’adresse IP du Pi, surtout dans les premiers moments de mise en place, le temps de configurer une IP statique au niveau du DNS local. Sinon, les informations d’utilisation du CPU et de la RAM sont toujours bonne à prendre lorsqu’on s’interroge sur la charge du Pi, et deviennent donc facilement accessible. Les deux boutons permettant de contrôler extinction, redémarrage et de maintenir éteint le Pi sont également utiles puisque je peux désormais effectuer ces opérations sans avoir à ouvrir une connexion SSH.

En bref, un projet intéressant, qui ne se limite pas à installer quelques lignes de codes, mais permet de pratiquer mes compétences en soudure, de découvrir la partie GPIO des Raspberry PI et au final d’améliorer l’organisation du rack.