Archivi tag: script

NRPE_NT e Nagios: script PowerShell per il controllo dell’uptime su Windows Server 2008 R2

Tenere sotto controllo l’uptime dei nostri server è molto importante, soprattutto se si ha a che fare con macchine in produzione. Per ciò che concerne i sistemi operativi di casa Microsoft (sia client che server), esiste un comando che può tornarci molto utile allo scopo, ovvero:

net stats srv

il quale, basandosi sul servizio Server di Windows, colleziona e fornisce tutta una serie di informazioni associate alla nostra macchina, tra cui, per l’appunto, il suo tempo di attività:

Statistics since 7/11/2016 9:21:08 AM

powershellAffinchè il suddetto task possa essere effettuato in automatico dal nostro NMS (Nagios), è necessario utilizzare uno scrip (get_uptime.ps1, creato da me allo scopo) da integrare al servizio NRPE_NT. Per ragioni di semplicità e comodità ho deciso di avvalermi di Windows PowerShell per la stesura dello stesso, il cui contenuto è il seguente:

$critical = $Args[0]
$warning = $Args[1]

if ($warning -and $critical) #both variable are defined and not empty
{
    if ($critical -lt $warning)
    {
        $os = Get-WmiObject win32_operatingsystem
        $uptime = (Get-Date) - ($os.ConvertToDateTime($os.lastbootuptime))
        $minutes_to_seconds = $Uptime.Minutes*60
        $hours_to_seconds = $Uptime.Hours*3600
        $days_to_seconds = $Uptime.Days*86400
        $uptime_in_seconds = $minutes_to_seconds + $hours_to_seconds + $days_to_seconds
        if ($uptime_in_seconds -gt $critical -and $uptime_in_seconds -gt $warning)
        {
            $Display = "OK: System Uptime is " + $Uptime.Days + " days, " + $Uptime.Hours + " hours, " + $Uptime.Minutes + " minutes"
            Write-Output $Display
            exit 0
        }
        elseif ($uptime_in_seconds -gt $critical -and $uptime_in_seconds -le $warning)
        {
            $Display = "WARNING: System Uptime is " + $Uptime.Days + " days, " + $Uptime.Hours + " hours, " + $Uptime.Minutes + " minutes"
            Write-Output $Display
            exit 1
        }
        else
        {
            $Display = "CRITICAL: System Uptime is " + $Uptime.Days + " days, " + $Uptime.Hours + " hours, " + $Uptime.Minutes + " minutes"
            Write-Output $Display
            exit 2
        }
    }
    else
    {
        $Usage = "Usage: .\get_uptime.ps1 <critical threshold in seconds> <warning threshold in seconds>"
        $Error1 = "Warning threshold must be greater then the critical one"
        Write-Output $Usage
        Write-Output $Error1
        exit 3
    }
}
else
{
    $Usage = "Usage: .\get_uptime.ps1 <critical threshold in seconds> <warning threshold in seconds>"
    $Error2 = "Warning threshold and critical threshold must be defined"
    Write-Output $Usage
    Write-Output $Error2
    exit 3
}

In soldoni, il suddetto scrip riceve due argomenti da linea di comando, ovvero la soglia critica e quella di warning, espresse entrambe in secondi. Ovviamente, trattandosi di tempo di attività, la prima deve essere strettamente minore della seconda.

Configurazione di NRPE_NT

Salviamo tale scrip all’interno della directory C:\nrpe\Plugins\V2 e successivamente editiamo il file C:\nrpe\Plugins\V2\V2_nrpe_commands.cfg, aggiungendo la seguente direttiva:

# =================================
# Windows System Uptime checks
# =================================

command[get_system_uptime]=cmd.exe /c echo C:\nrpe\Plugins\V2\get_uptime.ps1 "$ARG1$" "$ARG2$" | powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -command -

In particolare, stiamo definendo il comando get_system_uptime che si avvarrà di get_uptime.ps1 per l’individuazione del tempo di attività.

Occorre precisare che l’esecuzione dello scrip non può avvenire richiamando direttamente l’eseguibile powershell.exe, ma deve prima passare per cmd.exe (il cui output, mediante pipe, viene dato in pasto a PowerShell grazie alla direttiva -command –, dove il finale rappresenta “tutto ciò che proviene dal pipe”). Inoltre, si rende indispensabile bypassare l’executionpolicy di PowerShell, consentendo ad NRPE_NT di lanciare get_uptime.ps1 senza restrizioni.

Infine, riavviamo NRPE_NT per rendere effettive le suddette modifiche:

C:\Users\Administrator> net stop nrpe_nt
C:\Users\Administrator> net start nrpe_nt

Configurazione di Nagios

Per prima cosa è necessario creare un apposito comando all’interno del file /etc/nagios/object/commands.cfg:

# nagios-Windows-uptime check command definition

define command {
        command_name    check_Windows_uptime
        command_line    /usr/lib64/nagios/plugins/check_nrpe -H $HOSTADDRESS$ -t $ARG1$ -c get_system_uptime -a $ARG2$ $ARG3$
        }

Successivamente, all’interno del file che rappresenta l’host da monitorare, è necessario aggiungere un servizio specifico che si avvale del suddetto comando:

define service{
        use                             generic-service         ; Name of service template to use
        host_name                       Windows-Server
        service_descripion             query Windows System Uptime for Microsoft Windows Machine
        check_command                   check_Windows_uptime!30!60!900
        }

Ricarichiamo la configurazione di Nagios:

[root@linuxbox ~]# service nagios reload

ed abbiamo finito.

Alla prossima.

check_ntp_peers: script bash per il monitoraggio dei peer NTP mediante Nagios

Il protocollo NTP ci consente, in soldoni, di tenere aggiornate la data e l’ora di sistema, sincronizzandole con un’apposita sorgente di tempo. Per i sistemi *nix, nella stragrande maggioranza dei casi, è sufficiente configurare il servizio NTP (attivo mediante il demone ntpd, o, in alternativa, utilizzando l’accoppiata ntpdate + crontab) agendo sul suo file diconfigurazione (ovvero ntp.conf) e definendo le sorgenti alle quali sincronizzarsi.

ntpAd esempio:

server ntp1.inrim.it
server ntp2.inrim.it

Ora, per saggiare lo stato di sincronizzazione del nostro server, a parte il classico comando date, è possibile utilizzare un apposito tool per le interrogazioni NTP, ovvero ntpq:

[root@linuxbox ~]# ntpq
ntpq> pe
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*ntp1.inrim.it   .CTD.            1 u    4   64  377   45.819    0.219   2.655
+ntp2.inrim.it   .CTD.            1 u  903 1024  377   48.974    1.788   0.978

In particolare, con il comando pe, sono riuscito a recuperare la lista dei peers (ovvero le sorgenti di tempo) con i relativi valori di offset (i quali rappresentano la discrepanza, in ms, tra l’ora locale di sistema e quella del peer stesso), di jitter (ovvero il ritardo, anch’esso espresso in ms, accumulato tra 2 aggiornamenti immediatamente successivi) e di delay (RTT, in ms, associato alla comunicazione con il time server remoto). Da notare, inoltre, che il campo refid indica qual è (se esiste, dipende dallo stratum, specificato dal campo st) la sorgente di tempo utilizzata a loro volta dai server che sto interrogando (rispettivamente ntp1.inrim.it ed ntp2.inrim.it), mentre il carattere * mostra quale dei 2 server da me definiti all’interno del file ntp.conf sto utilizzando per sincronizzare la data e l’ora di sistema.

Dopo questa breve carrellata introduttiva veniamo al dunque, per cui riporto il contenuto dello scrip bash in grado di tenere sotto controllo i valori associati alle sorgenti NTP definite sul nostro sistema:

#!/bin/bash

warn_values=$1
crit_values=$2

