Archivi categoria: SO: Linux

CentOS 6: kernel tuning volto al system hardening

Qualunque amministratore di sistema sa bene che 2 regole di firewalling in croce ed una password sufficientemente complessa non sono in grado di garantire un livello di sicurezza accettabile. Occorre, infatti, avere ben presente quali sono i pericoli a cui si dovrà far fronte mettendo un dato sistema in produzione, rischi che possono essere mitigati (a volte anche molto efficacemente) utilizzando qualche applicativo creato ad hoc (come, ad esempio, SELinux) e monitorando il sistema 24/7, sia a livello di risorse che a livello di operazioni sospette su determinati file o directory (in quest’ultimo caso parliamo di auditing).

Detto ciò, è altrettanto importante sapere che il kernel linux prevede alcuni meccanismi in grado di aumentare notevolmente la sicurezza del sistema operativo stesso, mettendolo al riparo da attacchi di tipo overflow, ip spoofing, ecc.

kernel

Tali meccanismi possono essere abilitati in modo permanente editando file /etc/sysctl.conf (in tal caso per rendere i settaggi operativi sarà necessario utilizzare il comando sysctl -p oppure effettuare un reboot), o, in alternativa, operando mediante /proc (rendendo subito effettive le modifiche ma non permanenti).

Per quanto mi riguarda, di solito opero in questo modo:

1) modifico i parametri di interesse mediante /proc (uno per uno, in modo da essere sicuro che , dopo ogni step, il sistema continua a funzionare come dovrebbe);

2) modifico il file sysctl.conf popolandolo con le direttive necessarie.

Ecco le modifiche da apportare al kernel on-the-fly:

#Misure anti overflow

echo 1 > /proc/sys/kernel/exec-shield
echo 2 > /proc/sys/kernel/randomize_va_space

#Misure anti ip spoofing e source route

echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 1 > /proc/sys/net/ipv4/conf/all/log_martians
echo 1 > /proc/sys/net/ipv4/conf/default/log_martians
echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
echo 0 > /proc/sys/net/ipv4/conf/default/accept_source_route

#Misure anti ICMP redirect

echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects
echo 0 > /proc/sys/net/ipv4/conf/default/accept_redirects
echo 0 > /proc/sys/net/ipv4/conf/all/secure_redirects
echo 0 > /proc/sys/net/ipv4/conf/default/secure_redirects

#Misure anti DOS/DDOS basati su TCP (SYN FLOOD)

echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 2 > /proc/sys/net/ipv4/tcp_synack_retries

#Misure anti smurf

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

Nella fattispecie,  le misure anti overlflow hanno come obiettivo quello di impedire l’esecuzuone di shellcode su aree di memoria marcate come “non eseguibili” (exec-shield), facendo anche in modo che tutti i riferimenti a particolari funzioni presenti in memoria non siano accessibili facilmente da un utente malevolo, randomizzandone gli indirizzi di memoria (randomize_va_space)

Tra le misure anti ip spoofing e source route sono elencati 12 parametri, ovvero:

echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter

che abilitano il return path filter, ovvero se l’IP sorgente di un dato pacchetto ricevuto su una specifica interfaccia non è raggiungibile mediante quest’ultima, il suddetto pacchetto verrà scartato;

echo 1 > /proc/sys/net/ipv4/conf/all/log_martians
echo 1 > /proc/sys/net/ipv4/conf/default/log_martians

che abilitano il logging dei pacchetti spoofati;

echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
echo 0 > /proc/sys/net/ipv4/conf/default/accept_source_route

il cui compito è quello di disabilitare il cosiddetto source route (ovvero “instradare” il pacchetto appena ricevuto lungo il “cammino” di rete ben preciso – comprensivo di hop – specificato al suo interno);

echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects 
echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects

che disabilitano l’invio dei pacchetti ICMP redirect;

echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects 
echo 0 > /proc/sys/net/ipv4/conf/default/accept_redirects

che disabilitano la ricezione degli ICMP redirect;

echo 0 > /proc/sys/net/ipv4/conf/all/secure_redirects 
echo 0 > /proc/sys/net/ipv4/conf/default/secure_redirects

affinchè anche gli ICMP redirect provenienti dal default gateway (e quindi teoricamente “sicuri”) vengano scartati.

Per quanto riguarda, invece, le misure anti attacchi DOS/DDOS basati sul procotollo TCP, ho abilitato i cosiddetti syncookies e limitato l’invio dei SYN/ACK a 2 soli tentativi (in modo da mitigare i SYN FLOOD):

echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 2 > /proc/sys/net/ipv4/tcp_synack_retries

Infine, nelle misure anti smurf ho semplicemente disabilitato la ricezione dei broadcast ICMP:

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

Per quanto riguarda, invece, il file /etc/sysctl.conf, le direttive da inserire sono le seguenti:

#Enable Exec-Shield
kernel.exec-shield = 1

# Enable ASLR
kernel.randomize_va_space = 1

# Controls source route verification
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Controls the use of TCP syncookies
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2

# Do not accept source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Do not accept or send ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

# Log spoofed packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1

#Ignore ICMP broadcasts
net.ipv4.icmp_echo_ignore_broadcasts = 1

Fine del kernel tuning. Alla prossima.

yum-cron: verificare automaticamente la disponibilità degli aggiornamenti su CentOS 6

In questo post ho reso disponibile uno scrip bash (scritto di mio pugno) in grado di verificare (tramite yum) la disponibilità di security fix, bug fix e CVE.

Adesso vedremo come estendere tale funzionalità a qualsiasi tipo di update, usufruendo di un pacchetto software creato appositamente per CentOS 6, ovvero yum-cron.

yum

Per prima cosa installiamo il suddetto applicativo:

[root@linuxbox ~]# yum install yum-cron

Ad installazione completa modifichiamo il file di configurazione /etc/sysconfig/yum-cron secondo le nostre esigenze. In particolare, vogliamo fare in modo che ci venga notificata tramite email la disponibilità di eventuali update, senza installare nulla automaticamente:

# Don't install, just check (valid: yes|no)
CHECK_ONLY=yes

