Archivi tag: cisco

Cisco 877: configurazione del server DHCP integrato

Come ogni router che si rispetti, anche il Cisco 877 può essere configurato a mo’ di server DHCP, seguendo una procedura abbastanza semplice ed intuitiva. In questo post vedremo come tirare giù una configurazione base, comprensiva di una exclusion e di una reservation.

cisco 877Definizione dei pool DHCP e della reservation

Per prima cosa occorre definire i pool di indirizzi IP da cui il nostro server andrà “a pescare” quelli da assegnare alle macchine che ne faranno richiesta. Nello specifico, ho creato 2 pool, uno per gli indirizzi della LAN (pool wifi) ed uno necessario alla reservation (pool pcportatile), ovvero:

ip dhcp pool wifi
   network 192.168.7.0 255.255.255.0
   default-router 192.168.7.1
   dns-server 192.168.7.1
!
ip dhcp pool pcportatile
   host 192.168.7.3 255.255.255.0
   client-identifier 0149.d224.a5d4.0e
   default-router 192.168.7.1
   dns-server 192.168.7.1

Da notare che per il pool pcportatile ho utilizzato la direttiva client-identifier, recante il MAC address del dispositivo oggetto della reservation, nella forma aaaa.bbbb.cccc e con prefisso 01. Nella fattispecie, il MAC address del PC è 49:d2:24:a5:d4:0e, che in notazione Cisco diventa 49d2.24a5.d40e. Aggiungiamo quindi lo 01 come prefisso ed avremo il client-identifier finale, ovvero 0149.d224.a5d4.0e.

Detto ciò, un modo rapido di ricavare il suddetto identificativo consiste nell’utilizzo del seguente comando:

sh ip dhcp binding

il cui output sarà simile al seguente:

Bindings from all pools not associated with VRF:
IP address          Client-ID/              Lease expiration        Type
                    Hardware address/
                    User name
192.168.7.3         0149.d224.a5d4.0e       Infinite                Manual
192.168.7.8         0151.f0d3.fc7d.54       Sep 07 2016 09:15 AM    Automatic

Definizione dell’exclusion

Per ciò che concerne l’exclusion, la sua configurazione consta di un’unica direttiva, ovvero:

ip dhcp excluded-address 192.168.7.2

In tal modo faremo sì che l’IP 192.168.7.2 non rientri nei pool precedentemente definiti e quindi non possa essere assegnato a nessun dispositivo (poichè trattasi di un indirizzo statico associato ad una stampante).

Il post termina qui, alla prossima.

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).

Cisco 2811: utilizzare le route-map per creare delle regole di destination NAT basate su IP sorgente

Scenario

Supponiamo che si abbia a che fare con un ufficio centrale (main office) a cui sono collegati N uffici periferici (branch office) tramite dei tunnel VPN IPsec Site-to-Site dedicati (che concorrono a formare la classica topologia a stella). Supponiamo, inoltre, che i suddetti uffici periferici, per questioni di failover, debbano essere in grado di raggiungere i servizi presenti nell’ufficio centrale anche nel caso in cui i tunnel VPN non siano disponibili (passando quindi  direttamente per Internet).

vpn-ipsec1Utilizzando delle regole di destination NAT classiche, del tipo:

ip nat inside source static tcp 192.168.2.4 80 interface fastethernet0/0 80

(dove 192.168.4.2 è l’IP locale del server Web esposto su Internet), i branch office non saranno in grado di raggiungere il server in questione tramite il tunnel VPN (utilizzando il protocollo HTTP).

Ergo, il fatto che un determinato servizio sia pubblicato su Internet, implica automaticamente l’impossibilità di raggiungerlo anche tramite il tunnel VPN.

Per ovviare a tale problematica esistono 2 soluzioni: la prima, meno impegnativa (ma che richiede la modifica della URL lato client in caso di failover), consiste nel modificare la configurazione del server in modo tale che rimanga in ascolto su 2 porte distinte, ad esempio la TCP 80 per Internet e la TCP 81 per la VPN;  la seconda, più impegnativa (ma anche molto più scalabile), consiste nel creare sul nostro router Cisco 2811 (main office) delle route-map (che si avvalgono di opportune ACL) in grado di filtrare gli indirizzi IP sorgenti dei client che vogliono collegarsi al server Web. In questo modo, se la richiesta di connessione proviene da un determinato IP privato tipico di una VPN Site-to-Site (ad esempio 192.168.3.1), per essa non viene applicato il destination NAT; viceversa, nel caso in cui la richiesta di connessione provenga da Internet, verrà applicato il destination NAT come di consueto.

