Mise en place d'un Gitlab à la mise à jour automatisée et notifiée

14 Sep 2024 . category: tech . Comments
#gitlab #ntfy #discord #notification

Ce que vous allez découvrir dans cet article

Dans plusieurs de mes projets la première chose que je mets en place, s’il n’existe pas déjà, c’est un Gitlab, que j’aime avoir à jour sans consacrer plus de temps à cette tâche que nécessaire. J’ai donc créé une routine qui me permet d’automatiser ces mises à jour et d’obtenir les notifications sur mon téléphone, par deux moyens:

  • NTFY.sh: je m’en sers surtout pour moi, en tant que mainteneur de mes instances, afin de m’assurer que le processus se déroule correctement;
  • Discord: sert à avertir l’équipe qu’ils ne pourront pas utiliser Gitlab durant les quelques courtes minutes de mise à jour.

Certaines des personnes avec qui j’ai travaillé m’ont demandé comment procéder, je rédige donc cet article si cela peut en inspirer d’autres. Libre à vous d’utiliser NTFY et Discord ou seulement l’un des deux, car trop de notifications peut nuire à la santé mentale, on ne le rappelle jamais assez…

Bien que les différents scripts sont expliqués ici, vous les trouverez également sur Github dans ce dépôt:

ReadMe Card

Avertissement

La configuration que je vais expliquer ici est une base sur laquelle partir. Bien évidemment il faudra prêter attention aux vulnérabilités spécifiques à votre cas, surtout si votre Gitlab est exposé sur internet. Vous devrez donc prêter attention au paramétrage de votre serveur (ouverture de ports, changement de certains réglages par défaut, etc.). Néanmoins j’ai laissé certaines précautions dans le code que je partage, notamment le délai de sécurité avant de procéder à une mise à jour automatique. En effet, il y a deux dangers:

  • il faut se prémunir des failles “0 day” des nouvelles versions, et donc attendre un certain délai avant de mettre à jour à la dernière version. Les équipes de Gitlab sont assez réactives, donc en attendant une semaine, ceci peut être un délai acceptable avant de mettre à jour le Gitlab;
  • certaines montées de version peuvent entraîner des changements majeurs (ou “breaking changes”) qui ont des conséquences sur les projets hébergés sur le Gitlab. La lecture des notes de version est vivement conseillée, donc un délai d’un jour minimum est requis; à moins que vous aimiez le risque!

Prérequis

Avant de mettre en place le script de notification, il y a trois éléments à prendre en compte:

  • votre instance Gitlab;
  • votre instance NTFY;
  • votre salon Discord.

Gitlab

L’installation d’une version dockerisée de Gitlab, sous la forme d’un docker-compose.yml, permettra de relancer Gitlab facilement en effaçant les anciennes images.

version: '3'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    hostname: gitlab.EXAMPLE.TLD
    ports:
      - "4433:443"
      - "8080:80"
      - "2222:22"
    volumes:
      - /data/gitlab/config:/etc/gitlab
      - /data/gitlab/logs:/var/log/gitlab
      - /data/gitlab/data:/var/opt/gitlab
    restart: always
    networks:
      - gitlab
  gitlab-runner:
    image: gitlab/gitlab-runner:alpine
    container_name: gitlab-runner
    restart: always
    depends_on:
      - gitlab
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /data/gitlab-runner:/etc/gitlab-runner
    networks:
      - gitlab

networks:
  gitlab:
    name: gitlab-network

Voici le script pour relancer Gitlab avec la nouvelle version (notez bien le --rmi all dans le docker compose down):

#!/bin/bash

SCRIPT_DIR=$(dirname "$0")

docker compose -f $SCRIPT_DIR/docker-compose.yml down --rmi all
docker compose -f $SCRIPT_DIR/docker-compose.yml up -d

Placez ces deux scripts dans le même dossier, pour plus de facilité de manipulation, notamment lorsque vous mettrez en place le script de mise à jour et de notification.

Une fois le Gitlab installé, il vous faut créer un utilisateur qui sera dédié à la lecture du numéro de version du Gitlab. Quand vous créez votre instance, vous avez un projet gitlab-instance/Monitoring qui est automatiquement ajouté. Il faut vous rendre dans ce projet et créer un jeton d’accès via le menu “Paramètres” puis “Access tokens”, sur la gauche.

Vous arrivez sur une page, a priori vide lors de la première consultation, qui contient un bouton “Ajouter un nouveau jeton”. Afin de vous rappeler qu’il s’agira d’un robot, je vous conseille de nommer le jeton maj_gitlab_bot. Il faut uniquement lui donner les droits de lecture comme ceci, avec le rôle “Guest” et l’unique autorisation read_api:

Gitlab

Une fois que vous avez validé ces éléments, pensez à bien noter le contenu du jeton sur l’écran suivant car il ne réapparaîtra pas. Si vous l’oubliez il faudra le détruire pour en créer un autre. Ce jeton vous servira lors de la configuration du script de mise à jour et de notification.

ntfy.sh

C’est ce service qui vous permettra d’envoyer des notifications sur votre téléphone, en utilisant une application dédiée qui écoutera les événements. Vous créerez un “sujet” que je nommerai dans ce billet de blog maj_gitlab, avec un robot qui aura les droits d’écriture sur ce sujet, et des utilisateurs qui auront le droit de lire les notifications de ce sujet.

Vous avez le choix: soit vous payez le service NTFY, soit vous l’installez sur un serveur, tout dépend du temps que vous souhaitez consacrer à la gestion de la sécurité de votre instance NTFY. Auto-héberger son instance permet plus de liberté sur le paramétrage et la gestion des comptes à mon goût, c’est pour cette raison que dans plusieurs projets j’ai préféré ne pas acheter le service, mais, encore une fois, comme pour tout ce qui concerne l’auto-hébergement, c’est à vous que revient la décision. Après, si vous avez déjà installé votre instance Gitlab, c’est que vous avez les épaules pour maintenir en condition opérationnelle un serveur NTFY! D’autant plus que les explications fournies dans la documentation sont assez claires.

Vous pouvez laisser les notifications de mise à jour Gitlab accessibles au public, néanmoins je déconseille cette approche. En effet, un utilisateur malveillant pourrait capter une notification et exploiter une faille “0 day” sur votre instance Gitlab. Un réglage important est donc d’interdire par défaut l’accès à des sujets: vous devez gérer qui a accès aux notifications.

Il faut ajouter un utilisateur qui sera dédié à l’envoi de notifications de mise à jour du Gitlab. Ce sera le seul qui aura le droit d’écrire sur ce “sujet”:

# Création de l'utilisateur dédié
ntfy user add gitlab_update_notifier

# Ajout des permissions de lecture et d'écriture sur le sujet
ntfy access gitlab_update_notifier maj_gitlab rw

# Création du jeton pour pouvoir l'utiliser dans un CURL
ntfy token add gitlab_update_notifier

# Vérification de l'existence du jeton
token list

Notez bien ce jeton car il faudra l’inclure plus tard dans la configuration du script de notification.

Il faut ensuite créer tous les comptes qui auront accès au sujet maj_gitlab, et tout dépend de votre équipe. Pour une équipe dont la composition évolue régulièrement, la gestion nominative est préconisée, afin que des anciens membres n’aient pas accès à des éléments dont ils n’ont pas à avoir connaissance. Dans la cas contraire, vous pouvez tout à fait créer un utilisateur “organisationnel” du type MON_UNITE. L’idée ici est de créer un ou plusieurs utilisateurs qui n’ont qu’un accès en lecture:

# Création d'un utilisateur qui aura les droits en lecture, s'il n'existe pas déjà
ntfy user add UTILISATEUR

# Ajout de la permission unique de lecture sur le sujet
ntfy access UTILISATEUR maj_gitlab ro

Vous pouvez ensuite vérifier dans votre client web NTFY que tout fonctionne correctement.

Discord

Il n’y a qu’une chose à faire dans Discord: créer un webhook qui vous permettra d’écrire dans le bon salon. Vous pouvez créer un salon dédié “Mises à jour Gitlab”, un salon “Outils de développement” ou publier dans une discussion ouverte aux développeurs, il n’y a pas de préconisation en la matière, tout dépend de votre mode de fonctionnement.

La documentation sur le site de Discord permet de comprendre sans difficulté comment vous pouvez créer un webhook.

Une fois que votre webhook est créé, notez bien l’adresse car il vous faudra l’inclure plus tard dans la configuration du script de notification.

Script de mise à jour

Objectif

L’objectif du script est de vérifier si la version actuelle du Gitlab est bien la dernière en date, sur la base de comparaison des “digest” entre la version de votre Gitlab et celle du latest disponible sur Dockerhub. Si elle ne l’est pas, on vérifie si on a dépassé le délai de sécurité que l’on s’est fixé. Si le délai n’est pas dépassé on reçoit une notification comme quoi la version est encore trop récente, sinon on reçoit une notification comme quoi la version a bien été mise à jour.

