Syslog-ng

Posted On 01 Gen 2008
Comment: Off

linuxtutorialTutorial LinuxSyslog-ng (ng sta per: next generation) nasce con l’intento di sopperire a tutte quelle mancanze che caratterizzano il syslogd standard. L’obiettivo di questo programma e’ consentire un dispatching semplice, ma allo stesso tempo granulare, di tutti i messaggi di log, consentendo inoltre di filtrare ogni evento senza che l’applicazione che lo genera debba preoccuparsi di specificare una determinata facility.
Grazie a questo potente mezzo di filtering, siamo in grado di loggare, in maniera ordinata, i messaggi provenienti da tutte applicazioni.

Installazione

Abbiamo bisogno di due pacchetti, libol che e’ una libreria di supporto per il syslog-ng e syslog-ng stesso.
Come consuetudine scarichiamoli e installiamoli, la home page di syslog-ng e’ http://www.balabit.com/products/syslog_ng/. Sul cdrom allegato alla rivista trovate l’ultima versione disponibile di syslog-ng e libol. Procediamo:

$ tar xzf libol-0.3.14.tar.gz
$ cd libol-0.3.14
$ ./configure && make
# make install

$ tar xzf syslog-ng-1.6.5.tar.gz
$ cd syslog-ng-1.6.5
$ ./configure && make
# make install

L’installazione ha copiato in /etc/syslog-ng/syslog-ng.conf un file base di configurazione, questo file e’ idealmente diviso in 5 sezioni che sono:

– options
– source
– destination
– filter
– log

Ho detto “idealmente” perche’ in realta’ possiamo mescolarle tra di loro senza alcun problema, ma per una questione di ordine le terremo ben distinte, sara’ cosi piu’ semplice, in futuro, rimettere le mani nella configurazione per cambiare qualche parametro.
Facciamo una breve panoramica delle sezioni prima di passarle in rassegna una ad una:

Options: serve a specificare delle opzioni relative al sistema di logging ma non influenza il filtraggio dei messaggi.
Source: dice al demone da dove leggere i messaggi di log.
Destination: i file in cui scrivere i messaggi di log.
Filter: i filtri da applicare ai messaggi in arrivo
Log: prende i messaggi dal source, li fa passare per i filtri e infine li scrive nella destinazione.
E’ questo l’iter che deve seguire ogni messaggio prima di venir scritto su disco, in questa maniera possiamo filtrare facilmente i log provenienti da ogni applicazione e separarli come meglio crediamo, soprattutto possiamo riversare tutto il traffico dei log di ogni macchina della nostra rete, su un singolo logging-computer, che lavora magari in append-only.
Diviene cosi possibile isolare i messaggi di un’intera lan su un singolo host che potremo utilizzare come archivio con tutti i vantaggi del caso, vale a dire: non dovremo spulciare i log di ogni macchina singolarmente, e soprattutto sara’ molto piu’ difficile per un attaccante modificare i log per coprire le proprie tracce. Ma lo stesso browsing sara’ facilitato perche’ c’e’ anche l’opportunita’ di scrivere i messaggi all’interno di un database. Se poi pensiamo che tutto il traffico puo’ viaggiare in un tunnel SSL, ci rendiamo conto di quanto possa esser comodo disporre di un’utility versatile come syslog-ng.
Se tutto questo solletica il vostro appetito, significa che possiamo iniziare a configurare questo demone.

Configurazione

Iniziamo con qualcosa di semplice, facciamo in modo che tutti i messaggi provenienti dal kernel vengano loggati in /var/log/kern.log. Quindi apriamo il file di configurazione (/etc/syslog-ng/syslog-ng.conf) e iniziamo a riempire la “sezione” options:

options {
long_hostnames(off);
sync(0);
stats(86400);
};