Ho definito la seconda soluzione come la più scalabile delle 2 per un semplice motivo: impostando la route-map sul router del main office e modificando sul nameserver locale il record di tipo A che punta all’IP del server Web, si può fare in modo che quest’ultimo possa essere contattato tramite tunnel VPN o tramite Internet a seconda dei casi senza dover modificare la URL lato browser (passando, ad esempio, da http://www.vostrodominio.com a http://www.vostrodominio.com:81).

Vediamo adesso come mettere in pratica la soluzione #2.

Configurazione del router Cisco 2811 (main office)

Per prima cosa occorre creare l’ACL in grado di “riconoscere” gli IP locali e di negare il destination NAT:

Router(config)# access-list 150 deny ip host 192.168.2.4 192.168.3.0 0.0.0.255
Router(config)# access-list 150 deny ip host 192.168.2.4 192.168.4.0 0.0.0.255
Router(config)# access-list 150 deny ip host 192.168.2.4 192.168.5.0 0.0.0.255
Router(config)# access-list 150 deny ip host 192.168.2.4 192.168.6.0 0.0.0.255
Router(config)# access-list 150 permit ip host 192.168.2.4 any

Successivamente creiamo la route-map vera e propria:

Router(config)# route-map nonat
Router(config-route-map)# match ip address 150

dove 150 è il numero dell’ACL estesa precedentemente definita.

Infine, associamo la route-map appena creata alla regola di destination NAT:

Router(config)# ip nat inside source static tcp 192.168.2.4 <IP Pubblico> 80 route-map nonat extendable

Ovviamente, affinchè la suddetta soluzione sia realmente scalabile, è necessario che il vostro collegamento ad Internet sia dotato di indirizzo IP pubblico statico.

Salviamo adesso la configurazione del nostro router:

Router# copy run start

e passiamo al vaglio alcune soluzioni alternative alle route-map.

1) Utilizzo dei record DNS di tipo SRV (vedi qui per ulteriori dettagli). Essi ci consentono non solo di specificare il protocollo di comunicazione ma anche la porta su cui è in ascolto il server, definendo una priorità per ciascuna entry che li compone:

_http._tcp.vostrodominio.com. 86400 IN SRV 0 5 81 www.vostrodominio.com.
_http._tcp.vostrodominio.com. 86400 IN SRV 1 5 80 www1.vostrodominio.com.

dove 0 e 1 sono le priorità, 81 e 80 le porte su cui è in ascolto il server. In caso di timeout sulla porta 81 e l’IP di www (raggiungibile via VPN) il browser “dovrebbe” switchtare automaticamente sulla 80 e l’IP di www1. Ho utilizzato il condizionale poichè non tutti i broswer supportano tale meccanismo ed un workaround (applicato però solo da alcuni di essi), consiste nel definire record A con il medesimo hostname ma indirizzi IP differenti: nel caso in cui la connessione al primo IP della lista vada in timeout, il broswer tenterà automaticamente di connettersi al secondo IP (e così via).

2) Utilizzo di un firewall interno per filtrare le connessioni in uscita (outbound). ln questo caso, grazie ad esso, potremmo creare delle regole ad hoc (source NAT) per il mapping delle porte di destinazione, ad esempio (utilizzando iptables):

[root@firewall ~]# iptables -t nat -A OUTPUT -p tcp -d www.vostrodominio.com --dport 80 -j DNAT --to-destination www.vostrodominio.com:81

Anche in questo caso, prima di applicare la suddetta regola di firewalling, sarà necessario modificare sul nameserver il record A per l’hostname www.

E’ tutto. Alla prossima.

PIX 501: configurazione del protocollo TACACS+

Premessa

Più volte, nell’ambito dei miei ultimi post, ho discusso della configurazione del protocollo TACACS+ per i Router/Switch di casa Cisco.

Adesso vedremo come configurare il suddetto protocollo su un Cisco PIX 501.

pixPrima di iniziare, occorre precisare che le uniche feature che abiliteremo saranno l’autenticazione e l’accounting (tralasciando quindi la parte di autorizzazione).

Configurazione generale

Dopo aver effettuato il login sul firewall in questione, entriamo in modalità enable e digitiamo il comando:

PIX# conf t

per poi creare una nuova istanza TACACS+:

PIX(config)# aaa-server AuthInbound protocol tacacs+

A questo punto possiamo specificare l’IP del server AAA, l’interfaccia attraverso cui è possibile raggiungerlo, la chiave TACACS+ ed il timeout:

PIX(config)# aaa-server AuthInbound (inside) host <IP> <key> timeout <secondi>

Authentication

Ora possiamo definire i protocolli che dovranno interfacciarsi col server per l’autenticazione dell’utente:

PIX(config)# aaa authentication telnet console AuthInbound LOCAL

Accounting

Infine, facciamo in modo che tutte le sessioni provenienti dall’esterno (Internet) vengano loggate dal nostro server:

PIX(config)# aaa accounting any inbound 0.0.0.0 0.0.0.0 0.0.0.0 0.0.0.0 AuthInbound

Creazione delle utenze locali

Per una questione di fallback, prima di salvare la suddetta configurazione, conviene creare delle utenze locali utilizzando il comando:

PIX(config)# username <user> password <pass>

ed abbiamo finito.

Alla prossima.

RANCID e CentOS: creare un sistema centralizzato di controllo e backup per le configurazioni dei dispositivi di rete

Avere a disposizione uno o più backup della configurazione dei dispositivi di rete è indispensabile, soprattutto nell’ottica di un eventuale disaster recovery. Inoltre, tenere traccia (mediante versioning) delle modifiche apportate alle suddette configurazioni ci può certamente aiutare a tracciare le operazioni svolte dai vari sistemisti di rete (oppure ad identificare eventuali modifiche “malevoli” effettuate dagli hacker/cracker di turno).

