Archivi tag: expect

check_noise_margin e check_attenuation: script Nagios per verificare la qualità della nostra linea ADSL

Premessa

Vi sono numerosi fattori che possono influenzare negativamente o positivamente la qualità della nostra linea ADSL, ma i più importanti sono sicuramente il rapporto segnale rumore (SNR o noise margin) e l’attenuazione.

In particolare, nel primo caso un valore elevato indica una qualità migliore; viceversa, nel secondo caso, un valore elevato indica una qualità peggiore. Inoltre, i valori di attenuazione sono molto influenzati dalla distanza che intercorre tra la nostra abitazione e la centrale di zona dell’ISP (va da se che maggiore sarà questa distanza, maggiore sarà l’attenuazione).

Esistono comunque dei valori di massima (sia per l’SNR che per l’attenuazione) sui quali ci si può basare per fare una stima qualitativa del nostro collegamento ADSL. Li riporto di seguito:

SNR

1) <= 6dB : pessimo, numerosi errori si sincronizzazione con la portante;
2) tra i 7dB ed i 10dB: scarso;
3) tra gli 11dB ed i 20dB: buono;
4) tra i 20dB ed i 28dB: eccellente;
5) >= 29dB: eccezionale.

 Attenuazione

1) < = 20dB: eccezionale;
2) tra i 20dB ed i 30dB: eccellente:
3) tra i 30dB ed i 40dB: molto buona;
4) tra i 40dB ed i 50dB: buona;
5) tra i 50dB ed i 60dB: scarsa, con diversi errori di connessione.
6) >= 60dB: pessima, con numerosi errori di connessione.

Tenendo conto dei suddetti valori, ho deciso di realizzare 2 scrip bash (da integrare a Nagios), in modo da tenere traccia dei valori di SNR ed attenuazione relativi alla mia linea ADSL. Il router di riferimento è un Cisco 877.

Entrambi gli scrip in questione (check_noise_margin e check_attenuation), si basano su un ulteriore scrip expect (get_dsl_info) che esegue la query sul router, lanciando il comando sh dsl int atm0.

Il contenuto di tale scrip è il seguente:

#!/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 "sh dsl int atm0\r"
send " "
expect "#"
send "exit\r"
expect eof

L’output da esso generato verrà quindi dato in pasto ed elaborato da check_noise_margin e da check_attenuation. Il loro contenuto è molto simile ed è (rispettivamente):

#!/bin/bash

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

usage="check_noise_margin <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" -gt "$warning" ];then

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

                                        else

                                                output=`/usr/lib64/nagios/plugins/get_dsl_info $1 $2 $3 | grep "Noise"  | awk -F " " '{print $3,$5}'`
                                                output1=`echo $output | awk '{print $1}'`
                                                output2=`echo $output | awk '{print $2}'`
                                                unit="db"
                                        fi

                                        if [ -n "$output" ];then

                                                if [ $(echo "$output1 < $critical" | bc) -eq 1 -o $(echo "$output2 < $critical" | bc) -eq 1 ];then

                                                        echo "CRITICAL: downstream noise margin is $output1 db, upstream noise margin is $output2 db | downstream_noise_margin=$output1$unit;$warning;$critical upstream_noise_margin=$output2$unit;$warning;$critical";
                                                        exit 2;

                                                elif [ $(echo "$output1 > $critical" | bc) -eq 1 -a  $(echo "$output1 < $warning" | bc) -eq 1 -o $(echo "$output2 > $critical" | bc) -eq 1 -a $(echo "$output2 < $warning"  |bc) -eq 1 ];then

                                                        echo "WARNING: downstream noise margin is $output1 db, upstream noise margin is $output2 db| downstream_noise_margin=$output1$unit;$warning;$critical upstream_noise_margin=$output2$unit;$warning;$critical" ;
                                                        exit 1;

                                                else

                                                        echo "OK: downstream noise margin is $output1 db, upstream noise margin is $output2 db | downstream_noise_margin=$output1$unit;$warning;$critical upstream_noise_margin=$output2$unit;$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

per check_noise_margin, e:

#!/bin/bash

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

