last update: 25/ jan / 2007

Monitoraggio di sistemi - parte2

Just network

Prosegue la serie di articoli dedicata al monitoraggio dei sistemi in rete andando ad analizzare alcune caratteristiche avanzate di Nagios!

Ci eravamo lasciati nel precedente articolo con la configurazione necessaria al monitoraggio di una rete molto semplice. Vediamo di fare qualche passo ulteriore.

Una rete più complessa...

Penso sia lecito ipotizzare che la vostra rete sia un po più complessa di quella finora esemplificata. Per non strafare ipotizziamo un FTP server all'indirizzo 192.168.1.137 come da schema.

Diagram 2

Aggiungiamo quindi l' host ed il servizio nei relativi file:

define host{
        use                     my_host         
        host_name               ftp_server  ;Nome del server  
        alias                   FTP server
        address                 192.168.1.137
        }
define hostextinfo {        
        host_name               ftp_server
        icon_image              linux.png
        icon_image_alt          FTP
        vrml_image              linux.png
        statusmap_image         linux.gd2
        }
################################################################################
# HOST GROUP DEFINITIONS
################################################################################
# 'linux-boxes' host group definition
define hostgroup{       ; al gruppo è stato aggiunto il server FTP
        hostgroup_name  linux-boxes
        alias           Linux Servers
        members         web_server, ftp_server  
        }
e ora il servizio da controllare...
define service{
        use         generic-service         ; Name of service template to use
        host_name                       ftp_server
        service_description             FTP
        is_volatile                     0
        check_period                    24x7
        max_check_attempts              3
        normal_check_interval           10
        retry_check_interval            1
        contact_groups                  admins
        notification_interval           120
        notification_period             24x7
        notification_options            w,u,c,r
        check_command                   check_ftp
        }

Dopo il solito test e riavvio otteniamo quanto segue

nagios8

con la corrispondente mappa degli host come si vede nella seguente immagine:

nagios7

La cosa sarebbe banale, ma ipotizziamo che dopo circa dieci minuti Nagios ci segnali che entrambi gli host siano interrotti. Da una rapida verifica ci accorgiamo che il problema è lo switch a cui entrambi i server sono collegati: Nagios può tenerne conto!

Configuriamo quindi il sistema in modo che controlli anche lo switch e, in caso di problemi a quest'ultimo, ci segnali solo il guasto dell'apparecchiatura e non quello dei server ad essa collegati. Lo switch in questione ha indirizzo 192.168.1.235.

Nel file host oltre ad aggiungere lo switch

# 'switch7' host definition
define host{
        use                     my_host
        host_name               switch7
        alias                   HP switch 7 armadio principale
        address                 192.168.1.235
        }
define hostextinfo {
        host_name               switch7
        icon_image              switch40.png
        icon_image_alt          Switch 7
        vrml_image              switch40.png
        statusmap_image         switch40.gd2
        }

bisogna modificare gli altri host per indicare che dipendono da questo nodo utilizzando il parametro parents. Tale informazione fa sì che in caso di problemi di connettività al nodo "padre" non vengano segnalati errori per i nodi "figli". A titolo di esempio mostro l'aggiunta per il solo FTP server:

# 'ftp_server' host definition
define host{
        use                     my_host          ; Name of host template to use
        host_name               ftp_server
        alias                   FTP server
        address                 192.168.1.137
        parents                 switch7 ; modifica per indicare il nodo padre 
        }

Non è necessario il monitoraggio dei servizi sullo switch in quanto il solo ping test dello stesso sarebbe sufficiente a determinare se esso è raggiungibile o meno. Lo switch in questione però è di tipo amministrabile da remoto via SNMP e, pur non andando per il momento a prendere in esame questo protocollo, è utile sapere che da la possibilità di eseguire alcuni test su tali apparecchi. Andiamo quindi a costruire un comando che verifica il carico della CPU dello switch e configuriamo il servizio relativo.

Nel file checkcommands.cfg aggiungiamo

define command{
  command_name check_sw_cpu
  command_line $USER1$/check_snmp -H $HOSTADDRESS$ -C $ARG1$ -o 
  .1.3.6.1.4.1.11.2.14.11.5.1.9.6.1.0 -t 5  -w $ARG2$ -c $ARG3$ -u % -l "5min cpu"
 }

e nel file services.cfg