Il software (open source) che fa al caso nostro prende il nome di RANCID ed in questo post vedremo come installarlo e configurarlo su una macchina CentOS 6.5.

router_ciscoInstallazione

Prima di eseguire l’installazione vera e propria occorre fare una premessa: tale applicativo verrà installato mediante repository yum e non mediante compilazione dei sorgenti, in modo da risparmiarci tutta una trafila di configurazioni aggiuntive che introdurrebbero solo entropia alla nostra attività.

Per prima cosa, quindi, è necessario installare il repository EPEL da cui attingere per ottenere il suddetto software. Una volta fatto ciò possiamo passare all’installazione vera e prorpia dell’applicativo, digitando:

[root@linuxbox opt]# yum install rancid

Configurazione

Editiamo il file di configurazione /etc/rancid/rancid.conf, apportando le seguenti modifiche:

FILTER_PWDS=YES; export FILTER_PWDS
NOCOMMSTR=YES; export NOCOMMSTR
LIST_OF_GROUPS="main-office branch-office"
MAILHEADERS=""; export MAILHEADERS

Da notare che tali opzioni ci consentono, nell’ordine, di:

1) filtrare le password dalla configurazione backuppata. Ad esempio, per i dispositivi di casa Cisco, la secret è in formato digest MD5 mentre le altre password (se il comando service password-encryption è stato abilitato) sono in formato “digest” proprietario, che è assolutamente reversibile, quindi insicuro;

2) filtrare le community string utilizzate dal protocollo SNMP. Certo, nel caso in cui si stia utilizzando il suddetto protocollo nella versione 1 o 2 tale informazione può essere facilmente ricavata sniffando il traffico in oggetto, ma questa operazione è comunque più complicata (sia dal punto di vista logistico che da quello tecnico) rispetto alla lettura di un file di configurazione in cui le community string sono riportate in chiaro;

3) definire i gruppi di dispositivi da monitorare. Nel mio caso ho identificato solo 2 gruppi, ovvero branch-office (uffici periferici) e main-office (ufficio centrale). Entrambi utilizzano dei dispositivi di rete marchiati Cisco (router e switch);

4) non customizzare l’header delle email di notifica inviate da RANCID.

Controlliamo che sia presente il cron job in grado di lanciare la verifica delle configurazioni in modo automatico ad ogni ora:

[root@linuxbox opt]# cat /etc/cron.d/rancid

il cui contenuto dovrà essere:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/var/rancid
# Run config differ hourly
1 * * * * rancid /usr/libexec/rancid/rancid-run

Ora verifichiamo che sia presente il file di configurazione relativo a logrotate, il cui scopo è quello di ruotare ad intervalli regolari i file di log generati da RANCID:

[root@linuxbox rancid]# cat /etc/logrotate.d/rancid

il cui contenuto dovrà essere:

/var/log/rancid/* {
    weekly
    rotate 1
    notifempty
    missingok
    compress
    olddir old
}

Infine, verifichiamo che sia presente l’utente rancid (creato in automaticamente durante l’installazione dell’applicativo in questione):

[root@linuxbox rancid]# finger rancid
Login: rancid                           Name: RANCID
Directory: /var/rancid/                 Shell: /bin/bash
Never logged in.
No mail.
No Plan.

Se tutte le verifiche preliminari sono andate a buon fine possiamo dedicarci all’inizializzazione del repository CVS (versioning) di RANCID, lanciando i comandi:

[root@linuxbox opt]# su rancid
[rancid@linuxbox opt]$ /usr/bin/rancid-cvs

Posizioniamoci adesso nella home dell’utente e creiamo il file (nascosto) .cloginrc. Esso conterrà tutte le informazioni necessarie a RANCID per effettuare il login in modo automatizzato sui dispositivi di rete.

[rancid@linuxbox opt]$ cd --
[rancid@linuxbox opt]$ nano .cloginrc

Il cui contenuto dovrà avere il seguente formato:

add method <IP> <protocollo>
add user <IP> <username>
add password <IP> <password> <enable password>

ad esempio:

add method 192.168.1.1 ssh
add user 192.168.1.1 admin
add password 192.168.1.1 p4ssw0rd s3cr3t3n4bl3

Assegniamo i giusti permessi al file appena creato:

[rancid@linuxbox opt]$ chmod 700 .cloginrc

e lanciamo manualmente un login di prova, individuando dapprima dove si trova il binario clogin:

[rancid@linuxbox opt]$ locate clogin

il cui output potrebbe essere:

/usr/libexec/rancid/clogin

quindi digitiamo:

[rancid@linuxbox opt]$ /usr/libexec/rancid/clogin <IP>

Nel caso in cui volessimo testare contemporaneamente il login e l’invio di un comando specifico, possiamo utilizzare il comando:

[rancid@linuxbox opt]$ /usr/libexec/rancid/clogin -t <timeout> -c "comando1;comando2" <IP>

A questo punto occorre fare una precisazione: poichè alcuni tool messi a disposizione dal nostro applicativo fanno uso implicito di clogin, è necessario che il suo percorso venga esportato tra le variabili d’ambiente. Per fare ciò è necessario lanciare il comando:

[rancid@linuxbox opt]$ export PATH=$PATH:/usr/libexec/rancid

Adesso possiamo testare il funzionamento di RANCID per il singolo apparato, digitando:

[rancid@linuxbox opt]$ rancid -t <tipo> <IP>

dove <tipo> rappresenta, molto banalmente, il tipo di dispositivo di rete (marca e macromodello – per una panoramica dei dispositivi supportati potete consultare il file /etc/rancid/rancid.types.base).

Ora possiamo dedicarci all’editing dei gruppi. In particolare, tale attività consta di una sola operazione, ovvero l’inserimento all’interno del file router.db delle specifiche dei dispositivi di rete che intendiamo monitorare.

Però, prima di fare ciò, occorre indentificare la versione di RANCID installata, lanciando il comando:

[root@linuxbox ~]# rancid -V

Ciò è fondamentale poichè dalla versione 3.0 è stato introdotto il supporto all’IPv6, che, come sapete, utilizza abbondantemente i :. Proprio per questo motivo, il formato del contenuto del file router.db è diventato simile al seguente:

IP;marca;stato

anzichè:

IP:marca:stato

Premesso che la versione installata è la 3.1, il file router.db per il gruppo main-office avrà questo contenuto:

192.168.1.1;cisco;up
192.168.1.2;cisco;up

mentre per il gruppo branch-office avremo:

192.168.110.1;cisco;up
192.168.111.1;cisco;up
192.168.112.1;cisco;up
192.168.113.1;cisco;up
192.168.114.1;cisco;up
192.168.115.1;cisco;up

Testiamo RANCID sui gruppi:

[rancid@linuxbox rancid]$ rancid-run main-office
[rancid@linuxbox rancid]$ rancid-run branch-office

e definiamo gli indirizzi a cui dovranno essere inviate le email di allarme/notifica:

[root@linuxbox rancid]# nano /etc/aliases
rancid-main-office: vostro.indirizzo@email.it
rancid-admin-main-office: vostro.indirizzo@email.it
rancid-branch-office: vostro.indirizzo@email.it
rancid-admin-branch-office: vostro.indirizzo@email.it

In particolare, ciascun gruppo necessità di 2 indirizzi email: uno per il recapito delle informazioni “standard” e l’altro per le notifiche agli amministratori di rete.

Rendiamo effettive le modifiche apportate al file /etc/aliases:

[root@linuxbox rancid]# newaliases

e passiamo all’integrazione con tac_plus.

Integrazione tra RANCID e tac_plus

In questo post ho illustrato i vari step necessari per la creazione di un server AAA basato sul demone tac_plus (ed il protocollo made in Cisco TACACS+). Adesso faremo in modo che RANCID possa collegarsi ai vari dispositivi di rete contattando direttamente il server AAA. Nella fattispecie, è sufficiente editare il file /etc/tac_plus.conf aggiungendo le seguenti direttive:

user = rancid {

        login = des CjzxbdLRbG6sY

        service = exec {
                priv-lvl = 15
        }

        cmd = show { permit .* }
        cmd = exit { permit .* }
        cmd = dir { permit .* }
        cmd = more { permit .* }
        cmd = write { permit term }
}

dove la password (in formato DES + salt) è stata creata utilizzando l’applicativo tac_pwd. Da notare che all’utente rancid viene garantito il login in modalità enable (privilege 15 in gergo Cisco) e gli viene data la possibilità di lanciare 5 comandi (e relativi sub comandi), ovvero show, exit, dir, more e write.

Ricarichiamo la configurazione del demone tac_plus:

[root@linuxbox rancid]# service tac_plus reload

ed abbiamo finito.

Alla prossima.

Configurazione “IOS resilient” sui dispositivi Cisco

Pensate a cosa accadrebbe se un file di vitale importanza per il fuzionamento del nostro dispositivo Cisco (come l’immagine dell’IOS o lo startup-config) venisse cancellato erroneamente (dall’amministratore) o intenzionalmente (da un hacker/cracker).

cisco

Proprio per evitare che un evento del genere possa accadere, Cisco ci mette a disposizione una serie di funzioni che concorrono a formare la cosiddetta IOS resilient configuration. Esse ci consentono rispettivamente di salvare l’immagine dell’IOS all’interno di una memoria PCMCIA e lo startup-config in una locazione protetta del filesystem. Così facendo, lanciando ad esempio il comando:

Router# show flash

oppure

Router# dir flash:

non verrà mostrato il file *.bin che rappresenta l’immagine dell’IOS.

Inoltre, nel caso in cui lo startup-config venisse cancellato erroneamente digitando:

Router# erase startup-config

e successivamente:

Router# reload

La configurazione potrà essere comunque ripristinata lanciando i comandi:

Router(config)# secure boot-config restore flash:archived-config

e

Router#configure replace flash:archived-config

dove il primo serve ad estrarre lo startup-config archiviato (in modo sicuro) all’interno del filesystem, mentre il secondo è necessario per fare in modo che il running-config venga sostituito con il file di configurazione appena estratto.

Da notare che per abilitare le suddette funzioni occorre che il dispositivo sia dotato di una memoria PCMCIA (indispensabile per la messa in sicurezza dell’IOS) e che, una volta configurate, possono essere disabilitate solo ed esclusivamente tramite console.

Ma veniamo al sodo. I comandi da lanciare sono 2, ovvero:

Router(config)# secure boot-image

per l’IOS, e:

Router(config)# secure boot-config

per lo startup-config.

Infine, per essere certi che i suddetti comandi siano andati a buon fine, possiamo digitare:

Router# show secure bootset

il cui output sarà simile al seguente:

IOS image resilience version 12.4 activated at 02:00:30 UTC Sun Oct 17 2010
Secure archive flash:c181x-advipservicesk9-mz.124-24.T.bin type is image (elf) []
  file size is 23587052 bytes, run size is 23752654 bytes
  Runnable image, entry point 0x80012000, run from ram

IOS configuration resilience version 12.4 activated at 02:00:41 UTC Sun Oct 17 2010
Secure archive flash:.runcfg-20101017-020040.ar type is config
configuration archive size 1544 bytes

Fine del post, alla prossima.

Realizzare un server AAA mediante CentOS 6 e tac_plus

Premessa

Quando si ha a che fare con reti di grandi dimensioni (che contano centinaia e centinaia di dispositivi connessi tra di loro), occorre fare in modo che gli account utente (ed i relativi permessi) vengano gestiti in modo centralizzato. Tale funzione viene svolta dai cosiddetti server AAA, i quali, mediante l’uso di determinati protocolli (come RADIUS, DIAMETER o TACACS+), forniscono dei meccanismi per l’autenticazione dell’utente (Athentication), per la gestione dei relativi permessi (Authorization) e per la “registrazione” delle azioni  compiute dopo aver ottenuto accesso ai dispositivi (Accounting).

aaa

Un po’ di teoria

Come già detto in precedenza, i protocolli maggiormente utilizzati per le funzioni AAA sono RADIUS (anche nella variante più recente DIAMETER) e TACACS+.

Il primo è uno standard open, basato sul protocollo UDP, meno sicuro rispetto al TACACS+ (in quanto cifra solo ed esclusivamente il pacchetto che contiene le credenziali di autentica, lasciando il resto delle comunicazioni in plaintext) e che combina in un unico processo i meccanismi di autenticazione ed autorizzazione.

Il secondo, invece, è uno standard basato sul protocollo TCP (porta 49), proprietario (Cisco), più sicuro rispetto al RADIUS (cifra tutti i pacchetti) e che gestisce ciascuna funzione tipica dell’AAA in modo separato e distinto.

Dati i presupposti, la scelta del protocollo da utilizzare nel nostro ambiente ricade abbondantemente su TACACS+ (essendo inoltre di casa Cisco i dispositivi di rete che si intende gestire).

Installazione e configurazione del server AAA

Come si può banalmente evincere dal titolo, il server in questione è stato realizzato utilizzando una Linux box (CentOS 6) con a bordo il demone tac_plus.

Per prima cosa, occorre quindi installare il suddetto applicativo, aggiungendo il repository yum specifico (nux-misc.repo) alla repolist della nostra macchina:

[root@linuxbox ~]# cd /etc/yum.repos.d/
[root@linuxbox ~]# nano nux-misc.repo

il cui contenuto dovrà essere:

[nux-misc]
name=Nux Misc
baseurl=http://li.nux.ro/download/nux/misc/el6/x86_64/
enabled=0
gpgcheck=1
gpgkey=http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro

Fatto ciò possiamo procedere con l’installazione vera e propria:

[root@linuxbox ~]# yum --enablerepo=nux-misc install tac_plus

Una volta installato il pacchetto facciamo un backup della sua configurazione di default:

[root@linuxbox ~]# cp /etc/tac_plus.conf /etc/tac_plus.conf.bak

e focalizziamo la nostra attenzione sul file di configurazione che andremo a creare ex-novo:

[root@linuxbox ~]# nano /etc/tac_plus.conf

Il cui contenuto dovrà essere simile al seguente:

key = <passphrase tacacs+>

#ACL

acl = default   {
                permit = 192\.168\.0\.1
}

# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
# *                      ACCOUNTING                       *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 

accounting file = /var/log/tac_accounting.log

#

# * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# *                     AUTHENTICATION                    *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

user = Pippo {
        default service = permit
        login = des 43v/eMDQqTT2o
        member = NOC
}

user = Pluto {
        login = des 44LWBaN31RmGg
        member = NOC

        #AUTHORIZATION ENTRIES
        cmd = show {
                permit .*
        }
        cmd = exit {
                permit .*
        }
}

group = NOC {
        acl = default
}

# End config file

Analizziamo il suddetto file. Esistono fondamentalmente due macro sezioni, ovvero authentication ed accounting.

Nella prima ho specificato i 2 utenti (con relativa password) che possono avere accesso ai dispositivi di rete, mentre nella seconda ho indicato il file di log in cui “registrare” tutti gli eventi associati all’accounting.

Da notare che la password degli utenti è in formato DES (cifrata), ed è stata ottenuta utilizzando il tool tac_pwd nel modo seguente:

[root@linuxbox ~]# tac_pwd <salt>

Inoltre, all’interno della sezione authentication, ho inserito, per l’utente Pluto, alcune restrizioni sui comandi che possono essere lanciati. Nella fattispecie, egli potrà utilizzare solo ed esclusivamente i comandi show (il permit = .* indica tutto ciò che viene dopo la keyword show) ed exit. Per quanto riguarda l’utente Pippo, essendoci un deny implicito per il servizio di authorization (quindi tutti i comandi non espressamente dichiarati vengono automaticamente inibiti), ho dovuto aggiungere la direttiva default service = permit.

Da notare che ho inserito anche un’opportuna ACL per fare in modo che solo i dispositivi autorizzati possano dialogare col suddetto demone (ACL coadiuvata da regole di firewalling ad hoc, in modo da rispettare il più possibile il modello defense in depth).

A configurazione ultimata, possiamo avviare il demone:

[root@linuxbox ~]# service tac_plus start

e fare in modo che venga avviato automaticamente dopo ogni reboot:

[root@linuxbox ~]# chkconfig tac_plus on

Come ultimo step relativo alla configurazione del server AAA, sarà necessario creare un’opportuna regola netfilter (utilizzando iptables) per consentire il traffico TACACS+ in ingresso:

-A INPUT -m state --state NEW -m tcp -p tcp -s 192.168.0.1/32 --dport 49 -j ACCEPT -m comment --comment "router"

 Configurazione dei dispositivi di rete

Per semplificare un po’ le cose utilizzerò come modello di riferimento la configurazione di un router 2811, anche se essa dovrebbe essere comunque molto simile (se non identica) per la stragrande maggioranza dei dispositivi marchiati Cisco.

Per prima cosa abilitiamo le funzionalità AAA:

Router(config)# aaa new-model

per poi soffermarci sull’autentica:

Router(config)# aaa authentication login default group tacacs+ local

e sulla definizione del server TACACS+ con relativa chiave:

Router(config)# tacacs-server host <ip>
Router(config)# tacacs-server key 0 <pass>

Occorre precisare che l’autenticazione è stata configurata in modo da prevedere un meccanismo di fallback, ovvero nel caso in cui il server AAA non fosse più raggiungibile, sarà comunque possibile loggarsi sul dispositivo utilizzando gli account utente definiti localmente.

Inoltre, nel caso in cui il suddetto server fosse raggiungibile dagli uffici periferici mediante dei tunnel VPN dedicati, occorrerà specificare nella loro configurazione l’interfaccia dalla quale esso puotrà essere contattato:

Router(config)# ip tacacs source-interface <int>

Successivamente possiamo procedere con la configurazione dell’authorization:

aaa authorization exec default group tacacs+ local
aaa authorization commands 1 default group tacacs+ local
aaa authorization commands 15 default group tacacs+ local

e dell’accounting:

Router(config)# aaa accounting system default start-stop group tacacs+
Router(config)# aaa accounting network default start-stop group tacacs+
Router(config)# aaa accounting exec default start-stop group tacacs+
Router(config)# aaa accounting commands 0 default start-stop group tacacs+
Router(config)# aaa accounting commands 15 default start-stop group tacacs+
Router(config)# aaa accounting session-duration ntp-adjusted

Prima di salvare la configurazione, testiamo il corretto funzionamento del meccanismo di autentica, digitando:

Router# test aaa group tacacs+ <user> <pass> legacy

il cui output dovrebbe essere:

Attempting authentication test to server-group tacacs+ using tacacs+
User was successfully authenticated.

Adesso si può finalmente procedere con il salvataggio della configurazione:

Router# copy run start

ed abbiamo finito.

Alla prossima.

CentOS 6 ed rsyslog: creare un sistema di log centralizzato per i dispositivi di rete

Scenario

Diversi uffici periferici (in gergo branch office), connessi all’ufficio centrale (main office) mediante dei tunnel IPSec site-to-site dedicati (classici link VPN utilizzati per creare una intranet con topologia a stella).

Problema

Creare un sistema di log centralizzato per tutti i dispositivi di rete, compresi i router degli uffici periferici.

Soluzione

Utilizzare una Linux box (CentOS 6) con a bordo il demone che funge da syslog server, ovvero rsyslog.

syslog

Configurazione della Linux box e del syslog server

Per prima cosa occorre fare in modo che la nostra Linux box sia in grado di ricevere correttamente (sulla porta UDP 514) i log inoltrati dai dispositivi di rete. Per fare ciò è sufficiente creare la seguente regola di netfilter (ultilizzando iptables):

-A INPUT -m state --state NEW -m udp -p udp -s 192.168.0.0/16 --dport 514 -j ACCEPT

ed aggiungerla all’interno del file /etc/sysconfig/iptables, per poi lanciare un:

[root@linuxbox ~]# service iptables restart

in modo da rendere la suddetta regola operativa (da notare che 192.168.0.0/16 è la subnet classe B che raggruppa tutte le /24 utilizzate dai branch office).

Una volta fatto ciò è necessario aggiungere la seguente direttiva all’interno del file di configurazione rsyslog, ovvero /etc/rsyslog.conf:

$template CiscoVPN,"/var/log/cisco/system-%HOSTNAME%.log"

*.* -?CiscoVPN

e creare la dir di target in cui verranno salvati i log, ovvero /var/log/cisco:

[root@linuxbox ~]# mkdir -p /var/log/cisco

A questo punto possiamo riavviare rsyslog in modo da rendere effettive le suddette modifiche:

[root@linuxbox ~]# service rsyslog restart

Configurazione dei dispositivi di rete

Per semplicità mi soffermerò solo ed esclusivamente nella configurazione dei router (Cisco) dei branch office. Tale operazione consiste in 2 fasi: la definizione di una sorgente SNTP affidabile ed identica per tutti i network device (in modo da poter effettuare un’eventuale correlazione tra gli eventi) e la configurazione del syslog server target.

Per configurare la sorgente SNTP è sufficiente lanciare il comando:

Router(config)# sntp server <IP del server SNTP>

Ad esempio, se volessimo utilizzare come server SNTP ntp1.inrim.it, dovremmo digitare:

Router(config)# sntp server 193.204.114.232

Per quanto riguarda la configurazione del target dei log, è sufficiente lanciare i seguenti comandi:

Router(config)# service timestamps log
Router(config)# logging source-interface Vlan1
Router(config)# logging <IP del syslog server>

Il primo comando serve a fare in modo che il timestamp dell’evento venga aggiunto automaticamente alla entry del log; il secondo comando specifica l’interfaccia dalla quale i log devono essere inviati (essendo in VPN, l’interfaccia di riferimento è quella della LAN, in questo caso la Vlan 1);  l’ultimo comando specifica molto banalmente l’indirizzo IP del syslog server.

Infine, controlliamo che i log vengano popolati in real time, lanciando il comando:

[root@linuxbox ~] #tail -f /var/log/system-<hostname>.log

dove <hostname> è l’hostname del dispositivo di rete di cui volete consultare il file di log.

Se tutto funziona a dovere possiamo dire di aver finalmente realizzato il nostro sistema di log centralizzato.

A presto.

Configurare il protocollo SNMP su un router Cisco 2811

In questo post abbiamo visto come configurare una linux box affinchè possa ricevere le trap SNMP. Adesso vedremo come configurare il portocollo SNMP (sia polling che trap) su un router Cisco 2811.

cisco2811Configurazione generica

Per prima cosa occorre accedere alla CLI del router in modalità ena e successivamente lanciare i seguenti comandi:

Router# conf t
Router(config)# snmp-server host <IP> keypublic
Router(config)# snmp-server community keypublic RO
Router(config)# snmp-server community keyprivate RW

Mediante il primo comando stiamo definento l’host target delle trap SNMP, mentre con il secondo ed il terzo comando specifichiamo rispettivamente la community string in lettura e quella in scrittura.

Abilitazione delle trap

A questo punto possiamo definire le trap che dovranno essere inviate alla nostra linux box, ad esempio:

Router(config)# snmp-server enable traps snmp
Router(config)# snmp-server enable traps vrrp
Router(config)# snmp-server enable traps hsrp
Router(config)# snmp-server enable traps tty
Router(config)# snmp-server enable traps ethernet cfm cc
Router(config)# snmp-server enable traps ethernet cfm crosscheck
Router(config)# snmp-server enable traps memory
Router(config)# snmp-server enable traps config-copy
Router(config)# snmp-server enable traps config
Router(config)# snmp-server enable traps resource-policy
Router(config)# snmp-server enable traps pppoe
Router(config)# snmp-server enable traps cpu
Router(config)# snmp-server enable traps syslog
Router(config)# snmp-server enable traps isakmp policy add
Router(config)# snmp-server enable traps isakmp policy delete
Router(config)# snmp-server enable traps isakmp tunnel start
Router(config)# snmp-server enable traps isakmp tunnel stop
Router(config)# snmp-server enable traps ipsec cryptomap add
Router(config)# snmp-server enable traps ipsec cryptomap delete
Router(config)# snmp-server enable traps ipsec cryptomap attach
Router(config)# snmp-server enable traps ipsec cryptomap detach
Router(config)# snmp-server enable traps ipsec tunnel start
Router(config)# snmp-server enable traps ipsec tunnel stop
Router(config)# snmp-server enable traps ipsec too-many-sas

Inutile dire che la tipologia di trap da abilitare dipende strettamente dalla configurazione del router, dalle feature abilitate (VPN IPsec), dal tipo di connessione WAN (PPP), ecc.

Polling SNMP tramite Nagios

Premesso che gli OID SNMP possono variare in base alla versione di IOS installata sul nostro router (a tal proposito vi consiglio di consultare questo sito), per prima cosa occorre definire i comandi specifici (all’interno dei file /etc/nagios/object/commands.cfg) che dovranno essere utilizzati dal nostro NMS per interrogare il dispositivo target.

Eccone un esempio:

# 'check_snmp_if_status' command definition
define command{
        command_name    check_snmp_if_status
        command_line    $USER1$/check_snmp -t 20 -H $HOSTADDRESS$ -C $ARG1$ -o $ARG2$ -r $ARG3$
        }
# 'check_snmp_cpu_load' command definition

define command{
        command_name    check_snmp_cpu_load
        command_line    $USER1$/check_snmp -t 20 -H $HOSTADDRESS$ -C $ARG1$ -o $ARG2$ -w $ARG3$ -c $ARG4$
        }

# 'check_snmp_used_memory' command definition

define command{
        command_name    check_snmp_used_memory
        command_line    $USER1$/check_snmp -t 20 -H $HOSTADDRESS$ -C $ARG1$ -o $ARG2$ -w $ARG3$ -c $ARG4$
        }

# 'check_snmp_uptime' command definition

define command{
        command_name    check_snmp_uptime
        command_line    $USER1$/check_snmp -t 20 -H $HOSTADDRESS$ -C $ARG1$ -o $ARG2$ -w $ARG3$ -c $ARG4$
        }

Infine, definiamo i servizi che si avvarranno dei suddetti comandi:

define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_description             Uptime
 check_command                   check_snmp_uptime!keypublic!.1.3.6.1.6.3.10.2.1.3.0!@61:900!@0:60
 }
define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_description             Last Minute CPU Load
 check_command                   check_snmp_cpu_load!keypublic!.1.3.6.1.4.1.9.2.1.57.0!80!90
 }
define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_description             Last 5 Minutes CPU Load
 check_command                   check_snmp_cpu_load!keypublic!.1.3.6.1.4.1.9.2.1.58.0!80!90
 }
define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_description             Used Memory
 check_command                   check_snmp_used_memory!keypublic!1.3.6.1.4.1.9.9.48.1.1.1.5.1!100000000!120000000
 }
define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_description             Status Interface WAN
 check_command                   check_snmp_if_status!keypublic!.1.3.6.1.2.1.2.2.1.8.1!1
 }
define service{
 use                             local-service         ; Name of service template to use
 host_name                       cisco
 service_description             Status Interface LAN
 check_command                   check_snmp_if_status!keypublic!.1.3.6.1.2.1.2.2.1.8.2!1
 }

A questo punto sarà sufficiente ricaricare la configurazione di Nagios mediante il comando:

[root@linuxbox ~]# service nagios reload

ed abbiamo finito.

Alla prossima.

Firewall Cisco ASA ed attacchi DDoS/DoS

Ultimamente mi è capitato di dover far fronte a tutta una serie di attacchi DDoS, per lo più basati sul protocollo HTTP (connessioni half-open/embryonic, in cui manca il l’ACK finale poichè l’indirizzo IP sorgente del SYN è falso o soggetto a spoofing), mitigati senza troppe difficoltà da un cluster di Firewall Cisco ASA.

ddosLa configurazione che ho utilizzato per mitigare tale tipologia di attacchi è simile alla seguente:

ASA(config)# class-map mpf-policy
ASA(config-cmap)# match access-list outside_access_in
ASA(config-cmap)# exit
ASA(config)# policy-map mpf-policy-map
ASA(config-pmap)# class mpf-policy
ASA(config-pmap-c)# set connection conn-max 11500
ASA(config-pmap-c)# set connection embryonic-conn-max 3000
ASA(config-pmap-c)# set connection per-client-embryonic-max 200
ASA(config-pmap-c)# set connection per-client-max 95
ASA(config-pmap-c)# exit
ASA(config-pmap)# exit
ASA(config)# service-policy mpf-policy-map interface outside
ASA(config)# exit

Tutto è filato liscio per un po’ di tempo, fino a quando l’attaccante non ha deciso di cambiare strategia, passando da un attacco DDoS ad uno DoS (dunque con un solo IP sorgente) e dal protocollo TCP a quello UDP (molto più adatto a questa tipologia di attacco poichè connection-less).

La VPS utilizzata come sorgente di traffico, dotata di ampia banda (si trovava in una farm tedesca) è stata in grado, in un lasso di tempo brevissimo, di far collassare i firewall. Una volta identificato il suo IP ho creato un’ACL del tipo:

ASA(config)#deny ip host <IP attaccante> any

che però si è rivelata essere un’arma a doppio taglio visto che il firewall si è visto costretto a “maneggiare” e “bloccare” una mole di traffico tale per cui la sua CPU ha raggiunto livelli di carico assurdi (la dashboard di ASDM indicava un picco di oltre il 300% a fronte di un rate di pacchetti droppati superiore ai 100k  – vedi immagine sottostante).

dropped_ratedropped_rate

Fondamentalmente ciò è dovuto al fatto che ciascun modello di ASA può gestire solo una determinata mole di traffico (sessioni concorrenti, nuove connessioni/secondo, pacchetti/secondo, ecc – per ulteriori dettagli vedi qui), limite imposto soprattutto dalle caratteristiche hardware di cui è dotato (CPU, RAM, ecc.). Inoltre, per ciascun pacchetto scartato, è necessario un certo overhead, ragion per cui la suddetta ACL ha prodotto effetti collaterali che hanno portato ad un peggioramento della situazione.

Dopo qualche minuto di smarrimento, ho deciso di modificare la mia strategia (sulla falsariga di quella dell’attaccante), consentendo sul firewall tutto il traffico in ingresso dall’IP della VPS incriminata, demandando ai server la sua gestione. Nella fattispecie, ho configurato dei blackhole (aka null route) a livello 3, mediante il seguente comando:

route add -net <IP attaccante> gw 127.0.0.1 lo

Ho scelto questa strategia per 2 motivi:

1) I server sono dotati di maggiori risorse hardware rispetto ai firewall, quindi più “propensi” a gestire una tale mole di traffico in ingresso;

2) Lavorare a livello 3 anzichè a livello 4 implica un minor overhead.

Alla fine tale strategia si è rivelata vincente e l’attaccante ha deposto le armi.

E’ tutto, alla prossima.