Une webcam à base de Raspberry Pi

Après maintenant plus d’un an en télétravail, et pouvant compter mes jours de présences en agence sur les doigts de mes deux mains (sans avoir à recourir à un changement de base de numération), et à l’heure de l’explosion des communications en visioconférence, ou au minimum en audioconférence, se pose la question de la webcam. Si mon ordinateur professionnel en possède une intégrée, ce n’est en revanche pas le cas de mon ordinateur fixe. Au détour dans mon agrégateur de flux RSS, j’ai vu passer un article évoquant Raspberry Pi et webcam. Trouvant l’idée intrigante: contourner l’utilisation première d’un Pi pour en faire une webcam, je me suis donc lancé dans la réalisation de ce petit projet.

Composants

Côté composants, rien de bien compliqué:

  • Un Raspberry Pi zéro W.
  • Un module image HQ.
  • Un objectif.
  • Une carte microSD.
  • Un câble USB.

Installation

Pour l’installation, que du classique dans le monde du Raspberry Pi. Il suffit de récupérer l’image système dans la partie release du projet showmewebcam sur GitHub, puis de l’écrire sur une carte micro SD avec son logiciel habituel, pour ma part, dd. Il ne reste plus qu’à connecter le module au Pi, insérer la carte et connecter le câble USB en utilisant le port USB de données du Pi (et pas celui d’alimentation, celui situé au milieu du Pi donc), puis à connecter le tout à un ordinateur.

Boîtier

Du côté du boîtier, j’ai trouvé ce modèle 3D plutôt esthétique, que j’ai imprimé en PETG noir. Après impression, petite déception, car l’assemblage nécessite des vis format M2 et/ou M2.5. Mon stock de M3 n’étant d’aucune utilité, j’étais bon pour quelques semaines supplémentaires d’attente pour recevoir quelques modèles de vis compatibles de longueurs variées. Ce qui nous donne au final le panachage suivant :

  • Pi: 3x M2x5.
  • Camera/Pi: 2x M2x8 and 1x M2x6.
  • Case: 4x M2.5×14.

Mise en place

Pour installer la caméra, j’ai modélisé un modèle 3D relativement simple permettant de connecter d’autres pièces provenant d’un modèle de bras articulé. Cela fait l’affaire, même si à l’usage, j’aurais dû mettre plus d’espace au niveau du connecteur. Ce sera pour la version 2.

Les modèles qui composent mon ensemble sont donc les suivants :

Autre mésaventure, après avoir assemblé le boîtier et visser la camera sur le support, j’ai constaté qu’à quelques millimètres près, je ne pouvais plus connecter la caméra, même en utilisant un câble coudé. J’ai donc fait l’acquisition d’un adaptateur 1/4 femelle vers 1/4 mâle pour rehausser la caméra de quelques centimètres.

Résultat

Le résultat est plutôt satisfaisant. La qualité d’image plutôt bonne, même si celle-ci est limitée à du 720p, la limite se situant à priori du côté du traitement des codecs sur le Pi pour espérer voir arriver du 1080p. Évoquons maintenant le sujet qui fâche (un peu) : la latence. La caméra est réactive, mais je crois détecter un léger décalage entre l’image et le son. Il faudrait effectuer le même test avec un Pi 4 à la place du Pi zéro, pour vérifier si la limite provient de la puissance de la plateforme, ou est propre au code utilisé. Raison pour laquelle il ne serait pas inutile de toujours avoir un Pi inutilisé en plus en réserve.

Bref, un projet intéressant à base de Raspberry Pi et qui permet de se familiariser avec les modules caméra du Raspberry Pi.

Sources

Test d’un role Ansible

Après avoir réorganisé l’ensemble de mes rôles Ansible, j’avais en tête de trouver un moyen de tester leur exécution dans un environnement vierge. L’idée n’est pas nouvelle. Le but : vérifier l’exécution du rôle plus facilement, c’est-à-dire, sans savoir à installer une nouvelle VM, ou à louer un serveur à cette occasion, même si cette dernière solution est, par expérience : pratique, assez rapide à mettre en œuvre et d’un faible coût en utilisant un serveur facturé à l’heure.

Ayant parcouru quelques dépôts de rôle Ansible présent sur Ansible Galaxy et ayant sauvegardé quelques articles évoquant le sujet, j’avais donc une idée des outils disponibles et des exemples fonctionnels. Il ne restait plus qu’à se retrousser les manches, et à tenter l’implémentation sur l’un de mes rôles les plus simples consistant à installer fail2ban.

Installation

Première étape avant configuration, l’installation des outils qui permettront de tester le rôle en exécutant une seule commande, à savoir : Molecule et virtualenv. Avec python3 installé, je commence donc par installer virtualenv via: pip install virtualenv. J’initialise ensuite un environnement virtuel python avec virtualenv dans le dossier contenant mon rôle ansible, soit les commandes :

$ pip3 install virtualenv
$ virtualenv -p python3 .venv
$ source .venv/bin/activate

Étape suivante, l’installation de molecule via pip.

$ pip3 install --upgrade setuptools
$ pip3 install 'molecule[ansible,docker,lint]'

Configuration basique