Facendo cosi diciamo a syslog-ng di non utilizzare i nomi “lunghi” degli hostname, questa opzione e’ utile sulle LAN perche’, cosi invece di avere i log nella forma src@hostname, avremo solo l’hostname. Se non vi interessa la compattezza potete anche settarlo a “on”.
La seconda riga specifica il numero di righe di log che il demone deve tenere in memoria prima di scriverle su disco, generalmente il traffico non e’ cosi intenso da giustificare un buffering, tuttavia se fate uso di applicazioni che producono molti kb di log, allora un po’ di buffering rendera’ le operazioni di scrittura piu’ leggere (perche’ non c’e’ continuamente accesso al disco, e viene fatta solo una “grande” operazione di scrittura ogni tanto, piuttosto che tante piccole di continuo).
La terza riga indica ogni quanti secondi il demone deve stampare una “statistica”. La riga stampata indica quanti messaggi sono andati persi e non sono quindi stati scritti. L’opzione stats torna utile per due motivi, il primo e’ che possiamo sapere se dei messaggi sono andati persi, il secondo e’ perche’ possiamo usare tale riga come mark, cioe’ per renderci conto se il demone e’ stato stoppato (da qualcuno) o e’ crashato, e per quanto tempo e’ rimasto inattivo. Esiste un’altra opzione in corso di implementazione che si chiama mark() e stampa semplicemente una riga “MARK” nei log con data e ora, l’ho omessa perche’ sarebbe ridondante utilizzare stats e mark insieme.
Le opzioni sono facoltative, quindi potete anche non settarle, tenete pero’ a mente che se non specifichiamo un intervallo per l’opzione stats(), syslog-ng stampera’ la statistica ogni 10 minuti (e puo’ essere noioso ritrovarsi 144 STATS nei log ogni 24 ore).
Passiamo ora alla configurazione del source che prende questa forma:

source nome { dove leggere i log; opzioni… };

I log del kernel possiamo leggerli da /proc/kmsg, ma dobbiamo specificare se si tratta di un file, di uno unix-socket o di un socket di rete, quindi scegliamo un nome adatto per il source e scriviamo la nostra riga:

source kernel { file(“/proc/kmsg”); };

Abbiamo chiamato “kernel” la nostra prima sorgente, e abbiamo detto al demone che i log vanno presi da un file che si trova in /proc/.
Specifichiamo ora una destination per i nostri file, il formato e’ identico a quello del source:

destination kern { file(“/var/log/kern.log”); };

Mettiamo i messaggi del kernel in /var/log/kern.log.
Come vedete il nome e’ diverso e non a caso, infatti i nomi non sono vincolanti, potete usare quello che volete, vedremo tra poco perche’.
Dobbiamo ora filtrare i messaggi perche’ magari non ci interessa proprio tutto quello che il kernel stampa, ma soltanto informazioni di una certa importanza o messaggi inviati solo da determinati task, il filtro utilizza questa sintassi:

filter nome { facility(a, b, c…); level(a, b, c….); };

Il livello indica l’importanza del messaggio, valori possibili sono (dal piu’ importante al meno importante):

emerg: il sistema e’ inutilizzabile
alert: vanno presi provvedimenti immediati
crit: ci sono delle condizioni critiche
err: ci sono delle condizioni di errore
warning: ci sono condizioni di potenziale pericolo
notice: messaggio significativo da parte di un processo
info: messaggio informativo
debug: messaggio di debug

Mentre le facility rappresentano le categorie di provenienza dei messaggi e sono:

auth: il messaggio proviene dal sistema di autenticazione (oramai deprecato, al suo post va usato authpriv)
authpriv: proviene dal sistema di autenticazione (va usato questo)
cron: proviene da demoni come cron e at
daemon: proviene da un demone che non ha una facility dedicata
ftp: proviene dal demone ftp
kern: messaggio proveniente dal kernel
local0…local7: facility locali, possono essere stabilite dalle applicazioni
lpr: proviene dal demone di stampa
mail: proviene dal sistema di gestione delle mail
news: proviene dal demone di gestione delle news (nntp…)
syslog: messaggio generato dal demone di logging (syslogd, syslog-ng)
user: messaggio generico proveniente dall’userspace
uucp: messaggio proveniente da uucp

Supponendo di voler catturare tutti i messaggi provenienti dal kernel, bastera’ quindi fare:

filter f_kern { facility(kern); };

Ora che abbiamo sistemato tutte le sezioni, dobbiamo metterle assieme utilizzando la direttiva log che prende questa forma:

log { source(sorgente1); filter(filtro1)…; destination(destinazione); };

Essendo “log” a prendersi cura di raggruppare le opzioni specificate nelle altre sezioni, possiamo utilizzare liberamente i nomi, senza per questo dover assegnare a source e destination lo stesso nome. Nel nostro caso diremo a log di utilizzare il source/destination e filtri specificati poco sopra, quindi:

log { source(kernel); filter(f_kern); destination(kern); };

