Mettre à jour nos images Docker et recevoir les notifications

Nous avons vu dans l’article sur les bonnes pratiques de sécurité que la mise à jour de notre système, mais aussi celle de nos applications, était un impératif. Elle a un double objectif :

  • maintenir le niveau de sécurité le plus élevé raisonnablement atteignable,
  • bénéficier des nouvelles fonctionnalités des applications.

Pourquoi mettre à jour nos images Docker ?

Sur le premier point, il faut garder en tête que compte tenu de leur complexité, les systèmes d’exploitation et les applications modernes ont inévitablement des vulnérabilités. De nombreux acteurs recherchent ces vulnérabilités soit pour les exploiter, soit pour aider à leur correction. La rapidité de mise en oeuvre de ces corrections dans le code des applications est un élément clé de la confiance que nous pouvons leur accorder. Ainsi, si une application n’a pas fait l’objet d’évolution depuis des mois, nous devrons rechercher une solution alternative car elle pourrait être en voie d’abandon. Mais la correction des failles dans le code est inutile si on ne met pas à jour les applications sur notre serveur. Cet article vise à mettre en oeuvre un outillage simple et routinier pour nous assurer que nous disposons toujours des versions à jour de nos applications installées via Docker.

Pour cela, nous allons nous appuyer sur l’application Watchtower. Cette application va nous indiquer quelles sont les applications dont l’image Docker a évolué. Je vous propose ici de ne pas automatiser l’installation des nouvelles images ; en effet, certaines évolutions peuvent être en rupture (« breaking changes »). Dans ces cas, une réflexion peut être nécessaire sur l’impact global sur notre système. Par exemple, nous avons installé l’application Immich, qui est au moment de la rédaction de cet article dans une phase de développement très actif : elle fait l’objet d’évolutions fortes, avec des livraisons une à plusieurs fois par semaine, et quelquefois des changements d’architecture. Dans ces derniers cas, l’impact sur notre fichier docker-compose.yml, voire éventuellement sur d’autres éléments, doit être analysé. Aussi, je proposerai, conjointement à Watchtower, l’installation d’une application de notification, ntfy, par laquelle Watchtower nous informera des évolutions d’images, sans intervenir opérationnellement sur celles-ci.

L’installation de Watchtower

Mais suffisamment de baratin, mettons-nous au boulot. En fait, il n’y a rien de plus simple que de mettre en place Watchtower. Une fois connecté à notre Raspberry Pi, nous allons évidemment le mettre à jour, puis créer un répertoire watchtower, aller dans ce répertoire et éditer un fichier docker-compose.yml.

sudo mkdir watchtower
cd watchtower
sudo nano docker-compose.yml

Et nous allons y saisir les éléments suivants :

services:
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - WATCHTOWER_SCHEDULE=0 0 15 * * 0
      - WATCHTOWER_MONITOR_ONLY=true
      - TZ=Europe/Paris
      - UID=1000
      - GID=1000

Quelques petites nouveautés sont à signaler dans notre cheminement pour comprendre Linux et Docker. La première porte sur la ligne des volumes : vous voyez apparaître /var/run/docker.sock. Il s’agit de la « Docker socket » ou interface de communication avec Docker. Cette interface va permettre à Watchtower d’échanger des données avec Docker et notamment d’avoir de la visibilité sur les images que nous utilisons. Vous aurez remarqué que nous sommes en lecture seule (« ro », read only) sur cette interface. En effet, nous ne voulons pas que Watchtower puisse lancer des actions Docker, telles que redémarrer un conteneur avec la nouvelle image : nous allons le faire manuellement, ce que vous retrouverez dans la variable « WATCHTOWER_MONITOR_ONLY » que nous mettons à « true ». Par ailleurs, nous cherchons avec Docker à compartimenter nos applications ; le fait de donner à une application des droits d’administration Docker serait une très mauvaise pratique.