Maintenant que les outils sont installés, je génère la configuration de base avec la commande: molecule init scenario. Cette commande a pour effet de créer quatre fichiers.

  • INSTALL.rst : contient des instructions pour l’installation d’autres dépendances, ou des étapes de configuration à réaliser. Dans mon cas, installation de molecule[docker], que j’ai donc directement intégré dans la commande citée précédemment.
  • molecule.yml : fichier principal décrivant les outils utilisés pour le test.
  • converge.yml : le playbook ansible chargé de déployer le rôle testé.
  • verify.yml : un fichier ansible permettant de décrire les vérifications à effectuer après déploiement du rôle.

Comme j’utilise docker pour la gestion de l’instance de test, je suis les préconisations de la documentation pour vérifier que docker fonctionne correctement.

$ docker run hello-world

Une fois l’environnement prêt, je peux passer à l’exécution du test.

$ molecule test

En théorie, ce premier test devrait au minimum réussir jusqu’à l’étape de vérification, en fonction des modifications effectuées dans verify.yml. En cas d’erreur, je trouve très utile d’exécuter manuellement les différentes étapes de molecule test. A savoir:

  1. molecule create : Création de l’instance.
  2. molecule converge: Déploiement du rôle.
  3. molecule login : Connexion à l’instance pour aller explorer son état, très pratique en cas d’erreur.
  4. molecule verify : Exécuter les vérifications.
  5. molecule destroy : Nettoyage.

Il convient de noter que, si le rôle ansible utilise service pour vérifier l’état d’un service, la configuration par défaut présente dans le fichier converge.yml n’est pas suffisante. J’ai été bloqué un bon moment avant de trouver des précisions à ce sujet au détour d’une issue sur Github. C’est pourquoi je vais maintenant décrire la configuration que j’ai mise en place.

Cas concret

La configuration que je décrite à la suite permet donc de tester un rôle relativement simple, que j’utilise pour installer fail2ban à partir de l’un des paquets deb du projet. Paquet distribué dans la partie release du projet GitHub. Commençons par le fichier molecule.yml.

---
dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: instance
    image: geerlingguy/docker-debian10-ansible:latest
    command: ${MOLECULE_DOCKER_COMMAND:-""}
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
    privileged: true
    pre_build_image: true
provisioner:
  name: ansible
  playbooks:
    prepare: prepare.yml
verifier:
  name: ansible

Cette configuration présente quelques adaptations par rapport à la configuration par défaut. En particulier, du côté de platforms, où les instructions command et volumes sont nécessaires pour que l’instruction service dans ansible soit fonctionnelle. Notons aussi l’ajout de prepare dans provisioner. Cet ajout a pour but d’effectuer quelques actions supplémentaires sur l’instance juste après son installation et avant de commencer le déploiement du rôle. Le contenu du fichier prepare.yml est le suivant :

---
- name: Prepare
  hosts: all
  become: true
  tasks:
    - name: Install ssh
      apt:
        name: [ 'ssh' ]
        state: present
        update_cache: yes
        cache_valid_time: 3600
    - name: restart ssh
      service:
        name: ssh
        state: restarted

Après quelques tentatives de tests et d’erreur au démarrage du service fail2ban, dernière étape de mon rôle, je constate que ssh ne semble pas installé par défaut dans l’image docker choisie. Problème, fail2ban sous debian active par défaut une jail pour ssh et se plaint donc de ne pas trouver les fichiers de logs pour ssh. Ces quelques lignes ont donc pour but d’installer ssh et de démarrer le service associé. On pourrait discuter du fait que ces vérifications devraient être portées par le rôle, mais cela ne me semble pas pertinent, étant donné que mon utilisation du rôle se fait toujours dans le cas d’une installation sur une machine à laquelle j’accède via ssh. Le service est donc toujours présent dans ma situation.

Vient ensuite le fichier converge.yml. Pas de modification de ce côté là.

---
- name: Converge
  hosts: all
  become: true
  tasks:
    - name: "Include role-install-fail2ban"
      include_role:
        name: "role-install-fail2ban"

Enfin verify.yml dernier fichier de configuration. Pour un premier test, j’ai fait au plus simple et je me contente de vérifier que fail2ban fait bien partie des paquets installés sur le système.

---
- name: Verify
hosts: all
tasks:
- name: Gather package facts
package_facts:
manager: auto

- name: Verify Packages
assert:
that: "'{{ item }}' in ansible_facts.packages"
with_items:
- fail2ban

Voilà pour la configuration du test de mon rôle d’installation de fail2ban.

Conclusion

Après une première soirée passée sur le sujet du test d’un rôle ansible, je suis plutôt satisfait du résultat. Bien que le rôle soit l’un de mes rôles les plus simples, cela ne devrait pas être trop compliqué de généraliser cette méthode à l’ensemble de mes rôles, mais cela nécessitera sans aucun doute de nombreuses heures de développement. Comme souvent, c’est un processus itératif qui sera effectué progressivement sur les prochains mois. La simplicité de réaliser un test sur un système vierge est vraiment appréciable et permettra de vérifier qu’un rôle modifié fonctionne toujours, et cela, sans avoir à passer par le déploiement d’un serveur vierge. Prochaines étapes : étudier l’intégration avec le système d’actions GitHub et tester le playbook permettant le déploiement automatique d’Unicoda.

Sources

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