# Check to see if you can reach the repos before updating (valid: yes|no)
CHECK_FIRST=yes

MAILTO=vostro.indirizzo@email.it

A questo punto non ci rimane che avviare il servizio yum-cron:

[root@linuxbox ~]# service yum-cron start

e fare in modo che venga eseguito in automatico dopo ogni reboot della nostra macchina:

[root@linuxbox ~]# chkconfig yum-cron on

E’ tutto. Alla prossima.

checksmart: script bash per effettuare le query S.M.A.R.T.

Gli hard disk di ultima generazione (ATA e SCSI) supportano una funzionalità molto interessante, denominata S.M.A.R.T. (Self-Monitoring, Analysis and Reporting Technology)

Il significato del suddetto acronimo è già di per sé chiaro. Infatti, tale tecnologia può effettuare alcuni test di auto diagnostica rivolti alle componenti elettromeccaniche del disco (o solo elettriche nel caso in cui si avesse a che fare con gli SSD). I risultati di tali test potranno quindi essere visualizzati dall’utente con un grado di dettaglio variabile a seconda delle sue esigenze, il quale potrà anche consultare lo storico degli ultimi test effettuati ed il log degli errori individuati durante le ore di normale esercizio del disco.

harddisk

Di tool in grado di interagire con la suddetta funzionalità ne esistono a bizzeffe e per tutti i sistemi operativi (vedi qui per ulteriori dettagli). Tra questi merita sicuramente una particolare menzione smartmontools, soprattutto se si vogliono effettuare le query S.M.A.R.T. in ambienti Unix like. Utilizzando proprio il suddetto tool ho deciso di creare uno scrip bash (checksmart) da richiamare mediante crontab in modo da schedulare dei check più o meno approfonditi ad intervalli di tempo regolari. I risultati ottenuti potranno quindi essere inviati alla casella email dell’utente, utilizzando la flag opzionale –email.

Ecco lo scrip in todo:

#!/bin/bash

bin=`/usr/bin/which smartctl`

logfile=/var/log/checksmart/checksmart.log

ROOT_UID=0

if [ "$UID" -ne "$ROOT_UID" ];then

        data=`date +'%F %H:%M:%S'`
        echo "$data - Error: you need to be root to run this scrip"
        exit 1

fi

if [[ ! -d /var/log/checksmart ]];then

        mkdir -p /var/log/checksmart
fi

if [[ ! -f /var/log/checksmart/checksmart.log ]];then

        touch /var/log/checksmart/checksmart.log
fi

diskno=`df -h | awk '{print $1}' | grep sd | wc -l`

type=$1

subtype=$2

address=$3

if [[ ! -z $address ]];then

        regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"

        checkmail() {
                if [[ ! $address =~ $regex ]];then
                        data=`date +'%F %H:%M:%S'`
                        echo "$data - Error: invalid email address" | tee -a $logfile
                        exit 1;
                fi
        }

        checkmail;
fi

pre_running_controls() {

if [[ ! -z $bin ]];then
        if [[ ! -z $diskno ]];then
                data=`date +'%F %H:%M:%S'`
                echo "$data - Executing pre-running controls..." | tee -a $logfile
                sleep 2
                data=`date +'%F %H:%M:%S'`
                echo "$data - Checking for S.M.A.R.T. capabilities..." | tee -a $logfile
                for (( d=1; d<=$diskno; d++ ))
                do
                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                        smart_capable=`$bin -a $disk | grep "SMART support is: Available"`
                        data=`date +'%F %H:%M:%S'`
                        echo "$data - 1. Checking if S.M.A.R.T. support for disk $disk is available" | tee -a $logfile
                        if [[ ! -z $smart_capable ]];then
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - S.M.A.R.T. support for $disk is available" | tee -a $logfile
                        else
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - Error: S.M.A.R.T. support for $disk is NOT available, exiting..." | tee -a $logfile
                                exit 1;
                        fi

                        smart_enabled=`$bin -a $disk | grep "SMART support is: Enabled"`
                        data=`date +'%F %H:%M:%S'`
                        echo "$data - 2. Checking if S.M.A.R.T. support for disk $disk is enabled" | tee -a $logfile
                        if [[ ! -z $smart_enabled ]];then
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - S.M.A.R.T. support for $disk is enabled" | tee -a $logfile
                        else
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - Error: S.M.A.R.T. support for $disk disk is NOT enabled, trying to enable it..." >> $logfile
                                smart_enabled=`$bin -s on $disk |  grep "SMART support is: Enabled"`
                                if [[ ! -z $smart_enabled ]];then
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - S.M.A.R.T. support for $disk disk is enabled NOW enabled" | tee -a $logfile
                                else
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - Error: Unable to turn on S.M.A.R.T. support for disk $disk, exiting ..." | tee -a $logfile
                                        exit 1;
                                fi
                        fi
                        smart_self=`$bin -a $disk | grep "No self-tests have been logged"`
                        data=`date +'%F %H:%M:%S'`
                        echo "$data - 3. Checking if S.M.A.R.T. self-tests have been logged" | tee -a $logfile
                        if [[ ! -z $smart_self ]];then
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - No S.M.A.R.T. self-tests have been logged yet, trying to run a short one..." | tee -a $logfile
                                smart_self=`$bin -t short $disk | grep "No self-tests have been logged"`
                                if [[ ! -z $smart_self ]];then
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - Error: Unable to run a short self-test for disk $disk, exiting..." | tee -a $logfile
                                        exit 1;
                                else
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - S.M.A.R.T. self-tests have been logged" | tee -a $logfile
                                fi
                        else
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - S.M.A.R.T. support for $disk disk is enabled" | tee -a $logfile
                        fi
                done
        else
                data=`date +'%F %H:%M:%S'`
                echo "$data - Error: no ATA disks found" | tee -a $logfile
                exit 1;
        fi
else
        data=`date +'%F %H:%M:%S'`
        echo "$data - Error: no smartctl binary found - please install smarmontools" | tee -a $logfile
        exit 1;
fi
}

