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:
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:
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:
Avant de mettre en place le script de notification, il y a trois éléments à prendre en compte:
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
:
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.
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.
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.
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.
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.
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.
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.