usage="check_attenuation <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_dsl_info $1 $2 $3 | grep "Attenuation"  | awk -F " " '{print $2, $4}'`

                                                output1=`echo $output | awk '{print $1}'`
                                                output2=`echo $output | awk '{print $2}'`
                                                unit="db"
                                        fi

                                        if [ -n "$output" ];then

                                                if [ $(echo "$output1 > $critical" | bc) -eq 1 -o $(echo "$output2 > $critical" | bc) -eq 1 ];then

                                                        echo "CRITICAL: downstream attenuation is $output1 db, upstream attenuation is $output2 db | downstream_attenuation=$output1$unit;$warning;$critical upstream_attenuation=$output2$unit;warning;$critical";
                                                        exit 2;

                                                elif [ $(echo "$output1 < $critical" | bc) -eq 1 -a  $(echo "$output1 > $warning" | bc) -eq 1 -o $(echo "$output2 < $critical" | bc) -eq 1 -a  $(echo "$output2 > $warning" | bc) -eq 1 ];then

                                                        echo "WARNING: downstream attenuation is $output1 db, upstream attenuation is $output2 db | downstream_attenuation=$output1$unit;$warning;$critical upstream_attenuation=$output2$unit;$warning;$critical";
                                                        exit 1;

                                                else

                                                        echo "OK: downstream attenuation is $output1 db, upstream attenuation is $output2 db | downstream_attenuation=$output1$unit;$warning;$critical upstream_attenuation=$output2$unit;$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

per check_attenuation.

Da notare che in entrambi i casi ho aggiunto le perfdata da dare in pasto a pnp4nagios. Inoltre, poichè i valori restituiti possono contenere dei decimali, ho dovuto utilizzare il comando bc per i calcoli aritmetici. Ciò si è reso necessario poichè bash tratta nativamente le variabili come stringhe.

Una volta fatto ciò, ho semplicemente creato i comandi per Nagios:

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

collegandoli, successivamente, ai servizi del router Cisco:

define service{
 use                             local-service         ; Name of service template to use
 host_name                       router
 service_descripion             Noise Margin
 check_command                   check_noise_margin!pass1!pass2!10!6
 }
define service{
 use                             local-service         ; Name of service template to use
 host_name                       router
 service_descripion             Attenuation
 check_command                   check_attenuation!pass1!pass2!51!61
 }

Infine, ho lanciato un reload del servizio:

[root@nightbox objects]# service nagios reload

e finalmente la qualità della mia linea ADSL è sotto monitoraggio.

Alla prossima.

PS: per chi le preferisse, ecco le varianti in Perl dei suddetti scrip:

#!/usr/bin/perl

use strict;
use warnings;

my $host=$ARGV[0];
my $password1=$ARGV[1];
my $password2=$ARGV[2];
my $warning=$ARGV[3];
my $critical=$ARGV[4];

my $usage="check_noise_margin.pl <host> <password1> <password2> <warning> <critical>";

if ($host ne "") {
        if ($password1 ne "")
        {
                if ($password2 ne "")
                {
                        if ($warning ne "")
                        {
                                if ($critical ne "")
                                {
                                        if ($critical > $warning)
                                        {
                                                print "UNKNOWN: critical has to be less than warning";
                                                exit 3;
                                        }
                                        else
                                        {
                                                my $output=`/usr/lib64/nagios/plugins/get_dsl_info $host $password1 $password2 | /bin/grep "Noise"`;

                                                if($output ne "")
                                                {

                                                        my @columns = split /\s+/, $output;
                                                        my $downstream = $columns[2];
                                                        my $upstream = $columns[4];
                                                        my $unit = "db";

                                                        if ($downstream < $critical || $upstream < $critical)
                                                        {
                                                                print "CRITICAL: downstream noise margin is $downstream db; upstream noise margin is $upstream db | downstream_noise_margin=$downstream$unit;$warning;$critical upstream_noise_margin=$upstream$unit;$warning;$critical\n";
                                                                exit 2;
                                                        }
                                                        elsif ($downstream > $critical && $downstream < $warning || $upstream > $critical && $upstream < $warning)
                                                        {
                                                                print "WARNING: downstream noise margin is $downstream db; upstream noise margin is $upstream db | downstream_noise_margin=$downstream$unit;$warning;$critical upstream_noise_margin=$upstream$unit;$warning;$critical\n";
                                                                exit 1;
                                                        }
                                                        else
                                                        {
                                                                print "OK: downstream noise margin is $downstream db, upstream noise margin is $upstream db | downstream_noise_margin=$downstream$unit;$warning;$critical upstream_noise_margin=$upstream$unit;$warning;$critical\n";
                                                                exit 0;
                                                        }
                                                }
                                                else
                                                {
                                                                print "UNKNOWN: output is null";
                                                                exit 3;
                                                }
                                        }
                                }
                                else
                                {
                                        print "$usage";
                                        exit 3;
                                }
                        }
                        else
                        {
                                print "$usage";
                                exit 3;
                        }
                }
                else
                {
                        print "$usage";
                        exit 3;
                }
        }
        else
        {
                print "$usage";
                exit 3;
        }
}
else
{
        print "$usage";
        exit 3;
}