get_time_required() {

if [[ ! -z $bin ]];then
        if [[ ! -z $diskno ]];then
                for (( d=1; d<=$diskno; d++ ))
                do
                        data=`date +'%F %H:%M:%S'`
                        echo "$data - Getting time required by each test for each disk" | tee -a $logfile
                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                        time_short=`$bin -c $disk | grep -A 1 'Short' | grep minutes | awk '{print $5}' | sed -e 's/)//g'`
                        if [[ -z $time_short ]];then
                                echo "$data - Error: unable to get the time required by the short test for disk $disk" | tee -a $logfile
                        else
                                time_short_seconds=$((time_short*60 + 2))
                        fi
                        time_long=`$bin -c $disk | grep -A 1 'Extended' | grep minutes | awk '{print $5}' | sed -e 's/)//g'`
                        if [[ -z $time_long ]];then
                                echo "$data - Error: unable to get the time required by the long test for disk $disk" | tee -a $logfile
                        else
                                time_long_seconds=$((time_long * 60 + 2))
                        fi
                        time_conveyance=`$bin -c $disk | grep -A 1 'Conveyance' | grep minutes | awk '{print $5}' | sed -e 's/)//g'`
                        if [[ -z $time_conveyance ]];then
                                echo "$data - Error: unable to get the time required by the conveyance test for disk $disk" | tee -a $logfile
                        else
                                time_conveyance_seconds=$((time_conveyance * 60 + 2))
                        fi

                done
        else

                data=`date +'%F %H:%M:%S'`
                echo "$data - Error: no ATA disks found" | tee -a $logfile
                exit 1;

        fi
else

        data=`date +'%F %H:%M:%S'`
        echo "$data - Error: no smartctl binary found - please install smarmontools" | tee -a $logfile
        exit 1;

fi
}

get_time_required;

