Archivi tag: bash

Nagios: script bash + expect per monitorare il numero di NAT translation sui router Cisco

Dopo aver cercato a lungo un OID SNMP che potesse restituirmi on-the-fly il numero di NAT translation attive sul mio router Cisco, sono arrivato alla conclusione che il modo più semplice per ricavare tale informazione consiste nel creare uno scrip bash + expect da dare in pasto a Nagios.

Nagios_logo_blackPer prima cosa, dunque, mi sono soffermato sulla creazione dello scrip expect, in modo da poter interrogare il router oggetto di monitoraggio, lanciando un comando specifico, che è il seguente:

Router# show ip nat statistics

Ed ecco lo scrip expect vero e proprio:

#!/usr/bin/expect

set ip [lindex $argv 0]
set password1 [lindex $argv 1]
set password2 [lindex $argv 2]

spawn ssh -l nightfly "$ip"
expect "*?assword:*"
send "$password1\r"
expect ">"
send "ena\r"
expect "Password:"
send "$password2\r"
expect "#"
send "show ip nat statistics\r"
expect "#"
send "exit\r"
expect eof

Esso riceve come parametri l’IP del dispositivo, la password vty e la password ena.
Dopo averlo chiamato get_nat_num (ed averlo reso eseguibile con chmod +x get_nat_num), mi sono soffermato sulla creazione dello scrip bash, che è il seguente:

#!/bin/bash

host=$1
password1=$2
password2=$3
warning=$4
critical=$5

usage="check_nat_translations <host> <password1> <password2> <warning> <critical>"

if [ -n "$host" ]; then

        if [ -n "$password1" ];then

                if [ -n "$password2" ];then

                        if [ -n "$warning" ];then

                                if [ -n "critical" ];then

                                        if [ "$critical" -lt "$warning" ];then

                                                echo "UNKNOWN: critical has to be greater than warning"
                                                exit 3;

                                        else

                                                output=`/usr/lib64/nagios/plugins/get_nat_num $1 $2 $3 | grep "Total active translations"  | awk '{print $4}'`

                                        fi

                                        if [ -n "$output" ];then

                                                if [ "$output" -ge "$critical" ];then

                                                        echo "CRITICAL: total number of active NAT translations is $output | nat_translations=$output;$warning;$critical";
                                                        exit 2;

                                                elif [ "$output" -lt "$critical"  -a  "$output" -ge "$warning" ];then

                                                        echo "WARNING: total number of active NAT translations is $output | nat_translations=$output;$warning;$critical";
                                                        exit 1;

                                                else

                                                        echo "OK: total number of active NAT translations is $output | nat_translations=$output;$warning;$critical";
                                                        exit 0;

                                                fi
                                        else

                                                echo "UNKNOWN: output is null"
                                                exit 3;

                                        fi

                                else

                                        echo "$usage"
                                        exit 3;
                                fi

                        else

                                echo "$usage"
                                exit 3;
                        fi

                else

                        echo "$usage"
                        exit 3;
                fi
        else

                echo "$usage"
                exit 3;
        fi

else

        echo "$usage"
        exit 3;

fi

Esso accetta in ingresso 5 parametri: i primi 3 da passare allo scrip get_nat_num, gli ultimi 2 da utilizzare come soglie per Nagios (una warning ed una critical).

Rendiamo eseguibile anche questo scrip (chmod +x check_nat_translations) e soffermiamoci sulla configurazione di Nagios.

Come primo step occorre creare il comando che utilizza i plugin appena creati:

# 'check_nat_translations' command definition
 define command{
 command_name    check_nat_translations
 command_line    $USER1$/check_nat_translations $HOSTADDRESS$ $ARG1$ $ARG2$ $ARG3$ $ARG4$
 }

Successivamente è necessario creare un servizio (da inserire nel file di configurazione relativo ad un host determinato), che si avvalga del comando creato in precedenza:

define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_descripion             NAT Translations Number
 check_command                   check_nat_translations!password1!password2!40000!50000
 }

