syslog-ng

Rôle

Point de collecte central des logs de toute la flotte Indio (53 VM) par UDP/TCP 514, avec rangement automatique par application. Assure l'archivage à froid (brut, non parsé, terminal) — voir Vue d'ensemble SOC pour la place de ce composant dans le pipeline complet et la distinction avec le traitement « à chaud » assuré par ELK/Logstash.

syslog-ng ne relaie rien plus loin

Clarifié explicitement par l'exploitant (2026-07-16) : « le serveur syslog sert à stocker les logs brut est pas les envoyer ». syslog-ng est un puits terminal — il reçoit, range et archive, mais ne retransmet jamais un message vers un autre système. La distribution vers Elasticsearch (pipeline « à chaud ») est un chemin parallèle et indépendant, pas une suite de celui-ci — voir « Architecture cible du pipeline » sur la page Vue d'ensemble SOC.

Architecture

VM unique Rocky 9.6 (tmpl-rocky96-hardened), INDSOC001 (10.100.20.130), segment seg-soc.

Cible réelle vs ancien inventaire

inventory.ini et MANUEL_INSTALLATION.md ont longtemps pointé vers INDSUPV009 (10.100.2.233) — VM détruite le 2026-07-07 (voir Historique des décommissionnements). La cible réelle actuelle, confirmée par terraform state show vsphere_virtual_machine.syslogng, est INDSOC001 — corrigé dans les deux fichiers depuis le 2026-07-16.

Schéma — collecte, rangement et archivage

flowchart TD
    Fleet["Flotte Indio<br/>53 VM (49 + 4 proxies DMZ)<br/>rsyslog omfwd TCP/514"]
    NSXFG["NSX Manager / FortiGate<br/>export syslog UDP/514<br/>hors baseline"]
    SNG["syslog-ng -- INDSOC001<br/>10.100.20.130<br/>source network(), flags(syslog-protocol)"]
    Disk[("Disque local dedie 2 To<br/>/dev/sdb, XFS<br/>/var/log/syslogng")]
    Retention["Script retention + timer systemd<br/>quotidien : compression J+7, purge J+365"]
    NFS[("vSAN File Service<br/>file-node01:/Log<br/>montage NFSv3 soft")]

    Fleet -->|"RFC5424, tout le trafic local"| SNG
    NSXFG -->|"RFC5424 attendu"| SNG
    SNG -->|"ecriture continue,<br/>EXCLUSIVEMENT locale"| Disk
    Disk --> Retention
    Retention -.->|"rsync -a --update<br/>*.log.gz UNIQUEMENT, timeout 300"| NFS

Rangement produit sur le disque local : /var/log/syslogng/${PROGRAM:-unknown}/${YEAR}/${MONTH}/${DAY}/messages.log — un dossier par application distincte, créé à la volée (create_dirs(yes)), aucune liste à maintenir. Le lien pointillé vers le NFS représente le seul chemin retenu vers ce stockage externe (rsync différé de fichiers déjà clos) — l'écriture continue directe a été testée et abandonnée, voir Points d'attention.

Historique du rangement