Diagramme de séquence

Mise en place du script

Voici le script à mettre en place, dans le même dossier que les précédents fichiers:

#!/bin/bash

SCRIPT_DIR=$(dirname "$0")

# Récupérer les données de configuration
GITLAB_TOKEN=$(jq -r '.gitlab.token' $SCRIPT_DIR/verif_version.conf)
GITLAB_URL=$(jq -r '.gitlab.url' $SCRIPT_DIR/verif_version.conf)
NTFY_TOKEN=$(jq -r '.ntfy.token' $SCRIPT_DIR/verif_version.conf)
NTFY_URL=$(jq -r '.ntfy.url' $SCRIPT_DIR/verif_version.conf)
DISCORD_WEBHOOK=$(jq -r '.discord.webhook' $SCRIPT_DIR/verif_version.conf)
SERVER_NAME=$(jq -r '.name' $SCRIPT_DIR/verif_version.conf)

# Définir les URLs
GITLAB_URL="$GITLAB_URL/api/v4/metadata?private_token=$GITLAB_TOKEN"
DOCKER_HUB_URL="https://hub.docker.com/v2/namespaces/gitlab/repositories/gitlab-ce/tags?page=1&page_size=50"

# Récupérer la version actuelle du GitLab installé
GITLAB_VERSION=$(curl -s "$GITLAB_URL" | jq -r '.version')

# Récupérer le digest de la version actuelle
ACTUAL_DIGEST=$(curl -s "$DOCKER_HUB_URL" | jq -r --arg VERSION "$GITLAB_VERSION" '.results[] | select(.name | startswith("\($VERSION)")).digest')

# Récupérer le digest de la dernière version
LATEST_DIGEST=$(curl -s "$DOCKER_HUB_URL" | jq -r --arg VERSION "$GITLAB_VERSION" '.results[] | select(.name == "latest").digest')

# Récupérer le nom de la dernière version
LATEST_VERSION=$(curl -s "$DOCKER_HUB_URL" | jq -r --arg DIGEST "$LATEST_DIGEST" '.results[] | select(.digest == $DIGEST and .name != "rc" and .name != "latest") | .name')

# Récupérer la date de la dernière version
LATEST_DATE=$(curl -s "$DOCKER_HUB_URL" | jq -r --arg DIGEST "$LATEST_DIGEST" '.results[] | select(.digest == $DIGEST and .name != "rc" and .name != "latest").last_updated')
LATEST_DATE_TIMESTAMP=$(date -d "$LATEST_DATE" +%s)

# Comparer les digests
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
if [ "$ACTUAL_DIGEST" = "$LATEST_DIGEST" ]; then
    echo "$TIMESTAMP - Version $GITLAB_VERSION est a jour." >> $SCRIPT_DIR/maj_docker.log
else
    echo "$TIMESTAMP - Version $LATEST_VERSION disponible en mise à jour de $GITLAB_VERSION." >> $SCRIPT_DIR/maj_docker.log
    # Nombre de jours de sécurité avant une mise à jour automatique
    DELAY=$(jq -r '.delay' $SCRIPT_DIR/verif_version.conf)
    DATE_MARGIN=$(date -d "$DELAY days ago" +%s)
    if [ $LATEST_DATE_TIMESTAMP -lt $DATE_MARGIN ]; then
        echo "$TIMESTAMP - Version latest est plus à jour que la version actuelle $GITLAB_VERSION. On met à jour."
        # On notifie que la mise à jour vers telle version va se faire
        curl -H "Title: [$SERVER_NAME] Mise à jour Gitlab" -H "Tags: warning" -d "En cours vers $LATEST_VERSION" $NTFY_URL/maj_gitlab -u :$NTFY_TOKEN
        curl -H "Content-Type: application/json" -X POST -d "{\"content\": \":warning: **MAJ GITLAB** :warning:\\n\\nMise à jour en cours vers la version $LATEST_VERSION.\\n\\nCe processus dure environ 5 minutes, durant lesquelles le Gitlab sera inaccessible. Un nouveau  message sera publié quand la mise à jour est terminée.\"}" "$DISCORD_WEBHOOK"
        $SCRIPT_DIR/restart_compose_gitlab.sh && \
        # On notifie que la mise à jour est terminée avec le nouveau numéro de version
        curl -H "Title: [$SERVER_NAME] Mise à jour Gitlab" -H "Tags: white_check_mark" -d "Version $LATEST_VERSION" $NTFY_URL/maj_gitlab -u :$NTFY_TOKEN && \
        curl -H "Content-Type: application/json" -X POST -d "{\"content\": \":white_check_mark: **MAJ GITLAB**\\n\\nMise à jour effectuée sur la version $LATEST_VERSION.\"}" "$DISCORD_WEBHOOK"
    else
        echo "$TIMESTAMP - Datant de moins de $DELAY jour(s), la mise à jour est en attente." >> $SCRIPT_DIR/maj_docker.log
        # On notifie que la version est encore trop récente de X jours
        curl -H "Title: [$SERVER_NAME] Mise à jour Gitlab" -H "Tags: hourglass" -d "Version $LATEST_VERSION date de moins de $DELAY jour(s)" $NTFY_URL/maj_gitlab -u :$NTFY_TOKEN
    fi