Quindi con 4 righe abbiamo rediretto in un singolo file tutti quanti i messaggi provenienti dal kernel. Questa leggera ridondanza e’ il prezzo da pagare per la granularita’ concessaci dal programma, ma continuiamo a rifinire il nostro file di configurazione aggiungendo anche i messaggi provenienti dal sistema di autenticazione, in questo modo avremo un file di log dedicato agli accessi dei nostri utenti.
I messaggi che non provengono dal kernel ma dall’userspace possono essere letti da /dev/log, che diventera’ quindi il nostro source. Dovendo prendere solo i messaggi provenienti dal sistema di autenticazione filtreremo tutte le facilities diverse da auth/authpriv (li useremo entrambi per evitare possibili perdite di messaggi da parte di demoni non aggiornati) e scriveremo i messaggi in /var/log/auth.log. Oramai dovrebbe esser chiaro come procedere, ma una parola fa spesa sul source:

source src { unix-stream(“/dev/log”); internal(); };

La prima cosa importante e’ specificare che /dev/log e’ uno unix-socket e non un file standard, questo possiamo verificarlo semplicemente facendo:

# ls -l /dev/log
srw-rw-rw- 1 root root 0 Oct 8 15:09 /dev/log

Se il primo carattere e’ una “s” allora stiamo esaminando un socket, e syslog-ng deve saperlo, mentre la seconda opzione: “internal()”, serve a dire a syslog-ng che vogliamo usare quel sorgente anche per ricevere i messaggi interni del demone di log.
La destinazione e’ semplice:

destination authlog { file(“/var/log/auth.log”); };

Mentre per il filtro dovremo dire al demone che vogliamo scrivere nella destinazione solo i file che hanno facility auth/authpriv, si fa cosi:

filter f_authpriv { facility(auth, authpriv); };

Riuniamo quindi tutte le opzioni nella direttiva log:

log { source(src); filter(f_authpriv); destination(authlog); };

Salviamo il tutto e avviamo il demone con:

# syslog-ng

Se non abbiamo commesso errori nella configurazione, troveremo i nostri due file in /var/log.
Possiamo ora cimentarci nell’utilizzo di sintassi piu’ complesse, una cosa utile potrebbe essere quella di filtrare dai messaggi del kernel alcune stringhe. In particolar modo sulle tastiere dei laptop, o sulle tastiere multimediali, ci sono dei tasti che non vengono riconosciuti automaticamente e che generano messaggi come questo nel file di log:

atkbd.c: Unknown key pressed (translated set 2, code 0xd9 on isa0060/serio0).
atkbd.c: Use ‘setkeycodes e059 <keycode>’ to make it known.

Se vogliamo evitare di loggarli perche’ non abbiamo intenzione di utilizzarli, possiamo dire al demone di ignorare i messaggi che contengono la stringa “atkbd.c”, ci bastera’ modificare in questa maniera il filtro che si occupa di leggere i messaggi del kernel:

filter f_kern { facility(kern) and not match(“atkbd.c”); };

Creiamo ora un file contenenti tutti i messaggi di sistema non troppo importanti, come source useremo il solito /dev/log, quindi non c’e’ bisogno di aggiungere nulla, come destination useremo /var/log/messages:

destination messages { file(“/var/log/messages”); };

ignoreremo tutti i messaggi con priorita’ maggiore di warn e tutti i messaggi provenienti dal sistema di mail, news e autenticazione (perche’ conviene sempre loggare separatamente gli eventi provenienti da demoni differenti):

filter f_messages { level(info..warn)
and not facility(auth, authpriv, mail, news); };

e’ infatti possibile utilizzare operatori quale: and, or, not per concatenare piu’ eventi. Oltretutto non e’ neanche necessario specificare il livello dei messaggi uno ad uno (info, notice, warning) perche’ utilizzando i “..” diciamo a syslog-ng che vogliamo intercettare tutti i messaggi con prorita’ compresa tra info e warning inclusi.
Prima di ravviare il demone, aggiungiamo anche un log per gli eventi provenienti dai nostri router, la procedura e’ identica a quelle gia’ utilizzate, eccezion fatta per il source, che questa volta non e’ un demone ma una macchina presente in rete:

source net { udp(); };

Diciamo cosi che la sorgente e’ il traffico UDP in arrivo sulla porta 514 (che e’ la porta di default sulla quale binda il syslog daemon, ma e’ possibile configurarla a piacimento). Se la nostra rete utilizza dei router Cisco, possiamo utilizzare come destination un file di nome /var/log/cisco.log:

destination cisco { file(“/var/log/cisco.log”); };

Sul quale andremo a scrivere i messaggi provenienti dai router, con qualunque priorita’ ci giungano:

filter f_cisco { level(debug..emerg) and facility(local6); };

E raggruppiamo il tutto:

log { source(net); filter(f_cisco); destination(cisco); };

Configuriamo ora il router perche’ invii sul nostro ip (supponiamo che sia: 1.2.3.4) tutti i suoi messaggi con una facility locale di 6 (una qualunque facility locale tra 0 e 7 va bene), quindi logghiamoci sulla macchina:

Router> enable
Password:
Router# conf t
Router(config)# logging facility local6
Router(config)# logging source-interface Ethernet0
Router(config)# logging 1.2.3.4
^Z

Questo e’ ovviamente solo un esempio, quindi riadattatelo al vostro router (e alle vostre ACL) che puo’ essere di una marca diversa o puo’ avere una configurazione differente.
Riavviamo syslog-ng e proviamo ad effettuare qualche operazione sul router, subito dopo apriamo il file di log e verifichiamo che il messaggio sia giunto sulla macchina remota:

# cat /var/log/cisco.log
%SYS-5-CONFIG_I: Configured from console by user on vty0

Ed ora per concludere vi spieghero’ come fare in modo che tutti i log di una macchina, che non sia necessariamente un router, arrivino su un computer remoto. Creeremo in pochissimi passi una logging-authority (concedetemi il termine) per tutta un’intera lan. Il procedimento e’ estremamente semplice, sul file di configurazione dei client (che puo’ anche essere uguale per tutti), settiamo queste opzioni:

options {
keep_hostname(yes);
long_hostnames(off);
log_fifo_size(100);
};

Diciamo cosi a syslog-ng di spedire i log a blocchi di 100 righe. La configurazione e’ invece piuttosto semplice, leggiamo tutto da /dev/log, non filtriamo niente e inviamo tutto sulla nostra logging-authority (che supponiamo essere: 1.2.3.4):

source src { unix-stream(“/dev/log”); internal(); };
destination logging_authority {tcp(“1.2.3.4” port(514));};
log { source(src); destination(logging_authority); };

I messaggi di log vengono normalmente inviati tramite UDP (che come tutti sappiamo non e’ connection-oriented), ma per evitare di perderli nel tragitto, questa volta ho scelto di utilizzare TCP.
Tutto quello che abbiamo fatto e’ stato indicare come destination, non un file, ma un host remoto.
I client sono pronti, ora configuriamo la nostra logging-authority:

source net { tcp(ip(“1.2.3.4”) port(514) keep-alive(yes)); }

il resto lo possiamo lasciare come nel file di configurazione creato all’inizio, l’unica cosa da aggiungere sono due righe come queste:

destination hosts { file(“/var/log/remote/$YEAR/$MONTH/$DAY/$HOST_$YEAR_$MONTH_$DAY ”
owner(root) group(root) perm(0600) dir_perm(0700) create_dirs(yes)); };
log { source(remote); destination(hosts);
};

Con il file cosi creato avremo i log della macchina organizzati in /var/log e tutti i log delle altre macchine nella directory /var/log/remote. Syslog-ng definisce infatti delle macro piuttosto autoesplicative ($YEAR e’ l’anno, $MONTH il mese etc..) grazie alle quali possiamo suddividere i log per cartelle, in modo da non avere un solo file contenente tutti i dati della lan.
Le altre opzioni servono a specificare i permessi sui file, vale a dire che vogliamo creare i file il cui owner sia root, gruppo root, che abbiano permessi di scrittura e lettura solo per il root e che le directory siano accessibili solo dal root. Create_dirs da’ il permesso a syslog-ng di creare le dovute cartelle.

Conclusioni

Abbiamo visto quanto sia potente e granulare syslog-ng, e abbiamo creato con pochissime righe una logging-authority che tornera’ comodissima a chi ha piu’ macchine in lan. I pregi di questo logger sono davvero molti, la semplicita’ della configurazione ci consente di padroneggiarlo pienamente in meno di un’ora di “studio”, le numerose opzioni forniteci ci permettono di suddividere i messaggi non solo per categorie, ma anche per intervalli temporali o indirizzi e poi abbiamo un controllo totale su cio’ che vogliamo vedere e cio’ che invece non ci interessa… E non e’ assolutamente poco. Con un minimo sforzo e’ possibile far viaggiare i log di rete su un tunnel crittato, e’ possibile inserirli all’interno di un db invece che su tanti file, e i log piu’ importanti possono essere stampati direttamente sulla console. Per motivi di spazio non e’ possibile analizzare ogni aspetto, quindi come al solito riferitevi alla documentazione del programma e divertitevi.
Alla prossima.



About the Author

Related Posts