e

#!/usr/bin/perl

use strict;
use warnings;

my $host=$ARGV[0];
my $password1=$ARGV[1];
my $password2=$ARGV[2];
my $warning=$ARGV[3];
my $critical=$ARGV[4];

my $usage="check_attenuation.pl <host> <password1> <password2> <warning> <critical>";

if ($host ne "") {
        if ($password1 ne "")
        {
                if ($password2 ne "")
                {
                        if ($warning ne "")
                        {
                                if ($critical ne "")
                                {
                                        if ($critical < $warning)
                                        {
                                                print "UNKNOWN: critical has to be more than warning";
                                                exit 3;
                                        }
                                        else
                                        {
                                                my $output=`/usr/lib64/nagios/plugins/get_dsl_info $host $password1 $password2 | /bin/grep "Attenuation"`;

                                                if($output ne "")
                                                {

                                                        my @columns = split /\s+/, $output;
                                                        my $downstream = $columns[1];
                                                        my $upstream = $columns[3];
                                                        my $unit = "db";

                                                        if ($downstream > $critical || $upstream > $critical)
                                                        {
                                                                print "CRITICAL: downstream attenuation is $downstream db; upstream attenuation is $upstream db | downstream_attenuation=$downstream$unit;$warning;$critical upstream_attenuation=$upstream$unit;$warning;$critical\n";
                                                                exit 2;
                                                        }
                                                        elsif ($downstream < $critical && $downstream > $warning || $upstream < $critical && $upstream > $warning)
                                                        {
                                                                print "WARNING: downstream attenuation is $downstream db; upstream attenuation is $upstream db | downstream_attenuation=$downstream$unit;$warning;$critical upstream_attenuation=$upstream$unit;$warning;$critical\n";
                                                                exit 1;
                                                        }
                                                        else
                                                        {
                                                                print "OK: downstream attenuation is $downstream db, upstream attenuation is $upstream db | downstream_attenuation=$downstream$unit;$warning;$critical upstream_attenuation=$upstream$unit;$warning;$critical\n";
                                                                exit 0;
                                                        }
                                                }
                                                else
                                                {
                                                                print "UNKNOWN: output is null";
                                                                exit 3;
                                                }
                                        }
                                }
                                else
                                {
                                        print "$usage";
                                        exit 3;
                                }
                        }
                        else
                        {
                                print "$usage";
                                exit 3;
                        }
                }
                else
                {
                        print "$usage";
                        exit 3;
                }
        }
        else
        {
                print "$usage";
                exit 3;
        }
}
else
{
        print "$usage";
        exit 3;
}

PPS: se sul vostro router è stato abilitato il protocollo SNMP, gli OID che consentono di monitorare SNR ed attenuazione sono i seguenti:

.1.3.6.1.2.1.10.94.1.1.3.1.5.11 = downstream attenuation 
.1.3.6.1.2.1.10.94.1.1.2.1.5.11 = upstream attenuation 
.1.3.6.1.2.1.10.94.1.1.3.1.4.11 = downstream noise margin
.1.3.6.1.2.1.10.94.1.1.2.1.4.11 = upstream noise margin

In particolare, l’ultima parte dell’OID (.11) si riferisce all’Interface Index della Dialer (nel mio caso si tratta della Dialer0).

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.

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.

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.

Reboot script per i router Draytek Vigor

Premesso che giornalmente devo “scontrarmi” con un Draytek Vigor 3300V, sono pian piano giunto alla conclusione che tale aggeggio abbia più difetti che pregi.

vigor3300.jpg

Ad esempio, il content filtering consente di bloccare al massimo 8 (e dico 8!) keyword e non ne vuole sapere di gestire l’HTTPS; la pagina relativa ai DDNS, nonostante la presenza delle credenziali per aggiornare l’associazione IP – FQDN, non inoltra alcuna richiesta al sito del provider e quindi i record A scadono; il server DHCP integrato non consente di settare delle normalissime exclusion su alcuni IP che rientrano nel pool ma che non devono essere assegnati a nessun utente.