define service{
        use                           generic-service
        host_name                     switch7
        service_description           CPU
        is_volatile                   0
        check_period                  24x7
        max_check_attempts            3
        normal_check_interval         5
        retry_check_interval          1
        contact_groups                admins
        notification_interval         60
        notification_period           24x7
        notification_options          c,r
        check_command                 check_sw_cpu!public!95:90!100:95
 }

Ed ecco quanto ci eravamo prefissi.

nagios9
nagios10

In caso di errore dello switch ci verrà segnalato solo questo e non l'irraggiungibilità degli host ad esso collegati. Un effetto collaterale positivo è che abbiamo anche a disposizione il dato relativo al carico della CPU dello switch che può essere utile per individuare un eventuale problema allo stesso.

Controllo indiretto

Oltre ai test diretti, eseguiti tramite i plugin, Nagios mette a disposizione altri due sistemi per eseguire test su host remoti.
Il primo viene utilizzato quando i servizi che si devono verificare non sono interrogabili da remoto, ad esempio quando non esiste un modo di verificare da remoto lo spazio libero sul disco di un' altro PC ed il solo modo di farlo è quello di verificarlo localmente. La delega di controllo viene demandata ad un demone chiamato NRPE che viene eseguito sull' host da controllare.

NRPE

E` un metodo che non ho avuto ancora modo di provare quindi ne espongo solo la teoria come da manuale.

Nagios tramite un plugin di nome check_nrpe contatta il demone remoto di NRPE, che è in ascolto, richiedendo il test. Il demone remoto riceve la richiesta di Nagios ed esegue i test locali tramite i plugin in modo del tutto analogo a quanto farebbe Nagios e restituisce la risposta al plugin chiamante.

Trovo molto più interessante parlare dei test indiretti o passivi in quanto permettono di generare qualsiasi tipo di test senza oltretutto pesare sul server Nagios per l'elaborazione.

NSCA

Per l'esecuzione di test passivi è necessaria l'installazione di un applicativo chiamato NSCA client sulla macchina remota e l' installazione del corrispondente demone NSCA, che rimane in ascolto delle comunicazioni sul server Nagios .

Il passaggio dei risultati fra il demone NSCA e Nagios avviene attraverso un file definito external command file (traducibile come "file esterno dei comandi"). Uno degli errori più frequenti, che impediscono il corretto uso dei test passivi, è proprio un' errata impostazione dei permessi del file in questione.

Vediamo quindi tutto il processo di installazione.

Decomprimiamo i sorgenti e compiliamo il programma:

# tar -zxvf nsca-2.4.tar.tar 
# cd nsca-2.4/   
# ./configure ; make all

Poiché il demone viene eseguito tramite il wrapper TCP, ovvero attraverso il demone xinet, dobbiamo apportare alcuni cambiamenti alla configurazione di quest'ultimo: iniziamo con il modificate il file /etc/services aggiungendo la linea seguente:

nsca            5667/tcp        # NSCA

Aggiungiamo nella directory /etc/xinetd.d un file chiamato nsca che contenga quanto segue:

# default: on
# description: NSCA Nagios alert
# version.
service nsca
{
        disable = no
        flags           = REUSE
        socket_type     = stream
        protocol        = tcp
        user            = nagios
        group           = nagios
        wait            = no
        server          = /usr/local/nagios/bin/nsca
        server_args     = -c /usr/local/nagios/etc/nsca.cfg --inetd
        log_on_failure  += USERID
}

Copiamo il file binario e quello di configurazione a destinazione

# cp nsca-2.4/src/nsca /usr/local/nagios/bin/
# cp nsca-2.4/nsca.cfg /usr/local/nagios/etc/

ed apportatiamo a quest'ultimo le seguenti modifiche:

[...]
# ALLOWED HOST ADDRESSES
# nella rete due macchine 192.168.1.6-7 sono autorizzate 
# all'invio di messaggi
allowed_hosts=127.0.0.1
allowed_hosts=192.168.1.6
allowed_hosts=172.168.1.7
[...]
# DECRYPTION PASSWORD
# password da inserire sia sul server che sul client
password=mia_pass

A questo punto se non esiste bisogna creare il file esterno dei comandi, aggiungere il corretto utente e gruppo e gestire i permessi:

# touch /usr/local/nagios/var/rw/nagios.cmd 
# /usr/sbin/groupadd nagiocmd
# /usr/sbin/usermod -G nagiocmd nagios
# /usr/sbin/usermod -G nagiocmd nobody 
# chown nagios.nagiocmd /usr/local/nagios/var/rw
# chmod u+rwx /usr/local/nagios/var/rw
# chmod g+rwx /usr/local/nagios/var/rw
# chmod g+s /usr/local/nagios/var/rw

Riavviamo il servizio con il comando

service xinetd restart

ed il server NSCA è pronto ad accogliere i messaggi del client.

Il client, in caso di diversa architettura, va compilato sulla macchina da monitorare. Se avete la stessa versione e distribuzione di GNU/Linux o di UNIX su entrambe le macchine potete semplicemente copiare il file generato nella compilazione precedente.

Per omogeneità di struttura ho creato dei percorsi analoghi a quelli del server Nagios su ciascuno dei due client da monitorare. La serie di comandi che seguono dovrebbe essere esplicativa:

$ pwd
/usr/local/nagios
[rudig@venus nagios]$ ls -l
totale 8
drwxr-xr-x    2 root     root         4096 16 mag 15:20 bin
drwxr-xr-x    2 root     root         4096 16 mag 16:35 etc
$ ls bin
send_nsca
[rudig@venus nagios]$ ls etc
send_nsca.cfg

Il file send_nsca.cfg è stato modificato dallo standard inserendo la riga relativa alla password:

[...]
# DECRYPTION PASSWORD
# password da inserire sia sul server che sul client
password=mia_pass

Tutto ciò rappresenta la pura e semplice fase di installazione. Ora è necessario configurare un servizio da controllare.

Si porterà un caso reale come esempio: nasce la necessità di controllare il numero di processi di un determinato applicativo su un server. Questo applicativo è costituito da un processo che elabora una lista di transazioni su un server SQL.

Se nessuna istanza dell'applicativo è attiva le transazioni si accumulano in coda e i dati mostrati non sono più aggiornati. Questo non è un errore grave in quanto una volta fatto ripartire questo motore i dati si riallineano, ma è opportuno che ci sia una segnalazione, un modo da poter riavviare il processo dopo aver verificato il motivo del blocco.

Se invece per un errore vengono avviate più istanze del processo è possibile che la coda venga elaborata fuori sequenza creando degli errori nelle transazioni. Gli errori non sono certi ma probabili e questa condizione va quindi evitata per quanto possibile.

E` quindi desiderabile