Nous trouvons également une notation étrange pour la variable « WATCHTOWER_SCHEDULE ». Il s’agit d’indiquer la planification des traitements de Watchtower selon le format Crontab. Je vous invite à approfondir le fonctionnement de Crontab et de Cron, qui vous seront probablement souvent utiles. La notation est basée sur des nombres successifs représentant les minutes, les heures, les jours dans le mois, les mois et les jours de la semaine ; ici, le traitement se déclenchera chaque dimanche à 15:00.

Le reste de notre fichier docker-compose.yml est très standard. Il constitue la base de notre système de vérification de la mise à jour des images, mais nous n’avons pas encore de système de notification. Nous allons donc le mettre en place.

L’installation du client Ntfy

Ntfy (prononcer notifaï, comme notifier en anglais) est une application de notification comme son nom l’indique. Elle est disponible sur Android comme sur iOS, et présente le gros avantage de constituer une brique importante de l’utilisation de GrapheneOS. Nous l’installons sur notre téléphone.

Après l’avoir ouverte, nous tapons sur l’icône « + » en bas à droite pour créer un nouveau sujet et saisissons une chaîne de caractères générée par notre gestionnaire de mots de passe (pour être certains que nous seuls aurons accès à ce canl. Cela crée un canal de communication du même nom sur l’application. Pour vérifier qu’il fonctionne bien, nous retournons sur notre Raspberry Pi et saisissons :

curl -d "Le canal est ouvert" ntfy.sh/<notre-chaîne-de-caractères>

Si nous recevons sur notre téléphone une notification indiquant « Le canal est ouvert », nous avons réussi.

Nous réouvrons notre fichier docker-compose.yml et ajoutons les éléments mis en évidence ci-dessous :

services:
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - WATCHTOWER_SCHEDULE=0 0 15 * * 0
      - WATCHTOWER_MONITOR_ONLY=true
      - WATCHTOWER_NOTIFICATIONS=shoutrrr
      - WATCHTOWER_NOTIFICATION_SKIP_TITLE=True
      - WATCHTOWER_NOTIFICATION_URL=ntfy://ntfy.sh/<notre-chaîne-de-caractères>?title=WatchtowerUpdates
      - TZ=Europe/Paris
      - UID=1000
      - GID=1000

Notre conteneur est prêt à démarrer. Nous le lançons :

sudo docker compose up -d

Nous devrions alors recevoir une notification nous indiquant que le prochain passage du traitement est prévu pour le dimanche à 15 heures.

Monter notre serveur de notifications

Bon, on a dit qu’on ne voulait pas transmettre nos données aux GAFAM. Là nous avons mis en place un système de notifications, non pas basé sur les GAFAM mais quand-même sur un serveur qui nous est extérieur : le serveur de notifications ntfy.sh. Ayons en tête que nous pouvons mettre en place notre propre serveur de notifications et ne plus nous appuyer sur celui d’un tiers. Si cela vous intéresse, nous pouvons le faire ensemble, même si c’est loin d’être indispensable.

Donc nous allons créer un répertoire ntfy, aller dedans et créer un fichier docker-compose.yml :

sudo mkdir ntfy
cd ntfy
sudo nano docker-compose.yml

Et saisir :

services:
  ntfy:
    image: binwiederhier/ntfy:latest
    container_name: ntfy
    command:
      - serve
    environment:
      - TZ="Europe/Paris"
      - PUID=1000
      - GUID=1000
      - NTFY_BASE_URL=https://ntfy.<domaine.tld>
      - NTFY_BEHIND_PROXY=true
      - NTFY_AUTH_FILE=/var/lib/ntfy/auth.db
      - NTFY_AUTH_DEFAULT_ACCESS=deny-all
    volumes:
      - ./cache:/var/cache/ntfy
      - ./etc:/etc/ntfy
      - ./lib:/var/lib/ntfy
    ports:
      - 8089:80
    restart: unless-stopped

Ce fichier est assez basique pour se passer de nombreux commentaires. On peut simplement préciser qu’on a indiqué

  • le domaine sur lequel notre application va s’accrocher,
  • qu’il va être positionné derrière notre proxy inverse,
  • que nous allons créer une gestion des accès (NTFY_AUTH_FILE) ; cela n’est pas indispensable, mais nous permettra de nous assurer que personne d’autre que nous ne reçoive les notifications qui nous sont destinées,
  • et que nous allons donc refuser l’accès à quiconque n’est pas authentifié.

Le numéro de port est choisi de manière plus ou moins aléatoire ; il suffit qu’il ne soit utilisé par aucune autre application et qu’il soit supérieur à 1024 (les ports inférieurs à 1024 sont réservés à certains usages).

Nous lançons le conteneur :

sudo docker compose up -d

Puis créons notre utilisateur afin de nous donner l’accès :

sudo docker exec -ti ntfy ntfy user add --role=admin k-sper.fr

Nous saisissons un mot de passe. Ensuite, nous allons créer un jeton (token) pour que nos applications (en l’occurrence Watchtower) passent des messages à notre serveur sans que nous ayons à saisir notre mot de passe :

sudo docker exec -ti ntfy ntfy token add k-sper.fr

Nous notons soigneusement ce jeton et nous disposons désormais d’un serveur de notification et d’un accès administrateur via le compte k-sper.fr.

Maintenant nous allons modifier le comportement de Watchtower afin qu’il s’appuie sur ce nouveau serveur. Nous retournons dans le répertoire watchtower et arrêtons le service :

sudo docker compose down

Nous éditons le fichier docker-compose.yml :

services:
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - WATCHTOWER_SCHEDULE=0 0 15 * * 0
      - WATCHTOWER_MONITOR_ONLY=true
      - WATCHTOWER_NOTIFICATIONS=shoutrrr
      - WATCHTOWER_NOTIFICATION_SKIP_TITLE=True
      - WATCHTOWER_NOTIFICATION_URL=ntfy://ntfy.sh/<notre-chaîne-de-caractères>?title=WatchtowerUpdates
      - WATCHTOWER_NOTIFICATION_URL=ntfy://k-sper.fr:<notre-jeton>@ntfy.<domaine.tld>/watchtower?priority=default
      - TZ=Europe/Paris
      - UID=1000
      - GID=1000

Et voilà. Un petit coup de

sudo docker compose up -d

Et nos serveurs sont presque opérationnels. Il ne nous reste plus qu’à paramétrer notre proxy inverse (Swag) et notre domaine auprès de notre registraire, comme nous l’avons fait préalablement pour Immich et Vaultwarden : je ne vous tiens plus la main pour cela ; l’espace commentaires est là pour que vous posiez vos éventuelles questions.

La mise à jour des images Docker

Avec toutes ces mises en place on a presque oublié que notre objectif était de mettre à jour nos images Docker lorsqu’on recevait des notifications. Donc que faisons-nous à réception d’une notification ?

Les notifications devraient ressembler à ceci :


31/03/2024 15:00
Watchtower updates on …
Found new ghcr.io/immich-app/immich-server:release image (…)


Nous savons donc qu’une nouvelle image Immich est disponible. Nous allons sur la page des versions d’Immich sur Github : https://github.com/immich-app/immich/releases.

Ainsi, nous voyons quelles évolutions ont été apportées à Immich. Nous voyons notamment si des évolutions doivent être apportées à notre fichier docker-compose.yml, mais aussi si d’autres évolutions sont susceptibles d’avoir un impact quelconque sur notre système et notamment sur sa sécurité.

Une fois que nous avons pris en compte les changements, nous relançons le service après nous être positionné dans le répertoire correspondant :

sudo docker compose down
sudo docker compose up -d

A l’issue de ces opérations, pour faire de la place sur notre disque, nous pouvons supprimer les images anciennes devenues inutiles, ainsi que tous les éléments Docker rendus inutiles :

sudo docker system prune -a

Avant de lancer cette commande qui va avoir un impact sur toute votre installation Docker, je vous invite à vous documenter sur ce sujet.


Si vous avez apprécié cet article, n’hésitez pas à laisser à son auteur un petit commentaire : il l’encouragera à le maintenir et à le développer.


Commentaires

Laisser un commentaire

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