warn_delay=`echo $warn_values | awk -F "," '{print $1}'`
warn_offset=`echo $warn_values | awk -F "," '{print $2}'`
warn_jitter=`echo $warn_values | awk -F "," '{print $3}'`

warn_delay_len=$((${#warn_delay} - 1))
warn_offset_len=$((${#warn_offset} - 1))
warn_jitter_len=$((${#warn_jitter} - 1))

crit_delay=`echo $crit_values | awk -F "," '{print $1}'`
crit_offset=`echo $crit_values | awk -F "," '{print $2}'`
crit_jitter=`echo $crit_values | awk -F "," '{print $3}'`

crit_delay_len=$((${#crit_delay} - 1))
crit_offset_len=$((${#crit_offset} - 1))
crit_jitter_len=$((${#crit_jitter} - 1))

if [[ "$warn_delay_len" -gt 5 ]];then
        echo "UNKNOWN: bad value for warning delay"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

if [[ "$warn_offset_len" -gt 5 ]];then
        echo "UNKNOWN: bad value for warning offset"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

if [[ "$warn_jitter_len" -gt 5 ]];then
        echo "UNKNOWN: bad value for warning jitter"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

if [[ "$crit_delay_len" -gt 5 ]];then
        echo "UNKNOWN: bad value for critical delay"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

if [[ "$crit_offset_len" -gt 5 ]];then
        echo "UNKNOWN: bad value for critical offset"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

if [[ "$crit_jitter_len" -gt 5 ]];then
        echo "UNKNOWN: bad value for critical jitter"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

ntp_delay=`/usr/sbin/ntpq -p | grep '*' | awk '{print $8}'`
ntp_offset=`/usr/sbin/ntpq -p | grep '*' | awk '{print $9}'`
ntp_jitter=`/usr/sbin/ntpq -p | grep '*' | awk '{print $10}'`

if [[ ! -z $warn_values ]];then
         if [[ ! -z $crit_values ]];then
                if [[ ! -z $warn_delay ]];then
                        if [[ ! -z $crit_delay ]];then
                                if [[ "$(echo $ntp_delay '>=' $warn_delay | bc)" -eq 1 ]] && [[ "$(echo $ntp_delay '<' $crit_delay | bc)" -eq 1 ]];then
                                        delay="NTP delay is $ntp_delay ms";
                                        delay_perf="| ntp_delay=$ntp_delay"
                                        retval_1=1;
                                elif [[ "$(echo $ntp_delay '>=' $crit_delay | bc)" -eq 1 ]];then
                                        delay="NTP delay is $ntp_delay ms";
                                        delay_perf="| ntp_delay=$ntp_delay"
                                        retval_1=2;
                                else
                                        delay="NTP delay is $ntp_delay ms";
                                        delay_perf="| ntp_delay=$ntp_delay"
                                        retval_1=0;
                                fi
                        else
                                echo "UNKNOWN: NTP critical delay is unknown"
                                exit 3;
                        fi
                else
                        echo "UNKNOWN: NTP warning delay is unknown"
                        exit 3;
                fi
                if [[ ! -z $warn_offset ]];then
                        if [[ ! -z $crit_offset ]];then
                                if [[ "$(echo $ntp_offset '<' 0 | bc)" -eq 1 ]];then
                                        warn_offset=$(echo "-1 * $warn_offset" | bc)
                                        crit_offset=$(echo "-1 * $crit_offset" | bc)
                                        if [[ "$(echo $ntp_offset '<=' $warn_offset | bc)" -eq 1 ]] && [[ "$(echo $ntp_offset '>' $crit_offset | bc)" -eq 1 ]];then
                                                offset="NTP offset is $ntp_offset ms";
                                                offset_perf=" ntp_offset=$ntp_offset"
                                                retval_2=1;
                                        elif [[ "$(echo $ntp_offset '<' $crit_offset | bc)" -eq 1 ]];then
                                                offset="NTP offset is $ntp_offset ms";
                                                offset_perf=" ntp_offset=$ntp_offset"
                                                retval_2=2;
                                        else
                                                offset="NTP offset is $ntp_offset ms";
                                                offset_perf=" ntp_offset=$ntp_offset"
                                                retval_2=0;
                                        fi
                                else
                                        if [[ "$(echo $ntp_offset '>=' $warn_offset | bc)" -eq 1 ]] && [[ "$(echo $ntp_offset '<' $crit_offset | bc)" -eq 1 ]];then
                                                offset="NTP offset is $ntp_offset ms";
                                                offset_perf=" ntp_offset=$ntp_offset"
                                                retval_2=1;
                                        elif [[ "$(echo $ntp_offset '>' $crit_offset | bc)" -eq 1 ]];then
                                                offset="NTP offset is $ntp_offset ms";
                                                offset_perf=" ntp_offset=$ntp_offset"
                                                retval_2=2;
                                        else
                                                offset="NTP offset is $ntp_offset ms";
                                                offset_perf=" ntp_offset=$ntp_offset"
                                                retval_2=0;
                                        fi
                                fi
                        else
                                echo "UNKNOWN: NTP critical offset is unknown"
                                exit 3;
                        fi
                else
                        echo "UNKNOWN: NTP warning offset is unknown"
                        exit 3;
                fi
                if [[ ! -z $warn_jitter ]];then
                        if [[ ! -z $crit_jitter ]];then
                                if [[ "$(echo $ntp_jitter '>' $warn_jitter | bc)" -eq 1 ]] && [[ "$(echo $ntp_jitter '<' $crit_jitter | bc)" -eq 1 ]];then
                                        jitter="NTP jitter is $ntp_jitter ms";
                                        jitter_perf=" ntp_jitter=$ntp_jitter"
                                        retval_3=1;
                                elif [[ "$(echo $ntp_offset '>' $crit_jitter | bc)" -eq 1 ]];then
                                        jitter="NTP jitter is $ntp_jitter ms";
                                        jitter_perf=" ntp_jitter=$ntp_jitter"
                                        retval_3=2;
                                else
                                        jitter="NTP jitter is $ntp_jitter ms";
                                        jitter_perf=" ntp_jitter=$ntp_jitter"
                                        retval_3=0;
                                fi
                        else
                                echo "UNKNOWN: NTP critical jitter is unknown"
                                exit 3;
                        fi
                else
                        echo "UNKNOWN: NTP warning jitter is unknown"
                        exit 3;
                fi
        else
                 echo "UNKNOWN: Critical values are unknown"
                 echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
                 exit 3;
        fi
else
        echo "UNKNOWN: Warning values are unknown"
        echo "Usage: check_ntp_peers warndelay,warnoffset,warnjitter critdelay,critoffset,critjitter"
        exit 3;
fi

if [[ "$retval_1" -eq 1 ]] || [[ "$retval_2" -eq 1 ]] || [[ "$retval_3" -eq 1 ]];then

        echo "WARNING: $delay $offset $jitter $delay_perf $offset_perf $jitter_perf"
        exit 1

elif [[ "$retval_1" -eq 2 ]] || [[ "$retval_2" -eq 2 ]] || [[ "$retval_3" -eq 2 ]];then

        echo "CRITICAL: $delay $offset $jitter $delay_perf $offset_perf $jitter_perf"
        exit 2

else

        echo "OK: $delay $offset $jitter $delay_perf $offset_perf $jitter_perf"
        exit 0

fi

Per prima cosa viene verificata la consistenza delle soglie di WARNING e CRITICAL per ciauscuno dei 3 valori monitorati (offset, jitter e delay), sia per quanto riguarda il numero di cifre utilizzate (5) che per ciò che concerne la coerenza logica (ad esempio il jitter/offset/delay di WARNING deve essere strettamente minore di quello CRITICAL).

A questo punto non ci rimane che configurare Nagios, editando, in prima istanza, il file /etc/nagios/objects/commands.cfg, dove verrà definito il comando che si avvarrà del suddetto plugin:

 # 'check_ntp_peers' command definition
define command{
        command_name check_ntp_peers
        command_line $USER1$/check_ntp_peers $ARG1$ $ARG2$
}

e successivamente associando uno specifico servizio all’host che rappresenta la nostra macchina, in modo da monitorare i valori associati all’NTP:

define service{
        use                             local-service         ; Name of service template to use
        host_name                       localhost
        service_description             NTP Peers Status
        check_command                   check_ntp_peers!80.000,2.000,2.000!90.000,3.000,3.000
        }

Facciamo il solito reload del nostro NMS:

[root@linuxbox ~]# service nagios reload

ed abbiamo finito.

Alla prossima.

PS: ho deciso di scrivere il suddetto scrip per sopperire alle limitazioni intrinseche del plugin Nagios (nativo) check_ntp_peer (senza la s finale). Nella fattispecie, quest’ultimo consente di ricavare solo ed esclusivamente il valore di offset associato alla sorgente di tempo remota e per funzionare è necessario che il server interrogato sia configurato in modo da rispondere alle query NTP (e quindi non solo alle richieste di sincronizzazione). Da notare che, nella stragrande maggioranza dei casi e per questioni di sicurezza, il server consentirà la sola sincronizzazione, ignorando gli altri tipi di query, come riportato nero su bianco (ad esempio) nella configurazione standard di ntpd:

restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery

CentOS 6 e Nagios: script bash per monitorare la scadenza dei certificati X.509

A quanti di voi sarà capitato di dover fare i conti con un certificato X.509 scaduto (magari installato su un server in produzione) che avete scordato (per un motivo o per un altro) di rinnovare?

Ebbene, tramite il seguente scrip bash (da integrare a Nagios) non dovremo più “appuntarci” la data di scadenza dei suddetti certificati, poichè sarà proprio l’NMS a ricordarci (a tempo debito) di procedere con il rinnovo.

nagiosEcco lo scrip:

#!/bin/bash

############Scrip powered by nightfly. Default values for warn and crit############
############(if not defined by command line) are 10 and 3 respectivley##############

host=$1
port=$2
warn=$3
crit=$4

function get_cert_datetime() {

        date=`echo | openssl s_client -connect $host:$port 2>/dev/null | openssl x509 -noout -dates | grep notAfter | awk -F "=" '{print $2 }'`

        day=`echo $date | awk '{print $2}'`

        case $day in

        1)

                day=01;
                ;;

        2)

                day=02;
                ;;

        3)

                day=03;
                ;;

        4)

                day=04;
                ;;

        5)

                day=05;
                ;;

        6)

                day=06;
                ;;

        7)

                day=07;
                ;;

        8)

                day=08;
                ;;

        9)

                day=09;
                ;;

        esac

        month=`echo $date | awk '{print $1}'`

        case $month in

        Jan)

                 month=01;
                ;;

        Feb)

                month=02;
                ;;

        Mar)

                month=03;
                ;;

        Apr)

                month=04;
                ;;

        May)

                month=05;
                ;;

        Jun)

                month=06;
                ;;

        Jul)

                month=07;
                ;;

        Aug)

                month=08;
                ;;

        Sep)

                month=09;
                ;;

        Oct)

                month=10;
                ;;

        Nov)

                month=11;
                ;;

        Dec)

                month=12;
                ;;
        esac

        year=`echo $date | awk '{print $4}'`

        time=`echo $date | awk '{print $3}'`

        hours=`echo $time | awk -F ":" '{print $1}'`

        minutes=`echo $time | awk -F ":" '{print $2}'`

        seconds=`echo $time | awk -F ":" '{print $3}'`

        datecheck="$year$month$day"

        datecheckepoch=`date -d "$datecheck" +"%s"`

        datenow=`date +"%Y%m%d"`

        datenowepoch=`date -d "$datenow" +"%s"`

}

