Set-OPS-Public/roles/client_journal/templates/config.alloy.j2
Daniel Allaire 5f5af70a9c audit : la piste sort de la machine auditee, et quelqu un regarde
auditd tournait, onze regles etaient armees, et rien de tout cela ne servait
a grand-chose. Quatre manques, mesures avant d etre combles.

1. Rien ne sortait. Alloy ne contenait aucune reference a l audit et les cinq
greffons etaient inactifs. Il lit maintenant audit.log et le pousse vers Loki
— sans toucher une permission, alloy etant deja dans le groupe adm. On ecarte
le greffon syslog : journald limite le debit, et une rafale d evenements est
exactement le moment qui compte.

2. Aucune sonde. Elle mesure la COLLECTE, pas l armement : le nombre de
regles est precisement le chiffre qui restait bon pendant la panne du
2026-08-30. Il part en perfdata, jamais en verdict.

3. Retention non declaree. Mesuree a 4,6 Mo/jour au repos ; portee de 40 a
128 Mo. Depuis (1), le local n est plus l archive mais un tampon.

4. Les regles ignoraient la cle privee de l hote. -w /etc/step/ partout, et
-w /etc/step-ca/ sur la seule autorite : auditctl refuse un watch vers un
chemin absent et fait echouer le chargement entier.

Verifie sur l autorite seule d abord, puis 14/14 : regles armees, auditd
actif, sonde a 0, et les quatorze hotes presents dans Loki sous job=audit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-11 09:00:28 -04:00

123 lines
5.2 KiB
Django/Jinja

// Gere par Set-OPS (role client_journal). Ne pas editer a la main.
loki.write "loki" {
endpoint {
url = "{{ client_journal_loki_url }}"
{% if client_journal_loki_tls | default(false) %}
tls_config {
ca_file = "{{ client_journal_loki_ca }}"
}
{% endif %}
}
}
loki.source.journal "journal" {
forward_to = [loki.write.loki.receiver]
max_age = "12h"
labels = {
job = "systemd-journal",
host = "{{ inventory_hostname }}",
}
}
{% if client_journal_audit_actif %}
// --- LA PISTE D'AUDIT SORT DE LA MACHINE AUDITEE (2026-09-11) ---
//
// `auditd` ecrit dans `/var/log/audit/audit.log` et nulle part ailleurs. Tant que la
// piste reste sur la machine qu'elle surveille, qui compromet la machine possede la
// preuve de l'avoir fait — et le fichier est precisement ce qu'on efface en premier.
//
// POURQUOI PAS LE GREFFON `syslog.conf` D'AUDITD, qui ferait passer les evenements par
// journald (deja collecte) : journald applique une LIMITE DE DEBIT. Une rafale
// d'evenements d'audit — c'est-a-dire exactement le moment qui compte — serait tronquee
// en silence. On lit donc le fichier directement.
//
// AUCUNE PERMISSION A POSER : `/var/log/audit` est `root:adm` en 0750, et l'utilisateur
// `alloy` appartient DEJA au groupe `adm` (mesure le 2026-09-11). Si cela changeait,
// Alloy se tairait sans erreur visible — la sonde `audit` reste le filet cote machine.
loki.source.file "audit" {
targets = [{
__path__ = "{{ client_journal_audit_chemin }}",
job = "audit",
host = "{{ inventory_hostname }}",
}]
forward_to = [loki.write.loki.receiver]
}
{% endif %}
{% if client_journal_syslog_ecoute %}
// --- RECEPTEUR SYSLOG : CE QUI NE PEUT PAS PORTER D'AGENT (2026-09-10) ---
//
// La frontiere OPNsense n'accueille aucun agent. Elle sait emettre du syslog, et rien
// d'autre. Loki ne parle pas syslog : il faut un traducteur, et Alloy en est un.
//
// POURQUOI CE RECEPTEUR VIT ICI plutot que dans un role a lui : c'est la meme machine qui
// pousse deja ses propres journaux vers le meme Loki. Un second agent ne servirait qu'a
// avoir deux configurations a garder en phase.
//
// L'ETIQUETTE `host` VIENT DE L'EMETTEUR, pas de nous : plusieurs equipements peuvent
// pointer ici, et les confondre sous le nom du recepteur rendrait les journaux
// inutilisables au moment ou l'on en a besoin.
loki.source.syslog "frontiere" {
listener {
address = "{{ client_journal_syslog_ecoute }}"
protocol = "{{ client_journal_syslog_protocole }}"
label_structured_data = true
labels = { job = "syslog", host = "{{ client_journal_syslog_emetteur }}" }
}
forward_to = [loki.relabel.syslog.receiver]
}
// QUI A PARLE ? On veut la meme etiquette `host` que pour une machine qui pousse, sans
// quoi la requete change de forme selon la provenance et personne ne s'en souvient.
//
// DEUX SOURCES, DANS CET ORDRE, ET C'EST DELIBERE.
//
// L'en-tete RFC5424 porte un nom que l'emetteur se donne — pratique quand il est juste,
// et vide ou faux quand il ne l'est pas. Mesure du 2026-09-10 : ni les journaux reels de
// la frontiere, ni un message d'essai forge a la main avec un nom explicite n'ont produit
// d'etiquette. Le champ existe dans la specification ; il n'arrive pas ici.
//
// L'ADRESSE DE LA CONNEXION, ELLE, EST TOUJOURS LA — et elle ne s'invente pas : c'est le
// pare-feu qui l'observe, pas l'emetteur qui la declare. Un nom qu'on se donne peut
// mentir ; une adresse de connexion, non.
//
// CE QUI A FINALEMENT SERVI : l'etiquette posee sur l'ECOUTEUR, depuis la carte de
// l'hebergeur. Aucune des trois regles ci-dessous n'a produit quoi que ce soit — ni sur
// les journaux reels, ni sur un message forge a la main. On garde le relabel : le jour ou
// l'emetteur enverra un nom, il prendra le dessus. En attendant, le nom vient de la ou il
// est DECLARE, pas de la ou on l'espere.
//
// CE QUE CA SUPPOSE, ET QU'IL FAUT SAVOIR : un SEUL emetteur par ecouteur. Le site n'a
// qu'une frontiere ; le jour ou un second equipement pointe ici, il faudra un ecouteur par
// emetteur, ou faire enfin marcher le relabel. La supposition est ecrite pour qu'on la
// retrouve, plutot que decouverte devant des journaux melanges.
//
// La derniere regle qui correspond gagne : le nom l'emporte quand il existe.
loki.relabel "syslog" {
forward_to = [loki.write.loki.receiver]
// `regex` SUR LES TROIS, Y COMPRIS CELLE-CI (mesure du 2026-09-10).
//
// Sans elle, la regle correspond TOUJOURS — meme quand sa source n'existe pas — et pose
// `host = ""`. Loki jette une etiquette vide, et l'etiquette posee sur l'ecouteur juste
// au-dessus disparaissait avec elle. Le symptome : un flux sans `host`, alors que la
// configuration deposee en portait un, visible a l'oeil dans le fichier.
//
// Une regle de relabel sans garde ne « laisse pas passer » : elle EFFACE.
rule {
source_labels = ["__syslog_connection_ip_address"]
target_label = "host"
regex = "(.+)"
}
rule {
source_labels = ["__syslog_connection_hostname"]
target_label = "host"
regex = "(.+)"
}
rule {
source_labels = ["__syslog_message_hostname"]
target_label = "host"
regex = "(.+)"
}
}
{% endif %}