Salviamo la configurazione, riavviamo Nagios, ed il gioco è fatto.

Alla prossima.

check_arptables: script Nagios per verificare lo stato di arptables_jf

In questo post ho discusso della configurazione di LVS in modalità Direct Routing. Tale configurazione richiedeva l’installazione e la corretta configurazione di arptables_jf per la gestione delle richieste ARP dirette ai frontend. Adesso vedremo come verificare l’effettivo funzionamento del suddetto applicativo attraverso l’NMS open source per eccellenza, Nagios.

nagiosCreazione dell’eseguibile e configurazione dell’NMS

Il codice bash che ho creato per effettuare i controlli sullo status di arptables_jf è il seguente:

#!/bin/bash

host=$1
warning=$2
critical=$3

usage="check_arptables <host> <warning> <critical>"

if [ -n "$host" ]; then

        if [ -n "$warning" ];then

                if [ -n "critical" ];then

                        if [ "$warning" -lt "$critical" ];then

                                echo "UNKNOWN: warning has to be greater than critical"
                                exit 3;

                        else

                                output=`sudo arptables -L | grep -v Chain | grep -v target | wc -l`

                                if [ "$output" -le "$critical" ];then

                                        echo "CRITICAL: total number of rules is $output";
                                        exit 2;

                                elif [ "$output" -gt "$critical"  -a  "$output" -le "$warning" ];then

                                        echo "WARNING: total number of rules is $output";
                                        exit 1;

                                else

                                        echo "OK: total number of rules is $output";
                                        exit 0;

                                fi
                        fi
                else

                        echo "$usage"
                        exit 3;

                fi
        else

                echo "$usage"
                exit 3;

        fi
else

        echo "$usage"
        exit 3;

fi

Il suddetto codice è abbastanza esplicativo: dopo aver verificato la presenza di tutti i parametri richiesti e la loro consistenza (ovvero la soglia di warning deve essere strettamente maggiore di quella di critical), viene effettuato il conteggio delle regole arptables attive sul frontend monitorato. Il comando principale è il seguente:

sudo arptables -L | grep -v Chain | grep -v target | wc -l

Da notare che è presente la direttiva sudo poichè l’applicativo arptables richiede i privilegi di root per essere eseguito. Rendiamo il suddetto codice eseguibile:

[root@NMS ~]# chmod +x check_arptables

e copiamolo nella directory /usr/lib64/nagios/plugins del frontend:

[root@NMS ~]# scp check_arptables root@frontend.dominio.com:/usr/lib64/nagios/plugins

Ora è possibile, all’interno della configurazione di Nagios, definire il comando da lanciare per monitorare arptables sul frontend (all’interno del file /etc/nagios/commands.cfg):

# 'check_remote_arptables' command definition
define command{
        command_name    check_remote_arptables
        command_line    $USER1$/check_by_ssh -H $HOSTADDRESS$ -C "/usr/lib64/nagios/plugins/check_arptables $ARG1$ $ARG2$ $ARG3$"
        }

Tale comando non fa altro che connettersi al frontend via SSH (senza password, mediante un semplice scambio di chiavi RSA) e lanciare l’eseguibile precedentemente creato.

Nella configurazione di Nagios relativa allo specifico frontend, invece, occorre creare un servizio simile al seguente:

define service{
        use                             local-service         ; Name of service template to use
        host_name                       frontend.dominio.com
        service_description             Arptables Rules Status
        check_command                   check_remote_arptables!localhost!3!2
        notifications_enabled           0
        }

Configurazione del frontend

Sul frontend è sufficiente editare il file /etc/sudoers per consentire all’utente nagios di lanciare il comando arptables senza sessione TTY e senza richiesta di inserimento della password. Nella fattispecie, nel primo caso è sufficiente commentare la direttiva:

#Defaults requiretty

mentre nel secondo caso occorre inserire la seguente stringa:

nagios  ALL=(root)      NOPASSWD: /sbin/arptables

A configurazione del frontend terminata possiamo lanciare sull’NMS il comando:

[root@NMS ~]# service nagios reload