Le schéma de rangement a changé deux fois avant de se stabiliser :

  1. Rangement par IP source (avant le 07-16) : filtre netmask() sur FortiGate (10.15.91.14) / NSX Manager (10.15.100.15), fourre-tout network-other.
  2. Rangement par catégorie Kafka fixe (2026-07-16) : filtre sur le champ syslog PROGRAM/APP-NAME, avec 5 catégories calquées sur les topics Kafka (cloud, firewall, identity, infra, windows) + unknown. Contrat imposé au futur pont Logstash : positionner PROGRAM à l'une de ces 5 valeurs.
  3. Rangement par application réelle (2026-07-17, remplace le précédent — demande explicite de l'exploitant : « je veux qui range par appli stp avec année mois et jour », jugé plus simple) : une seule destination dynamique ${PROGRAM:-unknown}, sans énumération de filtres à maintenir. C'est la configuration actuelle (voir syslog-ng.conf.j2 ci-dessous). Le contrat pour le futur pont Logstash change en conséquence : préserver/positionner PROGRAM au nom réel de l'application d'origine, plus proche d'un passthrough que d'une transformation catégorielle.

Vérifié fonctionnel sur trafic réel (pas seulement logger de test) : après le déploiement fleet-wide du 2026-07-17, des dossiers applicatifs sont apparus automatiquement — chronyd, netbox/netbox-redis/netbox-worker, repmgrd, rsyslogd, sshd-session, sudo, systemd, systemd-logind, vault.

Provisioning Terraform

Fichiers : main.tf, variables.tf, versions.tf, terraform.tfvars.example.

  • VM unique (vsphere_virtual_machine.syslogng), 1 vCPU / 2048 Mo RAM par défaut, réseau seg-soc, réservation IP via NetBox, disque OS unique issu du template.
  • Le disque dédié de 2 To utilisé pour l'archivage n'apparaît pas dans ce main.tf : ajouté manuellement côté vSphere le 2026-07-17 (/dev/sdb, UUID b884941a-65dc-46a4-be07-10720aa1b0ad), hors Terraform — voir Procédure manuelle ci-dessous et Points d'attention (dette technique à régulariser si ce disque doit redevenir reproductible par IaC).

Configuration Ansible

Rôle unique syslog_ng :

  • Installe syslog-ng + rsync + nfs-utils depuis le dépôt interne indio-epel (miroir Nexus de dl.fedoraproject.org — distinct de indio-extras, qui est le miroir du dépôt Rocky « Extras » natif malgré la ressemblance du nom). syslog-ng est un paquet EPEL en amont, pas fourni par indio-baseos/indio-appstream.
  • Crée un compte système dédié (syslog-ng:syslog-ng), propriétaire des fichiers/dossiers archivés (permissions restrictives 0750/0640) ; le démon continue de tourner en root par défaut, nécessaire pour le bind direct sur le port privilégié 514 — c'est la propriété des fichiers créés qui est restreinte au compte dédié via owner()/group() dans syslog-ng.conf, pas l'UID du process lui-même.
  • Monte le disque dédié (ansible.posix.mount, state: mounted), épingle le contexte SELinux var_log_t sur toute l'arborescence d'archivage (community.general.sefcontext + restorecon).
  • Déclare le montage NFS externe en fstab sans forcer son état monté à chaque exécution (state: present, pas mounted) — voir Points d'attention pour la justification complète.
  • Déploie syslog-ng.conf (template), valide sa syntaxe après déploiement, en tâche séparée (syslog-ng --syntax-only -f /etc/syslog-ng/syslog-ng.conf) — le validate: intégré du module template échoue sur le fichier temporaire (permissions restrictives, Permission denied même sous become), piège Ansible connu.
  • Déploie le script + timer systemd de rétention/synchronisation.
  • Ouvre 514/udp et 514/tcp dans firewalld (zone drop, template durci).

syslog-ng.conf — points clés réels

source s_network {
    network(transport("udp") port(514) so-rcvbuf(1048576) flags(syslog-protocol));
    network(transport("tcp") port(514) max-connections(200) flags(syslog-protocol));
};

destination d_by_app {
    file("/var/log/syslogng/${PROGRAM:-unknown}/${YEAR}/${MONTH}/${DAY}/messages.log"
        create_dirs(yes)
        dir_owner("syslog-ng") dir_group("syslog-ng") dir_perm(0750)
        owner("syslog-ng") group("syslog-ng") perm(0640)
        template(t_archive_line)
        frac_digits(3)
    );
};

options{} globales notables : keep-hostname(yes) (ne jamais écraser le HOSTNAME du message par une résolution DNS), use-dns(no)/dns-cache(no) (pas de dépendance DNS pour des IP inconnues), threaded(yes).

Piège majeur : flags(syslog-protocol) indispensable sur la source

Sans ce flag, syslog-ng ne détecte pas le format RFC5424 (IETF) sur une source network() — il retombe sur un parsing RFC3164 (BSD legacy) par défaut, où le champ VERSION RFC5424 (le 1 juste après le PRI) est interprété à tort comme un fragment du header BSD. Conséquence : ${PROGRAM}/${APP-NAME} ne se remplissent jamais, peu importe que les filtres soient syntaxiquement corrects — tout part dans unknown.

Symptôme trompeur : le texte archivé (${MSGHDR}${MSG}, via t_archive_line) affichait quand même la bonne valeur visuellement au bon endroit, donnant l'illusion que le parsing fonctionnait — c'était en fait juste le texte brut du message, pas la vraie valeur de macro interne utilisée par le filtre/la destination.

Diagnostiqué via une instance syslog-ng de debug isolée sur un port séparé (source + destination minimalistes affichant ${PROGRAM}/ ${APP-NAME} littéralement dans un fichier) — méthode à réutiliser pour tout futur diagnostic de macro syslog-ng douteuse. Effet de bord positif du fix : ${HOST} se remplit alors correctement depuis le HOSTNAME du message RFC5424 au lieu de retomber sur l'IP source brute.

Piège méthodologique associé : un premier essai de debug via heredoc SSH multi-niveaux (bash -c '... << EOF ... ${APP-NAME} ...') a produit un résultat corrompu — ${APP-NAME} expansé par le bash local comme une expansion de paramètre shell (${parameter-word}) avant même d'atteindre le serveur distant. Toujours écrire un fichier de config de test en local (Write) puis le transférer (scp) plutôt qu'un heredoc à travers SSH quand le contenu contient des ${...}.

Procédure manuelle

Deux éléments réels de cette VM ne sont, à ce jour, décrits par aucun code versionné et devraient être reproduits manuellement en cas de rebuild :

  1. Ajout du disque de données dédié (2 To, /dev/sdb) côté vSphere — hors Terraform (voir Provisioning ci-dessus) :
    govc vm.disk.create -vm INDSOC001 -name INDSOC001/syslogng-data -size 2T
    
    puis, côté VM, initialisation une seule fois si le disque est neuf :
    sudo mkfs.xfs /dev/sdb
    
    Le montage lui-même (/etc/fstab, point de montage /var/log/syslogng) est géré par le rôle Ansible (ansible.posix.mount, state: mounted) une fois le disque présent — seule la création du disque est manuelle.
  2. Montage initial du NFS externe — le rôle Ansible déclare la ligne fstab (state: present, volontairement passif, voir Points d'attention) mais ne monte jamais activement le point de montage. Premier montage réel à faire une fois, à la main :
    sudo mkdir -p /mnt/syslog-nfs
    sudo mount /mnt/syslog-nfs
    sudo findmnt /mnt/syslog-nfs   # confirmer vers=3, soft, timeo=30, retrans=3
    
    Un reboot complet remonte aussi ce point via _netdev dans fstab, sans action manuelle supplémentaire.

L'export NFS file-node01.infra.indio:/Log lui-même (partage vSAN File Service) a été créé côté vCenter par l'exploitant — voir vSAN File Service pour ce composant (aucune automatisation possible côté serveur : pas de SSH, pas de module Ansible, pas d'endpoint REST vSphere fonctionnel pour ce type d'objet).

Procédure de déploiement

cd /root/syslogng/ansible
ansible-playbook --syntax-check site.yml
ansible-playbook site.yml -e @/root/.hap_secrets.yml

Contrôle de santé / Vérification

systemctl status syslog-ng --no-pager
logger -n 127.0.0.1 -P 514 -T "test message"          # test TCP local
echo "<134>1 - - test - - - test message UDP" | nc -u -w1 127.0.0.1 514

find /var/log/syslogng -maxdepth 1 -type d             # dossiers applicatifs existants
df -h /var/log/syslogng /mnt/syslog-nfs                # espace disque local vs NFS

# Rétention / synchronisation NFS :
systemctl list-timers syslogng-retention.timer
journalctl -t syslogng-retention -n 50 --no-pager

Espace observé au 2026-07-17 (mesure ponctuelle, à ne pas considérer comme une jauge en continu) : disque local à 1 % (~14 Go sur 2 To), NFS à 1 % (~10 Mo sur 14 To disponibles côté vSAN) — aucune pression de rétention à court terme.

Points d'attention

Point de collecte central — toute modification est sensible

Ce service reçoit les logs de l'ensemble de la flotte Indio (~53 VM). Une modification de la configuration de collecte ou des règles de rangement doit être testée avec précaution avant tout déploiement en production ; cette page ne documente pas de procédure de modification à chaud.

Écriture NFS directe et continue : échec catastrophique, testé et abandonné (2026-07-17)

Faire écrire syslog-ng en continu, directement sur le NFS externe (file-node01.infra.indio:/Log, vSAN File Service) a été testé sous deux versions NFS, toutes deux inutilisables en pratique :

  • NFSv4.1 : un process de test s'est retrouvé bloqué en état noyau D (uninterruptible sleep), injoignable même par SIGKILL, pendant plusieurs minutes avant de se débloquer seul.
  • NFSv3 : pas de blocage franc, mais échec silencieux — plus aucune nouvelle ligne écrite (confirmé 8+ minutes sans nouvelle entrée malgré un trafic fleet-wide continu), alors que de simples opérations manuelles (mkdir, echo >) sur le même point de montage fonctionnaient sans problème.

Root cause jamais pleinement identifiée (sémantique de verrouillage/flock NFS, delegation/callback NFSv4.1, ou pattern d'écriture trop fsync-heavy pour ce vSAN File Service — hypothèses non tranchées). Ne pas retenter l'écriture directe et continue sans diagnostic plus poussé (strace, nfsstat) en isolation.

Design final retenu (validé bout en bout le 2026-07-17) : syslog-ng écrit exclusivement sur le disque local dédié. Un script de rétention (syslogng-retention.sh, timer quotidien) ajoute une étape de synchronisation entre la compression et la purge : rsync -a --update des fichiers déjà compressés (.log.gz) uniquement — jamais les .log actifs, ni en cours d'écriture ni en cours de compression — vers le NFS, avec :

  • timeout 300 en dur : ce NFS s'est montré capable de bloquer des opérations bien plus simples qu'un rsync — mieux vaut un cycle partiel/manqué (retenté au suivant, --update ne re-transfère pas ce qui est déjà là) qu'un housekeeping qui ne rend jamais la main.
  • Vérification défensive du point de montage (mountpoint -q) avant chaque cycle — un cycle est simplement sauté, proprement journalisé, si le NFS est transitoirement indisponible.
  • set -uo pipefail (pas -e) dans le script : la purge (étape suivante) doit s'exécuter même si le rsync échoue ou expire — un NFS externe indisponible ne doit jamais empêcher le housekeeping local.

Des micro-coupures NFS récurrentes sont observées en usage normal sur ce vSAN File Service (dmesg : nfs: server file-node01.infra.indio not responding, timed out, plusieurs fois par heure) — gérées proprement par ce design (un cycle sauté plutôt qu'un blocage). Le NFS contenait déjà, avant même un premier test manuel, les dossiers de nombreuses applications réelles (vault, haproxy, netbox*, kafka-server-start.sh, etc.) — preuve que le rsync périodique fonctionne aussi tout seul en usage normal, pas seulement en déclenchement manuel.

Statut de l'export /Log non reconfirmé au 2026-07-19

Tout ce qui précède décrit l'état validé le 2026-07-17. Une vérification en direct menée le 2026-07-19 pour la page vSAN File Service n'a pas pu reconfirmer l'export /Log (showmount -e file-node01.infra.indio ne liste plus que /veeam ; une tentative de montage /Log en lecture seule reste sans réponse jusqu'au timeout, sans rejet propre). Cause non déterminée (suppression manuelle du partage, bascule NFSv4.1, ou nouvel épisode d'instabilité du service — voir cette page pour le détail). Le comportement défensif décrit ci-dessus (montage soft, vérification avant chaque cycle, timeout dur) signifie qu'une éventuelle indisponibilité actuelle de /Log est silencieuse côté syslog-ng — le disque local reste la copie faisant foi tant que ce point n'a pas été revérifié par un humain.

Piège Ansible évité en connaissance de cause : ansible.posix.mount et state: mounted

Le module ansible.posix.mount avec state: mounted réévalue activement l'état du montage à chaque exécution du rôle — risque de retoucher ce NFS instable à chaque ansible-playbook, même quand rien n'a changé. Pour le montage NFS spécifiquement (pas pour le disque local, sans risque identifié), le rôle utilise state: present à la place : écrit seulement la ligne fstab de façon idempotente, sans vérification active. Le montage réel se fait une fois manuellement (voir Procédure manuelle) ou au prochain boot (_netdev).

  • Pont Logstash Kafka → syslog-ng (prévu, non construit) : voir Vue d'ensemble SOC pour le schéma complet. Ce pont devrait consommer les 5 topics Kafka historiques (logs-cloud/firewall/identity/infra/windows) et réémettre en syslog standard vers INDSOC001:514, en préservant PROGRAM au nom réel de l'application d'origine (voir « Historique du rangement » ci-dessus). Une fois ce pont construit, le filtrage par IP source devenu obsolète dans la configuration actuelle n'a plus d'objet — mais un nouveau souci apparaîtra côté visibilité de la source : tous les paquets arriveront avec l'IP du pont Logstash, pas celle de l'équipement d'origine (FortiGate, NSX Manager, etc.). Pas encore résolu ; piste envisagée mais non validée : filtrer/enrichir sur le ${HOST} du message plutôt que sur l'IP paquet, si Logstash préserve bien le hostname d'origine dans le message réémis.
  • Flux audit.log séparé : les événements auditd (relayés par audisp-syslog) empruntent aussi ce chemin générique — ils apparaissent donc normalement dans /var/log/syslogng/audisp-syslog/... comme n'importe quelle autre application. Un chemin distinct et déjà fonctionnel existe en parallèle, directement vers Kafka puis Elasticsearch (sans repasser par syslog-ng) — voir Kafka — topic logs-audit et ELK — pipeline audit.log.
  • Le disque dédié de 2 To n'est pas géré par Terraform (ajouté manuellement côté vSphere) — voir Provisioning Terraform et Procédure manuelle ci-dessus.
  • Export syslog NSX/FortiGate : configuré hors Terraform via l'API node NSX (pas de ressource dédiée dans le provider vmware/nsxt) — voir NSX. La conformité RFC5424 de cet export n'a pas été vérifiée spécifiquement ; en cas de non-conformité, ces messages tomberaient dans unknown comme tout message ne respectant pas syslog-protocol.