get_cert_datetime;

get_time_diff() {

        #time difference in hours, minutes and seconds

        hoursnow=`date +"%H"`

        minutesnow=`date +"%M"`

        secondsnow=`date +"%S"`

        hourstoseconds=`expr $hoursnow "*" 3600`

        minutestosecond=`expr $minutesnow "*" 60`

        secondsnowtot=`expr $hourstoseconds + $minutestosecond + $secondsnow`

        hourschecktoseconds=`expr $hours "*" 3600`

        minuteschecktoseconds=`expr $minutes "*" 60`

        secondschecktot=`expr $hourschecktoseconds + $minuteschecktoseconds + $seconds`

        secondsdiff=`expr $secondschecktot - $secondsnowtot`

        secondstoexpire=`expr 86400 - $secondsnowtot + $secondschecktot`

        minutestoexpire=`expr $secondstoexpire / 60`

        spareseconds=`expr $secondstoexpire % 60`

        hourstoexpire=`expr $minutestoexpire / 60`

        spareminutes=`expr $minutestoexpire % 60`
}

get_time_diff;

timestamp_to_days() {

        #unix timestamp to days conversion

        secondsleft=`expr $datecheckepoch - $datenowepoch`

        minutesleft=`expr $secondsleft / 60`

        hoursleft=`expr $minutesleft / 60`

        daysleft=`expr $hoursleft / 24`

}

timestamp_to_days;

if [[ ! -z $host && ! -z $port ]];then

        if [[ $datecheckepoch -gt $datenowepoch ]];then

                if [[ -z $warn && -z $crit ]];then

                        if [[ $daysleft -ge 10 ]];then

                                echo "OK: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                exit 0

                        elif [[ $daysleft -lt 10 && $daysleft -gt 3 ]];then

                                echo "WARNING: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                exit 1

                        elif [[ $daysleft -lt 3 && $daysleft -gt 0 ]];then

                                echo "CRITICAL: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                exit 2

                        elif [[ $daysleft -eq 0 && $secondsdiff -gt 0 ]];then

                                echo "CRITICAL: the X.509 certificate will expire in $hourstoexpire hours $spareminutes minutes $spareseconds seconds"

                                exit 2

                        fi

                elif [[ ! -z $warn && ! -z $crit ]];then

                        if [[ $warn -gt $crit ]];then

                                if [[ $daysleft -ge $warn ]];then

                                        echo "OK: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 0

                                elif [[ $daysleft -lt $warn && $daysleft -gt $crit ]];then

                                        echo "WARNING: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 1

                                elif [[ $daysleft -lt $crit && $daysleft -gt 0 ]];then

                                        echo "CRITICAL: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 2

                                elif [[ $daysleft -eq 0 && $secondsdiff -gt 0 ]];then

                                        echo "CRITICAL: the X.509 certificate will expire in $hourstoexpire hours $spareminute minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 2

                                fi

                        fi

                        else

                                echo "Usage: check_ssl_cert_exp <host> <port> [<warn left days> <crit left days>]"
                                echo "Warn threshold has to be greater then the Crit one"
                                exit 3
                        fi

                else

                        echo "Usage: check_ssl_cert_exp <host> <port> [<warn left days> <crit left days>]"
                        echo "Both thresholds have to be used"

                        exit 3
                fi
        else

                echo "CRITICAL: the X.509 certificate is expired"

                exit 2

        fi

else

        echo "Usage: check_ssl_cert_exp <host> <port> [<warn left days> <crit left days>]"
        echo "Please set the hostname and the port at least"

        exit 3
fi

Ho deciso di adoperare le funzioni in modo da suddividere logicamente le varie operazioni che vengono effettuate per il calcolo del tempo che intercorre alla data di scadenza del certificato.

I parametri da utilizzare sono 4 in tutto, di cui 2 mandatori (hostname/FQDN e porta) e 2 facoltativi (soglia di WARNING e soglia di CRITICAL).