ed abbiamo finito.

Ora arptables è correttamente monitorato.

Alla prossima.

Nagios e gli event handlers

Come già affermato in più occasioni, Nagios è sicuramente uno dei migliori NMS open source in circolazione.  Esso mette a disposizione del sysadmin tutta una serie di funzioni utilissime per verificare lo stato di salute relativo agli host monitorati e consente, in alcune circostanze, di gestire i downtime in modo automatico (ad esempio riavviando il servizio che ha generato lo status di tipo CRITICAL).

Nagios_logo_blackTale gestione automatica dei downtime viene realizzata attraverso i cosiddetti event handlers. Vediamo come configurarli.

Per prima cosa è necessario creare un comando simile al seguente, editando il file /etc/nagios/object/commands.cfg

define command {
command_name      flushcache
command_line      /usr/lib/nagios/plugins/eventhandlers/flushcache $HOSTADDRESS$ $SERVICESTATE$ $SERVICESTATETYPE$ $SERVICEATTEMPT$
}

Nella fattispecie, il suddetto comando utulizza l’eseguibile flushcache, il cui compito è quello connettersi alla macchina target e di cancellare la cache del sistema operativo in caso di memory leak.

Occorre ora configurare l’event handler all’interno del servizio che si occupa del monitoraggio della RAM:

define service{        
use                            local-service              host_name                       vostrohost.vostrodominio.it        service_description             FREE RAM        check_command                   check_remote_ram!10!5        event_handler                   flushcache        notifications_enabled           0        
}

A questo punto la configurazione di Nagios può ritenersi ultimata. Non ci rimane che creare l’eseguibile flushcache all’interno della directory /usr/lib/nagios/plugins/eventhandlers/:

[root@NMS eventhandlers]# nano flushcache

il cui contenuto dovrà essere il seguente:

#!/bin/bash
if [ -n "$1" ];then
        case "$2" in
        OK)
                ;;
        WARNING)
                ;;
        UNKNOWN)
                ;;
        CRITICAL)
                case "$3" in
                SOFT)
                        case "$4" in
                        3)
                                echo -n "Flushing cache memory (3rd soft critical state)..."
                                /usr/lib/nagios/plugins/eventhandlers/connect_server.sh $1
                                ;;
                                esac
                        ;;
                HARD)
                        echo -n "Flushing cache memory..."
                        /usr/lib/nagios/plugins/eventhandlers/connect_server.sh $1
                        ;;
                esac
                ;;
        esac
else
        echo "Usage: flushcache <hostname>"
fi

exit 0

Dove $1 è la variabile $HOSTADDRESS$, $2 è il $SERVICESTATE$, $3 è il $SERVICESTATETYPE$ e $4 è il $SERVICEATTEMPT$ (sono semplicemente le variabili utilizzate nella definizione del comando flushcache in command.cfg).

Premesso che per il tipo di servizio local-service il max_check_attempts è impostato a 4 (nel file template.cfg), il suddetto codice bash non fa altro che interrogare la macchina target ($HOSTADDRESS), ricavando il $SERVICESTATE$ (OK, WARNING, UNKNOWN o CRITICAL), il $SERVICESTATETYPE$ (SOFT oppure HARD) ed il $SERVICEATTEMPT$ (ovvero il numero di check già eseguiti da Nagios nel caso in cui il $SERVICESTATE$ sia diverso da OK).

Il suddetto eseguibile prevede delle operazioni solo se il $SERVICESTATE$ è di tipo CRITICAL: in tal caso, al terzo check consecutivo (siamo ancora in SOFT state – parliamo di HARD state solo se il $SERVICESTATE$ è ancora CRITICAL dopo il quarto check), verrà lanciato l’eseguibile connect_server.sh (expect) che si occuperà di svuotare la cache della macchina monitorata. La suddetta operazione verrà eseguita nuovamente appena il $SERVICESTATETYPE$ passerà da SOFT a HARD.

Inoltre, tale eseguibile prevede come unico parametro in ingresso l’IP dell’host, ovvero $HOSTADDRESS$ (connect_server.sh $1).