Ora, potrei continuare quasi all’infinito, ma l’ultima bega che mi son dovuto accollare riguarda la gestione delle VPN. Si, esatto, questo fantastico router funge anche da VPN concentrator, riuscendo (per modo di dire) a gestire VPN IPSec, L2TP e PPTP. Peccato che ogni “tot” si incarti miseramente, lasciando fuori alcuni utenti che cercano di atterrare sulla LAN via VPN, mentre altri riescono tranquillamente ad accedere alla rete interna.

Ho provato a cercare una soluzione un pò più pulita rispetto al classico riavvio, ma credetemi se vi dico che non c’è (forse si potrebbe procedere con la disattivazione/riattivazione della VPN ma non sono sicuro che una cosa del genere non preveda comunque un reboot).

In definitiva, ecco lo scrip expect che mi permette di riavviare il router ogni notte:

#!/usr/bin/expect

set password1 "password"

exec date
spawn ssh -l draytek 10.1.10.1
expect "?"
send "yr"
expect "*?assword:*"
send "$password1r"
expect ">"
send "sys rebootr"
expect "?"
send "yr"
expect eof

Ovviamente l’esecuzione di tale scrip avviene mediante cron e non fa altro che connettersi al router, lanciare un sys/reboot ed uscire.

Attenzione però: dopo ogni riavvio il router cambia il proprio fingerprint SSH (non chiedetemi il perchè), quindi il suddetto scrip, dopo il primo riavvio, fallirebbe miseramente in quanto il client SSH, vedendosi cambiare di punto in bianco il fingerprint dell’host a cui sta provando a connettersi, penserebbe in un attacco MITM.

Per evitarè ciò è necessario editare il file /etc/ssh/ssh_config aggiungendo in testa le seguenti direttive:

Host 10.1.10.1
     StrictHostKeyChecking no
     UserKnownHostsFile=/dev/null

Infine, voglio precisare che il router è comunque dotato di un comando in grado di definire il reboot automatico (mi sa che gli stessi costruttori fossero a conoscenza del fatto che tale aggeggio tende ad incartarsi di continuo). Nonostante questo comando bundle, chiamato appunto autoreboot, ho preferito operare come indicato in precedenza per un semplice motivo:

DrayTek> sys autoreboot ?
 Full Name:
    auto reboot function
 Description:
 Syntax:
    autoreboot -s                                (Show status)
    autoreboot -e <Enable>
    autoreboot -t <Hours>
 Parameters:
    <Enable>                                         (0: Disable, 1: Enable)
    <Hours>                                          (Number of hours)

Si, avete capito bene, se volessi riavviare il router ogni mattina alle 3 (quindi esattamente ogni 24 ore) dovrei lanciare i comandi:

DrayTek> sys autoreboot -e 1

DrayTek> sys autoreboot -t 24

indovinate a che ora? ALLE 3 DEL MATTINO. No comment.

Alla prossima.

Script per il backup automatico della configurazione di un router Cisco SOHO 77

Premessa

Un sistemista di rete previdente sa che è di vitale importanza poter contare su di un backup valido delle configurazioni relative ai vari network device. Proprio per questo motivo ho deciso di creare uno scrip che permettesse di automatizzare tale procedura.

soho.JPG

 

Configurare un server TFTP

Occorre precisare che il backup della configurazione del nostro router Cisco SOHO 77 verrà archiviato su di un server TFTP basato su una distribuzione *buntu.

Ma veniamo al dunque. Per prima cosa installiamo il server TFTP digitando:

nightfly@nightbox:~$ sudo apt-get install tftp tftpd xinetd

A questo punto posizioniamoci sulla directory /etc/xinetd.d e creiamo il file tftp il cui contenuto dovrà essere:

service tftp
 {
 protocol        = udp
 port            = 69
 socket_type     = dgram
 wait            = yes
 user            = nobody
 server          = /usr/sbin/in.tftpd
 server_args     = /tftpboot
 disable         = no
 }

Come si può notare dalla direttiva server_args     = /tftpboot il nostro server salverà la configurazione del router nella directory /tftpboot. Inoltre, poichè tale dir non è presente sulla nostra macchina è necessario crearla digitando:

nightfly@nightbox:/$ mkdir tftpboot

posizioniamoci all’interno di /tftpboot e creiamo il file vuoto router.cfg che conterrà la configurazione del SOHO 77:

