Les journaux d'un parc de serveurs
Pourquoi
Un mardi matin, la mairie signale que des signalements déposés la veille au soir ont disparu. Vous ouvrez une session sur sig-app-2 pour lire le journal de Gunicorn : la machine a été recréée dans la nuit à partir de son image, après un plantage, et son journal est reparti de zéro. Sur sig-app-1, le journal ne remonte qu'à quatre jours, parce que Camille avait réduit SystemMaxUse= à 200 Mo pour régler un disque plein. Le fichier /var/log/signalements/acces.log existe bien, mais il fait 11 Go et n'a jamais tourné. Quant à sig-outils, censé « recevoir les journaux », il n'a rien reçu depuis le 3 août : le certificat TLS a expiré et personne ne l'a vu.
Chacune de ces situations a la même cause : les journaux ont été traités comme un sous-produit, pas comme un service. Or ils servent à trois choses qui comptent toutes en production :
- diagnostiquer une panne, souvent après coup, parfois sur une machine qui n'existe plus ;
- prouver ce qui s'est passé après un incident de sécurité, ce qui suppose qu'un attaquant devenu
rootsur la machine n'ait pas pu les effacer ; - respecter des obligations : les journaux de Signalements contiennent les adresses IP des personnes qui déposent un signalement, donc des données personnelles, que l'on ne peut ni perdre trop tôt ni garder indéfiniment.
La leçon 11 de Linux : premiers pas vous a appris à interroger le journal avec journalctl et vous a présenté rsyslog et logrotate. Cette leçon passe de l'usage à l'administration : régler le journal de chaque machine, faire sortir les journaux de sig-app-1 et sig-app-2 vers sig-outils de façon fiable et chiffrée, faire tourner les fichiers texte proprement, et décider combien de temps on garde quoi.
Les concepts
Deux chaînes qui cohabitent
Sur une machine Ubuntu ou Debian récente, deux systèmes de journalisation coexistent, et il faut savoir lequel fait quoi.
Le journal de systemd (journal système) est tenu par systemd-journald. Il reçoit la sortie standard et la sortie d'erreur des services, les messages du noyau, ceux envoyés par l'interface syslog classique (la socket /dev/log) et ceux envoyés par l'API native du journal. Il les écrit dans des fichiers binaires indexés, où chaque entrée est un ensemble de champs (MESSAGE, PRIORITY, _SYSTEMD_UNIT, _PID...). Les champs dont le nom commence par un souligné sont ajoutés par journald lui-même, à partir de ce que le noyau lui dit de l'émetteur : un processus ne peut pas les falsifier.
syslog est l'ancien monde, toujours bien vivant : un démon, ici rsyslog, reçoit des messages texte, les trie selon leur facility (la catégorie de l'émetteur : auth, cron, daemon, kern, local0 à local7...) et leur priorité, et les écrit dans des fichiers ou les envoie sur le réseau. Le format des messages est décrit par la RFC 3164 (le format historique « BSD ») et la RFC 5424 (le format moderne, avec un horodatage complet et des données structurées).
Le lien entre les deux est le réglage ForwardToSyslog= de journald : s'il vaut yes, journald recopie chaque message vers la socket /run/systemd/journal/syslog, sur laquelle rsyslog écoute. En amont, systemd l'a désactivé par défaut depuis la version 216 (la note de version expliquait que rsyslog lisait désormais le journal par lui-même). Debian et Ubuntu font l'inverse : leur paquet systemd installe le fichier /usr/lib/systemd/journald.conf.d/syslog.conf, qui contient ForwardToSyslog=yes. C'est ce qui permet à rsyslog d'Ubuntu, configuré avec le seul module imuxsock, de remplir /var/log/syslog.
flowchart LR
G[Gunicorn<br/>stdout / stderr] --> J[systemd-journald]
K[noyau] --> J
L[logger, /dev/log] --> J
J --> F[(/var/log/journal)]
J -- ForwardToSyslog=yes --> R[rsyslog<br/>imuxsock]
R --> T[(/var/log/syslog<br/>/var/log/auth.log)]
R -- omfwd, TCP + TLS, port 6514 --> C[rsyslog sur sig-outils<br/>imtcp]
C --> D[(/srv/donnees/journaux/sig-app-1/...)]
Sur sig-app-1 et sig-app-2 (Ubuntu 24.04), rsyslog est installé par défaut. Sur sig-outils (Debian 13), il ne l'est plus depuis Debian 12 : seul le journal existe, jusqu'à ce que vous installiez rsyslog pour en faire le collecteur.
Pourquoi envoyer par syslog et pas par le journal
On pourrait imaginer de copier les fichiers du journal d'une machine à l'autre. systemd fournit d'ailleurs un outil pour cela, systemd-journal-upload, présenté plus bas. Mais le protocole syslog a trois avantages pour un parc : tout le monde le parle (un équipement réseau, un pare-feu, un service managé, une plateforme de centralisation), il passe par TCP avec TLS selon une norme (RFC 5425, port 6514), et rsyslog sait mettre les messages en attente sur disque quand le collecteur est injoignable. L'ANSSI, dans son guide sur l'architecture d'un système de journalisation, recommande justement de centraliser (R9), de privilégier un transfert « en temps réel » (R14), par des protocoles fiables reposant sur TCP (R16) et sécurisés (R17).
Le prix à payer : en passant par syslog, on perd les champs structurés du journal. Seuls le message, l'identifiant de l'émetteur, son PID, la facility, la priorité et l'horodatage voyagent. On en tient compte en gardant un journal local de quelques semaines, interrogeable avec tous ses champs, et en envoyant au collecteur ce qui doit survivre à la machine.
Fiabilité du transport
Trois transports sont possibles, du moins fiable au plus fiable :
- UDP (port 514) : chaque message est un datagramme, sans accusé de réception. Si le collecteur redémarre, les messages envoyés pendant ce temps sont perdus, et l'émetteur ne le sait pas.
- TCP (port 514 en clair, 6514 avec TLS) : l'émetteur voit que la connexion est coupée et peut garder les messages en attente. Il reste une petite fenêtre de perte : les messages déjà écrits dans la socket au moment où la connexion tombe, que TCP n'a pas encore acquittés au niveau applicatif. La documentation de
rsyslogparle pour cette raison de transfert « (quite) reliable ». - RELP (Reliable Event Logging Protocol) : un protocole propre à
rsyslog, avec des acquittements applicatifs, qui ferme cette fenêtre. Il demandersyslogdes deux côtés (modulesomrelpetimrelp, paquetrsyslog-relp).
TCP avec TLS et une file d'attente sur disque est le bon compromis pour Signalements : normalisé, chiffré, et sans perte lors des pannes courantes du collecteur.
Le framing (la façon de séparer les messages dans un flux TCP) mérite un mot. Le framing « traditionnel » termine chaque message par un saut de ligne, ce qui découpe une trace d'erreur Python sur plusieurs messages. Le framing par comptage d'octets (octet-counting), imposé par la RFC 5425, préfixe chaque message de sa longueur. imtcp reconnaît les deux, omfwd envoie en traditionnel par défaut : on lui demandera explicitement le comptage d'octets.
Trois étages de rétention
Un journal n'est utile que s'il existe encore quand on en a besoin, et il devient un risque s'il existe encore quand on n'en a plus besoin. Dans la chaîne de Signalements, chaque étage a sa propre politique de rétention :
| Étage | Outil qui borne | Durée visée | Pourquoi |
|---|---|---|---|
journal local de sig-app-1/2 | SystemMaxUse=, MaxRetentionSec= | environ un mois | diagnostic avec tous les champs, sans saturer le disque |
fichiers texte locaux (/var/log/syslog, acces.log) | logrotate | quelques semaines | compatibilité, outils qui lisent des fichiers |
collecteur sig-outils | logrotate sur /srv/donnees/journaux | six mois à un an | enquête après incident, preuve, obligations |
La dernière ligne vient de la CNIL : sa recommandation sur la journalisation (délibération n° 2021-122) préconise de conserver les traces des accès et des actions des utilisateurs habilités pendant une durée comprise entre six mois et un an, une durée plus longue devant être justifiée. Le guide de l'ANSSI reprend cette fourchette. On y revient dans la section Sécurité.
En pratique
Aucune des sorties de cette section n'est une capture : elles sont construites d'après la documentation et le code source des versions indiquées, pour montrer à quoi s'attendre.
Lire la configuration effective de journald
La configuration de journald se répartit sur plusieurs fichiers : /etc/systemd/journald.conf (entièrement commenté, il montre les valeurs par défaut), puis les drop-ins des répertoires journald.conf.d/ sous /usr/lib/systemd/, /usr/local/lib/systemd/, /run/systemd/ et /etc/systemd/. D'après journald.conf(5), les drop-ins sont lus dans l'ordre lexicographique de leur nom, tous répertoires confondus, et ceux de /etc/ l'emportent à nom égal. La page recommande de préfixer les drop-ins de l'administrateur par un nombre entre 60 et 90, pour passer après ceux des paquets.
Plutôt que d'ouvrir chaque fichier, demandez à systemd de les concaténer dans l'ordre où il les lit :
$ systemd-analyze cat-config systemd/journald.conf
Sortie typique sur sig-app-1 (les lignes commentées du fichier principal sont abrégées) :
# /etc/systemd/journald.conf
[Journal]
#Storage=auto
#Compress=yes
...
#ForwardToSyslog=no
...
# /usr/lib/systemd/journald.conf.d/syslog.conf
# Undo upstream commit 46b131574fdd7d77 for now. For details see
# http://lists.freedesktop.org/archives/systemd-devel/2014-November/025550.html
[Journal]
ForwardToSyslog=yesLe fichier principal dit #ForwardToSyslog=no (la valeur par défaut amont), le drop-in du paquet dit yes : c'est lui qui s'applique. Voilà pourquoi on ne modifie jamais /etc/systemd/journald.conf directement : on perd la vue d'ensemble, et une mise à jour du paquet peut proposer de le remplacer.
Écrire le drop-in de Signalements
Les réglages par défaut de systemd 255 et 257, d'après journald.conf(5) et le code source :
| Réglage | Défaut | Sens |
|---|---|---|
Storage= | auto | persistant si /var/log/journal existe, sinon en mémoire |
SystemMaxUse= | 10 % du système de fichiers, plafonné à 4 Gio | taille maximale de tous les fichiers du journal |
SystemKeepFree= | 15 % du système de fichiers, plafonné à 4 Gio | espace à laisser libre aux autres |
SystemMaxFileSize= | un huitième de SystemMaxUse=, plafonné à 128 Mio | taille d'un fichier avant rotation |
MaxFileSec= | 1month | durée maximale couverte par un fichier avant rotation |
MaxRetentionSec= | 0 (désactivé) | âge maximal des entrées |
RateLimitIntervalSec= / RateLimitBurst= | 30s / 10000 | limitation de débit, par service |
Note
systemd 260 a changé la valeur par défaut de Storage= en persistent. Ni Ubuntu 24.04 ni Debian 13 ne sont concernées, mais la page de manuel « latest » en ligne décrit déjà ce nouveau comportement. Fixer Storage=persistent explicitement évite toute ambiguïté.
Les disques de sig-app-1 et sig-app-2 font 20 Go. Par défaut, le journal peut donc occuper 2 Go et doit laisser 3 Go libres ; journald respecte les deux limites et retient la plus contraignante. Créez /etc/systemd/journald.conf.d/60-signalements.conf :
[Journal]
Storage=persistent
SystemMaxUse=1G
SystemKeepFree=4G
MaxRetentionSec=1monthLigne par ligne :
Storage=persistent: écrire sous/var/log/journal, et créer le répertoire s'il manque. Au démarrage, journald écrit d'abord en mémoire sous/run/log/journal, puis bascule sur le disque quandsystemd-journal-flush.services'exécute, une fois/varmonté.SystemMaxUse=1G: un gigaoctet suffit pour un mois de journaux d'une API qui envoie ses accès au collecteur ; c'est aussi une borne connue, que l'on peut écrire dans la fiche de serveur (leçon 1).SystemKeepFree=4G: si un autre processus remplit le disque, journald supprime ses fichiers archivés les plus anciens pour laisser 4 Go aux autres, plutôt que de participer à la saturation.MaxRetentionSec=1month: au-delà d'un mois, les fichiers archivés sont supprimés même s'il reste de la place. C'est la limite de durée qui manque aux réglages de taille : une machine peu bavarde pourrait sinon garder des années de journaux, et avec elles des adresses IP.
La suppression se fait par fichier archivé, jamais entrée par entrée : la durée réelle de conservation dépasse donc MaxRetentionSec= de la durée couverte par un fichier, au plus MaxFileSec= (un mois par défaut). Pour une limite plus fine, ajoutez MaxFileSec=1week.
journald ne sait pas relire sa configuration à chaud dans ces versions (son unité ne déclare pas d'ExecReload=). Redémarrez-le, puis contrôlez :
$ sudo systemctl restart systemd-journald
$ journalctl --disk-usage
Archived and active journals take up 1.4G in the file system.
$ journalctl -u systemd-journald -n 3 --no-pager
La dernière commande affiche le message que journald émet en ouvrant son stockage, de la forme System Journal (/var/log/journal/<identifiant de machine>) is 1.4G, max 1.0G, 0B free. Le journal dépasse ici son nouveau plafond : il supprimera ses fichiers archivés les plus anciens à la prochaine rotation. Pour libérer la place tout de suite, sudo journalctl --rotate archive les fichiers actifs, et sudo journalctl --vacuum-size=1G supprime les archives excédentaires.
La limitation de débit
Après une livraison, une boucle de reconnexion à sig-db écrit des milliers de lignes par seconde. Dans le journal apparaît :
systemd-journald[312]: Suppressed 41287 messages from signalements.serviceC'est la limitation de débit de journald (rate limiting) : au-delà de RateLimitBurst= messages dans un intervalle de RateLimitIntervalSec=, les messages suivants du même service sont jetés jusqu'à la fin de l'intervalle, puis journald écrit ce message de synthèse avec le nombre de messages perdus (le champ N_DROPPED le porte aussi). Trois détails tirés du code source de systemd 255 :
- La limite s'applique par unité : un service bavard ne fait pas perdre les messages des autres.
- Elle s'applique séparément par groupe de priorités :
emerg,alertetcritensemble, puiserr,warning,noticeavecinfo, etdebug. Une avalanche de messagesinfon'étouffe donc pas les erreurs du même service. - Le seuil est multiplié selon l'espace disque libre : ×1 jusqu'à 1 Mo libre, ×4 vers 4 Go, ×5 vers 64 Go. C'est l'espace libre qui compte, pas la taille du disque : avec 20 Go libres, le facteur vaut environ 4,5, soit quelque 45 000 messages par tranche de 30 secondes et par groupe avec la rafale par défaut de 10 000.
Faut-il relever la limite ? Rarement. Un service qui dépasse 10 000 lignes en 30 secondes a un problème de journalisation qu'il vaut mieux corriger dans le code. Mais pendant un diagnostic, on peut assouplir la limite pour ce seul service, par un drop-in de son unité (sudo systemctl edit signalements) :
[Service]
LogRateLimitIntervalSec=30s
LogRateLimitBurst=50000Ces deux directives de systemd.exec(5) remplacent les valeurs globales pour cette unité. Une valeur de 0 désactive la limitation, ce qui est à proscrire en production : c'est le disque, puis le collecteur, qui encaisseraient l'avalanche.
Warning
Une seconde limite existe entre journald et rsyslog. journald écrit vers la socket de rsyslog sans jamais attendre : si rsyslog ne lit pas assez vite, les messages sont perdus pour lui (pas pour le journal), et journald le signale, au plus une fois toutes les 30 secondes, par Forwarding to syslog missed 312 messages. Ce message dans le journal veut dire que le collecteur n'a pas tout reçu.
Les sorties structurées
Le journal n'est pas du texte : c'est une suite d'entrées faites de champs. journalctl -o json les rend telles quelles, une entrée par ligne, ce qui en fait la meilleure entrée pour un script :
$ journalctl -u signalements -n 1 -o json-pretty
Sortie typique, raccourcie, pour une ligne écrite par Gunicorn sur sa sortie d'erreur :
{
"__REALTIME_TIMESTAMP" : "1791361812467301",
"_BOOT_ID" : "9c4f0d0e5b1f4e2c8a7d3b6e1f2a4c5d",
"_HOSTNAME" : "sig-app-1",
"_TRANSPORT" : "stdout",
"PRIORITY" : "6",
"SYSLOG_FACILITY" : "3",
"SYSLOG_IDENTIFIER" : "gunicorn",
"_PID" : "1874",
"_UID" : "998",
"_COMM" : "gunicorn",
"_SYSTEMD_UNIT" : "signalements.service",
"_SYSTEMD_SLICE" : "system.slice",
"_SYSTEMD_INVOCATION_ID" : "3b1e6f0a9d2c4b7e8f5a1c3d2e4f6a7b",
"MESSAGE" : "[2026-10-07 08:30:12 +0000] [1874] [ERROR] Error handling request /signalements"
}Les champs à connaître :
_SYSTEMD_UNIT: l'unité d'origine, déduite du cgroup du processus ; c'est ce que filtre-u.PRIORITY: de 0 (emerg) à 7 (debug). Une ligne écrite sur la sortie standard reçoit la priorité par défaut de l'unité (info, 6), même si elle contient le motERROR: journald ne lit pas le texte. Une application qui veut des priorités justes peut préfixer ses lignes par<3>(convention desd-daemon(3)) ou utiliser l'API native.SYSLOG_IDENTIFIER: l'étiquette de l'émetteur, celle que l'on voit avant le PID dans la sortie courte._TRANSPORT: comment le message est arrivé :stdout,syslog,journal(API native),kernel,audit,driver(messages de journald lui-même)._SYSTEMD_INVOCATION_ID: un identifiant différent à chaque démarrage du service, pratique pour isoler les messages d'une exécution précise.__REALTIME_TIMESTAMP: l'heure de réception, en microsecondes depuis le 1er janvier 1970 UTC.
On filtre par champ en écrivant CHAMP=valeur sur la ligne de commande. Deux conditions sur des champs différents se combinent en ET, deux valeurs du même champ en OU, et un + isolé sépare deux groupes reliés par OU :
$ journalctl _SYSTEMD_UNIT=signalements.service PRIORITY=3
$ journalctl _SYSTEMD_UNIT=signalements.service + SYSLOG_IDENTIFIER=export-csv
$ journalctl -F _SYSTEMD_UNIT
$ journalctl -N
-F liste toutes les valeurs prises par un champ dans le journal (ici, toutes les unités qui ont écrit quelque chose), -N liste tous les noms de champs utilisés. Pour ne garder que quelques champs dans la sortie JSON, --output-fields=MESSAGE,PRIORITY,_PID.
Avec jq, on répond alors en une ligne à une question d'exploitation, par exemple « quelles unités ont écrit des erreurs depuis une heure, et combien » :
$ journalctl --since "-1h" -p err -o json --output-fields=_SYSTEMD_UNIT \
| jq -r '._SYSTEMD_UNIT // "noyau ou hors unité"' | sort | uniq -c | sort -rn
-p err garde les priorités 0 à 3, jq -r extrait le champ sans guillemets, et l'opérateur // de jq fournit une valeur de repli quand le champ est absent (messages du noyau, par exemple).
Pour lire le journal à l'œil, préférez -o short-iso (horodatage RFC 3339, avec le décalage horaire) à la sortie par défaut, qui omet l'année et le fuseau : sur un parc dont les machines sont en UTC (leçon 9), c'est la seule façon de comparer deux journaux sans se tromper d'heure.
Écrire dans le journal depuis un script
L'export CSV nocturne de sig-outils est un script Bash lancé par un minuteur (leçon 10). Il doit laisser des traces exploitables : un identifiant stable pour le retrouver, et des priorités justes pour que -p err remonte ses échecs. Deux outils :
$ logger -t export-csv -p user.err "échec de l'export : sig-db injoignable"
$ systemd-cat -t export-csv -p info /opt/signalements/bin/export-csv.sh
logger(paquetutil-linux) envoie un message à la socket syslog/dev/log, que journald reçoit.-tfixe l'étiquette (SYSLOG_IDENTIFIER),-pla priorité, sous la formefacility.niveau(par défautuser.notice). Avec-n hôte -T -P port, il envoie directement à un serveur syslog distant, ce qui sert à tester un collecteur.systemd-catexécute un programme en branchant sa sortie standard et sa sortie d'erreur sur le journal, avec l'identifiant-tet la priorité par défaut-p.--stderr-priority=warningdonne une autre priorité à la sortie d'erreur. La page de manuel préfère cette forme àcommande | systemd-cat, qui ne capture que la sortie standard.
Pour un service systemd, aucun des deux n'est nécessaire : tout ce que le script écrit est déjà dans le journal, sous le nom de son unité. Ajoutez seulement SyslogIdentifier=export-csv dans l'unité pour remplacer l'étiquette, qui serait sinon le nom de l'exécutable.
Enfin, logger --journald lit sur son entrée standard des lignes CHAMP=valeur (par exemple EXPORT_LIGNES=4312) et crée une entrée avec vos propres champs, interrogeables comme les autres, mais qui ne voyageront pas par syslog.
Vérifier l'intégrité du journal
$ sudo journalctl --verify
PASS: /var/log/journal/4a1c.../system.journal
PASS: /var/log/journal/4a1c.../system@0006412b3c...-00000000000a1f2e.journal~
...
--verify contrôle la cohérence interne de chaque fichier : en-têtes, tables de hachage, chaînage des entrées. Un fichier dont le nom se termine par ~ a été mis de côté par journald, qui l'a jugé endommagé ou mal fermé (après une coupure brutale, par exemple) ; journald l'annonce par File ... corrupted or uncleanly shut down, renaming and replacing. Ces fichiers restent lisibles par journalctl dans la mesure du possible.
--verify ne prouve pas que personne n'a modifié le journal : root peut réécrire un fichier cohérent. Pour cela, systemd propose le scellement Forward Secure Sealing (FSS) : sudo journalctl --setup-keys crée une clé de scellement, qui reste sur la machine et évolue toutes les 15 minutes par défaut (--interval=), et une clé de vérification, à noter et à conserver hors de la machine. Ensuite, journalctl --verify --verify-key=<clé> détecte toute modification des intervalles déjà scellés. Un attaquant qui obtient root peut toujours supprimer les fichiers ou falsifier l'intervalle en cours : le scellement complète l'envoi hors machine, il ne le remplace pas.
Préparer les certificats
Les deux côtés de la connexion s'authentifient par certificat, signés par une autorité de certification interne dédiée aux journaux. Les noms sont ceux que le DNS interne du réseau privé Scaleway attribue aux instances, de la forme <nom de la ressource>.<nom du réseau privé>.internal : sig-outils.pn-signalements.internal, sig-app-1.pn-signalements.internal, etc.
Sur votre poste d'administration, jamais sur un serveur du parc :
$ openssl req -x509 -new -noenc -days 1825 \
-newkey ec -pkeyopt ec_paramgen_curve:P-256 \
-keyout ca-journaux.key -out ca-journaux.pem \
-subj "/CN=AC journaux Signalements"
$ openssl req -new -noenc -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
-keyout sig-outils.key -out sig-outils.csr \
-subj "/CN=sig-outils.pn-signalements.internal" \
-addext "subjectAltName=DNS:sig-outils.pn-signalements.internal"
$ openssl x509 -req -in sig-outils.csr -days 365 \
-CA ca-journaux.pem -CAkey ca-journaux.key -CAcreateserial \
-copy_extensions copy -out sig-outils.pem
La première commande crée l'autorité, valable cinq ans : -x509 produit un certificat autosigné, -newkey ec avec la courbe P-256 une clé elliptique, et -noenc laisse la clé en clair, à protéger dans un gestionnaire de secrets. La deuxième crée la clé et la demande du collecteur ; -addext y ajoute le nom DNS en subjectAltName (le champ que les clients TLS vérifient). La troisième signe pour un an ; -copy_extensions copy (OpenSSL 3.0 et suivants) recopie le subjectAltName dans le certificat, sinon il serait perdu.
Répétez les deux dernières commandes pour sig-app-1 et sig-app-2. Vous déposerez sur chaque machine trois fichiers : ca-journaux.pem, son certificat et sa clé.
Le collecteur : rsyslog sur sig-outils
$ sudo apt install rsyslog rsyslog-gnutls logrotate
$ sudo install -d -m 0750 /etc/rsyslog.d/tls
$ sudo install -m 0644 ca-journaux.pem sig-outils.pem /etc/rsyslog.d/tls/
$ sudo install -m 0600 sig-outils.key /etc/rsyslog.d/tls/
$ sudo install -d -o root -g adm -m 0750 /srv/donnees/journaux
rsyslog-gnutls fournit le pilote TLS gtls (il existe aussi rsyslog-openssl et son pilote ossl). Sur Debian, rsyslog tourne en root : la clé peut rester en 0600. /srv/donnees/journaux sera idéalement un volume dédié (leçon 6), comme le recommande l'ANSSI (R21) : un afflux de journaux ne doit pas remplir la racine du collecteur.
Créez /etc/rsyslog.d/10-reception.conf :
global(
DefaultNetstreamDriver="gtls"
DefaultNetstreamDriverCAFile="/etc/rsyslog.d/tls/ca-journaux.pem"
DefaultNetstreamDriverCertFile="/etc/rsyslog.d/tls/sig-outils.pem"
DefaultNetstreamDriverKeyFile="/etc/rsyslog.d/tls/sig-outils.key"
)
module(load="imtcp"
StreamDriver.Name="gtls"
StreamDriver.Mode="1"
StreamDriver.AuthMode="x509/name"
PermittedPeer=["sig-app-1.pn-signalements.internal",
"sig-app-2.pn-signalements.internal"]
)
template(name="FichierParHote" type="string"
string="/srv/donnees/journaux/%FROMHOST%/syslog.log")
ruleset(name="distant") {
action(type="omfile" dynaFile="FichierParHote"
template="RSYSLOG_FileFormat"
dirCreateMode="0750" fileCreateMode="0640")
stop
}
input(type="imtcp" port="6514" address="172.16.8.20" ruleset="distant")Bloc par bloc :
global(...)désigne le pilote réseau par défaut et les fichiers de l'autorité, du certificat et de la clé de la machine.module(load="imtcp" ...)charge l'écoute TCP.StreamDriver.Mode="1"impose TLS (0 serait du TCP en clair).StreamDriver.AuthMode="x509/name"exige un certificat client signé par l'autorité et dont le nom figure dansPermittedPeer. Le tutoriel dersyslogmontre un serveur en modeanon, qui chiffre sans authentifier les clients : n'importe qui sur le réseau pourrait alors injecter des journaux.template(... type="string")construit le chemin du fichier de destination.%FROMHOST%est le nom de la machine d'où vient la connexion (résolution inverse de son adresse, ou l'adresse elle-même). N'utilisez pas%HOSTNAME%, le nom écrit dans le message : un émetteur compromis pourrait écrire dans le répertoire d'une autre machine.ruleset(name="distant")isole les messages reçus : ils ne passent pas par les règles locales de/etc/rsyslog.conf(sans cela, ils finiraient aussi dans le/var/log/syslogdesig-outils).omfileavecdynaFileécrit dans le fichier calculé par le modèle, en créant les répertoires.RSYSLOG_FileFormathorodate chaque ligne au format RFC 3339 avec les microsecondes et le décalage horaire.stoptermine le traitement du message.input(type="imtcp" ...)ouvre le port 6514, uniquement sur l'adresse du réseau privé, et attache le ruleset.
Validez la syntaxe avant tout redémarrage, puis vérifiez l'écoute :
$ sudo rsyslogd -N1
rsyslogd: version 8.2504.0, config validation run (level 1), master config /etc/rsyslog.conf
rsyslogd: End of config validation run. Bye.
$ sudo systemctl restart rsyslog
$ sudo ss -tlnp 'sport = :6514'
-N1 lit toute la configuration et s'arrête, sans rien démarrer. Ouvrez ensuite le port 6514 en entrée depuis 172.16.8.0/22 uniquement, sur le pare-feu de l'hôte comme dans le groupe de sécurité (leçon 8).
Les émetteurs : sig-app-1 et sig-app-2
Sur Ubuntu, rsyslog abandonne les droits de root après son démarrage pour tourner sous le compte syslog (directives $PrivDropToUser et $PrivDropToGroup de /etc/rsyslog.conf), et il est confiné par un profil AppArmor (/etc/apparmor.d/usr.sbin.rsyslogd) qui l'autorise à lire /etc/rsyslog.d/**, mais pas /etc/ssl/private/. D'où l'emplacement et les droits des fichiers :
$ sudo apt install rsyslog-gnutls
$ sudo install -d -m 0750 -o root -g syslog /etc/rsyslog.d/tls
$ sudo install -m 0644 ca-journaux.pem sig-app-1.pem /etc/rsyslog.d/tls/
$ sudo install -m 0640 -o root -g syslog sig-app-1.key /etc/rsyslog.d/tls/
Puis /etc/rsyslog.d/90-vers-sig-outils.conf :
global(
DefaultNetstreamDriver="gtls"
DefaultNetstreamDriverCAFile="/etc/rsyslog.d/tls/ca-journaux.pem"
DefaultNetstreamDriverCertFile="/etc/rsyslog.d/tls/sig-app-1.pem"
DefaultNetstreamDriverKeyFile="/etc/rsyslog.d/tls/sig-app-1.key"
)
action(type="omfwd"
target="sig-outils.pn-signalements.internal" port="6514" protocol="tcp"
StreamDriver="gtls" StreamDriverMode="1"
StreamDriverAuthMode="x509/name"
StreamDriverPermittedPeers="sig-outils.pn-signalements.internal"
TCP_Framing="octet-counted"
template="RSYSLOG_ForwardFormat"
action.resumeRetryCount="-1"
queue.type="LinkedList"
queue.filename="vers-sig-outils"
queue.maxDiskSpace="2g"
queue.saveOnShutdown="on"
)Les paramètres de l'action :
target,port,protocol="tcp": le collecteur, par son nom interne. Utiliser le nom plutôt que l'adresse permet de vérifier le certificat.StreamDriverMode="1"etStreamDriverAuthMode="x509/name"avecStreamDriverPermittedPeers: l'émetteur n'envoie qu'à un serveur qui présente un certificat signé par l'autorité au nom attendu. Sans cette vérification, un usurpateur sur le réseau récupérerait les journaux.TCP_Framing="octet-counted": chaque message est préfixé de sa longueur, comme l'impose la RFC 5425 ; une trace d'erreur sur plusieurs lignes reste un seul message.template="RSYSLOG_ForwardFormat": le modèle par défaut d'omfwd,RSYSLOG_TraditionalForwardFormat, envoie un horodatage à la seconde, sans année ni fuseau.RSYSLOG_ForwardFormatenvoie un horodatage RFC 3339 complet.action.resumeRetryCount="-1": réessayer indéfiniment quand le collecteur est injoignable, au lieu d'abandonner les messages.queue.type="LinkedList": donner à l'action sa propre file d'attente en mémoire. Par défaut, une action est en modeDirect: si elle bloque, elle bloque toutrsyslog.queue.filename: active le débordement sur disque (disk-assisted queue), dans le répertoire de travail/var/spool/rsyslogdéjà déclaré sur Ubuntu par$WorkDirectory. Le nom doit être unique dans toute la configuration.queue.maxDiskSpace="2g": borne la place de la file sur disque. Au-delà,rsyslogcesse d'accepter des messages pour cette action plutôt que de remplir le disque.queue.saveOnShutdown="on": écrire sur disque les messages encore en mémoire lors d'un arrêt propre dersyslog, pour les envoyer au redémarrage.
Validez, redémarrez, puis testez de bout en bout :
$ sudo rsyslogd -N1
$ sudo systemctl restart rsyslog
$ logger -t test-collecte -p user.notice "essai depuis sig-app-1"
Sur sig-outils, sudo tail -n 1 /srv/donnees/journaux/sig-app-1.pn-signalements.internal/syslog.log doit afficher une ligne de la forme :
2026-10-07T08:41:07.214311+00:00 sig-app-1 test-collecte[23017]: essai depuis sig-app-1Le nom du répertoire est celui que %FROMHOST% a obtenu par résolution inverse. Si vous préférez des noms courts, utilisez %FROMHOST-IP% et une table de correspondance documentée, ou nettoyez le nom avec une propriété de remplacement.
L'alternative de systemd : journal-upload et journal-remote
Le paquet systemd-journal-remote fournit une autre chaîne : systemd-journal-upload envoie le journal local en HTTPS, systemd-journal-remote le reçoit (port 19532 par défaut) et l'écrit sous /var/log/journal/remote/, un fichier par machine avec --split-mode=host. Avantage : tous les champs voyagent, et le collecteur s'interroge avec journalctl -D /var/log/journal/remote. La reprise après coupure repose sur le curseur de la dernière entrée envoyée, mémorisé dans /var/lib/systemd/journal-upload/state : le journal local sert de file d'attente. Limites : seul systemd sait lire ce format (ni équipement réseau, ni la plupart des plateformes de centralisation), aucun filtrage à l'émission, et une longue coupure peut dépasser la rétention du journal local. Un bon choix pour un petit parc homogène, rarement pour un parc mixte.
logrotate en profondeur
logrotate fait tourner les fichiers texte : il renomme le fichier courant, en crée un nouveau, compresse et supprime les anciens. Il est lancé une fois par jour par logrotate.timer (OnCalendar=daily, avec AccuracySec=1h sur Ubuntu 24.04 et RandomizedDelaySec=1h sur Debian 13) et lit /etc/logrotate.conf, qui inclut /etc/logrotate.d/. Il se souvient de la date de dernière rotation de chaque fichier dans /var/lib/logrotate/status : c'est ce fichier d'état qui lui permet de savoir qu'une rotation weekly est due, puisqu'il ne tourne lui-même que chaque jour.
Voici la règle livrée par le paquet rsyslog sur Ubuntu et Debian, /etc/logrotate.d/rsyslog :
/var/log/syslog
/var/log/mail.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/cron.log
{
rotate 4
weekly
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}weeklyetrotate 4: une rotation par semaine, quatre fichiers anciens conservés (syslog.1,syslog.2.gz...syslog.4.gz), le cinquième est supprimé.missingok: ne pas signaler d'erreur si un fichier n'existe pas (cron.logn'existe pas sur Ubuntu, qui ne l'active pas).notifempty: ne pas faire tourner un fichier vide.compressetdelaycompress: compresser avecgzip, mais seulement à la rotation suivante. Le fichier.1reste en clair, parce qu'un programme peut encore y écrire juste après la rotation, et que c'est celui que l'on consulte le plus.postrotate ... endscript: un script exécuté après le renommage./usr/lib/rsyslog/rsyslog-rotateenvoieSIGHUPàrsyslog(systemctl kill -s HUP rsyslog.service), qui ferme et rouvre ses fichiers.sharedscripts: exécuter ce script une seule fois pour l'ensemble des fichiers, et non une fois par fichier.
Sur Ubuntu, /etc/logrotate.conf contient en plus une directive globale su root adm, absente de Debian : elle fait tourner les fichiers sous l'identité root:adm, pour les raisons que la section Pièges courants détaille.
Pourquoi un signal ? Un programme écrit dans un fichier par un descripteur de fichier ouvert, qui désigne un inode, pas un nom. Quand logrotate renomme syslog en syslog.1, rsyslog continue d'écrire dans le même inode, donc dans syslog.1, jusqu'à ce qu'on lui demande de rouvrir le fichier par son nom. C'est le rôle du signal. Deux méthodes s'opposent :
create + signal dans postrotate | copytruncate | |
|---|---|---|
| Fonctionnement | renomme, crée un fichier vide, demande au programme de rouvrir | copie le fichier, puis le vide sur place |
| Perte possible | aucune | les lignes écrites entre la copie et la troncature |
| Exige | un programme qui sait rouvrir ses fichiers sur signal | rien |
| Coût | un renommage | une copie complète du fichier à chaque rotation |
La page logrotate(8) est explicite sur copytruncate : « some logging data might be lost ». On le réserve aux programmes qui ne savent pas rouvrir leurs fichiers.
La règle du collecteur. Sur sig-outils, créez /etc/logrotate.d/journaux-distants :
/srv/donnees/journaux/*/*.log
{
daily
rotate 180
dateext
compress
delaycompress
missingok
notifempty
create 0640 root root
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}daily avec rotate 180 conserve six mois de fichiers quotidiens, la borne basse de la recommandation de la CNIL. dateext nomme les archives par leur date (syslog.log-20261007.gz) plutôt que par un numéro qui change à chaque rotation, ce qui simplifie les sauvegardes incrémentales (leçon 16) et les recherches. Le motif /srv/donnees/journaux/*/*.log prend en compte automatiquement une nouvelle machine émettrice.
Le journal d'accès de Gunicorn. Si l'équipe tient à un fichier d'accès local, lancé avec --access-logfile /var/log/signalements/acces.log, voici la règle sur sig-app-1 (/etc/logrotate.d/signalements) :
/var/log/signalements/*.log
{
su signalements signalements
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 signalements signalements
sharedscripts
postrotate
systemctl kill -s USR1 --kill-whom=main signalements.service
endscript
}Gunicorn rouvre ses fichiers de journaux à la réception de SIGUSR1 (documentation de Gunicorn, Signal Handling). --kill-whom=main n'envoie le signal qu'au processus maître, qui le relaie à ses workers. su signalements signalements fait la rotation sous l'identité du propriétaire du répertoire, pour ne pas manipuler en root des fichiers qu'un compte non privilégié contrôle.
Tester une règle sans rien casser :
$ sudo logrotate -d /etc/logrotate.d/journaux-distants
-d (debug) simule : rien n'est renommé et le fichier d'état n'est pas mis à jour. Sortie typique :
reading config file /etc/logrotate.d/journaux-distants
...
Handling 1 logs
rotating pattern: /srv/donnees/journaux/*/*.log after 1 days (180 rotations)
empty log files are not rotated, old logs are removed
considering log /srv/donnees/journaux/sig-app-1.pn-signalements.internal/syslog.log
Now: 2026-10-07 09:12
Last rotated at 2026-10-07 00:00
log does not need rotating (log has already been rotated)Pour forcer une vraie rotation et la voir se dérouler : sudo logrotate -f -v /etc/logrotate.d/journaux-distants. Le résultat des exécutions planifiées se lit dans journalctl -u logrotate.service.
Sous le capot
Le format des fichiers du journal. Un fichier .journal est une base où l'on ne fait qu'ajouter : chaque couple CHAMP=valeur distinct n'est stocké qu'une fois puis référencé par les entrées, et des tables de hachage retrouvent les entrées d'une valeur sans tout lire. D'où la rapidité de journalctl -u, et le coût presque nul d'un champ répétitif. Les valeurs de plus de 512 octets sont compressées. Le fichier actif system.journal (les utilisateurs ordinaires ont leur user-<UID>.journal, SplitMode=uid) est renommé system@<identifiants>.journal quand il atteint SystemMaxFileSize= ou MaxFileSec= : les suppressions ne portent que sur ces archives.
Pourquoi redémarrer journald ne coupe pas les services. Les services écrivent dans le journal par leur sortie standard, qui est une socket connectée à journald. Si journald redémarrait en fermant ces sockets, chaque service recevrait une erreur d'écriture. L'unité systemd-journald.service déclare FileDescriptorStoreMax=4224 : journald confie ses sockets ouvertes à systemd avant de s'arrêter, et les récupère au redémarrage. Les services ne voient rien.
Comment journald connaît l'émetteur. Sur une socket Unix, le noyau transmet au destinataire le PID, l'UID et le GID de l'émetteur (SCM_CREDENTIALS). journald lit ensuite dans /proc/<pid>/ la commande et le cgroup, donc l'unité : voilà pourquoi ces champs, préfixés d'un souligné, ne peuvent pas être imposés par l'application. Si le processus s'est terminé avant cette lecture (commande très brève), certains champs manquent.
La file d'attente de rsyslog. Chaque action d'rsyslog possède sa file. En mode LinkedList avec queue.filename, la file vit en mémoire tant qu'elle est petite ; elle ne déborde sur disque que lorsqu'elle atteint son seuil haut (par défaut, une fraction de queue.size) ou lors d'un arrêt avec queue.saveOnShutdown. Ne cherchez donc pas de fichier dans /var/spool/rsyslog lors d'une courte coupure du collecteur : la documentation précise que des fichiers n'apparaissent que si la mémoire ne suffit plus. Cette file n'est pas une file purement sur disque : un arrêt brutal (OOM, coupure électrique) perd ce qui était en mémoire. Pour une garantie de bout en bout résistant aux plantages, la documentation de rsyslog renvoie à RELP avec une file purement disque.
Le signal HUP pour rsyslog. Depuis la version 5.8, rsyslog ne relit plus sa configuration sur SIGHUP : il ferme seulement ses fichiers ouverts, qu'il rouvrira à la prochaine écriture. C'est exactement ce dont logrotate a besoin, et c'est aussi pourquoi un changement de configuration exige un systemctl restart rsyslog, pas un reload.
Pièges courants
Les fichiers TLS illisibles sur Ubuntu. Vous avez déposé la clé dans /etc/ssl/private/ comme d'habitude, et rsyslog refuse de démarrer l'action. Dans journalctl -u rsyslog, le pilote GnuTLS écrit error reading file - a common cause is that the file does not exist (avec deux espaces, tel quel dans le code source), et journalctl -k montre une ligne apparmor="DENIED" operation="open" profile="rsyslogd" name="/etc/ssl/private/sig-app-1.key". Deux causes, souvent ensemble : le profil AppArmor de rsyslogd n'autorise pas ce chemin, et la clé n'est pas lisible par le compte syslog. Rangez les fichiers sous /etc/rsyslog.d/tls/, groupe syslog, ou ajoutez une règle dans /etc/apparmor.d/local/usr.sbin.rsyslogd.
Un nom de certificat qui ne correspond pas. Côté collecteur : error: peer name not authorized - not permitted to talk to it. Names: DNS:sig-app-1, CN:sig-app-1;. Le certificat présenté porte un nom absent de PermittedPeer. Côté émetteur, si le collecteur présente un certificat périmé ou signé par une autre autorité : not permitted to talk to peer 'sig-outils...', certificate invalid: certificate expired (ou signer not found). Inspectez le certificat avec openssl x509 -in fichier.pem -noout -subject -ext subjectAltName -enddate.
L'action suspendue en silence. Quand le collecteur est injoignable, rsyslog écrit dans son propre journal un message de la forme action 'action-8-builtin:omfwd' suspended (module 'builtin:omfwd'), retry 0. There should be messages before this one giving the reason for suspension., puis action '...' resumed (module 'builtin:omfwd') au retour. La file d'attente garde les messages, dans la limite de queue.maxDiskSpace, mais rien n'arrive au collecteur : c'est ainsi que sig-outils est resté sans journaux depuis le 3 août, et qu'au bout de quelques semaines la file pleine a commencé à perdre des messages. Surveillez ces messages, et surtout l'arrivée des journaux sur le collecteur (voir En production).
logrotate qui refuse un répertoire. Le message exact :
error: skipping "/var/log/signalements/acces.log" because parent directory has insecure permissions (It's world writable or writable by group which is not "root") Set "su" directive in config file to tell logrotate which user/group should be used for rotation.logrotate, lancé en root, refuse de travailler dans un répertoire inscriptible par d'autres que root (par un groupe autre que root, ou par tout le monde) : un utilisateur pourrait y placer un lien symbolique et lui faire écraser n'importe quel fichier. La solution n'est pas de changer les droits du répertoire, mais d'ajouter su <utilisateur> <groupe> dans la règle, comme Ubuntu le fait globalement avec su root adm.
Une règle ignorée. logrotate ignore dans /etc/logrotate.d/ les fichiers à extension « taboue » (.bak, .dpkg-old, .swp, ~, .disabled...) : une règle nommée signalements.bak ne s'applique jamais. De même, hourly ne sert à rien tant que logrotate.timer ne tourne qu'une fois par jour : surchargez le minuteur, ou utilisez maxsize.
Des caractères de contrôle dans MESSAGE. Une application qui colore sa sortie (codes ANSI) produit des messages que journalctl -o json ne rend pas comme une chaîne, mais comme un tableau d'octets ("MESSAGE" : [27, 91, 51, 49, 109, ...]). Il en va de même pour un champ qui apparaît plusieurs fois dans une entrée. Un script jq qui suppose une chaîne échoue sur ces entrées : désactivez la couleur côté application, ou traitez le cas dans jq.
Croire que SystemMaxUse= borne tout. Il ne borne que le journal persistant. Les fichiers texte de rsyslog et la file d'attente dans /var/spool/rsyslog ont leurs propres limites (logrotate, queue.maxDiskSpace). Un disque plein causé par les journaux vient le plus souvent d'un fichier texte sans règle logrotate.
Sécurité
- Les journaux sont des données personnelles. Les adresses IP des usagers de Signalements, un identifiant dans une URL, une adresse électronique dans un message d'erreur : le RGPD s'applique. Fixez une finalité (sécurité, diagnostic), une durée par étage, et écrivez-les dans le registre des traitements avec le DPO de la collectivité. La CNIL recommande six mois à un an pour la traçabilité des accès ; au-delà, il faut une justification (obligation légale, risque particulier). Ne collectez pas ce dont vous n'avez pas besoin : journaliser le corps des requêtes ou les en-têtes
Authorizationn'a aucune finalité défendable.rsyslogfournit un module d'anonymisation des adresses IP,mmanon, si une partie des journaux doit être conservée plus longtemps sous forme pseudonymisée. - Effacer les journaux est la première chose que fait un attaquant. Un journal qui ne quitte pas la machine ne prouve rien après une compromission de
root. L'envoi en temps réel verssig-outilsmet les traces hors de portée de l'attaquant, à condition quesig-outilsne soit pas administré avec les mêmes identifiants que les machines qu'il surveille (l'ANSSI recommande de cloisonner les serveurs de collecte, R20). - Authentifier dans les deux sens. Le collecteur n'accepte que des émetteurs connus (
x509/nameetPermittedPeer), les émetteurs n'envoient qu'au collecteur attendu (StreamDriverPermittedPeers). Sans la seconde vérification, une machine qui usurpe l'adresse du collecteur sur le réseau privé recevrait tous les journaux. - La clé de l'autorité est un secret majeur. Qui la détient peut fabriquer un certificat d'émetteur ou de collecteur. Elle ne vit sur aucun serveur du parc. Les clés privées des machines ne sont lisibles que par
rootou le compte dersyslog. - Les certificats expirent. L'ANSSI le souligne dans le même guide : une expiration non anticipée coupe la journalisation. Inscrivez la date d'expiration dans la supervision, et renouvelez un mois avant.
- Qui lit les journaux. Sur la machine : les groupes
systemd-journal,admetwheellisent tout le journal (journalctl(1)), etadmlit aussi les fichiers de/var/log. Sur le collecteur :/srv/donnees/journauxen0750, et des droits de suppression limités au strict nécessaire (ANSSI, R26 et R27). Revoyez ces groupes au départ d'une personne, comme ceux desudo.
En production
- Surveiller l'arrivée, pas seulement l'envoi. La seule preuve que la chaîne fonctionne est qu'un message récent est arrivé sur le collecteur. Un minuteur sur chaque émetteur envoie toutes les cinq minutes
logger -t battement "collecte ok"; sursig-outils, une sonde alerte si le dernier battement d'une machine a plus de quinze minutes. C'est le principe du signal attendu, déjà vu pour les tâches planifiées (leçon 10). L'ANSSI recommande de contrôler régulièrement la couverture de la chaîne de collecte (R12). - Dimensionner le collecteur. Estimez le volume quotidien par machine (
du -sh /srv/donnees/journaux/*sur quelques jours), multipliez par la durée de conservation, divisez par le taux de compression degzipsur du texte (souvent important, mais mesurez-le sur vos propres fichiers). Surveillez l'espace de/srv/donnees/journauxavec une alerte bien avant le remplissage (ANSSI, R22) : un collecteur plein bloque les files de tous les émetteurs. - Un collecteur est un point unique de défaillance. Si
sig-outilstombe, les files des émetteurs absorbent la coupure dans la limite dequeue.maxDiskSpace. Pour davantage de résilience, une seconde actionomfwdvers un autre collecteur, avec son proprequeue.filename, fonctionne indépendamment de la première. Et sauvegardez/srv/donnees/journauxvers Object Storage (leçon 16), en appliquant la même durée de conservation aux sauvegardes : un journal supprimé du collecteur mais conservé trois ans dans un bucket ne respecte pas votre politique. - Tout est versionné. Les drop-ins de journald, les fichiers de
/etc/rsyslog.d/et de/etc/logrotate.d/sont des fichiers de configuration comme les autres : ils vivent dans le dépôt de l'équipe et sont déployés par l'outil d'automatisation (cours Ansible : les fondamentaux), jamais retouchés à la main sur une seule machine. - Structurer à la source. Des journaux d'application en JSON (un objet par ligne, horodatage RFC 3339 en UTC) se filtrent sans expressions régulières fragiles : un changement dans le code de Signalements, qui rapporte plus que tout réglage du serveur.
- Au-delà de rsyslog et de fichiers texte. Fichiers par machine et
grepsuffisent pour trois serveurs. À l'échelle, on passe à une plateforme qui indexe et interroge : Loki, OpenSearch, ou Cockpit, le service d'observabilité de Scaleway, qui expose un point d'entrée compatible Loki dans la région fr-par et reçoit les journaux d'agents comme Fluent Bit ou Grafana Alloy. Le cours Journaux centralisés avec Loki construit cette étape. Les principes de cette leçon (transport chiffré et authentifié, file d'attente, rétention par étage, surveillance de l'arrivée) restent les mêmes.
Exercices
1. Un message de suppression (niveau 100). Dans le journal de sig-app-2, vous lisez Suppressed 4312 messages from signalements.service. Qu'est-ce qui a été perdu, où, et ces messages sont-ils arrivés sur sig-outils ?
Solution
journald a appliqué sa limitation de débit à l'unité signalements.service : au-delà du seuil (10 000 messages par 30 secondes par défaut, modulé selon l'espace disque libre, et par groupe de priorités), il a jeté 4 312 messages de ce service. Ils ne sont pas dans le journal, et ils ne sont pas non plus partis vers rsyslog, donc pas arrivés sur sig-outils : la limitation intervient avant l'écriture et avant le transfert. Les autres services n'ont rien perdu. Il faut chercher la cause de l'avalanche dans les messages qui précèdent (souvent une boucle d'erreur), puis, si besoin pendant le diagnostic, relever la limite pour ce seul service avec LogRateLimitBurst= dans un drop-in de l'unité.
2. Régler le journal de sig-outils (niveau 200). sig-outils a un disque système de 40 Go. On veut : un journal persistant, au plus 2 Go, au moins 8 Go toujours libres pour le reste du système, aucune entrée de plus de 30 jours (à une semaine près). Écrivez le drop-in, dites où le placer et comment l'appliquer, puis calculez ce que journald aurait utilisé au plus sans votre fichier.
Solution
/etc/systemd/journald.conf.d/60-sig-outils.conf :
[Journal]
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=8G
MaxRetentionSec=30day
MaxFileSec=1weekMaxFileSec=1week est nécessaire pour la précision demandée : la suppression se fait par fichier archivé, et un fichier peut couvrir jusqu'à un mois par défaut. Appliquer : sudo systemctl restart systemd-journald, puis vérifier avec systemd-analyze cat-config systemd/journald.conf et journalctl --disk-usage.
Sans le fichier : SystemMaxUse= vaudrait 10 % de 40 Go, soit 4 Go (exactement le plafond de 4 Gio, à l'arrondi près entre Go et Gio), et SystemKeepFree= 15 %, soit 6 Go, plafonné à 4 Gio. journald retient la contrainte la plus forte des deux : au plus 4 Go, et moins si le disque est déjà presque plein.
3. Une rotation qui ne se fait pas (niveau 200). Le script d'export écrit dans /var/log/export-csv/export.log. Le répertoire appartient à export:adm en 0770. Votre règle logrotate contient weekly, rotate 8, compress, et le fichier fait déjà 3 Go. sudo logrotate -d /etc/logrotate.d/export-csv affiche error: skipping "/var/log/export-csv/export.log" because parent directory has insecure permissions .... Expliquez, corrigez, et dites si le script doit recevoir un signal.
Solution
Le répertoire est inscriptible par le groupe adm, qui n'est pas root : logrotate, lancé en root, refuse d'y travailler, car un membre d'adm pourrait y placer un lien symbolique et lui faire écraser un fichier arbitraire. On ajoute à la règle su export adm : la rotation se fait sous cette identité. Il faut aussi create 0640 export adm pour recréer le fichier avec les bons droits.
Le script d'export est lancé chaque nuit, écrit, puis se termine : il n'a pas de fichier ouvert en permanence, donc ni signal ni copytruncate. Le renommage suffit, la prochaine exécution ouvrira le nouveau fichier par son nom. Enfin, 3 Go en une semaine plaide pour maxsize 500M en plus de weekly, et pour revoir ce que le script écrit.
4. Une maintenance du collecteur (niveau 300). sig-outils doit être arrêté deux heures pour l'agrandissement de son volume. Décrivez ce qui arrive aux journaux de sig-app-1 pendant et après la coupure avec la configuration de cette leçon, puis dans trois variantes : (a) sans les paramètres queue.* ni action.resumeRetryCount, (b) avec protocol="udp", (c) si sig-app-1 est redémarré pendant la coupure. Proposez enfin un moyen de vérifier, après la maintenance, que rien n'a été perdu.
Solution
Configuration de la leçon. omfwd constate la coupure TCP, l'action est suspendue (action ... suspended), les messages s'accumulent dans la file de l'action, en mémoire puis sur disque dans /var/spool/rsyslog quand la mémoire est pleine, dans la limite de 2 Go. Le reste de rsyslog (fichiers locaux) continue normalement. Au retour, l'action reprend (resumed) et la file se vide vers le collecteur ; les lignes arrivent en retard, mais avec leur horodatage d'origine (RSYSLOG_ForwardFormat). Il reste la petite fenêtre de perte de TCP au moment de la coupure.
(a) Sans file ni réessais. L'action est en mode Direct, et son nombre de réessais par défaut est faible : les messages qui ne peuvent pas être envoyés sont abandonnés, et une action bloquée peut ralentir tout rsyslog. Deux heures de journaux manquent sur le collecteur.
(b) En UDP. L'émetteur ne voit pas la coupure : il envoie dans le vide, rien n'est mis en attente, tout est perdu pendant deux heures, sans aucun message d'erreur. (Et UDP ne permet pas TLS avec omfwd.)
(c) Redémarrage de sig-app-1. Un arrêt propre de rsyslog écrit la file sur disque grâce à queue.saveOnShutdown="on", et elle est reprise au démarrage suivant. Les messages émis entre l'arrêt de rsyslog et son redémarrage restent dans le journal local, mais journald ne les rejoue pas vers rsyslog : ils manqueront au collecteur. Un arrêt brutal perdrait en plus la partie en mémoire de la file.
Vérification. Les battements périodiques (logger -t battement toutes les cinq minutes) donnent une mesure directe : sur sig-outils, il doit y avoir un battement par tranche de cinq minutes pour chaque machine sur toute la période, y compris pendant la coupure, arrivés en retard. Un trou dans la série montre une perte. Pour aller plus loin, comparer le nombre de lignes d'un identifiant donné dans le journal local (journalctl -t battement --since ... --until ... | wc -l) et dans le fichier du collecteur.
Récapitulatif
- Deux chaînes coexistent : le journal de systemd (binaire, champs fiables) et syslog (texte, transportable). Debian et Ubuntu relient les deux par
ForwardToSyslog=yes, dans un drop-in de leur paquetsystemd. - On règle journald par drop-in dans
/etc/systemd/journald.conf.d/, on lit la configuration effective avecsystemd-analyze cat-config, et on redémarresystemd-journaldpour l'appliquer.SystemMaxUse=,SystemKeepFree=etMaxRetentionSec=bornent la taille et la durée ; la suppression se fait par fichier archivé. - La limitation de débit agit par service et par groupe de priorités ;
Suppressed N messagessignale une perte, que l'on traite à la source ou parLogRateLimitBurst=dans l'unité. journalctl -o json, les filtresCHAMP=valeur,-F,-Netjqfont du journal une source de données ;loggeretsystemd-caty écrivent depuis un script avec un identifiant et une priorité.rsyslogenvoie vers un collecteur avecomfwden TCP + TLS (port 6514), authentificationx509/namedans les deux sens, framing par comptage d'octets, et une file d'attente sur disque avec réessais infinis. Le collecteur reçoit avecimtcpet range par machine d'après%FROMHOST%, jamais%HOSTNAME%.logrotate:createavec un signal danspostrotatequand le programme sait rouvrir ses fichiers,copytruncatesinon (au prix d'une perte possible) ;supour les répertoires nonroot;-dpour tester.- Chaque étage a sa durée de conservation ; au collecteur, six mois à un an selon la CNIL, en cohérence avec les sauvegardes. Les journaux sont des données personnelles et une preuve : ils sortent de la machine, chiffrés, et l'on surveille leur arrivée.
Pour aller plus loin
- Les pages
journald.conf(5),journalctl(1)etsystemd.journal-fields(7): la dernière décrit chaque champ du journal. - La documentation de
rsyslog, en particulier les modulesomfwdetimtcp, le tutoriel Reliable Forwarding of syslog Messages et la page sur les files d'attente. - Le guide de l'ANSSI Recommandations de sécurité pour l'architecture d'un système de journalisation, dont les 31 recommandations forment une bonne liste de contrôle pour un collecteur.
- La recommandation de la CNIL relative aux mesures de journalisation, pour fixer les durées avec votre DPO.
- Les RFC 5424 (format syslog) et 5425 (transport TLS).
- Le cours Journaux centralisés avec Loki, pour passer des fichiers par machine à une plateforme qui indexe et interroge.
- La leçon suivante, Authentification : PAM, NSS et annuaires.
Sources
- systemd, page de manuel journald.conf(5)
- systemd, page de manuel journalctl(1)
- systemd, pages de manuel systemd-journal-remote(8) et systemd-journal-upload(8)
- systemd, page de manuel systemd-cat(1)
- util-linux, page de manuel logger(1)
- systemd, fichier NEWS (ForwardToSyslog désactivé en v216, stockage persistant par défaut en v260)
- systemd v255, code source de la limitation de débit de journald (journald-rate-limit.c)
- Paquet Debian systemd 257, drop-in journald.conf.d/syslog.conf
- rsyslog, documentation du module omfwd
- rsyslog, documentation du module imtcp
- Rainer Gerhards, Reliable Forwarding of syslog Messages with Rsyslog
- rsyslog, tutoriel TLS : configurer un client
- rsyslog 8.2312.0, code source du pilote TLS GnuTLS (nsd_gtls.c)
- Paquet Ubuntu rsyslog (noble) : configuration, profil AppArmor et rotation
- logrotate, page de manuel logrotate(8)
- logrotate 3.21.0, code source (logrotate.c)
- RFC 5425, Transport Layer Security (TLS) Transport Mapping for Syslog
- ANSSI, Recommandations de sécurité pour l'architecture d'un système de journalisation (v2.0, 2022)
- CNIL, recommandation relative aux mesures de journalisation (délibération n° 2021-122)
- Debian 12, notes de publication : changements de la journalisation système
- Scaleway, documentation VPC : résolution DNS interne des réseaux privés
- Scaleway, documentation Cockpit : envoyer des métriques et des journaux