if [[ ! -z $bin ]];then
        if [[ ! -z $diskno ]];then
                pre_running_controls;
                if [[ ! -z $type ]];then
                        if [[ $type == "--brief" ]];then
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - checking S.M.A.R.T entities for $disk disk" | tee -a $logfile
                                        if [[ $subtype == "--email" ]];then
                                                if [[ ! -z $address ]];then
                                                        data=`date +'%F %H:%M:%S'`
                                                        echo $data >> $logfile
                                                        $bin -a $disk | grep -E "SMART overall-health self-assessment test result:|Reallocated_Sector|Spin_Retry_Count|Runtime_Bad_Block|End-to-End_Error|Reported_Uncorrect|Command_Timeout|Current_Pending_Sector|Offline_Uncorrectable" >> result
                                                        cat result | mail -iv -s "brief S.M.A.R.T report for disk $disk" $address
                                                        cat result >> $logfile
                                                        rm -rf result
                                                else
                                                        echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary | --help>  [ --email <address>]" | tee -a $logfile
                                                        exit 1;
                                                fi
                                         elif [[ ! $subtype =~ "--email" ]] && [[ ! -z $subtype ]];then
                                                data=`date +'%F %H:%M:%S'`
                                                echo "$data - Error: unknown subtype $subtype" | tee -a $logfile
                                                echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary | --help> [ --email <address>]" | tee -a $logfile
                                                exit 1;
                                        else
                                                $bin -a $disk | grep -E "SMART overall-health self-assessment test result:|Reallocated_Sector|Spin_Retry_Count|Runtime_Bad_Block|End-to-End_Error|Reported_Uncorrect|Command_Timeout|Current_Pending_Sector|Offline_Uncorrectable" | tee -a $logfile
                                        fi
                                done
                                exit 0;
                        elif [[ $type == "--short" ]];then
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - checking S.M.A.R.T entities for $disk disk" | tee -a $logfile
                                        if [[ $subtype == "--email" ]];then
                                                if [[ ! -z $address ]];then
                                                        data=`date +'%F %H:%M:%S'`
                                                        echo $data >> $logfile
                                                        $bin -t short $disk
                                                        if [[ ! -z $time_short_seconds ]];then
                                                                sleep $time_short_seconds
                                                        else
                                                                sleep 120
                                                        fi
                                                        $bin -a $disk >> result
                                                        cat result | mail -iv -s "short test S.M.A.R.T report for disk $disk" $address
                                                        cat result >> $logfile
                                                        rm -rf result
                                                else
                                                        echo "$data - Error: wrong email address format" | tee -a $logfile
                                                        echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help> [ --email <address>]" | tee -a $logfile
                                                        exit 1;
                                                fi
                                        elif [[ ! $subtype =~ "--email" ]] && [[ ! -z $subtype ]];then
                                                data=`date +'%F %H:%M:%S'`
                                                echo "$data - Error: unknown subtype $subtype" | tee -a $logfile
                                                echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help> [ --email <address>]" | tee -a $logfile
                                                exit 1;
                                        else
                                                $bin -t short $disk | tee -a $logfile
                                                if [[ ! -z $time_short_seconds ]];then
                                                        sleep $time_short_seconds
                                                else
                                                        sleep 120
                                                fi
                                                $bin -a $disk | tee -a $logfile
                                        fi
                                done
                                exit 0;
                         elif [[ $type == "--long" ]];then
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data - checking S.M.A.R.T entities for $disk disk" | tee -a $logfile
                                        if [[ $subtype == "--email" ]];then
                                                if [[ ! -z $address ]];then
                                                        data=`date +'%F %H:%M:%S'`
                                                        echo $data >> $logfile
                                                        $bin -t long $disk
                                                        if [[ ! -z $time_long_seconds ]];then
                                                                sleep $time_long_seconds
                                                        else
                                                                sleep 3600
                                                        fi
                                                        $bin -a $disk >> result
                                                        cat result | mail -iv -s "long test S.M.A.R.T report for disk $disk" $address
                                                        cat result >> $logfile
                                                        rm -rf result
                                                else
                                                        echo "$data - Error: wrong email address format" | tee -a $logfile
                                                        echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help>  [ --email <address>]" | tee -a $logfile
                                                        exit 1;
                                                fi
                                        elif [[ ! $subtype =~ "--email" ]] && [[ ! -z $subtype ]];then
                                                data=`date +'%F %H:%M:%S'`
                                                echo "$data - Error: unknown subtype $subtype" | tee -a $logfile
                                                echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help> [ --email <address>]" | tee -a $logfile
                                                exit 1;
                                        else
                                                $bin -t long $disk | tee -a $logfile
                                                if [[ ! -z $time_long_seconds ]];then
                                                        sleep $time_long_seconds
                                                else
                                                        sleep 3600
                                                fi
                                                $bin -a $disk | tee -a $logfile
                                        fi
                                done
                                exit 0;
                        elif [[ $type == "--conveyance" ]];then
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data: checking S.M.A.R.T entities for $disk disk" | tee -a $logfile
                                        if [[ $subtype == "--email" ]];then
                                                if [[ ! -z $address ]];then
                                                        data=`date +'%F %H:%M:%S'`
                                                        echo $data >> $logfile
                                                        $bin -t conveyance $disk
                                                        if [[ ! -z $time_conveyance_seconds ]];then
                                                                sleep $time_conveyance_seconds
                                                        else
                                                                sleep 240
                                                        fi
                                                        $bin -a $disk >> result
                                                        cat result | mail -iv -s "conveyance test S.M.A.R.T report for disk $disk" $address
                                                        cat result >> $logfile
                                                        rm -rf result
                                                else
                                                        echo "$data - Error: wrong email address format" | tee -a $logfile
                                                        echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help> [ --email <address>]" | tee -a $logfile
                                                        exit 1;
                                                fi
                                        elif [[ ! $subtype =~ "--email" ]] && [[ ! -z $subtype ]];then
                                                data=`date +'%F %H:%M:%S'`
                                                echo "$data - Error: unknown subtype $subtype" | tee -a $logfile
                                                echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help> [ --email <address>]" | tee -a $logfile
                                                exit 1;
                                        else
                                                $bin -t conveyance $disk | tee -a $logfile
                                                if [[ ! -z $time_conveyance_seconds ]];then
                                                        sleep $time_conveyance_seconds
                                                else
                                                        sleep 240
                                                fi
                                                $bin -a $disk | tee -a $logfile
                                        fi
                                done
                                exit 0;
                        elif [[ $type == "--summary" ]];then
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        disk=`df -h | awk '{print $1}' | grep sd | sed s'/.$//' | sed -n "$d p" | uniq`
                                        data=`date +'%F %H:%M:%S'`
                                        echo "$data: checking S.M.A.R.T entities for $disk disk" | tee -a $logfile
                                        if [[ $subtype == "--email" ]];then
                                                if [[ ! -z $address ]];then
                                                        data=`date +'%F %H:%M:%S'`
                                                        echo $data >> $logfile
                                                        $bin -l selftest $disk >> result
                                                        cat result | mail -iv -s "summary test S.M.A.R.T. report for disk $disk" $address
                                                        cat result >> $logfile
                                                        rm -rf result
                                                else
                                                        echo "$data - Error: wrong email address format" | tee -a $logfile
                                                        echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary | --help> [ --email <address>]"
                                                        exit 1;
                                                fi
                                        elif [[ ! $subtype =~ "--email" ]] && [[ ! -z $subtype ]];then
                                                data=`date +'%F %H:%M:%S'`
                                                echo "$data - Error: unknown subtype $subtype" | tee -a $logfile
                                                echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary | --help> [ --email <address>]"
                                                exit 1;
                                        else
                                                $bin -l selftest $disk | tee -a $logfile
                                        fi
                                done
                                exit 0;
                        elif [[ $type == "--help" ]];then
                                echo "Usage: checksmart < --brief| --short| --long| --conveyance| --summary| --help> [ --email <address>]"
                                echo "       --brief will display only critical pre-failure S.M.A.R.T. indicators"
                                echo "       --short will run a short S.M.A.R.T. test"
                                echo "       --long will run a long S.M.A.R.T. test"
                                echo "       --conveyance will run a conveyance S.M.A.R.T. test"
                                echo "       --summary will display only the self test history report"
                                echo "       --help will display this help"
                                echo "       --email <address> will send the S.M.A.R.T. report via email to the specified address"
                                echo "Example: checksmart --brief --email youraddress@yourdomain.com"
                                exit 0;
                        else
                                data=`date +'%F %H:%M:%S'`
                                echo "$data - Error: unknown type $type" | tee -a $logfile
                                echo "Usage: checksmart < --brief| --short| --long| --conveyance|  --summary| --help> [ --email <address>]"
                                exit 0;
                        fi
                else
                        data=`date +'%F %H:%M:%S'`
                        echo "$data - Error: type not defined" | tee -a $logfile
                        echo "Usage: checksmart < --brief| --short| --long| --conveyance|  --summary| --help | [ --email <address>]"
                        exit 0;
                fi
        else
                data=`date +'%F %H:%M:%S'`
                echo "$data - Error: no ATA disks found" | tee -a $logfile
                exit 1;
        fi
else
        data=`date +'%F %H:%M:%S'`
        echo "$data - Error: no smartctl binary found - please install smarmontools" | tee -a $logfile
        exit 1;
fi

Per prima cosa verifico che i dischi supportino la funzionalità S.M.A.R.T, che sia abilitata e che siano stati eseguiti in precedenza alcuni test di autodiagnostica. L’insieme delle 3 condizioni appena enunciate costituisce i pre-running controls (contenuti all’interno dell’omonima funzione).

Sucessivamente (mediante la funzione get_time_required()) ricavo i tempi richiesti per l’esecuzione di ciascun test (short, long e conveyance), da dare in pasto al comando sleep, così da consentire agli smartmontools di elaborarne i risultati ed al mio scrip di restituirli all’utente.

Completati i pre-running controls ed individuati i tempi di esecuzione, vengono lanciati i test veri e propri. Nello specifico, lo scrip supporta 5 modalità di funzionamento, ovvero:

1) –brief; viene restituito all’utente solo l’overall status ed i valori associati alle voci che potrebbero indicare un guasto imminente del disco (per ulteriori dettagli potete consultare questa pagina), quali Read Error Rate, Reallocated Sectors Count, Spin Retry Count, Runtime Bad Block, End to End Error, Reported Uncorrect, Command Timeout, Current Pending Sector, Offline Uncorrectable. A tal proposito occorre fare una precisazione: poichè la tecnologia S.M.A.R.T. non è uno standard, le suddette voci potrebbero non essere presenti nei risultati restituiti dallo scrip, ergo, in questo caso, potrete modificarlo in funzione delle voci supportate.