Per prima cosa (tramite la funzione get_cert_datetime()) individuo la data di scadenza riportata sul certificato  (campo notAfter), utilizzando l’applicativo s_client della suite openssl. Successivamente effettuo un parsing dell’output, ricavando giorno, mese, anno, ora, minuto e secondo di scadenza, convertendo quindi tali informazioni in Unix epoch.

Successivamente, mediante la funzione get_time_diff(), calcolo la differenza (in termini di ore, minuti e secondi), tra l’ora attuale e quella di scadenza del certificato. Inoltre, utilizzando la variabile secondsdiff, nel caso in cui mi dovessi trovare proprio nel giorno di scadenza, riesco a capire se il certificato sta per scadere oppure è già scaduto.

A questo punto procedo con tutta una serie di confronti tra variabili e nella fattispecie verifico che la data attuale sia anteriore a quella di scadenza:

if [[ $datecheckepoch -gt $datenowepoch ]];then

Se è effettivamente questo il caso, controllo, rispettivamente, che le soglie di WARNING e di CRITICAL siano state definite e che siano logicamente coerenti (con WARNING > CRITICAL):

 elif [[ ! -z $warn && ! -z $crit ]];then                        
      if [[ $warn -gt $crit ]];then

Dopodichè faccio un confronto tra il giorno attuale e quello di scadenza, tenendo in considerazione le suddette soglie oppure i valori di default (10 e 3 nel caso in cui esse non siano state definite esplicitamente). Se mi trovo proprio nel giorno di scadenza verifico che la variabile secondsdiff sia un intero strettamente positivo, restituendo un errore di tipo CRITICAL ed il tempo mancante alla scadenza.

Ovviamente, anche nel caso in cui la data attuale sia successiva a quella di scadenza verrà restituito un errore di tipo CRITICAL segnalando che il certificato è già scaduto.

Configurazione di Nagios

Per prima cosa occorre definire il comando che fa uso del suddetto scrip (check_ssl_cert_exp), ovvero:

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

Successivamente possiamo creare il servizio che si occuperà di controllare la scadenza del certificato X.509:

define service{
        use                             local-service         ; Name of service template to use
        host_name                       localhost
        service_descripion              SSL Certificate Expiration Date
        check_command                   check_ssl_exp!443!10!3
        notifications_enabled           0
        }

Infine riavviamo Nagios per rendere effettive le suddette modifiche:

[root@NMS ~]# service nagios reload

E’ tutto. Alla prossima.

NRPE_NT: script powershell per la modifica della direttiva allowed_hosts in nrpe.cfg

Scenario

Sistema di monitoraggio (Nagios) connesso ad Internet mediante indirizzo IP pubblico di tipo dinamico. Esso deve tenere sotto controllo, tramite opportune query NRPE, alcuni servizi attivi su di un server remoto (Windows 2008 R2), raggiungibile mediante FQDN ed IP statico.

powershellProblema

Per evitare che eventuali client non autorizzati possanno connettersi al servizio NRPE_NT in ascolto sul server remoto (porta 5666/TCP), è possibile definire, all’interno del suo file di configurazione (nrpe.cfg), gli indirizzi IP a cui è consentito eseguire le query. Ad esempio, mediante la direttiva allowed_hosts, siamo in grado di dichiarare la seguente regola:

allowed_hosts=79.37.83.59

ed ecco, per l’appunto, dov’è il problema: essendo il suddetto indirizzo definito in modo statico, nel momento in cui, per un motivo o per un altro, Nagios dovesse cambiare il proprio IP pubblico, le query NRPE fallirebbero miseramente (check timeout).

Soluzione

A questo punto è necessario fare una premessa: esiste una soluzione più pulita di quella che sto per riportare, che consiste, fondamentalmente, nell’uso di NSClient++ abbinato ad NRPE_NT, grazie ai quali è possibile realizzare un meccanismo di autenticazione client/server mediante lo scambio di certificati X.509 (in tal caso la direttiva allowed_host sarebbe del tutto superflua poichè autenticazione e confidenzialità verrebbero garantite mediante i suddetti certificati).

Per ciò che concerne, invece, la soluzione da me individuata, essa consiste nella realizzazione di uno scrip powershell che “interroga” l’FQDN di Nagios ogni 5 minuti, verificando che il suo indirizzo IP pubblico sia diverso da quello presente nel file di configurazione di NRPE. In tal caso lo scrip modificherà l’IP definito nella direttiva allowed_hosts.

Di seguito riporto il contenuto dello scrip:

$IP = nslookup nagios.vostrodominio.com | Select-String -Pattern '\d{1,3}(\.\d{1,3}){3}' | Select-String -notmatch 'IP del server da monitorare'

$PublicIP = $IP.tostring().Split(" ")[-1]

$OldIP = (Get-Content C:\nrpe\bin\nrpe.cfg) | Select-String 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}'

$OldPublicIP = $OldIP.tostring().Split("=")[-1]

$data = Get-Date;

if ($PublicIP -ne $OldPublicIP -and $PublicIP)
{

    (Get-Content C:\nrpe\bin\nrpe.cfg) -replace 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}', "allowed_hosts=$(echo $PublicIP)" | Set-Content C:\nrpe\bin\nrpe.cfg

    net stop nrpe_nt

    Start-Sleep -s 5

    net start nrpe_nt

    echo "$data NRPE_NT configuration has been updated. The new authorized Public IP is $PublicIP"

    exit 0;
}
else
{
    echo "$data Nothing to do. Exiting..."

    exit 0;
}

Per prima cosa ho individuato l’indirizzo IP pubblico di Nagios mediante una query di tipo A effettuata mediante nslookup:

$IP = nslookup nagios.vostrodominio.com | Select-String -Pattern '\d{1,3}(\.\d{1,3}){3}' | Select-String -notmatch 'IP del server da monitorare'

$PublicIP = $IP.tostring().Split(" ")[-1]

Ovviamente ho dovuto formattare l’output relativo alla suddetta query, identificando, tramite espressione regolare, le stringhe che riportano gli indirizzi IP coinvolti, escludendo successivamente quella che contiene l’IP del server da monitorare. Inoltre, è stato necessario salvare (dopo opportuna conversione) l’indirizzo IP di Nagios all’interno della variabile $PublicIP.

Discorso molto simile riguarda l’individuazione dell’IP configurato all’interno di nrpe.cfg, ottenuta mediante l’applet Get-Content:

$OldIP = (Get-Content C:\nrpe\bin\nrpe.cfg) | Select-String 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}'

$OldPublicIP = $OldIP.tostring().Split("=")[-1]

A questo punto è stato possibile procedere con il confronto tra l’IP attuale di Nagios e quello presente nel file di configurazione di NRPE_NT. Nel caso in cui questi differiscano (e la variabile $PublicIP non sia nè vuota, nè nulla), la direttiva allowed_hosts verrà modificata, con il conseguente riavvio del servizio in oggetto:

(Get-Content C:\nrpe\bin\nrpe.cfg) -replace 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}', "allowed_hosts=$(echo $PublicIP)" | Set-Content C:\nrpe\bin\nrpe.cfg

    net stop nrpe_nt

    Start-Sleep -s 5

    net start nrpe_nt

    echo "$data NRPE_NT configuration has been updated. The new authorized Public IP is $PublicIP"

Infine, per eseguire tale scrip in modo automatico, ho creato un task mediante l’applicativo Task Scheduler di Windows, il quale si ripete giornalmente ogni 5 minuti e la cui azione è la seguente:

powershell -command "C:\Users\Administrator\Documents\scrip\nrpe_ip_update.ps1 >> C:\nrpe_ip_update.log"

Da ora in poi le query NRPE da indirizzo IP dinamico non saranno un problema.

A presto.