nightfly@nightbox:/tftpboot$ sudo touch router.cfg

A questo punto lavoriamo sui permessi della directory appena creata e del suo contenuto:

nightfly@nightbox:/$ sudo chmod 777 -R tftpboot

nightfly@nightbox:/$ sudo chown nobody tftpboot

Riavviamo xinetd digitando:

nightfly@nightbox:/$ sudo /etc/init.d/xinetd stop

nightfly@nightbox:/$ sudo /etc/init.d/xinetd start

Infine, verifichiamo che il nostro server TFTP sia in ascolto digitando:

nightfly@nightbox:/$ sudo nmap -sU localhost

se l’output sarà simile al seguente:

69/udp   open|filtered tftp

vuol dire che il server risulta effettivamente in ascolto.

Scrip per il backup automatico della configurazione relativa al SOHO 77

Prima di mostrarvi lo scrip, è necessario installare un tool indispensabile al suo funzionamento. Tale tool prende il nome di expect:

nightfly@nightbox:/$ sudo apt-get install expect

Creiamo ora il file backup_conf_soho77 il cui contenuto dovrà essere il seguente:

#!/usr/bin/expect

set password1 "<pass1>"
set password2 "<pass2>" #necessaria nel caso in cui la password per l'enable sia diversa da quella per l'accesso via telnet

spawn telnet <IP del router>
expect "Password:"
send "$password1r"
expect ">"
send "enar"
expect "Password:"
send "$password2r"
expect "#"
send "copy system:running-config tftp://<IP del server TFTP>/router.cfgr"
expect "?"
send "r"
expect "?"
send "r"
expect "#"
send "exitr"
expect eof

NB: prima della r all’interno delle virgolette ci vuole il backslash, in modo da inviare al router un ritorno a capo (nello scrip non appare in quanto viene automaticamente skippato da myblog per ragioni di sicurezza).

Rendiamo lo scrip eseguibile mediante il comando:

nightfly@nightbox:~$ sudo chmod +x backup_conf_soho77

e successivamente spostiamolo nella directory /usr/bin:

nightfly@nightbox:~$ sudo mv backup_conf_soho77 /usr/bin

Adesso non ci resta che “schedulare” l’esecuzione dello scrip. Per fare questo è sufficiente inserire una entry in /etc/crontab:

00 00   * * * root backup_conf_router

In particolare, ogni mezzanotte verrà eseguito il backup della configurazione mediante lo scrip descritto in precedenza.

Riavviamo cron per rendere effettive le nuove impostazioni:

nightfly@nightbox:~$ sudo /etc/init.d/cron restart

ed abbiamo finito.

A presto.

 

Script per il forward automatico della porta SSH sul router Cisco 837

La IOS C837-K9O3Y6-M sviluppata per il router Cisco 837 ha un bug, ovvero è possibile che dopo qualche reload si “dimentichi” di una o più PAT translations che riguardano porte più alte delle well-known (per intenderci, superiori alla 1023).

bug

Nel mio caso, il problema ha iniziato a verificarsi da quando ho deciso di mettere in ascolto SSH su una porta non standard, in modo da risparmiarmi i tentativi di attacco lanciati dagli scrip kiddie di turno.

Inoltre, per motivi di sicurezza, ho consentito l’accesso via SSH al router solo dagli host della LAN, quindi, nel caso in cui dovessi accedere alla sua configurazione, atterro sul server e da lì mi collego al router.

Ovviamente, se la porta SSH non è forwardata non posso atterrare sul server, dunque l’unica soluzione praticabile consiste nell’andare fisicamente dal cliente e ricreare la regola per il PAT.

E qui viene il bello: poichè mi sono stancato di dover perdere almeno un’ora per andare dal cliente, riconfigurare il tutto e tornarmene a casa, ho deciso di creare il seguente scrip, il quale si collega al router tre volte al giorno e ricrea la regola per l’SSH.

Ecco lo scrip:

#!/usr/bin/expect

set password1 "<password1>"
set password2 "<password2>"

spawn ssh -l <username> <IP del router>
expect "*?assword:*"
send "$password1r"
expect ">"
send "enar"
expect "Password:"
send "$password2r"
expect "#"
send "conf tr"
expect "(config)#"
send "no ip nat inside source static tcp <ip> <porta> interface Dialer0 <porta>r"
expect "(config)#"
send "ip nat inside source static tcp <ip> <porta> interface Dialer0 <porta>r"
expect "(config)#"
send "exitr"
expect "#"
send "copy run startr"
expect "?"
send "r"
expect "#"
send "exitr"
expect eof