2) –short; esegue uno short test.

3) –long; esegue un long test (molto più approfondito).

4) –conveyance; interroga il disco sugli eventuali danni che si sono verificati durante le operazioni di trasporto dello stesso. Non tutti i dischi supportano tale opzione.

5) –summary; equivale ad uno smartctl -l e consente di visualizzare solo lo storico dei test pregressi.

Come già accennato in precedenza, il parametro –email è opzionale ma, nel caso in cui l’utente decidesse di utilizzarlo, lo scrip verifica che il formato dell’indirizzo di posta a cui il report dovrà essere indirizzato sia corretto.

Infine, per consentire un maggiore controllo sui passaggi cruciali eseguiti dallo scrip, ho fatto in modo che essi vengano “registrati” all’interno di un apposito file di log.

E’ tutto. Alla prossima.

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

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

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

nagiosEcco lo scrip:

#!/bin/bash

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

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

function get_cert_datetime() {

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

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

        case $day in

        1)

                day=01;
                ;;

        2)

                day=02;
                ;;

        3)

                day=03;
                ;;

        4)

                day=04;
                ;;

        5)

                day=05;
                ;;

        6)

                day=06;
                ;;

        7)

                day=07;
                ;;

        8)

                day=08;
                ;;

        9)

                day=09;
                ;;

        esac

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

        case $month in

        Jan)

                 month=01;
                ;;

        Feb)

                month=02;
                ;;

        Mar)

                month=03;
                ;;

        Apr)

                month=04;
                ;;

        May)

                month=05;
                ;;

        Jun)

                month=06;
                ;;

        Jul)

                month=07;
                ;;

        Aug)

                month=08;
                ;;

        Sep)

                month=09;
                ;;

        Oct)

                month=10;
                ;;

        Nov)

                month=11;
                ;;

        Dec)

                month=12;
                ;;
        esac

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

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

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

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

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

        datecheck="$year$month$day"

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

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

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

}

get_cert_datetime;

get_time_diff() {

        #time difference in hours, minutes and seconds

        hoursnow=`date +"%H"`

        minutesnow=`date +"%M"`

        secondsnow=`date +"%S"`

        hourstoseconds=`expr $hoursnow "*" 3600`

        minutestosecond=`expr $minutesnow "*" 60`

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

        hourschecktoseconds=`expr $hours "*" 3600`

        minuteschecktoseconds=`expr $minutes "*" 60`

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

        secondsdiff=`expr $secondschecktot - $secondsnowtot`

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

        minutestoexpire=`expr $secondstoexpire / 60`

        spareseconds=`expr $secondstoexpire % 60`

        hourstoexpire=`expr $minutestoexpire / 60`

        spareminutes=`expr $minutestoexpire % 60`
}

get_time_diff;

timestamp_to_days() {

        #unix timestamp to days conversion

        secondsleft=`expr $datecheckepoch - $datenowepoch`

        minutesleft=`expr $secondsleft / 60`

        hoursleft=`expr $minutesleft / 60`

        daysleft=`expr $hoursleft / 24`

}

timestamp_to_days;

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

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

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

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

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

                                exit 0

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

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

                                exit 1

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

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

                                exit 2

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

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

                                exit 2

                        fi

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

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

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

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

                                        exit 0

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

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

                                        exit 1

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

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

                                        exit 2

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

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

                                        exit 2

                                fi

                        fi

                        else

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

                else

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

                        exit 3
                fi
        else

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

                exit 2

        fi

else

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

        exit 3
fi

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

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

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

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

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

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

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

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

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

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

Configurazione di Nagios

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

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

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

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

Infine riavviamo Nagios per rendere effettive le suddette modifiche:

[root@NMS ~]# service nagios reload

E’ tutto. Alla prossima.

WI-FI IP camera (made in China): mettere al sicuro l’accesso da remoto

Problema

Negli ultimi anni si è assistito ad un vero e proprio boom riguardante l’acquisto di dispositivi di videosorveglianza basati sul protocollo IP,  ergo facilmente integrabili con le piccole reti domestiche preesistenti. Fin qui nulla di male, se non fosse che, spesso e volentieri, tali “aggeggi” montino dei firmware poco affidabili dal punto di vista della cybersecurity. Mi spiego meglio: cosa accadrebbe se consentissi l’accesso da remoto alla pagina Web di amministrazione della mia telecamera IP? Chi mi garantisce che:

1) Il codice server-side della suddetta pagina non sia vulnerabile;

2) Che non siano presenti una o più backdoor all’interno del codice sorgente del firmware.

dbpowerSoluzione

Se disponete di un server Linux a risparmio energetico (come nel mio caso) è possibile mettere al sicuro l’accesso da remoto alla nostra telecamera IP mediante l’uso di un reverse proxy. In particolare, la configurazione che sto per illustrarvi riguarda uno dei server Web più utilizzati in ambito open source, ovvero Apache.

Supponendo che la mia telecamera IP sia in ascolto sulla porta TCP 81 (HTTP-Alt) e che risponda all’indirizzo 192.168.1.5 , ecco la configurazione da me adoperata:

<VirtualHost *:81>
    ProxyPass / http://192.168.1.5:81/
    ProxyPassReverse / http://192.168.1.5:81/

    <Proxy *>
        Order deny,allow
        Allow from all
        AuthName "IPCamera Access"
        AuthType Basic
        AuthUserFile /etc/ipcam/passwd
        Require valid-user
    </Proxy>

</VirtualHost>

Nella fattispecie, oltre a loggare tutte le richieste di accesso provenienti dall’esterno (in modo da tenerne traccia), ho aggiunto un ulteriore meccanismo di autentica a quello già previsto dalla pagina Web di gestione. In questo modo sono riuscito a garantirmi 2 risultati:

1) Protezione contro attacchi di tipo Web-based;

2) Protezione contro eventuali backdoor presenti nel firmware.