Di seguito il sorgente relativo a connect_server.sh:

#!/usr/bin/expect

set hostname [lindex $argv 0]
set password "vostrapass"

spawn ssh -l root $hostname
expect "*assword:"
send "$password\n"
expect "#"
send "sync && echo 3 > /proc/sys/vm/drop_caches\n"
expect "#"
send "exit\n"
expect eof

Nella fattispecie, il comando vero e proprio che si occupa dello svuotamento della cache sulla macchina target è:

sync && echo 3 > /proc/sys/vm/drop_caches

A questo punto possiamo ricaricare la configurazione di Nagios per rendere effettive le suddette modifiche:

[root@NMS eventhandlers]# service nagios reload

ed abbiamo finito.

A presto.

LVS e Nagios: script bash per controllare lo stato dell’interfaccia virtuale

Ho realizzato il seguente script bash (da integrare a Nagios) per verificare lo stato dell’interfaccia virtuale presente su ciascun frontend dietro bilanciatore LVS (configurato in direct routing).

nagios

#!/bin/bash

if [ -n "$1" ];then
        status=`/sbin/ifconfig | grep $1 2> /dev/null`

        if [ -n "$status" ]; then
                echo "OK"
                exit 0
        else
                echo "CRITICAL"
                exit 2
        fi

else
        echo "Usage: check_if_status <IP>"
fi

exit 0

Lo script è abbastanza semplice. Per prima cosa lancio il comando ifconfig facendo un grep sull’IP specificato nella configurazione di Nagios. Se tale comando genera un output diverso dalla stringa vuota significa che l’interfaccia virtuale è attiva e quindi esco (exit 0), altrimenti genero un errore di tipo critico (exit 2).

Alla fine dello script ho inserito anche la parte relativa allo usage.

Alla prossima.

Gentoo e MySQL all’avvio

Per installare un nuovo servizio su una macchina Gentoo è molto conveniente utilizzare il packet manager di cui è dotata, ovvero emerge.

gentoo-logo.jpg

Potrebbe succedere, però, che la versione del servizio richiesta sia più datata rispetto a quella presente nei repository ufficiali di emerge, ergo è necessario scaricare i sorgenti dell’applicativo e successivamente compilarli mediante gcc.

Nella fattispecie, il servizio oggetto del presente post è MySQL, che dovrà essere avviato automaticamente al boot del sistema operativo.

Generalmente MySQL mette a disposizione uno scrip di avvio per le distro più diffuse, da posizionare all’interno della directory /etc/init.d.

Purtroppo la versione di MySQL che ho scaricato non conteneva al suo interno uno scrip di startup per Gentoo e per quindi ho dovuto escogitare una specie di workaround.

Mi spiego meglio: affinchè il comando rc-update add mysqld boot possa funzionare, è necessario che lo scrip in questione (mysqld) contenga del codice specifico da dare in pasto al binario /sbin/runscrip. Infatti, è proprio grazie ad esso che il SO riconosce lo scrip e lo lancia durante lo specifico runlevel.

La cosa fuorviante è che, nonostante mysqld non contenesse all’interno la chiamata al binario /sbin/runscrip (essendo un semplice scrip bash) il comando rc-update non ha restituito alcun errore.

Infatti, un semplice rc-update mi ha mostrato il servizio correttamente assegnato al runlevel:

mysqld | boot

Per capirci qualcosa in più ho deciso di abilitare il logging del demone rc, editando il file /etc/rc.conf:

rc_logger="YES"

Dopo il riavvio della macchina ho quindi analizzato il file /var/log/rc.log, il quale non ha mostrato nessun tentativo di avvio per mysqld. Ed è stato proprio in questo momento che ho capito dove stava il problema: mysqld era uno scrip bash e non runscrip.

Per farla breve, il workaround di cui vi ho accennato precedentemente è il seguente:

1) all’interno della directory /etc/local.d ho creato lo scrip mysqld.start, il cui contenuto è:

#!/bin/sh
 /etc/init.d/mysqld start