Come al solito, mancano i backslash prima della r perchè myblog li filtra.

Una volta che avete creato lo scrip, convertitelo in eseguibile lanciando il comando:

nightfly@nightbox:~$ sudo chmod +x ssh_autoforward

e salvatelo nella directory /usr/bin:

nightfly@nightbox:~$ sudo mv ssh_autoforward /usr/bin

A questo punto potete creare la regola per cron, in modo da schedulare l’esecuzione dello scrip alle 6, alle 12 ed alle 18:

nightfly@nightbox:~$ sudo nano /etc/crontab

00 06,12,18   * * *     root          ssh_autoforward

Riavviamo cron:

nightfly@nightbox:~$ sudo service cron restart

ed abbiamo finito.

A presto.

PS: non preoccupatevi se lo scrip entra in esecuzione mentre siete loggati sul server: la regola per il forward dell’SSH non verrà rimossa poichè la state già utilizzando, quindi niente perdita di connessione.

Script per il backup giornaliero di un database remoto

Visto che la ridondanza non è mai troppa (Murphy vi dice qualcosa?), ho pensato di realizzare uno scrip per effettuare il backup di un database hostato su un server remoto.

shell

Ecco lo scrip (basato su expect):

#!/usr/bin/expect -f
set date [exec date +%d_%m_%y]
set password1 "<pass1>"
set password2 "<pass2>"
set database "<nomedb>"
spawn ssh user@hostname
expect "*?assword:*"
send "$passwordr"
send "r"
expect ":~$"
send "mysqldump $database -u root -ppassvostrodb > $database_$date.plr"
send "$database_$date.pl user@hostname:/home/userr"
expect "*?assword:*"
send "$password2r"
send "r"
expect ":~$"
send "rm database_*r"
expect eof

Lo scrip in questione si collega via SSH al server remoto, esegue un dump del database per poi copiarlo tramite SCP sul mio server.

Affinchè tale scrip venga eseguito giornalmente (per la precisione ogni sera alle 22) è necessario editare il file /etc/crontab aggiungendo la seguente direttiva:

00 22   * * * user  cd /home/user/ && ./backupremotedb > /dev/null 2>&1

Per ulteriori info contattatemi.

A presto.

Script per il backup automatico della configurazione di un firewall Cisco PIX 501

In questo post ho riportato uno scrip da me creato il cui scopo è quello di eseguire un backup della configurazione di un router Cisco SOHO 77. Inoltre, sempre nell’ambito del post in questione, ho descritto la procedura per installare e configurare correttamente un server TFTP sulla nostra linux box. Dando per scontato che il server TFTP sia già attivo e che il file vuoto firewall.cfg sia già presente nella directory /tftpboot, vediamo come configurare il nostro firewall affinchè possa comunicare con il server in ascolto.

pix501

Una volta effettuato il login sul PIX 501 lanciamo un conf t e successivamente digitiamo il comando:

PIX501(config)# tftp-server inside <IP del server TFTP> /tftpboot/firewall.cfg

In questo modo stiamo dicendo al PIX qual è l’indirizzo del server ed il pathname del file su cui salvare la configurazione. Lanciamo un write mem per salvare le modifiche relative alla configurazione del firewall e successivamente accediamo alla nostra linux box. Fattò ciò creiamo il file backup_conf_pix501 il cui contenuto dovrà essere il seguente:

 #!/usr/bin/expect

 set password1 "<pass1>"
 set password2 "<pass2>

 spawn telnet <IP del firewall>
 expect "Password:"
 send "$password1\r"
 expect "Password:"
 send "$password2\r"
 expect ">"
 send "ena\r"
 expect "Password:"
 send "$password\r"
 expect "#"
 send "write net\r"
 expect "#"
 send "exit\r"
 expect eof

Rendiamo il file eseguibile digitando:

nightfly@nightbox:~$ sudo chmod +x backup_conf_pix501

spostiamolo in /usr/bin e successivamente editiamo il file /etc/crontab aggiungendo la entry:

00 00   * * * root backup_conf_pix501

Riavviamo cron:

nightfly@nightbox:~$ sudo /etc/init.d/cron restart

ed il gioco è fatto. See ya.