Inoltre, dopo aver definito il suddetto VirtualHost, è stato necessario abilitare il modulo che fa funzionare Apache come reverse proxy, ovvero mod_proxy, decommentando o inserendo la seguente direttiva:

LoadModule proxy_module modules/mod_proxy.so

Per ciò che concerne le credenziali di accesso, invece, è stato possibile definirle utilizzando il comando htpasswd nel seguente formato:

[root@linuxbox ~]# htpasswd -c /etc/ipcam/passwd nomeutente

Infine ho ricaricato la configurazione di Apache per rendere effettive le suddette modifiche:

[root@linuxbox ~]# service httpd reload

Ora la vostra telecamera IP può essere considerata “relativamente” al sicuro.

Alla prossima.

CentOS 6 e Postfix: tenere a bada il fenomeno denominato backscatter

In questo e questo post ho mostrato come configurare Postfix in modo da farlo funzionare come antispam. In quest’altro post, invece, ho illustrato la configurazione di postgrey e qui ho parlato di opendkim e di come integrarlo a Postfix.

Adesso vedremo come mitigare un fenomeno tipico dello spam e sempre più in aumento, ovvero il backscatter.

postfixUn po’ di teoria

Supponiamo che uno spammer voglia utilizzare il vostro dominio come mittente di una mail fraudolenta appositamente forgiata, da inoltrare ad un server SMTP autoritativo per un dominio X, che chiameremo dominiotarget.com.

La sequenza dei comandi che lo spammer utilizzerà sarà simile alla seguente:

ehlo mail.dominiotarget.com
mail from:<pluto@vostrodominio.com>
rcpt to:<pippo@dominiotarget.com>
data
some data
.
quit

Come potete notare, lo spammer ha utilizzato il vostro dominio (mail from:) come mittente della mail di spam che intende inviare ad utente@dominiotarget.com.

Supponiamo adesso che, per qualche ragione, il server SMTP di dominiotarget.com respinga la suddetta email fraudolenta (poichè, ad esempio, non esiste la mailbox pippo@dominiotarget.com), generando, successivamente, un messaggio apposito per indicare che tale email è stata respinta (mail bounced). Tale messaggio di “errore” verrà successivamente inoltrato al server SMTP autoritativo (ovvero quello specificato nei record MX della zona DNS) per vostrodominio.com.

A questo punto, se non opportunamente configurato, il vostro server SMTP riceverà il suddetto messaggio e lo inoltrerà alla casella di posta dell’utente spoofato dallo spammer, ovvero pluto@vostrodominio.com (se esiste), oppure verrà semplicemente scartata. Nel primo caso si rischia di saturare abbastanza velocemente la casella email del malcapitato utente (soprattutto se dotata di DISK QUOTA); nel secondo caso, invece, si rischia di creare eccessiva entropia, non consentendo all’amministratore della macchina di discernere (ad esempio attraverso l’analisi del file maillog) tra le email di backscatter e quelle che sono state respinte per un motivo lecito.

Come fare dunque per limitare tale fenomeno? Ecco alcune possibili soluzioni.

Utilizzare SPF

Se il server SMTP di dominiotarget.com è stato configurato in modo da utilizzare il framework SPF, e nella vostra zona DNS è presente un record di tipo TXT che specifica l’elenco dei server che possono inviare email utilizzando vostrodominio.com, esso sarà in grado di bloccare il tentativo di inoltro senza inviare alcun messaggio di errore al vostro server SMTP. Purtroppo però l’SPF non è configurato su tutti i server SMTP sparsi per la Rete, ergo bisogna correre ai ripari configurando opportunamente Postfix sul server antispam che intendiamo utilizzare.

Configurazione di Postfix

Per prima cosa occorre fare in modo che le email dirette a caselle di posta non esistenti vengano automaticamente respinte. Per fare ciò è possibile configurare la seguente direttiva all’interno del file /etc/postfix/main.cf:

unknown_local_recipient_reject_code = 550

Tale settaggio rappresenta comunque un’ottima prima difesa contro il backscatter, poichè, nella stragrande maggioranza dei casi, lo spammer forgerà delle email pseudorandomiche da utilizzare come mittente, del tipo AjsheUbjdX@vostrodominio.com. Esistono, però, dei casi particolari in cui lo spammer è a conoscenza di alcuni indirizzi leciti (ed attivi) su vostrodominio.com, ragion per cui occorre affinare ulteriormente la nostra opera di filtraggio. In particolare, è possibile impostare delle regole opportune basate su espressioni regolari in grado di “capire” quando un messaggio di mail bounced è stato causato dal backscatter e quando no. Tali regole potranno essere applicate durante l’analisi dell’header e del corpo del messaggio.

Ad esempio, possiamo popolare il file /etc/postfix/header_checks con le seguenti direttive:

if /^Received:/
/^Received: +from +(vostrodominio\.com) +/
        reject forged client name in Received: header: $1
/^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(vostrodominio\.com)\)/
        reject forged client name in Received: header: $2
/^Received:.* +by +(vostrodominio\.com)\b/
        reject forged mail server name in Received: header: $1
endif
/^Message-ID:.* <!&!/ DUNNO
/^Message-ID:.*@(vostrodominio\.com)/
        reject forged domain name in Message-ID: header: $1

Inoltre, le suddette entry dovranno essere collocate anche all’interno del file /etc/postfix/body_checks.

Infine, è necessario editare il file /etc/postfix/master.cf come segue:

body_checks = pcre:/etc/postfix/body_checks

header_checks = pcre:/etc/postfix/header_checks

Da notare che non occorre postmappare i suddetti file ed è sufficiente un semplice reload di Postfix per rendere effettive le modifiche apportate:

[root@antispam ~]# service postfix reload

Adesso il nostro server antispam sarà sicuramente in grado di filtrare un gran numero di messaggi di backscatter ma non sarà comunque a prova di bomba (ecco perchè ho parlato di “mitigare” e non di “bloccare” tale fenomeno). Ciò è dovuto al fatto i messaggi di mail bounced possono variare sia nella sostanza che nella forma in base all’applicativo che funge da SMTP, rendendo comunque possibile l’eventualità che esso non venga matchato dai filtri appena configurati.