CentOS 6: Riavviare automaticamente il servizio barnyard2 mediante Nagios e gli event handlers

In questo post ho mostrato il codice sorgente del demone barnyard2 da me customizzato. Esso, in soldoni, non fa altro che monitorare il contenuto del file alert generato da snort, in modo da poter individuare gli allarmi creati dall’IDS in questione, inserendoli (dopo un’opportuna formattazione) all’interno di uno specifico DBMS (nel nostro caso MySQL).

Purtroppo, però, tale servizio tende a crashare in modo randomico, ragion per cui ho deciso di creare un event handler (per Nagios) in grado di riavviarlo automaticamente.

nagios

Configurazione del SO che ospita L’NMS (Nagios)

Poichè l’operazione di riavvio del demone barnyard2 richiede i privilegi di root, è stato necessario fare in modo che l’utente nagios (che esegue gli event handlers) fosse abilitato all’interno del file /etc/sudoers:

nagios   ALL=NOPASSWD: /sbin/service barnyard2 restart

In particolare, ho fatto in modo che l’esecuzione del comando sudo non richiedesse la password (ALL=NOPASSWD:) solo per il comando /sbin/service barnyard2 restart (per la serie less is more). Inoltre, sempre all’interno del suddetto file, ho inserito, subito dopo la direttiva Defaults    requiretty, la stringa:

Defaults:nagios        !requiretty

in modo tale da consentire all’utente nagios di lanciare il comando sudo anche quando non è in possesso di un terminale (tty). Ciò è necessario poichè tutti gli event handlers vengono lanciati dal nostro NMS in assenza di sessione tty.

Un’altra modifica altrettanto importante ha riguardato SElinux. Esso, infatti, essendo attivo in modalità Enforcing, vietava puntualmente l’esecuzione dell’event handler in questione. Per “aggirare” tale divieto, ho dovuto modificare alcune regole MAC (Mandatory Access Control), attraverso 2 passaggi: il settaggio di una variabile booleana e la creazione di un modulo custom per SElinux.

Nel primo caso è stato sufficiente lanciare il comando:

[root@NMS]# setsebool -P nagios_run_sudo 1

mentre nel secondo caso ho dapprima creato il file permitnagioseventhandlers.te, di cui riporto il contenuto:

module permitnagioseventhandler 1.0;

require {
        type nagios_system_plugin_t;
        type nagios_unconfined_plugin_exec_t;
        type nagios_eventhandler_plugin_t;
        type httpd_t;
        class file { getattr };
        class dir { getattr search };
}

#============= nagios_system_plugin_t ==============
allow nagios_system_plugin_t nagios_unconfined_plugin_exec_t:file {getattr};

#============= httpd_t ==============
allow httpd_t nagios_eventhandler_plugin_t:dir { getattr search };

per poi verificarne la sintassi:

[root@NMS]#  checkmodule -M -m -o permitnagioseventhandler.mod permitnagioseventhandler.te

compliarlo:

[root@NMS]# semodule_package -o permitnagioseventhandler.pp -m permitnagioseventhandler.mod

ed installarlo:

[root@NMS]# semodule -i permitnagioseventhandler.pp

Configurazione di Nagios

Il riavvio del demone barnyard2 richiede parecchio tempo (soprattutto se esso è stato associato a più interfacce di rete, come nel mio caso). Per questo motivo si è reso necessario modificare il paramentro event_handler_timeout presente all’interno del file di configurazione Nagios (/etc/nagios/nagios.cfg), portandolo da 30 secondi (valore di default) a 300 secondi:

event_handler_timeout=400

Per ciò che concerne il servizio (relativo al nostro NMS) che si occupa del monitoraggio di barnyard2, è stata creata la seguente configurazione:

define service{
        use                             local-service         ; Name of service template to use
        host_name                       localhost
        service_descripion             Barnyard2 Service Status
        check_command                   check_local_process_status_all!2!2!barnyard2
        event_handler                   restart_barnyard
        }

dove il comando check_local_process_status_all è così definito:

# 'check_local_process_status_all' command definition
define command{
        command_name    check_local_process_status_all
        command_line    $USER1$/check_procs -c $ARG1$:$ARG2$ -C $ARG3$
        }

Nella fattispecie, le variabili $ARG1$ e $ARG2$ rappresentano, rispettivamente, il numero minimo e massimo di processi (recanti la stringa barnyard2, specificata mediante la variabile $ARG3$) attivi sulla macchina da monitorare.

Inoltre, è stato definito il comando restart_barnyard, il cui scopo è quello di eseguire l’event handler in questione:

# 'restart_barnyard' command definition
define command {
        command_name      restart_barnyard
        command_line      /usr/lib64/nagios/plugins/eventhandlers/restart_barnyard $SERVICESTATE$ $SERVICESTATETYPE$ $SERVICEATTEMPT$
}

Ho quindi riavviato Nagios per rendere effettive le suddette modifiche:

[root@NMS]# service nagios reload

Contenuto dell’event handler (restart_barnyard)

Una volta completata la fase preliminare relativa alla configurazione del SO e dell’NMS, mi sono dedicato alla creazione dell’event handler vero e proprio (che ho chiamato, molto semplicemente, restart_barnyard). Esso presenta il seguente contenuto:

#!/bin/bash

case "$1" in
OK)
        ;;
WARNING)
        ;;
UNKNOWN)
        ;;
CRITICAL)
       case "$2" in
                SOFT)
                        case "$3" in
                        3)
                                echo -n "Restarting barnyard2 service (3rd soft critical state)..."
                                /usr/bin/sudo /sbin/service barnyard2 restart
                                ;;
                                esac
                        ;;
                HARD)
                        echo -n "Restarting barnyard2 service..."
                        /usr/bin/sudo /sbin/service barnyard2 restart
                        ;;
                esac
                ;;
        esac

exit 0

ovvero non fa altro che riavviare il servizio in oggetto nel caso del terzo critical state (soft) o del quarto critical state (hard).

L’ho quindi reso eseguibile:

[root@NMS]# chmod +x restart_barnyard

e l’ho salvato all’interno della dir /usr/lib64/nagios/plugins/eventhandlers:

[root@NMS]# mv restart_barnyard /usr/lib64/nagios/plugins/eventhandlers

Test di funzionamento

Infine, ho testato il tutto semplicemente arrestando il servizio barnyard2:

[root@NMS]# service barnyard2 stop

verificando che Nagios svolgesse tutta la trafila per ritirarlo su in modo automatico:

[root@NMS]# tail -f /var/log/nagios/nagios.log

il cui output mi ha mostrato le diverse fasi di passaggio dallo stato CRITICAL allo stato OK:

[1448964811] SERVICE EVENT HANDLER: localhost;Barnyard2 Service Status;CRITICAL;SOFT;3;restart_barnyard
[1448965026] SERVICE ALERT: localhost;Barnyard2 Service Status;CRITICAL;HARD;4;PROCS CRITICAL: 0 processes with command name 'barnyard2'
[1448965026] SERVICE EVENT HANDLER: localhost;Barnyard2 Service Status;CRITICAL;HARD;4;restart_barnyard
[1448965313] SERVICE ALERT: localhost;Barnyard2 Service Status;OK;HARD;4;PROCS OK: 2 processes with command name 'barnyard2'
[1448965313] SERVICE EVENT HANDLER: localhost;Barnyard2 Service Status;OK;HARD;4;restart_barnyard

Inoltre, digitando il comando:

[root@NMS]# ps aux | grep barn

ho visualizzato i processi di barnyard2 durante il loro avvio da parte dell’event handler:

nagios    2799  0.0  0.0 108208  1352 ?        S    11:17   0:00 /bin/bash /usr/lib64/nagios/plugins/eventhandlers/restart_barnyard CRITICAL HARD 4
root      2800  0.1  0.1 189744  3392 ?        S    11:17   0:00 /usr/bin/sudo /sbin/service barnyard2 restart
root      2803  0.0  0.0 106460  1680 ?        S    11:17   0:00 /bin/sh /sbin/service barnyard2 restart
root      2809  0.0  0.0 108568  1868 ?        S    11:17   0:00 /bin/sh /etc/init.d/barnyard2 restart
root      3194 65.8  1.2  92668 40796 ?        Rs   11:18   1:06 barnyard2 -D -c /etc/snort/barnyard2eth0.conf -d /var/log/snort/eth0 -w /var/log/snort/eth0/barnyard2.waldo -l /var/log/snort/eth0 -a /var/log/snort/eth0/archive -f snort.log --pid-path /var/run
root      3196  0.0  0.0 108200  1284 ?        S    11:18   0:00 /bin/bash -c ulimit -S -c 0 >/dev/null 2>&1 ; barnyard2 -D -c /etc/snort/barnyard2eth1.conf -d /var/log/snort/eth1 -w /var/log/snort/eth1/barnyard2.waldo -l /var/log/snort/eth1 -a /var/log/snort/eth1/archive -f snort.log --pid-path /var/run
root      3197 61.4  0.2  58856  7808 ?        R    11:18   1:01 barnyard2 -D -c /etc/snort/barnyard2eth1.conf -d /var/log/snort/eth1 -w /var/log/snort/eth1/barnyard2.waldo -l /var/log/snort/eth1 -a /var/log/snort/eth1/archive -f snort.log --pid-path /var/run
root      3710  0.0  0.0 103268   864 pts/2    S+   11:20   0:00 grep barn

E’ tutto. Alla prossima.

Modifica delle ACL NTFS mediante PowerShell

Scenario

Una macchina con a bordo Windows Server 2008 R2 che funge da Web server (IIS7). Si vuole fare in modo che gli utenti possano effettuare l’upload (mediante un form opportunamente creato) di immagini e file di vario genere.

Problema

Le ACL NTFS relative alla root directory di IIS (ovvero C:\inetpub\wwwroot) non consentono le operazioni di modifica agli utenti Web anonimi (appartenenti al gruppo IIS_IUSRS), ergo qualsiasi tentativo di upload avrà esito negativo. Inoltre, l’alberatura delle sottodirectory è abbastanza complessa ed è qualcosa del tipo /nomesubdir/immagini, in cui nomesubdir è variabile (ce ne sono circa un migliaio), mentre immagini è la dir di destinazione degli upload (sulla quale bisogna garantire i permessi di modifica).

Soluzione

La prima soluzione che viene in mente prevede per lo più tanta pazienza ed un lavoro certosino da attuare, fondamentalmente, in 2 passaggi: l’accesso manuale a ciascuna subdir e la modifica delle ACL NTFS per la directory immagini ivi contenuta.

La seconda soluzione è certamente più rapida e smart: creare uno scrip PowerShell in grado di modificare le suddette ACL per tutte le directory immagini poste all’interno delle subdir.

powershellEcco lo scrip:

$Acl = Get-Acl "C:\inetpub\wwwroot\*\immagini"
$Ar = New-Object  system.security.accesscontrol.filesystemaccessrule("IIS_IUSRS","Modify","ContainerInherit, ObjectInherit", "None","Allow")
foreach($Ac in $Acl)
{
    $Ac.SetAccessRule($Ar)
    Set-Acl "C:\inetpub\wwwroot\*\immagini" $Ac
}

Esso prevede, in soldoni, 2 fasi: la prima in cui vengono identificati i permessi relativi alla directory immagini presente in ciascuna subdir  (mediante la cmdlet Get-Acl); la seconda in vengono definiti i nuovi permessi per il gruppo IIS_IUSRS, applicandoli a tutte le  immagini (ed ai file in esse contenuti).

Rollback

Naturalmente la modifica dei permessi NTFS associati a file e cartelle è sempre un’operazione abbastanza invasiva e delicata, ergo è opportuno garantirsi la possibilità di ripristinare la situazione iniziale (ovvero quella precedente alle suddette modifiche).

Ecco lo scrip in grado di effettuare il rollback dei permessi:

$Acl = Get-Acl "C:\inetpub\wwwroot\*\immagini"
$Ar = New-Object  system.security.accesscontrol.filesystemaccessrule("IIS_IUSRS","Modify","ContainerInherit, ObjectInherit", "None","Allow")
foreach($Ac in $Acl)
{
    $Ac.RemoveAccessRuleAll($Ar)
    Set-Acl "C:\inetpub\wwwroot\*\immagini" $Ac
}

La logica è sempre la stessa, l’unica variazione riguarda la funzione RemoveAccessRuleAll($Ar) che ha sostituito la SetAccessRule($Ar) utilizzata in precedenza.

Alla prossima.

Demone nodejs per CentOS 6

Come già affermato in questo post, nodejs ha rappresentato una vera e propria rivoluzione nella programmazione Web, in quanto ha consentito ad un linguaggio nativamente client-side come javascrip, di essere riadattato ed utilizzato come linguaggio server-side.

nodejs

Il funzionamento del suddetto applicativo è abbastanza banale: si richiama il binario da CLI e gli si da in pasto il file di configurazione del “server javascrip”, ad esempio:

[root@linuxbox ~]# node server.js

Per lasciarlo attivo in background occorre utilizzare un tool apposito come screen oppure un semplice nohup:

[root@linuxbox ~]# nohup node server.js

Va da se che tale soluzione per “demonizzare” nodejs non è proprio pulitissima ed è per questa ragione che ho deciso di creare un demone ad hoc per il suddetto applicativo:

 #!/bin/bash

# chkconfig: 2345 95 20
# description: nodejs service
# processname: nodejs

. /etc/rc.d/init.d/functions

USER="node"

DAEMON="/usr/bin/node"
ROOT_DIR="/home/nodejs"

SERVER="$ROOT_DIR/server.js"

for i in {1..8}

do

        do_start()
        {
                PORT="900$i"
                LOG_FILE="$ROOT_DIR/server$i.js.log"

                result=`ps aux | grep "server.js $PORT" | grep -v grep`
                if [ -z "$result" ] ; then
                echo -n $"Starting $SERVER $i: "
                runuser -l "$USER" -c "$DAEMON $SERVER $PORT >> $LOG_FILE &" && echo_success || echo_failure
                RETVAL=$?
                echo
                [ $RETVAL -eq 0 ]
                else
                echo "$SERVER $i already started."
                RETVAL=1
                fi
        }

        do_status()
        {
                PORT="900$i"

                result=`ps aux | grep "server.js $PORT" | grep -v grep`
                if [ -n "$result" ] ; then
                echo "$SERVER $i is started."
                RETVAL=0
                else
                echo "$SERVER $i is stopped."
                RETVAL=1
                fi
        }

done

do_stop()
{
        echo -n $"Stopping $SERVER:"
        killall -w node
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
}

case "$1" in
start)
        for i in {1..8}
        do
            do_start
        done
        ;;
stop)
        do_stop
        ;;
status)
        for i in {1..8}
        do
            do_status
        done
        ;;
restart)
        do_stop
        for i in {1..8}
        do
            do_start
        done
        ;;
*)

echo "Usage: $0 {start|stop|status|restart}"
        RETVAL=1
esac

exit $RETVAL

Tale demone, ad ogni avvio, esegue 8 istanze di nodejs, ciascuna delle quali è in ascolto su una porta dedicata (dalla 9001 alla 9008). Per non definire 8 funzioni do_start() e do_status() ho utilizzato un banale ciclo for e poichè la funzione do_stop() effettua un kill di tutti i processi il cui nome contiente la stringa “node” (killall -w), essa è stata estromessa dal predetto loop.