2) ho convertito il suddetto scrip in eseguibile, mediante il comando:

server local.d # chmod +x mysqld.start

A questo punto, in virtù del fatto che Gentoo durante la fase di boot esegue tutti gli scrip presenti all’interno di /etc/local.d, ho finalmente ottenuto l’avvio automatico di MySQL.

E’ tutto.

Alla prossima.

Script bash per i port scan su range di IP

Problema

Mancato aggiornamento dell’associazione IP/FQDN (record A DNS) mediante ddclient, che si traduce nell’impossibilità di conoscere il vero indirizzo IP pubblico del server (essendo dinamico).

Possibile soluzione

Conoscendo l’ISP e partendo dal fatto che almeno una porta non standard ( > 1023) è pubblicata all’esterno (ad esempio la TCP 4338) si può procedere con la scansione automatizzata dei netblock appartenenti al nostro Internet Service Provider. Nella fattispecie, l’ISP è Telecom Italia ed alcuni dei netblock ad esso associati sono i seguenti:

79.46.0.0/15
79.56.0.0/16
62.77.32.0/19
62.211.0.0/16
77.238.3.3/19
79.140.80.0/20
80.104.0.0/15
80.116.0.0/15
80.180.0.0/15
80.204.0.0/15
80.241.224.0/20
81.72.0.0/22
81.112.0.0/20
82.48.0.0/20
82.88.0.0/22
82.104.0.0/22
83.175.0.0/18
83.221.96.0/18
89.221.32.0/18
93.186.128.0/20
95.74.0.0/15
95.224.0.0/11
188.8.0.0/13
195.14.96.0/19
195.22.192.0/19
212.171.0.0/16
212.216.0.0/16
213.45.0.0/16
213.144.160.0/19
213.230.128.0/19
217.27.64.0/19
217.169.96.0/20
217.169.112.0/20
217.172.192.0/19
217.200.0.0/14
217.222.0.0/15

E’ possibile inserire la suddetta lista all’interno di un apposito file testuale, chiamandolo semplicemente netblock. Inoltre, i netblock sono oggetto di continui aggiornamenti, ergo prima di procedere sinceratevi che appartengano ancora all’ISP di riferimento (attraverso un semplice whois <netblock>).

Una volta fatto ciò, mediante l’uso di nmap, possiamo realizzare uno script bash che si occupi di scansionare i suddetti range di IP pubblici. Lo script è il seguente:

nightfly@nightbox:~$ cat autoscanner.sh
#!/bin/bash

sourcefile=/home/nightfly/netblock

resfile=/home/nightfly/results

targetfile=/home/nightfly/openports

while read line
do

        nmap -P0 $line -p 4338 >> $resfile

done < $sourcefile

cat $resfile | grep -B2 open >> $targetfile

exit 0;

La logica dello script è questa:

1) viene lanciato uno scan verso ogni singolo range specificato nel file netblock, prendendo in considerazione esclusivamente la porta di destinazione TCP 4338;

2) una volta terminata la scansione vengono cercate tutte le occorrenze del termine open, considerando anche le due righe precedenti ad esso, in modo da ottenere l’IP pubblico degli host su cui la porta in questione risulta in ascolto;

3) a questo punto, supponendo che dietro la porta TCP 4338 vi sia in ascolto un demone SSH, è possibile lanciare delle sessioni Secure SHell verso ciascun IP specificato nel file /home/nightfly/openports. Se a tali tentativi di connessione seguirà una richiesta di login, è molto probabile che il nostro server sia stato effettivamente identificato. Ovviamente, se il login non andrà a buon fine, la macchina che stiamo cercando è un’altra.

Buona scansione.

PS: il port scan, di per se, non è reato, ma potrebbe diventarlo se lo associerete a più tentativi di login non autorizzati. Inoltre, essendo i netblock in questione piuttosto ampi, la scansione richiederà molto tempo.

Script Bash per controllare lo stato di un processo