fi


Vous pouvez tout à fait modifier ce fichier pour supprimer les éléments qui ne vous intéressent pas (notifications Discord et/ou NTFY). Il peut simplement vous servir à vous assurer que les mises à jour soient réalisées automatiquement, sans être notifié de quoi que ce soit.

Afin que le script soit exécuté régulièrement, il faut l’ajouter au CRON, et utiliser soit votre cerveau, soit le site crontab.guru pour déterminer la périodicité d’exécution. Par exemple, si vous voulez une vérification toutes les heures, à chaque heure pile:

0 * * * * /bin/bash /chemin/vers/le/script/verif_version.sh >> /chemin/vers/le/script/cron.log 2>&1

Il est très important de garder une trace des journaux d’exécution de ce script, si jamais des erreurs surviennent lors de la mise à jour, afin de comprendre ce qu’il s’est passé. Le script en lui-même contient déjà une journalisation de ses opérations, dans le fichier maj_gitlab.log, dont voici ce que peut être un extrait de contenu:

2024-09-11 20:00:03 - Version 17.3.1 est a jour.
2024-09-11 21:00:04 - Version 17.3.1 est a jour.
2024-09-11 22:00:04 - Version 17.3.1 est a jour.
2024-09-11 23:00:04 - Version 17.3.1 est a jour.
2024-09-12 00:00:05 - Version 17.3.2-ce.0 disponible en mise à jour de 17.3.1.
2024-09-12 00:00:05 - Datant de moins de 1 jour(s), la mise à jour est en attente.
2024-09-12 01:00:03 - Version 17.3.2-ce.0 disponible en mise à jour de 17.3.1.
2024-09-12 01:00:03 - Datant de moins de 1 jour(s), la mise à jour est en attente.
...
2024-09-13 01:00:04 - Version 17.3.2 est a jour.
2024-09-13 02:00:04 - Version 17.3.2 est a jour.
2024-09-13 03:00:06 - Version 17.3.2 est a jour.

Configuration

Voici un exemple de fichier de configuration que vous devrez mettre, toujours dans le même répertoire que les autres scripts:

{
  "name": "MYSERVER",
  "gitlab": {
    "url": "https://gitlab.EXAMPLE.TLD",
    "token": "abcdef132465" 
  },
  "ntfy": {
    "url": "https://ntfy.EXAMPLE.TLD",
    "token": "tk_654321fedcba"
  },
  "discord": {
    "webhook": "https://discordapp.com/api/webhooks/987654321/abcdef123456"
  },
  "delay": "7"
}

Rien de compliqué pour cette configuration: les URL et les jetons (token) sont ceux qui ont été abordés plus haut. Seul le paramètre delay mérite sans doute la précision qu’il s’agit d’un nombre de jours, précisément le nombre de jours que vous souhaitez attendre avant de mettre à exécution la mise à jour automatique.

Par exemple, si une nouvelle version sort le 25 mars et que vous avez mis un délai de sécurité de 3 jours, ce n’est que lors de l’exécution du script le 28 mars que la mise à jour sera réalisée (au moment fixé par le cron que vous aurez mis en place, pourquoi pas dès minuit).

Il y a également le paramètre name qui permet de préciser entre crochets dans les titres des notifications quel est le serveur concerné, ce qui est assez utile quand on doit gérer plusieurs serveurs.

Remarques

Il y a très certainement des améliorations à apporter à l’ensemble des scripts que je propose, auquel cas n’hésitez pas à m’en faire part dans les commentaires où à l’occasion d’une pull request sur GitHub.


Me

César Lizurey est un gendarme ayant occupé des fonctions de commandement mais également d'état-major, en tant que chef de projet ou dans le domaine de l'innovation et de la prospective. Il aime coder, faire de la musique et le sport. Et coder.