Inoltre, per ragioni di sicurezza, è stato definito un utente dedicato per l’esecuzione dell’applicativo (USER=”node”), la cui root directory è la propria home (ROOT_DIR=”/home/nodejs/), mentre il file di configurazione di nodejs è specificato all’interno della variabile SERVER (SERVER=”$ROOT_DIR/server.js”).

Infine, all’interno della funzione do_start(), ho definito il file di log per ciascuna istanza di nodejs, utilizzando la direttiva LOG_FILE=”$ROOT_DIR/server$i.js.log”.

Per completezza, di seguito riporto anche la versione senza cicli for:

#!/bin/bash

# chkconfig: 2345 95 20
# description: nodejs service
# processname: nodejs

. /etc/rc.d/init.d/functions

USER="node"

DAEMON="/usr/bin/node"
ROOT_DIR="/home/nodejs/"

SERVER="$ROOT_DIR/server.js"

PORT1="9001"
PORT2="9002"
PORT3="9003"
PORT4="9004"
PORT5="9005"
PORT6="9006"
PORT7="9007"
PORT8="9008"

LOG_FILE1="$ROOT_DIR/server1.js.log"
LOG_FILE2="$ROOT_DIR/server2.js.log"
LOG_FILE3="$ROOT_DIR/server3.js.log"
LOG_FILE4="$ROOT_DIR/server4.js.log"
LOG_FILE5="$ROOT_DIR/server5.js.log"
LOG_FILE6="$ROOT_DIR/server6.js.log"
LOG_FILE7="$ROOT_DIR/server7.js.log"
LOG_FILE8="$ROOT_DIR/server8.js.log"

do_start1()
{
        result1=`ps aux | grep "server.js $PORT1" | grep -v grep`
        if [ -z "$result1" ] ; then
        echo -n $"Starting $SERVER 1: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT1 >> $LOG_FILE1 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 1 already started."
        RETVAL=1
        fi
}

do_start2()
{
        result2=`ps aux | grep "server.js $PORT2" | grep -v grep`
        if [ -z "$result2" ] ; then
        echo -n $"Starting $SERVER 2: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT2 >> $LOG_FILE2 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 2 already started."
        RETVAL=1
        fi
}

do_start3()
{
        result3=`ps aux | grep "server.js $PORT3" | grep -v grep`
        if [ -z "$result3" ] ; then
        echo -n $"Starting $SERVER 3: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT3 >> $LOG_FILE3 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 3 already started."
        RETVAL=1
        fi
}

do_start4()
{
        result4=`ps aux | grep "server.js $PORT4" | grep -v grep`
        if [ -z "$result4" ] ; then
        echo -n $"Starting $SERVER 4: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT4 >> $LOG_FILE4 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 4 already started."
        RETVAL=1
        fi
}

do_start5()
{
        result5=`ps aux | grep "server.js $PORT5" | grep -v grep`
        if [ -z "$result5" ] ; then
        echo -n $"Starting $SERVER 5: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT5 >> $LOG_FILE5 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 5 already started."
        RETVAL=1
        fi
}

do_start6()
{
        result6=`ps aux | grep "server.js $PORT6" | grep -v grep`
        if [ -z "$result6" ] ; then
        echo -n $"Starting $SERVER 6: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT6 >> $LOG_FILE6 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 6 already started."
        RETVAL=1
        fi
}

do_start7()
{
        result7=`ps aux | grep "server.js $PORT7" | grep -v grep`
        if [ -z "$result7" ] ; then
        echo -n $"Starting $SERVER 7: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT7 >> $LOG_FILE7 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 7 already started."
        RETVAL=1
        fi
}

do_start8()
{
        result8=`ps aux | grep "server.js $PORT8" | grep -v grep`
        if [ -z "$result8" ] ; then
        echo -n $"Starting $SERVER 8: "
        runuser -l "$USER" -c "$DAEMON $SERVER $PORT8 >> $LOG_FILE8 &" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
        else
        echo "$SERVER 8 already started."
        RETVAL=1
        fi
}

do_status1()
{
        result1=`ps aux | grep "server.js $PORT1" | grep -v grep`
        if [ -n "$result1" ] ; then
        echo "$SERVER 1 is started."
        RETVAL=0
        else
        echo "$SERVER 1 is stopped."
        RETVAL=1
        fi
}

do_status2()
{
        result2=`ps aux | grep "server.js $PORT2" | grep -v grep`
        if [ -n "$result2" ] ; then
        echo "$SERVER 2 is started."
        RETVAL=0
        else
        echo "$SERVER 2 is stopped."
        RETVAL=1
        fi
}

do_status3()
{
        result3=`ps aux | grep "server.js $PORT3" | grep -v grep`
        if [ -n "$result3" ] ; then
        echo "$SERVER 3 is started."
        RETVAL=0
        else
        echo "$SERVER 3 is stopped."
        RETVAL=1
        fi
}

do_status4()
{
        result4=`ps aux | grep "server.js $PORT4" | grep -v grep`
        if [ -n "$result4" ] ; then
        echo "$SERVER 4 is started."
        RETVAL=0
        else
        echo "$SERVER 4 is stopped."
        RETVAL=1
        fi
}

do_status5()
{
        result5=`ps aux | grep "server.js $PORT5" | grep -v grep`
        if [ -n "$result5" ] ; then
        echo "$SERVER 5 is started."
        RETVAL=0
        else
        echo "$SERVER 5 is stopped."
        RETVAL=1
        fi
}

do_status6()
{
        result6=`ps aux | grep "server.js $PORT6" | grep -v grep`
        if [ -n "$result6" ] ; then
        echo "$SERVER 6 is started."
        RETVAL=0
        else
        echo "$SERVER 6 is stopped."
        RETVAL=1
        fi
}

do_status7()
{
        result7=`ps aux | grep "server.js $PORT7" | grep -v grep`
        if [ -n "$result7" ] ; then
        echo "$SERVER 7 is started."
        RETVAL=0
        else
        echo "$SERVER 7 is stopped."
        RETVAL=1
        fi
}

do_status8()
{
        result8=`ps aux | grep "server.js $PORT8" | grep -v grep`
        if [ -n "$result8" ] ; then
        echo "$SERVER 8 is started."
        RETVAL=0
        else
        echo "$SERVER 8 is stopped."
        RETVAL=1
        fi
}

do_stop()
{
        echo -n $"Stopping $SERVER:"
        killall -w node
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ]
}

case "$1" in
start)
        do_start1
        do_start2
        do_start3
        do_start4
        do_start5
        do_start6
        do_start7
        do_start8
        ;;
stop)
        do_stop
        ;;
status)
        do_status1
        do_status2
        do_status3
        do_status4
        do_status5
        do_status6
        do_status7
        do_status8
        ;;
restart)
        do_stop
        do_start1
        do_start2
        do_start3
        do_start4
        do_start5
        do_start6
        do_start7
        do_start8
        ;;
*)

echo "Usage: $0 {start|stop|status|restart}"
        RETVAL=1
esac

exit $RETVAL

Basta dare una semplice occhiata e vi renderete subito conto del perchè abbia preferito la prima versione a quest’ultima.

Non ci rimane che rendere il demone eseguibile:

[root@linuxbox ~]# chmod +x nodejs

spostarlo nella directory /etc/init.d:

[root@linuxbox ~]# mv nodejs /etc/init.d

ed avviarlo:

[root@linuxbox ~]# service nodejs start

E’ tutto.

Creazione di una logging facility mediante logrotate ed expect

Scenario

Diversi siti Web (Apache) con N virtual host, ciascuno dei quali utilizza 2 file di log dedicati (access ed error), per il salvataggio degli accessi (nel primo caso) e degli errori generati (nel secondo caso).

Problema