Quando si schedulano dei job su cron è opportuno distanziarli tra di loro dal punto di vista temporale, in quanto la sovrapposizione degli stessi potrebbe portare a comportamenti inaspettati. Tale regola andrebbe seguita sempre e comunque, soprattutto se i job si occupano di parsare dei dati e di inserirli dentro un database.

cron, job, parser, ps aux, wc -l, grep, output, gt, bash, script

Dopo questa breve premessa, ecco il codice che utilizzo per il suddetto scopo:

#!/bin/bash

ESEC=`ps aux | grep Parser | wc -l`
if [ $ESEC -gt 1 ]; then #la prima riga e' il grep stesso
echo "Parser is still running.";
exit 0;
fi

Lo scrip in questione è abbastanza semplice. Dapprima lancia un ps aux | grep Parser, dove Parser è il nome del processo di cui voglio controllare lo stato. Se tale processo non è in esecuzione, l’output del comando conterrà solo ed esclusivamente la riga relativa al grep. Dunque, se il numero di righe ricavate mediante un wc -l è strettamente maggiore di uno, vuol dire che una o più istanze del processo è in esecuzione (lo scrip esce). Viceversa, lo scrip continua in quanto nessuna istanza è attualmente attiva.

Alla prossima.

Linux alias

Creare degli alias mediante shell Linux è un’operazione banale e di un’utilità pazzesca. Infatti, essi consentono di ridurre notevolmente il numero di flag da digitare, semplicemente associando il comando originario ad uno molto più semplice da ricordare.

tux1.jpg

Per quanto mi riguarda, dato che spesso e volentieri mi ritrovo dietro proxy e sono costretto ad utilizzare un mio server italiano come bouncer per accedere alle macchine sparse qua e là per il globo, lanciare a mano delle sessioni SSH specificando ogni volta username, porta di destinazione, ecc. risulta essere un’operazione tediosa ed un’insopportabile perdita di tempo. Sono corso, quindi, ai ripari, semplicemente digitando un comando simile al seguente:

nightfly@nightbox:~$ alias ssh_server1='ssh -p <porta su cui è in ascolto il demone SSH> -l <nomeutente> <indirizzo IP del server>'

Per listare tutti gli alias attivi occorre digitare il comando alias:

nightfly@nightbox:~$ alias

il cui output sarà simile al seguente:

alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'

Mentre, per rimuovere un alias, basta scrivere:

unalias <nomealias>

Attenzione però, gli alias così definiti valgono solo per la sessione in cui vengono creati. Per fare in modo che siano permanenti è necessario inserirli all’interno del file /home/nomeutente/.bashrc.

Alla prossima.

lftp: script bash per verificare l’esistenza di una directory remota

Lftp rappresenta un utilissimo strumento per effettuare il mirroring (e non solo) di una o più directory remote.

mirror.jpg

Spulciando la manpage del suddetto tool ho notato che non esiste alcuna opzione in grado di verificare l’esistenza di una directory remota prima di inizializzare il mirroring. Proprio per questo motivo, ho deciso di realizzare il seguente scrip:

#!/bin/bash
HOST="hostname.ftpserver.com"
USER="username"
PASS="password"
LCD="/home/nightfly/prova"
RCD="/prova"

if [ ! -d $LCD ]; then
mkdir $LCD;
fi

date

lftp -c "set ftp:list-options -a;
set cmd:interactive;
open ftp://$USER:$PASS@$HOST;
lcd $LCD;
ls > /var/log/check;
quit"

if grep -q "prova" /var/log/check
        then
        lftp -c "set ftp:list-options -a;
        open ftp://$USER:$PASS@$HOST;
        lcd $LCD;
        cd $RCD;
        mirror -p -nvvv;
        quit;"
else
        cat /dev/null > /var/log/check
        exit 0
fi

In particolare, viene lanciata una prima sessione FTP che ha come unico scopo quello di listare il contenuto della root (/) remota, salvandolo in un apposito file di testo.

Successivamente, se tale file contiene la directory remota target, viene avviato il mirroring vero a proprio, altrimenti viene svuotato e lo scrip esce.

Alla prossima.