E` stato definito il server da controllare in hosts.cfg, un servizio in grado di notificare gli stati in questione nel file services.cfg ed il comando check_null in checkcommands.cfg per impostare un test attivo nullo:

# 'app_server' host definition
define host{
        use                     my_host           
        host_name               app_server    ;nome del server 
        alias                   Application server
        address                 192.168.1.6
        }
define hostextinfo {
        host_name               app_server
        icon_image              linux40.png
        icon_image_alt          Linux Host
        vrml_image              linux40.png
        statusmap_image         linux40.gd2
        }
define service{
        host_name                       app_server
        service_description             test_s_process  ;nome arbitrario
        is_volatile                     1     ; sempre a 1 per nsca
        active_checks_enabled           0     ; sempre a 0 per nsca
        check_period                    none
        passive_checks_enabled          1     ; è un servizio passivo
        max_check_attempts              1
        normal_check_interval           10
        retry_check_interval            1
        contact_groups                  admins
        notification_interval           120
        notification_period             24x7
        notification_options            w,u,c,r
        check_command                   check_null
        notifications_enabled           1   ; notifica attiva
        }
# 'check_null' command definition by rg
define command{
        command_name    check_null
        command_line    $USER1$/check_dummy
        }

Poi è stato necessario inventare un metodo per verificare, nel server da controllare, l'attività dell'applicativo in questione tenendo presente che il messaggio che viene trasmesso da send_nsca deve avere il seguente formato:

<nome dell'host> [tabulazione]<nome del servizio>[tab]<codice di ritorno>[tab]<descrizione><carattere di new line>

nell'esempio

app_server  test_s_process  0 Tutto OK

dove il codice di ritorno è

0 = se tutto funziona regolarmente
1 = per generare un WARNING
2 = per generare un errore CRITICAL

Nel nostro caso è stato creato lo script seguente che è ampiamente commentato:

# cat /usr/bin/sendalert.sh
#!/bin/sh
#sendalert.sh 
#Script per il test del motore dell'applicativo
#motore_app è il nome del processo sotto controllo, elenco con ps i processi,
filtro con grep quelli che contengono il termine con il nome applicativo e
#conto le linee con wc -l
NUMINST=$(ps ax | grep motore_app | wc -l)
#poichè compare anche la linea precedente nel computo dei processi segnalati da PS
#è necessario eliminare uno dal conteggio.
NUMINST=$[NUMINST-1]
#in base al numero di processi calcolato inviamo il risultato al server nagios
case $NUMINST in
        0)
                /usr/bin/printf "%s\t%s\t%s\t%s\n" "app_server"   "test_s_process" "1"  "WARNING! Processo applicativo non  attivo" | /usr/local/nagios/bin/send_nsca -H 192.168.1.4 -c /usr/local/nagios/etc/send_nsca.cfg
        ;;
        1)
                /usr/bin/printf "%s\t%s\t%s\t%s\n" "app_server"   "test_s_process" "0"  "OK! Processo applicativo attivo" | /usr/local/nagios/bin/send_nsca -H 192.168.1.4 -c /usr/local/nagios/etc/send_nsca.cfg
        ;;
        2)
                /usr/bin/printf "%s\t%s\t%s\t%s\n" "app_server"   "test_s_process" "2"  "CRITICAL! Troppe istanze Processo applicativo attive" | /usr/local/nagios/bin/send_nsca -H 192.168.1.4 -c /usr/local/nagios/etc/send_nsca.cfg
        ;;
esac
exit

Per effettuare il controllo è sufficiente schedulare nel cron un'esecuzione dello script appena preparato con un intervallo di tempo appropriato:

# crontab -e

Aggiungere:

## Verifica del processo
05 * * * * /usr/bin/sendalert.sh

Ciò che risulta a video nella pagina relativa ai servizi è una riga come la seguente:

nagios11

in cui il simbolo dopo il nome del test indica in maniera evidente che il servizio è di tipo passivo.

Controllo di eventi asincroni

Il controllo tramite NSCA è particolarmente indicato per il monitoraggio di eventi asincroni. Vorrei portare ad esempio un caso limite che mostra l'adattabilità di Nagios, anche se probabilmente vi sono altri strumenti in grado di effettuare lo stesso tipo di verifiche.

Un modulo dell'applicativo, di cui all'esempio precedente, scambia dei dati con degli applicativi esterni tramite importazione/esportazione di file ASCII. Per il corretto funzionamento di entrambi gli applicativi è necessario che ogni esportazione verso la periferia sia preceduta dalla relativa importazione dei dati precedenti nel sistema centrale. Lo scambio dei dati avviene attraverso un server FTP.

asinc nagios

Il tutto può essere così schematizzato:

La soluzione del problema è apparentemente semplice in quanto sarebbe sufficiente verificare la presenza del file di import nel server FTP prima di procedere all'export. La situazione è complicata dal fatto che, per logiche applicative, il nome del file cambia ogni giorno avendo come prefisso la data. Cambia inoltre anche l'ora di trasmissione in quanto la stessa è a discrezione dell'operatore e può avvenire al mattino o al pomeriggio.

Si è pensato quindi di ribaltare il problema: si suppone che sia sempre presente un errore di trasmissione a meno di una corretta trasmissione.

Viene quindi usato un file come semaforo:

  1. uno script nella crontable del server applicativo predispone un file di errore ogni mattina
  2. uno script nel server FTP cancella ogni mattina il file precedentemente inviato dal PC di raccolta dati
  3. il PC di raccolta dati contiene nella cartella dei dati un file semaforo che contiene l'indicazione di corretto funzionamento dell'importazione dati

Ipotizziamo di chiamare il servizio test_trasf.

In accordo con la sintassi già vista in precedenza prepareremo nel server applicativo un file test_trasf.sem con il seguente contenuto:

app_server   test_trasf 1       Warning! File non spediti da raccolta dati

Il file verrà copiato alla mattina in una cartella specificata, come ad esempio /temp/import ed indicherà che il trasferimento dati non è andato a buon fine.

Nel PC di raccolta dati è presente un file con lo stesso nome, ma dal contenuto indicante il corretto trasferimento

app_server   test_trasf 0       OK! File spediti da raccolta dati

quindi quando l'utente sincronizza i dati questo file viene trasferito nel server FTP.

Il server applicativo ritira i file dal server applicativo portandoli in /temp/import ove il file semaforo sovrascrive quello che indica l'errore.

Durante gli orari non lavorativi e prima dell'esportazione dati serale viene eseguito lo script seguente che invia i dati al server Nagios tramite NSCA:

sendtrasf.sh
#!/bin/sh
# per trasf
cat /mnt/intra/trasf.sem | /usr/local/nagios/bin/send_nsca -H 172.20.1.4 -c /usr/local/nagios/etc/send_nsca.cfg

Nel server Nagios è stato aggiunto al file service.cfg il servizio che segue:

define service{
        host_name                       app_server
        service_description             test_trasf
        is_volatile                     1
        active_checks_enabled           0
        check_period                    none
        passive_checks_enabled          1
        max_check_attempts              1
        normal_check_interval           10
        retry_check_interval            1
        contact_groups                  admins
        notification_interval           120
        notification_period             24x7
        notification_options            w,u,c,r
        check_command                   check_null
        notifications_enabled           1   ; Service notifications are enabled
        }

Quindi due volte al giorno viene visualizzato lo stato delle importazioni come segue:

nagios12

In caso di errore viene inviata una notifica al personale di assistenza che può intervenire per richiedere un nuovo invio di dati o bloccare l'esportazione dei dati ed evitare la sovrascrittura di quelli in ingresso.

Come anticipato vi sono sicuramente altri metodi, forse migliori, per eseguire verifiche di questo tipo ma questo esempio è operativo e funzionante in ambiente reale e si è reso più volte utile per evitare errori.

Ripristino automatico

A partire dalla versione 2.0 Nagios possiede una funzionalità di ripristino automatico di un servizio tramite una funzione detta "event handling" ovvero gestione di eventi. Per gestire tale funzione è sufficiente dichiarare all'interno di un servizio un riferimento ad un "event handler". L'esempio che segue è preso pari-pari dal manuale:

define service{
        host_name                       somehost
        service_description             HTTP
        max_check_attempts              4
        event_handler                   restart-httpd
        ...other service variables...
        } 

Come vedete la quarta riga richiama una funzione "restart-httpd" che ovviamente serve per riavviare il demone httpd nel caso risultasse indisponibile.

Tale funzione viene definita all'interno di uno dei file di configurazione come ad esempio checkcommands.cfg e conterrà il nome della funzione o del comando atto a gestire il servizio in questione come ad esempio:

define command{
        command_name    restart-httpd
        command_line    /usr/local/nagios/libexec/eventhandlers/restart-httpd  $SERVICESTATE$ $SERVICESTATETYPE$ $SERVICEATTEMPT$
        }

Qui notiamo che viene richiamata una command_line, ma come espone il manuale è un comando creato per l'occasione e che non funziona.

Per meglio chiarire: sicuramente non funziona in nessuna delle distribuzioni Red-Hat o Fedora in cui ho avuto modo di provarlo e concettualmente ciò è corretto. Il fatto è che per ragioni di sicurezza ci siamo dati tanto da fare affinché il demone Nagios e tutti i processi dipendenti avessero scarsi privilegi e permessi in modo da evitare una escalation in caso di intrusioni. Per gestire e riavviare i processi, Nagios deve necessariamente avere dei privilegi più elevati altrimenti non ha il permesso di accesso ai file semaforo dei servizi e a volte nemmeno agli script che gestiscono i servizi stessi.

Non ci sono rimedi ovvi: o si abbassano i livelli di sicurezza o si rinuncia a questa funzionalità. La scelta dipende dalla necessità di continuità del servizio rapportata con i requisiti di sicurezza. Per quel che mi riguarda ho preferito mantenere elevati gli standard di sicurezza ed ho evitato di proseguire i test su questa funzionalità.

La serie di articoli dedicata a Nagios si chiude con i riferimenti bibliografici sottoriportati e con la coscienza di non aver esaurito l'argomento. La serie di articoli sull'amministrazione dei sistemi quidni proseguirà.

Alla prossima!

Riferimenti bibliografici

  1. Il manuale di Nagios.
  2. Installing and configuring Nagios by Kate Harris at TOTKat site.
  3. I sorgenti di Nagios (of course).

di Rudi Giacomini Pilon


articoli