Data l’enorme mole di utenza, è necessario comprimere e salvare i suddetti file di log con cadenza settimanale, spostando l’archivio appena ottenuto su di una logging facility (nella fattispecie una linux box), utilizzando il protocollo SFTP.

Soluzione

Integrare lo scrip di logrotate con alcuni scrip expect fatti in casa.

logging-facility

Logrotate

Lo scrip logrotate che si occuperà della vera e propria rotazione dei file di log (con cadenza settimanale) è il seguente:

/var/log/httpd/*log {
    rotate 1
    missingok
    notifempty
    sharedscrips
    dateext
    compress
    copytruncate
    weekly
    postrotate
        /sbin/service httpd restart > /dev/null 2>/dev/null || true
    endscrip
    lastaction
        /root/logrotation/autoputlog/apache > /var/log/autoputlog/apache.log
    endscrip
}

/var/log/httpd/virtual/*/*log {
    rotate 1
    missingok
    notifempty
    sharedscrips
    dateext
    compress
    copytruncate
    weekly
    postrotate
        /sbin/service httpd restart > /dev/null 2>/dev/null || true
    endscrip
    lastaction
         /root/logrotation/autoputlog/apachecustom > /var/log/autoputlog/apachecustom.log
    endscrip
}

Nella fattispecie, la prima parte si occupa della rotazione dei file di log relativi alla root directory canonica di Apache, ovvero /var/www/html, mentre la seconda parte si occupa dei virtual host veri e propri.

I suddetti scrip ci permettono di integrare logrotate con expect grazie alla direttiva lastaction. In particolare, essa farà in modo che, una volta completata la rotazione (con la creazione di una tarball opportuna), venga eseguito lo scrip expect apache (nel primo caso) ed apachecustom (nel secondo caso), presenti nella directory /root/logrotation/autoputlog.

L’output realtivo al processo di esecuzione degli scrip in questione verrà salvato rispettivamente all’interno del file apache.log ed apachecustom.log, presenti in /var/log/autoputlog. Inoltre, sarà necessario creare i file in questione prima che la rotazione venga testata, utilizzando i comandi :

[root@frontend ~]# mkdir -p /var/log/autoputlog

e

[root@frontend ~]# touch apache.log apachecustom.log

Infine, di seguito riporto il contenuto dello scrip expect apache:

#!/usr/bin/expect
set timeout 60
set password "vostrapassword"
spawn sftp admin@iplogfacility:array1/logs/frontend1/httpd
expect "*?assword:*"
send "$password\r"
expect "sftp"
send "put /var/log/httpd/*.gz\r"
expect "sftp"
send "exit\r"
expect eof

ed apachecustom:

#!/usr/bin/expect
set timeout -1
set password "vostrapassword"
spawn sftp admin@iplogfacility:array1/logs/frontend1/httpd
expect "*?assword:*"
send "$password\r"
expect "sftp"
send "cd vhost1\r"
expect "sftp"
send "put /var/log/httpd/virtual/vhost1/*.gz\r"
expect "sftp"
send "cd ../vhost2\r"
expect "sftp"
send "put /var/log/httpd/virtual/vhost2/*.gz\r"
expect "sftp"
send "cd ../vhost3\r"
expect "sftp"
send "put /var/log/httpd/virtual/vhost3/*.gz\r"
expect "sftp"
send "exit\r"
expect eof

Non ci rimane che lanciare forzatamente logrotate per testarne il funzionamento, utilizzando il comando:

[root@frontend ~]# logrotate -f /etc/logrotate.d/httpd

ed abbiamo finito.

A presto.

sniffer.sh: script bash per l’individuazione dei site grabber

Scenario

Supponiamo che il vostro sito sia diventato oggetto delle attenzioni di un grabber (di cui non si conosce l’indirizzo IP pubblico), che ogni X tempo ne preleva il contenuto forzosamente.

Supponiamo, inoltre, che il suddetto grabber sia abbastanza intelligente da coprire le proprie tracce “spoofando” lo user agent in modo da apparire come un client lecito.

590x300

Nel caso in cui non si abbiano a disposizione strumenti di log analysis avanzati quali Splunk, l’unica alternativa per individuare l’IP pubblico del grabber consiste nel crearsi degli strumenti ad-hoc,  in grado di fare un conteggio del numero di hit per ciascun IP pubblico che ha contattato il nostro sito.

In soldoni si tratta di un semplice scrip bash, che riporto di seguito.

#!/bin/bash

filename1="/var/log/sniffer.log"

filename2="/var/log/sources.log"

filename3="/var/log/sources_sorted.log"

filename4="/var/log/sources_sorted_by_hits.log"

if [ -e $filename1 ];then

cat /dev/null > $filename1

fi

if [ -e $filename2 ];then

cat /dev/null > $filename2

fi

if [ -e $filename3 ];then

cat /dev/null > $filename3

fi

if [ -e $filename4 ];then

cat /dev/null > $filename4

fi

echo ""

echo -e "33[32mSniffing traffic... Press CTRL+C to stop"

echo -e "33[37m"

START=$(date +%s);

iftop -i eth0 -n -f 'port (80 or 443)' -t > $filename1

echo ""

read -p "Press [Enter] key to continue..."

END=$(date +%s);

ELAPSED_TIME=$((END-START))

echo -e "33[32mShowing hit number provided in $ELAPSED_TIME seconds, ordered by source..."

echo -e "33[37m"

cat $filename1 | grep '<=' | awk '{ print $1 }' > $filename2

cat $filename2 | sort -u > $filename3

while read line

do
 hits=`cat $filename2 | grep $line | wc -l`

host=`host $line`

echo "hits for IP $line ($host): $hits" >> $filename4

done < $filename3

cat $filename4 | sort -k 5 -nr

exit 0

Esso si basa principalmente sul software iftop e, una volta lanciato, rimane in ascolto sulle porte di interesse (nel nostro caso la TCP/80 e la TCP/443), per poi stilare una “classifica” di IP pubblici in base al numero di hit che hanno prodotto durante il tempo di osservazione.

Come informazione aggiuntiva, per ogni IP viene ricavato anche il suo FQDN (attraverso il comando host), in modo da individuare ancora più facilmente la provenienza del grabber.

Per maggiore chiarezza, ecco un esempio di output relativo al suddetto script:

hits for IP 87.18.215.154 (154.215.18.87.in-addr.arpa domain name pointer host154-215-dynamic.18-87-r.retail.telecomitalia.it.): 18
hits for IP 84.223.145.17 (17.145.223.84.in-addr.arpa domain name pointer dynamic-adsl-84-223-145-17.clienti.tiscali.it.): 23
hits for IP 84.222.212.172 (172.212.222.84.in-addr.arpa domain name pointer dynamic-adsl-84-222-212-172.clienti.tiscali.it.): 23
hits for IP 79.18.99.217 (217.99.18.79.in-addr.arpa domain name pointer host217-99-dynamic.18-79-r.retail.telecomitalia.it.): 24
hits for IP 2.35.156.49 (49.156.35.2.in-addr.arpa domain name pointer net-2-35-156-49.cust.vodafonedsl.it.): 21
hits for IP 188.87.64.235 (235.64.87.188.in-addr.arpa domain name pointer static-235-64-87-188.ipcom.comunitel.net.): 23
hits for IP 151.45.153.218 (218.153.45.151.in-addr.arpa domain name pointer adsl-ull-218-153.45-151.net24.it.): 14

Inoltre, consultando il file /var/log/sources_sorted_by_hits.log, sarà possibile ricavare la lista degli IP/FQDN ordinati (in modo decrescente) in base al numero delle hit che hanno effettuato.

Una volta individuato l’IP che ci interessa non ci resta che bloccarlo utilizzando, ad esempio, iptables:

iptables -I INPUT -s <ip del grabber> -j DROP

ed abbiamo finito.

Alla prossima.