A presto.

SElinux e CentOS 6: ricevere un’email di notifica in caso di policy violate

In questo post ho discusso di SElinux, illustrandone la logica di funzionamento ed i comandi da utilizzare per la sua configurazione e gestione.

Adesso vedremo come rendere tale sistema di sicurezza il più proattivo possibile, ovvero facendo in modo che sia in grado di inviarci una mail di notifica nel caso in cui una o più policy MAC vengano violate.

selinux-penguin-new_medium

Installazione e configurazione di SEtroubleshoot

Il software in grado di implementare la funzionalità in questione prende il nome di setroubleshoot. Per installarlo possiamo utilizzare il packet manager di casa CentOS/RedHat, ovvero yum:

[root@linuxbox ~]# yum install setroubleshoot

Successivamente, passiamo alla configurazione dell’applicativo in questione, editando il file /etc/setroubleshoot/setroubleshoot.conf. Le entry da modificare sono le seguenti:

recipients_filepath = /var/lib/setroubleshoot/email_alert_recipients
smtp_port = 25
smtp_host = localhost
from_address = mittente@vostrodominio.com
subject = SELinux AVC Alert for nomeserver

Va da sè che l’opzione smtp_host deve essere popolata in base al server SMTP che si vuole utilizzare. Ad esempio, nel mio caso, esiste un’unica macchina che funge da STMP “in uscita”, configurata in modo tale da interfacciarsi con un relay host “esterno” ed “affidabile” (in tal modo evito che le mie email vengano respinte costantemente poichè provenienti da un indirizzo IP pubblico di tipo dinamico e non dotato di record DNS PTR).

Inseriamo adesso, all’interno del file /var/lib/setroubleshoot/email_alert_recipients, il destinatario delle email di notifica:

[root@linuxbox ~]# echo "indirizzo.email@vostrodominio.com" >> /var/lib/setroubleshoot/email_alert_recipients

e riavviamo il servizio che si occupa di gestire tale funzionalità (in modo da attivare le suddette modifiche):

[root@linuxbox ~]# service messagebus restart

Test

Per testare il corretto funzionamento del sistema di notifica appena configurato è necessario fare in modo che uno dei moduli SELinux custom che abbiamo realizzato venga momentaneamente disattivato (in tal modo “forzeremo” il popolamento del file audit.log generando così il trigger che causerà l’inoltro della notifica email sulla nostra casella di posta):

[root@linuxbox ~]# semodule -d <nomemodulo>

Se tutto funziona come dovrebbe, riceveremo una notifica dal contenuto simile al seguente:

[SELinux AVC Alert for linuxbox] SELinux is preventing /usr/sbin/sedispatch from sendto access on the unix_dgram_socket 

SELinux is preventing /usr/sbin/sedispatch from sendto access on the unix_dgram_socket .

*****  Plugin catchall (100. confidence) suggests   **************************

... OMISSIS ...

Alla prossima.

NRPE_NT e Nagios: tenere sotto controllo gli aggiornamenti di Windows

Installare prontamente gli aggiornamenti di Windows, soprattutto se si ha a che fare con i security update, è sempre cosa buona e giusta. Spesso, però, capita che l’amministratore di sistema debba gestire N macchine e non abbia il tempo materiale di loggarsi su ciascuna di esse e constatare l’eventuale disposnibilità di aggiornamenti. Personalmente credo che non li si debba mai scaricare nè tantomeno installare automaticamente sulla macchina ospite, soprattutto se si ha a che fare con dei sistemi in produzione.

Cosa fare dunque per automatizzare tale task di verifica della disponibilità degli aggiornamenti? Semplice, utilizzare il nostro NMS preferito, ovvero Nagios.

nagiosIngredienti

Per prima cosa è necessario che sulla macchina da monitorare sia installato (ed attivo) il servizio NRPE_NT. Inoltre, ai plugin utilizzati dal servizio in questione, occorre aggiungere uno scrip PowerShell creato appositamente. Infine,  occorre creare su Nagios un comando ed un servizio specifico, in modo che il nostro NMS sia in grado di effettuare il controllo degli aggiornamenti in modo automatico e ad intervalli di tempo regolari.

Configurazione di NRPE_NT

Poichè la verifica della disponibilità degli aggiornamenti è un’operazione che può richiedere parecchio tempo, è necessario fare in modo che il timeout di NRPE_NT venga appositamente incrementato, editando la direttiva command_timeout presente all’interno del file nrpe.cfg (nel mio caso ho impostato una soglia pari a 600 secondi):

command_timeout= 600

Per ciò che concerne lo scrip PowerShell, esso può essere scaricato da qui, per poi posizionarlo all’interno della directory Plugins di NRPE_NT. A questo punto, sempre lato NRPE_NT, è possibile creare un comando specifico che si occuperà di verificare la disponibilità degli aggiornamenti di Windows. Tale definizione va inserita all’interno del file V2_nrpe_commands.cfg e presenta la seguente struttura:

command[get_windows_updates]=cscrip.exe //nologo //T:600 c:\nrpe\plugins\v2\check_windows_updates.wsf /w:0 /c:1

Restartiamo infine il servizio NRPE_NT mediante i comandi:

net stop NRPE_NT
net start NRPE_NT

e passiamo alla configurazione di Nagios.

Configurazione di Nagios

Come già affermato in precedenza, la configurazione dell’NMS si basa su due passaggi: la creazione del comando e l’assegnazione del check che ne fa uso ad un servizio legato all’host da monitorare.

Di seguito riporto il comando (da inserire nel file commands.cfg presente in /etc/nagios/objects):

# nagios-WMI-windows-updates check command definition

define command {
        command_name    check_WMI_windows_updates
        command_line    /usr/lib64/nagios/plugins/check_nrpe -H $HOSTADDRESS$ -t $ARG1$ -c get_windows_updates
        }

Mentre il servizio è così definito:

define service{
        use                             generic-service         ; Name of service template to use
        host_name                       Windows-Machine
        service_description             query WMI Updates for Microsoft Windows Machine
        check_command                   check_WMI_windows_updates!600
        normal_check_interval           120
        }

Da notare che l’unico parametro passato al comando riguarda il numero di secondi di attesa (600) prima del timeout. Inoltre, ho fatto in modo che i check vengano eseguiti a distanza di 2 ore l’uno dall’altro (normal_check_interval 120).

A configurazione ultimata riavviamo Nagios per rendere effettive le modifiche:

[root@NMS ~]# service nagios reload

In caso di disponibilità di aggiornamenti, il cruscotto dell’NMS ci apparirà in un modo simile al seguente:

cruscotto_nagios

Alla prossima.

CentOS 6: monitorare le ACL del nostro router Cisco mediante Nagios ed snmptt

Durante gli ultimi mesi ho discusso ampiamente della configurazione di Nagios per la ricezione dei check passivi, quali trap SNMP o security alert. Adesso vi mostrerò come integrare le predette tipologie di allarmi in modo da riuscire a monitorare le ACL del nostro router Cisco.

Premessa

Esiste un modo più veloce per ottenere il monitoraggio delle ACL rispetto a quello che sto per illustrare. Esso consiste, fondamentalmente, nella configurazione di un syslog server (dotato dell’applicativo swatch per l’analisi in tempo reale dei file di log) a cui il nostro router Cisco dovrà puntare. Nel caso in cui un determinato pattern (ad esempio / denied /) dovesse essere matchato da swatch, quest’ultimo genererà un’opportuna notifica email da inoltrare all’amministratore di rete.

La mia configurazione, invece, si basa sulla logica seguente: il router Cisco genererà delle opportune trap SNMP segnalando ciò che avviene a livello di ACL (traffico consentito oppure negato). Esse verranno intercettate da snmptrapd e tradotte da snmptt, per poi essere elaborate dall’event handler submit_check_result e date in pasto a Nagios. Ho optato per tale configurazione poichè sulla mia linux box che funge da syslog server e da NMS sono già attivi sia snmptrapd che snmptt e quindi ho ritenuto conveniente sfruttarli piuttosto che mettere in funzione swatch.

nagiosConfigurazione del router Cisco

La configurazione del router Cisco si basa in 3 passaggi: settaggio della direttiva log per ciascuna entry che andrà a formare l’ACL da monitorare,  settaggio del syslog server a cui inviare le trap ed abilitazione di queste ultime.

Ad esempio, l’ACL di nostro interesse dovrà essere formata da entry di questo tipo:

access-list 102 deny ip host 255.255.255.255 any log

Inoltre, per definire la community string RO (read only) e l’indirizzo IP del server che si occuperà della ricezione delle trap, occorrerà digitare le seguenti direttive:

snmp-server host 192.168.1.1 keypublic
snmp-server community keypublic RO

mentre le trap potranno essere abilitate nel modo seguente:

snmp-server enable traps snmp authentication linkdown linkup coldstart warmstart
snmp-server enable traps tty
snmp-server enable traps config-copy
snmp-server enable traps config
snmp-server enable traps resource-policy
snmp-server enable traps cpu threshold
snmp-server enable traps syslog
snmp-server enable traps firewall serverstatus

 Contenuto dello scrip submit_check_result

Secondo quanto già riportato in questo post, sappiamo che snmptt effettua una traduzione “statica” delle trap (mediante il comando snmpttconvertmib). Per tale motivo, affinchè si possa indurre Nagios a generare dei security alert solo dopo la ricezione di trap ben determinate, occorre modificare il codice sorgente relativo all’event handler submit_check_result. Di seguito riporto il contenuto dello scrip in questione da me customizzato:

 echocmd="/bin/echo"

CommandFile="/var/spool/nagios/cmd/nagios.cmd"

# get the current date/time in seconds since UNIX epoch
datetime=`date +%s`

# create the command line to add to the command file
if [[ $1 == "ip router" ]];then

        if [[ $4 =~ "denied igmp" ]];then

                exit 0;

        elif  [[ $4 =~ "denied" ]];then

                service="Router ACL Connection Denied";

                output="$4 | connection_denied=1"
                cmdline="[$datetime] PROCESS_SERVICE_CHECK_RESULT;router;$service;$3;$output";

        else

                cmdline="[$datetime] PROCESS_SERVICE_CHECK_RESULT;router;$2;$3;$4";

        fi

else

   cmdline="[$datetime] PROCESS_SERVICE_CHECK_RESULT;$1;$2;$3;$4";

fi

# append the command to the end of the command file
`$echocmd $cmdline >> $CommandFile`

Nella fattispecie, vengono dapprima identificate le trap che riguardano il router. Successivamente, nel caso in cui esse contengano il pattern igmp denied, lo scrip uscirà senza effettuare alcuna operazione. Tale “scrematura” è stata necessaria poichè il mio ISP effettua regolarmente del polling IGMP mediante il proprio querier 192.168.100.1 (per il servizio IPTV).

Nel caso in cui, invece, le trap dovessero contenere la stringa denied, la variabile cmdline verrà popolata con l’host name dell’oggetto monitorato da Nagios (per il quale si dovrà aggiornare lo stato del servizio che si occupa di tenere d’occhio le ACL). Nel mio caso tale host name è semplicemente router. Inoltre, l’output  verrà rimaneggiato in modo da tenere traccia delle performance data (graficizzandole mediante l’uso di pnp4nagios).

MIB da utilizzare e configurazione di Nagios

Per prima cosa occorre scaricare le MIB CISCO-SYSLOG-MIB e CISCO-SYSLOG-MIB-V1SMI dal repository FTP della Cisco, effettuandone il parsing mediante snmpttconvertmib.

Successivamente, su Nagios occorrerà definire per l’host name router il servizio che si occuperà di monitorare le ACL:

define service{
        use                             local-service
        host_name                       router
        service_descripion              Router ACL Connection Denied
        check_command                   check_passive
        passive_checks_enabled          1
        active_checks_enabled           0
        max_check_attempts              1
        is_volatile                     1
        check_freshness                 1
        freshness_threshold             600
        flap_detection_enabled          0
        }

Come ultimo step, a configurazione ultimata possiamo riavviare Nagios per rendere effettive le suddette modifiche:

[root@NMS ~]# service nagios reload

ed abbiamo finito.

A presto.