Archivi tag: ssl

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.

Load balancer software basato su LVS e Centos 6

Premessa

Oggigiorno esistono diverse soluzioni basate su distribuzioni Linux che consentono di realizzare dei load balancer piuttosto affidabili e performanti. Personalmente ritengo che la scelta dovrebbe ricadere su due software in particolare, ovvero HAProxy oppure LVS (acronimo di Linux Virtual Server).

Nel primo caso abbiamo a che fare con un applicativo che viene eseguito nello spazio utente, con tutte le limitazioni che questo comporta, soprattutto in termini di velocità e di carico computazionale. Esso, inoltre, lavora a livello 7 ISO/OSI (application).

Nel secondo caso, invece, parliamo di un software che viene eseguito a livello del kernel, riducendo significatamente i tempi di risposta e di elaborazione. Inoltre, esso operera a livello 4 (transport) della pila ISO/OSI.

Ovviamente, da buon freak control, ho scelto LVS per realizzare il mio load balancer. Certo, molti di voi obietteranno dicendo che questa tipologia di bilanciatore non ha la stessa affidabilità, le stesse features e la stessa velocità di una soluzione hardware dedicata. La mia risposta è che se non avete intenzione di vendervi un rene per acquistare una macchina che invece di fare N, fa semplicemente N+1 (o + 2, in base alle migliaia di euro in fattura), allora dovrete accontentarvi (si fa per dire) del load balancer software, che per lo meno è più malleabile.

Installazione di LVS

L’installazione di LVS è abbastanza semplice e può essere effettuata mediante yum:

[root@lb1 ~]#  yum groupinstall "Load Balancer"

[root@lb1 ~]#  yum install httpd

[root@lb1 ~]#  chkconfig --level 2345 pulse on
[root@lb1 ~]#  chkconfig --level 2345 piranha-gui on
[root@lb1 ~]#  chkconfig --level 2345 httpd on

In particolare, httpd è il server Web in ascolto sul load balancer (in quanto il mio intento è quello di realizzare un bilanciatore di carico per le richieste HTTP/HTTPS); pulse è il cuore del servizio LVS e si occupa anche della gestione dell’alta affidabilità (HA) mediante l’uso di heartbeat; piranha-gui è un’utile interfaccia grafica (Web based) per la configurazione del nostro Linux Virtual Server.

Una volta installato il pacchetto piranha-gui, occorre impostarne la password di accesso (trattasi di semplice autentica HTTP):

[root@lb1 ~]#  piranha-passwd

Lo username sarà quello di default, ovvero piranha.

A questo punto occorre abilitare il forwarding tra le varie interfacce di rete della macchina Centos adibita a bilanciatore, lanciando semplicemente il comando:

[root@lb1 ~]#  echo 1 > /proc/sys/net/ipv4/ip_forward

Tale comando va inserito anche all’interno del file rc.local, in modo da abilitare il forwarding dopo ogni reboot della macchina.

Infine, avviamo sia il server Web che piranha-gui:

service httpd start
service piranha-gui start

Prima di entrare nel merito della configurazione di LVS è opportuno introdurre un po’ di nozioni teoriche.

Modalità di funzionamento

LVS può operare in 3 diverse modalità (mutuamente esclusive):

1) NAT;

2) Tunneling;

3) Direct routing (DC).

Nel primo caso il load balancer prenderà in carico le richieste provenienti dai client e le girerà ai frontend mediante un semplice NAT. Le risposte dei frontend verranno quindi inoltrate al load balancer stesso che si occuperà di recapitarle ai client. A rigor di logica, tale modalità di funzionamento è certamente la meno performante, in quanto il load balancer rappresenta il classico collo di bottiglia dell’infrastruttura (dato che tutto il traffico bilanciato dovrà necessariamente utilizzare LVS come crocevia).

Nel secondo caso non viene utilizzato il NAT ma un tunnel ipip creato ad hoc per incapsulare le richieste dei client prese in carico dal load balancer e successivamente inoltrarle ai frontend. In questo caso le risposte verranno instratade ai client direttamente dai frontend, senza passare necessariamente dal director (ovvero la macchina che effettua il bilanciamento nella terminologia LVS).Inoltre, a differenza della modalità NAT, affichè l’infrastruttura possa funzionare correttamente è necessario configurare appositamente anche i frontend.

Infine, la terza modalità (che tratterò nell’ambito di questo post) può essere utilizzata nel caso in cui il director ed i frontend (real server per LVS), si trovino nella stessa subnet. Anche in questo caso i frontend vanno configurati appositamente.

In definitiva, la prima modalità richiede un minor numero di configurazioni a discapito di performance ottimizzate; la seconda e la terza modalità, invece, consentono di superare il problema del collo di bottiglia, ma richiedono alcune variazioni nella configurazione dei frontend.

Modalità di bilanciamento

Le modalità di bilanciamento messe a disposizione da LVS sono molteplici. Senza scendere troppo nel dettaglio, le due che secondo me meritano maggiore attenzione sono le seguenti:

1) round robin (classica o nella variante weighted);

2) least connection (anch’essa disponibile nella forma classica o nella variante weighted).

Nel primo caso le richieste vengono inoltrate ai frontend seguendo l’algoritmo round robin (vedi qui per ulteriori dettagli). Nel caso in cui i frontend non siano tutti dotati della medesima capacità di calcolo e risorse hardware, è possibile assegnare loro un peso specifico che li contraddistinguerà. In questo caso, scegliendo la weighted round robin sarà possibile inoltrare una mole di richieste maggiore ai server più corazzati, riservando un carico inferiore a quelli meno performanti.

Per quanto riguarda, invece, la least connection, un client che instaura la connessione con un frontend continuerà ad essere servito da quel frontend (fino ad un eventuale connection timeout). La modalità weighted tiene conto del peso assegnato a ciascun frontend, ovvero valgono i medesimi ragionamenti fatti per la weighted round robin.

Configurazione di LVS

Lo schema riportato di seguito mostra l’infrastruttura creata per il bilanciamento del traffico HTTP/HTTPS.

 

netschema.PNG

Come si può notare, esistono 2 director configurati in HA (active/standby) e 2 real server, identici dal punto di vista hardware e software.

Colleghiamoci alla GUI installata in precedenza attraverso la seguente URL:

http://ipdirector:3636

dove, banalmente, ipdirector è l’indirizzo IP del server che funge da director e 3636 (TCP) è la porta su cui è in ascolto piranha-gui.

Cliccando sul tab VIRTUAL SERVERS e successivamente sul tasto ADD possiamo creare un nuovo bilanciatore (come riportato nello screenshot sottostante):

 

screen1.PNG

Cliccando sul radio button presente alla nostra sinistra selezioniamo il virtual server appena creato. A questo punto sarà sufficiente cliccare su EDIT per definirne i parametri di configurazione:

screen2.PNG

Oltre al nome del virtual server (lb_cluster), abbiamo configurato la TCP 80 come application port (HTTP), abbiamo scelto il virtual IP (10.2.66.13) a cui dovrà rispondere il load balancer ed abbiamo scelto l’interfaccia virtuale (eth0:1) su cui mettere in bind l’IP in questione (appartenente ad una classe privata, appositamente “nattato” in IP pubblico da un firewall che gestisce le connessioni in ingresso alla LAN). Da notare che alla voce quiesce server il radio button è posizionato su Yes. In questo modo, nel caso in cui venisse aggiunto un nuovo real server, la tabella di bilanciamento presente sul director verrebbe resettata e la nuova macchina verrà coinvolta immediatamente nelle operazioni di smistamento delle richieste.

Adesso cliccliamo su REAL SERVER, su ADD e dopo aver selezionato la nuova entry, clicchiamo su EDIT.

La pagina di configurazione dei real server sarà simile alla seguente:

screen3.PNG

Questa, invece, è l’overview dei real server configurati:

screen4.PNG

Selezioniamo ciascuno di essi e clicchiamo su (DE)ACTIVATE.

Torniamo alla schermata dei virtual server, selezioniamo quello appena creato ed anche in questo caso clicchiamo su (DE)ACTIVATE.

Ora sarà possibile creare un virtual server anche per le richieste HTTPS. E’ sufficiente ricopiare la configurazione utilizzata per l’HTTP, eccezion fatta per i seguenti parametri:

1) Apllication port, che deve essere la 443.

2) Persistence, settata a 320 (ovvero il timeout in secondi della connessione inizializzata dal client).

Quest’ultima è necessaria per il buon funzionamento del protocollo HTTPS, in quanto una sessione di questo tipo deve essere gestita da un solo server per volta (rispettando il principio di autenticazione della fonte e la logica su cui si basa SSL/TLS).

Anche in questo caso cliccando su DE(ACTIVATE) renderemo attivo il bilanciamento per il protocollo in questione.

Occorre, inoltre, fare una precisazione: nel caso in cui il director debba gestire anche le connessioni HTTPS, è necessario installare su di esso (ed anche sui real server), il modulo mod_ssl di Apache:

[root@lb1 ~]# yum install mod_ssl

e successivamente restartare httpd:

[root@lb1 ~]# service httpd restart

in modo che rimanga in ascolto sulla porta 443.

La corretta installazione e configurazione dei certificati sui real server esula dallo scopo del presente post.

Configurazione del cluster (HA)

Anche la configurazione del cluster active/standby (per evitare il famigerato single point of failure) può essere portata a termine mediante piranha-gui.

Nello specifico, è sufficiente cliccare sul tab REDUNDANCY ed impostare l’IP reale del director di backup:

screen5.PNG

Alla fine della fiera, il file di configurazione di LVS presente sul nodo attivo dovrà essere simile al seguente:

[root@lb1 ~]# cat /etc/sysconfig/ha/lvs.cf

serial_no = 77
primary = 10.2.66.11
service = lvs
backup_active = 1
backup = 10.2.66.12
heartbeat = 1
heartbeat_port = 539
keepalive = 6
deadtime = 10
network = direct
debug_level = NONE
monitor_links = 1
syncdaemon = 0
virtual lb_cluster {
     active = 1
     address = 10.2.66.13 eth0:1
     vip_nmask = 255.255.255.255
     port = 80
     send = "GET / HTTP/1.0rnrn"
     expect = "HTTP"
     use_regex = 0
     load_monitor = none
     scheduler = lc
     protocol = tcp
     timeout = 6
     reentry = 15
     quiesce_server = 1
     server vmtest6 {
         address = 10.2.66.7
         active = 1
         weight = 1
     }
     server vmtest7 {
         address = 10.2.66.9
         active = 1
         weight = 1
     }
}
virtual lb_cluster_https {
     active = 1
     address = 10.2.66.13 eth0:1
     vip_nmask = 255.255.255.255
     port = 443
     persistent = 320
     use_regex = 0
     load_monitor = none
     scheduler = lc
     protocol = tcp
     timeout = 6
     reentry = 15
     quiesce_server = 1
     server vmtest6 {
         address = 10.2.66.7
         active = 1
         weight = 1
     }
     server vmtest7 {
         address = 10.2.66.9
         active = 1
         weight = 1
     }
}


Come potete notare, per quanto riguarda il virtual server dedicato alle richeste HTTPS, non sono presenti meccanismi di controllo (identificati dai parametri send ed expect), in quanto non sono pienamente supportati da LVS e la loro presenza potrebbe inficiare il funzionamento del bilanciatore per il tipo di richieste in questione.

Ora copiamo la suddetta configurazione sul server di backup mediante SCP:

[root@lb1 ~]#  scp /etc/sysconfig/ha/lvs.cf root@10.2.66.12:/etc/sysconfig/ha/lvs.cf

A configurazione dei nodi ultimata, possiamo avviare pulse:

[root@lb1 ~]# service pulse start

Controlliamo che il servizio sia effettivamente funzionante e che i real server siano stati attivati correttamente:

[root@lb1 ~]# watch ipvsadm

il cui output dovrebbe essere simile al seguente:

Every 2.0s: ipvsadm                                                                                 Mon Oct 21 11:32:53 2013

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.2.66.13:http lc
-> 10.2.66.7:http               Route   1      0          0
-> 10.2.66.9:http               Route   1      0          0
TCP  10.2.66.13:https lc persistent 320
-> 10.2.66.7:https              Route   1      0          0
-> 10.2.66.9:https              Route   1      0          0

Configurazione dei real server ed il problema delle richieste ARP

Come accennato in precedenza, la modalità Direct Connect prevede un minimo di configurazione anche sui frontend Web. Quindi, fin quando la loro configurazione non verrà ultimata, il bilanciatore non potrà funzionare.

Nello specifico, occorre mettere in bind l’IP dei due virtual server sui frontend, assegnandolo, anche in questo caso, ad un’interfaccia virtuale (eth0:1).

Lanciamo dunque il comando:

ifconfig eth0:1 10.2.66.13 netmask 255.255.255.255 up

ed inseriamo la suddetta stringa all’interno del file /etc/rc.local per rendere attiva l’interfaccia ache dopo un eventual reboot della macchina.

Con il comando:

[root@lb1 ~]# ifconfig

verifichiamo che l’interfaccia virtuale sia effettivamente attiva. L’output dovrà essere simile al seguente:

eth0:1    Link encap:Ethernet  HWaddr 00:0C:29:14:C7:79
           inet addr:10.2.66.13  Bcast:10.2.66.13  Mask:255.255.255.255
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

Una volta configurata l’interfaccia virtuale occorre risolvere il problema delle richieste ARP. Infatti, poichè sia il director che i due real server rispondono al medesimo indirizzo IP virtuale, nel caso in cui venisse fatta una richiesta da un client, la riposta potrebbe provenire direttamente da uno dei due frontend, senza passare per il director. Tale comportamento è dovuto al fatto che, in una LAN, ogniqualvolta si cerca di contattare un indirizzo IP, viene inoltrata una richiesta ARP (in broadcast) richiedendo l’indirizzo MAC dell’interfaccia a cui quell’indirizzo è stato assegnato. Ovviamente, a tale richiesta potrebbe seguire la risposta di uno dei due frontend o del director, in modo totalmente randomico, inficiando il corretto funzionamento del load balancer stesso.

Per evitare ciò si può installare arptables_jf, una sorta di firewall di livello 2, in modo da bloccare le richieste ARP provenienti dai client:

[root@lb1 ~]# yum install arptables_jf

Una volta installato è necessario configurarlo nel seguente modo:

[root@lb1 ~]# arptables -A IN -d 10.2.66.13 -j DROP
[root@lb1 ~]# arptables -A OUT -s 10.2.66.13 -j mangle --mangle-ip-s 10.2.66.7

La prima entry blocca le richieste ARP in ingresso al real server, contenenti l’indirizzo IP virtuale; la seconda entry, invece, consente di rispondere alla richieste ARP con l’IP reale del frontend (10.2.66.7).

Salviamo la configurazione di arptables lanciando il comando:

[root@lb1 ~]# service arptables_jf save

e successivamente avviamo il servizio:

[root@lb1 ~]# service arptables_jf start

Facciamo anche in modo che il suddetto servizio venga avviato automaticamente ad ogni boot del sistema:

[root@lb1 ~]# chkconfig --levels 2345 arptables_jf on

Per verificare che tutto stia funzionando correttamente spegnamo momentaneamente l’interfaccia virtuale eth0:1 sul director e proviamo a pingare l’IP assegnatogli:

[root@lb1 ~]# ifdown eth0:1

[root@lb1 ~]# ping 10.2.66.13

se al ping non seguirà nessuna reply allora arptables sta funzionando come dovrebbe.

Test di funzionamento per il bilanciamento del traffico HTTP/HTTPS

Un test banale per appurare che il load balancer funziona bene consiste nel creare due pagine Web leggermente differenti tra di loro (ad esempio nel tag <title> oppure nel <body>). Una di queste pagine dovrà essere caricata sul frontend 1 e l’altra sul frontend 2.

Facendo delle richieste all’indirizzo IP pubblico del director (ad esempio 37.88.91.154), a cui corrisponde l’IP locale e virtuale 10.2.66.13, dovremmo essere reindirizzati ad uno o all’altro frontend.

Stessa prova può essere fatta per testare il protocollo HTTPS, facendo attenzione che il timeout impostato per la persistenza delle sessioni venga rispettato.

Test di funzionamento per il cluster active/standby

Per testare il funzionamento del cluster è sufficiente stoppare il servizio pulse sul nodo attivo (per intenderci, quello su cui abbiamo lanciato il comando watch ipvsadm).

Lanciando il suddetto comando sul nodo di backup dovremmo riconoscere lo stesso output visto in precedenza sull’ex nodo attivo.

Test di carico

Per i test di carico vi rimando a questo post.

Fine della guida.

Alla prossima.

Apache, virtual host ed SSL su CentOS

La cosa fantastica di Apache è che ti consente di gestire i cosiddetti virtual host, ovvero N server Web virtuali, ognuno dei quali presenta le proprie caratteristiche peculiari.

Essi risultano particolarmente comodi quando si ha la necessità di imbastire dei frontend che rispondano per alcuni domini su HTTPS e per altri su HTTP.

logo_apache22.jpg

Come al solito, la presente guida si focalizzerà sulla configurazione del server vera e propria (httpd.conf), e successivamente sulla configurazione di ciascun virtual host.

Configurazione del server

La prima modifica da effettuare nel file di configurazione di Apache riguarda l’abilitazione del protocollo HTTPS. In particolare, esso verrà gestito grazie ad uno specifico modulo, ovvero mod_ssl.so, non presente nell’installazione di default relativa ad Apache.

Ergo, sarà necessario dapprima scaricarlo mediante il comando:

[root@serverweb conf]# yum install mod_ssl

Non ho installato anche openssl in quanto sono partito dal presupposto che siate già in possesso dei certificati X.509 (nel formato Apache) relativi al vostro dominio.

A questo punto è necessario abilitare il suddetto modulo aggiungendo la seguente direttiva nel file /etc/conf/httpd.conf:

LoadModule ssl_module modules/mod_ssl.so

Successivamente, si dovrà procedere con la messa in ascolto sul server della porta TCP 443 (HTTPS).

Per fare ciò si può aggiungere la seguente stringa nel file di configurazione di Apache:

Listen 443

Infine, sempre nel file di configurazione in questione, si dovrà specificare in quale directory sono presenti le direttive relative ai virtual host:

# Include the virtual host configurations:
Include /etc/httpd/vhosts.d/*.conf

Configurazione del virtual host

Posizioniamoci nella directory oggetto dell’include e creiamo un nuovo file di configurazione per il virtual host:

[root@serverweb vhosts.d]# nano virtualhost1.conf

Il cui contenuto sarà simile al seguente:

<VirtualHost *:443>
  ServerName www.miodominio.com

  DocumentRoot /var/www/virtualhosts/www.miodominio.com/htdocs

  SSLEngine on
  SSLCertificateFile /etc/ssl/sslcert.crt
  SSLCertificateKeyFile /etc/ssl/sslkey.pem
  SSLCertificateChainFile /etc/ssl/sf_bundle.crt

  <Directory /var/www/virtual/static.betuniq.info/htdocs>
      AllowOverride All
      Order allow,deny
      Allow from all
   </Directory>

  ErrorLog /var/www/virtualhosts/www.miodominio.com/logs/ssl_error.log
  CustomLog /var/www/virtualhosts/www.miodominio.com/logs/ssl_access.log combined
  #ServerSignature Off

  Redirect 404 /favicon.ico

  <Location /favicon.ico>
   ErrorDocument 404 “No favicon”
  </Location>

</VirtualHost>

Come avrete intuito, il virtual host in questione è in ascolto sulla porta TCP 443, quindi è in grado di servire solo ed esclusivamente il traffico HTTPS:

<VirtualHost *:443>

Con ServerName specifico il dominio associato al virtual host in questione mentre con DocumentRoot indico la direcotry in cui sono presenti le pagine Web (e relativi file, come immagini, fogli di stile, ecc.).

Con SSLEngine on abilito il motore SSL e con le direttive:

SSLCertificateFile /etc/ssl/sslcert.crt
SSLCertificateKeyFile /etc/ssl/sslkey.pem
SSLCertificateChainFile /etc/ssl/sf_bundle.crt

indico rispettivamente il pathname relativo al certificato X.509 (sslcert.crt), della chiave privata RSA (sslkey.pem) e del certificato di terze parti (sf_bundle.crt).

Le altre stringe riguardano semplicemente i file di log e le politiche di overwrite sulla directory (ad esempio mediante file .htaccess).

Ora, affinchè il suddetto dominio rimanga in ascolto sia sulla 443 che sulla 80 (HTTP puro), si può creare un ulteriore file di configurazione, contenente però la direttiva:

<VirtualHost *:80>

nessuna entry associata all’SSL ed ai certificati ma la stessa DocumentRoot del vhost precedente.

Per rendere effettive le modifiche senza creare disservizio è sufficiente lanciare il comando:

[root@serverweb conf]# service httpd reload

Se tale comando non restituisce errori è un ottimo segno.

Per avere ulteriore conferma del corretto funzionamento relativo al nostro server Web, si possono fare due check mediante netstat:

[root@serverweb conf]# netstat -anp | grep :80

e

[root@serverweb conf]# netstat -anp | grep :443

Se il server risulta in Listen è molto probabile che stia funzionando correttamente. Fate comunque delle prove con un browser per esserne certi.

E’ tutto. Alla prossima.

Configurazione ottimale di un server SMTP

Configurare al meglio un server SMTP non è una cosa semplice, soprattutto quando tale server si appoggia ad una normalissima linea ADSL. Infatti, la maggior parte dei mail provider utilizza tutta una serie di policy per limitare l’enorme mole di spam che, giornalmente, cerca di atterrare sui loro server.

 

smtp.jpg

Una di queste rende la vita difficile a tutti coloro che utilizzano una linea ADSL che prevede il rilascio di un IP pubblico dinamico, ovvero che varia ogni qual volta il router/modem si disconnette/riconnette. Per la precisione, i server dei mail provider controllano che l’IP del server SMTP che fa richiesta di inoltro email su una casella di posta che risiede sul loro POP3/IMAP4 abbia un record PTR associato.

Ora, senza addentrarmi troppo nei meandri del protocollo DNS e dei vari tipi di record che esso gestisce, per verificare che il nostro server SMTP abbia un record PTR associato è sufficiente lanciare il comando:

 nightfly@nightbox:~$ host smtp.nostrodominio.com
 smtp.nostrodominio.com has address 212.41.43.43

e successivamente:

 nightfly@nightbox:~$ host 212.41.43.43
 43.43.41.212.in-addr.arpa domain name pointer smtp.nostrodominio.it

Nel primo caso ho effettuato una query DNS diretta, richiedendo l’IP associato ad un determinato FQDN. Nel secondo caso, invece, ho eseguito una query DNS inversa, identificando un eventuale FQDN associato a quel particolare IP.

Ora, il comando host non è l’unico in grado di effettuare una query di questo tipo. Ad esempio, si potrebbe utilizzare dig oppure nslookup, a seconda del sistema operativo dal quale tale operazione viene effettuata.

Con dig potremmo lanciare una query diretta del tipo:

nightfly@nightbox:~$ dig smtp.nostrodominio.com

; <<>> DiG 9.7.0-P1 <<>> smtp.nostrodominio.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19912
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 0

;; QUESTION SECTION:
;smtp.nostrodominio.com.               IN      A

;; ANSWER SECTION:
smtp.nostrodominio.com.        41481   IN      A       212.41.43.43

;; AUTHORITY SECTION:
.                       427597  IN      NS      h.root-servers.net.
.                       427597  IN      NS      a.root-servers.net.
.                       427597  IN      NS      c.root-servers.net.
.                       427597  IN      NS      i.root-servers.net.
.                       427597  IN      NS      m.root-servers.net.
.                       427597  IN      NS      f.root-servers.net.
.                       427597  IN      NS      j.root-servers.net.
.                       427597  IN      NS      k.root-servers.net.
.                       427597  IN      NS      l.root-servers.net.
.                       427597  IN      NS      d.root-servers.net.
.                       427597  IN      NS      g.root-servers.net.
.                       427597  IN      NS      e.root-servers.net.
.                       427597  IN      NS      b.root-servers.net.

;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Dec 21 10:41:15 2012
;; MSG SIZE  rcvd: 260

Mentre, per la query inversa, è sufficiente lanciare il comando:

nightfly@nightbox:~$ dig -x  212.41.43.43

; <<>> DiG 9.7.0-P1 <<>> -x 212.41.43.43
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65061
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 0

;; QUESTION SECTION:
;43.43.41.212.in-addr.arpa.    IN      PTR

;; ANSWER SECTION:
43.43.41.212.in-addr.arpa. 79146 IN    PTR     smtp.nostrodominio.com.

;; AUTHORITY SECTION:
.                       427408  IN      NS      j.root-servers.net.
.                       427408  IN      NS      g.root-servers.net.
.                       427408  IN      NS      i.root-servers.net.
.                       427408  IN      NS      d.root-servers.net.
.                       427408  IN      NS      m.root-servers.net.
.                       427408  IN      NS      a.root-servers.net.
.                       427408  IN      NS      e.root-servers.net.
.                       427408  IN      NS      k.root-servers.net.
.                       427408  IN      NS      b.root-servers.net.
.                       427408  IN      NS      c.root-servers.net.
.                       427408  IN      NS      l.root-servers.net.
.                       427408  IN      NS      f.root-servers.net.
.                       427408  IN      NS      h.root-servers.net.

;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Dec 21 10:44:24 2012
;; MSG SIZE  rcvd: 285

E’ molto importante che nell’output relativo al suddetto comando, il contenuto di ;; ANSWER SECTION: non sia nullo. In quest’ultimo caso, infatti, significherebbe che il nostro server SMTP non è dotato di un record PTR e quindi, molto probabilmente, le email che cercherà di inoltrare a certi provider verranno respinte (bounced).

Ora, solitamente i record PTR non sono supportati dai DNS provider (uno su tutti GoDaddy), ma la loro gestione è a carico dell’ISP, poichè tali record sono direttamente associati agli indirizzi IP pubblici che quest’ultimo rilascia ai propri clienti.

Ma perchè controllare se esiste un record PTR associato all’indirizzo IP del nostro server SMTP? Su che cosa si basa la logica di questo sistema antispam elementare? Semplice: poichè gli IP che generano moli abnormi di email vengono inseriti all’interno di opportune blacklist (come SpamCop o SpamHouse), che, a loro volta, vengono consultate insistentemente dai mail provider per capire quali sono i server di posta che generano spam, nel caso in cui l’IP pubblico di un server SMTP fosse dinamico, basterebbe una connessione/disconnessione del router per aggirare tale blocco. Quindi, facendo una query DNS inversa, i server dei mail provider verificano che il PTR (se esiste) non contenga alcune keyword, quali dynamic, dyn, ecc.

Alcuni mail provider hanno definito delle regole un po’ più restrittive rispetto alla semplice risoluzione inversa dell’IP. Una di queste prevede che il banner del server SMTP che li contatta contenga un FQDN che sia identico a quello riportato nel record PTR.

Potete identificare il banner del vostro server SMTP utilizzando telnet, con il seguente comando:

C:\Userseldo>telnet smtp.nostrodominio.com 25

Il banner conterrà il prefisso 220 seguito dall’FQDN del server:

220 smtp.nostrodominio.com

Altro fattore molto importante da tenere in considerazione quando si configura un server SMTP riguarda la sicurezza. Infatti, è estremamente importante che il server preveda dei meccanismi di autenticazione prima di consentire l’inoltro di email (open relay, anyone?) e soprattutto utilizzi dei canali di comunicazione cifrati. In quest’ultimo caso, se il protocollo SMTP viene utilizzato in modo “standard”, l’invio delle credenziali da parte dell’utente avviene in chiaro, con pesanti risvolti dal punto di vista della confidenzialità. Per evitare ciò, è possibile costringere il server ad utilizzare SSL/TLS come protocollo di cifratura per tutte le connessioni inbound, oppure, se il server lo consente, utilizzare STARTTLS, molto più comodo e performante. Infatti, a differenza della cifratura SSL/TLS classica, la quale prevede l’apertura di una porta TCP apposita, ovvero la 456 (differente dalla classica TCP 25), con una maggiore mole di lavoro lato networking, il protocollo STARTTLS rende sicuro un canale nativamente non sicuro, rimanendo in ascolto sulla porta 25 ed appoggiandosi al protocollo SSL o TLS in base alla configurazione scelta (si, lo so, il nome STARTTLS può trarre in inganno).

Altro aspetto importante riguardante la sicurezza si basa sulla definizione dei domini autorizzati ad inoltrare le email. Non è un caso che fino a qualche anno fa, mail server di alcuni ISP italiani, come Telecom oppure Libero, consentissero, dopo la fase di autenticazione, di inoltrare email con dominio sorgente arbitrario. Mi spiego meglio: dopo la connessione ad uno dei suddetti server mediante un comunissimo client telnet, era possibile impostare arbitrariamente il campo MAIL FROM:, ad esempio presidente@governoitaliano.it. Ovviamente, non essendoci filtri, la mail sarebbe giunta a destinazione nonostante l’indirizzo mittente fraudolento (una manna per il phishing).

In definitiva, se volete testare la configurazione del vostro server SMTP potete utilizzare questo tool online:

http://mxtoolbox.com/diagnostic.aspx

Enjoy!

Client FTP+TLS gratuito

In questo post vi ho mostrato come abilitare il protocollo SSL/TLS su vsftpd. Un client Windows totalmente gratuito che supporta tale protocollo è Core FTP LE (potete scaricarlo da qui).

Ecco una configurazione di massima da applicare al client:

ftps_client.png

In soldoni, dovete rimuovere tutti i check relativi alle Opzioni SSL, lasciando solo la spunta su Windows SSL.

Alla prossima.

Abilitare FTPS esplicito su VSFTPD

Scenario

Il server FTPS esplicito (ovvero che utilizza la porta canonica TCP 21 per la ricezione dei comandi) si trova dietro un firewall, che filtra sia i tentativi di connessione in ingresso che quelli in uscita. Inoltre, tale firewall è nattato su indirizzo IP pubblico mediante uno screened router.

Anche il client è posizionato dietro un’accoppiata firewall/screened router e proprio per questo motivo si è deciso di utilizzare la modalità FTP passiva piuttosto che quella attiva.

Generazione del certificato

Ecco il comando per generare il certificato X.509 da utilizzare per l’autenticazione della fonte e per la confidenzialità:

nightfly@nightbox:~$ sudo openssl req -new -x509 -nodes -out vsftpd.pem -keyout vsftpd.pem

 

vsftpd, ftps, x.509, ssl, tls, filezilla client, fireftp, SmartFTP client

Configurazione del demone vsftpd

La configurazione del demone vsftpd per abilitare FTPS prevede le seguenti direttive:

rsa_cert_file=/etc/vsftpd.pem
rsa_private_key_file=/etc/vsftpd.pem

ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=NO
force_local_logins_ssl=YES

ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO

require_ssl_reuse=NO
ssl_ciphers=HIGH

pasv_address=<IP Pubblico Screened Router>
pasv_min_port=5012
pasv_max_port=5012

Le prime due righe servono a specificare il pathname del certificato e della chiave privata (a sua volta contenuta nel certificato).

La direttiva:

force_local_data_ssl=NO

fa in modo che il traffico dati non venga cifrato (preferisco usare questa impostazione perchè garantisce una maggiore velocità di trasferimento, l’importante è che vengano cifrati i comandi inviati al server e dunque anche le credenziali di accesso).

Come si evince dallo stralcio di configurazione che ho riportato, il protocollo di cifratura utilizzato è TLS (più robusto del suo predecessore SSL, il quale è stato esplicitamente disabilitato), mentre nelle ultime due righe ho specificato la porta su cui il client potrà instaurare la connessione dati. Tale porta dovrà essere consentita sul firewall, lanciando, ad esempio, il seguente comando (nel caso in cui si abbia a che fare con Netfilter):

iptables -A INPUT -i eth1 -m state --state NEW -m tcp -p tcp --dport 5012 -j ACCEPT

Inoltre, utilizzando il seguente paramentro di configurazione:

pasv_address=<IP Pubblico Screened Router>

si fa in modo che il server risponda al comando PASV inviando l’indirizzo IP pubblico dello screened router anzichè il proprio indirizzo privato. Inoltre, sarà necessario creare, direttamente sul router, un’opportuna regola di port forwarding per la porta TCP 5012.

Occorre precisare che la suddetta direttiva implica che su Netfilter venga abilitato il traffico in ingresso sulla porta in questione, poichè il modulo nf_conntrack_ftp funziona solo ed esclusivamente per i pacchetti che contengono gli indirizzi IP privati della linux box che funge da firewall.

Riavviamo vsftpd:

nightfly@nightbox:~$ sudo service vsftpd restart

e proviamo a connetterci al server, utilizzando un client che supporta FTPS. A tal proposito, vi sconsiglio di utilizzare FireFTP (in quanto presenta diversi bug relativi al protocollo FTPS) e FileZilla Client (perchè pialla la connessione se individua un certificato X.509 non firmato da una CA).

Solo con scopi di test (e per un periodo limitato) potete utilizzare un prodotto shareware, ovvero SmartFTP Client (lo trovate qui).

Enjoy.

Aggiornamento #1

A quanto pare le ultime versioni di FileZilla Client (quella che ho testato personalmente è la 3.17.0.1), consentono le connessioni verso il server FTPS anche in presenza di un certificato X.509 self-signed, a patto che all’interno della configurazione di VSFTPD vi sia la direttiva ssl_ciphers=HIGH.

File Transfer Protocol in tutte le salse

Chiunque abbia un background sistemistico avrà sentito parlare del File Transfer Protocol (FTP). Esso si basa sul protocollo TCP (lato trasporto) e dunque implica dei collegamenti di tipo affidabile. Inoltre, sfrutta le porte 20 e 21 (dette rispettivamente Data e Command), la prima per il trasferimento dei dati e la seconda per l’invio dei comandi.

Ma andiamo con ordine. Il suddetto protocollo può funzionare in 2 modalità:

1) attiva;

2) passiva.

Nel primo caso il client contatta il server sulla porta 21, inviandogli il comando PORT, in cui specifica appunto la porta ( >1023) su cui il server potrà provare a connettersi. A questo punto, dato che il server conosce la suddetta porta, lancerà un tentativo di connessione proveniente dalla porta 20 e diretta a quella precedentemente specificata dal client.

activeftp.gif

Nel secondo caso, invece, il client invia al server (sempre mediante la porta 21), il comando PASV (tentativo di connessione in modalità passiva) a cui il server risponde con il comando PORT (specificando una porta >1023). A questo punto sarà il client a tentare di instaurare la connessione dati verso la porta specificata dal server (a differenza di quanto avveniva nella modalità attiva).

passiveftp.gif

Ma quando utilizzare una o l’altra modalità? Semplice. Potrebbe accadere che il client si trovi dietro un firewall e che quindi i tentativi di connessione provenienti dal server vengano droppati. In questo caso è necessario utilizzare la modalità passiva, poichè in questo modo sarà sempre il client ad effettuare i tentativi di connessione, aggirando le restrizioni imposte dal suddetto firewall (essi, salvo eccezioni, solitamente non filtrano i tentativi di connessione in uscita da porte “alte”).

Avrete notato che la denominazione delle modalità avviene prendendo come punto di riferimento il server: se il traffico dati avviene mediante connessione instaurata dal server verso il client parliamo di modalità attiva; viceversa, se è il client ad effettuare la connessione dati verso il server parliamo di modalità passiva.

Ora, poichè il protocollo FTP è piuttosto datato, esso risulta abbastanza vulnerabile, soprattutto nei confronti operazioni di sniffing del traffico (poichè il trasferimento dati e l’invio di comandi viaggiano in chiaro).

Poprio per questo motivo negli anni si è cercato di correre ai ripari, creando dei meccanismi di cifratura che potessero garantire:

1) autenticazione della fonte;

2) confidenzialità.

Il primo tentativo di questo tipo prese il nome di SFTP (che sta per SSH File Transfer Protocol), ovvero un protocollo basato su SSH versione 2 (anche se esistono delle varianti basate su SSHv1, ma non tutti i client sono compatibili). Esso, più che un protocollo di trasferimento file, rappresenta una sorta di “file system remoto”, consentendo dunque di effettuare operazioni quali la creazione di nuove directory, la cancellazione dei file, ecc. Una restrizione del suddetto protocollo è rappresentata da SCP (Secure Copy), che consente solo il trasferimento dei file.

Entrambi i protocolli menzionati in precedenza, però, presentano una limitazione: necessitano di shell account per funzionare.

Proprio per evitare ciò è stato creato FTPS (FTP over SSL), che sfrutta i meccanismi di cifratura messi a disposizione dai protocolli SSL/TLS.

Esso può essere di due tipologie:

1) implicito;

2) esplicito.

Nel primo caso il server è in ascolto su una porta diversa dalla 21 (solitamente la 990) e tale tipologia è stata creata soprattutto per questioni di retrocompatibilità.

Nel secondo caso, invece, il server è in ascolto sulla classica porta 21 (FTP Command), ed accetta il comando esplicito AUTH <meccanismo di cifratura>, ad esempio AUTH TLS.

Dopo questa piccola premessa, prossimamente vedremo come abilitare FTPS su vsftpd.

A presto.

Creare una VPN SSL/TLS con OpenVPN

Lo standard de facto per realizzare i tunnel VPN è sicuramente IPSec. Tale protocollo è compatibile con dispositivi di vendor diversi (interoperabilità) e permette di creare dei tunnel altamente affidabili e robusti in termini di sicurezza. Per contro, però, esso risulta essere piuttosto complesso da configurare e proprio per questo motivo, soprattutto in ambito SOHO, si opta per soluzioni alternative. Una di queste è sicuramente OpenVPN, in grado di realizzare dei tunnel cifrati mediante i protocolli SSL/TLS.

Di seguito un breve howto in cui viene descritta la configurazione di OpenVPN per un bastion host dual-homed Linux.

Nello specifico, la rete locale è così definita:

Internet - Screened router - interfaccia UNTRUST del bastion - FW IPTABLES - interfaccia TRUST del bastion - LAN

Configurazione del server

Per prima cosa occorre installare l’applicativo in questione digitando il comando:

nightfly@nightbox:~$ sudo apt-get install openvpn

ad installazione completata, posizioniamoci nella directory /etc/openvpn e creiamo le subdir certificati e log:

 nightfly@nightbox:/etc/openvpn$ sudo mkdir log
 nightfly@nightbox:/etc/openvpn$ sudo mkdir certificati

Successivamente, creiamo i due file in cui verranno salvati i log:

 nightfly@nightbox:/etc/openvpn$ cd log
 nightfly@nightbox:/etc/openvpn/log$ sudo touch openvpn.log openvpn-status.log

A questo punto possiamo creare il file di configurazione:

nightfly@nightbox:/etc/openvpn$ sudo nano server.conf

il cui contenuto dovrà essere simile al seguente:

tls-server

port 5002

dev tun
ifconfig 192.168.110.1 192.168.110.2

persist-key
persist-tun

push "route 192.168.1.0 255.255.255.0"

#Percorsi dei certificati
ca certificati/ca.crt
cert certificati/server.crt
key certificati/server.key 
dh certificati/dh1024.pem
tls-auth certificati/ta.key 0 # 0 per il server ed 1 per il client, essenziale per la generazione dell'hash HMAC nell'header TLS

cipher BF-CBC
comp-lzo

keepalive 10 120

#log and security
user nobody
group nogroup
status log/openvpn-status.log
log-append log/openvpn.log
verb 3
scrip-security 2

Mediante la direttiva tls-server sto imponendo al mio bastion host di fungere da server per le sessioni TLS.

Con port specifico su quale porta OpenVPN deve rimanere in ascolto, mentre con dev tun sto scegliendo la tipologia di interfaccia da realizzare. A tal proposito, occorre precisare che i tipi di interfaccia che può tirar su OpenVPN sono 2, ovvero:

1) tun, per le VPN routed (livello 3);

2) tap, per le VPN bridged (livello 2).

Con ifconfig specifico l’indirizzo IP del tunnel VPN lato server (192.168.110.1) e lato client (192.168.110.2). Per la natura delle interfacce tun, trattasi di un collegamento point-to-point (/30).

con push “route 192.168.1.0 255.255.255.0” sto iniettando la rotta per raggiungere la mia LAN (interfaccia TRUST del bastion-host) sul client VPN.

Con le direttive:

 cipher BF-CBC
 comp-lzo

sto definendo il tipo di cifratura (BlowFish) ed il tipo di compressione (LZO – Lempel-Ziv-Oberhumer) messe in atto nel tunnel VPN.

Con keeplive faccio in modo che il tunnel rimanga attivo anche in assenza di traffico, mentre nella sezione #log and security definisco (in quest’ordine) i permessi, i file di log, il verbosity (accuratezza dei log) ed il livello di sicurezza di questo file di configurazione.

Salviamo il file in questione e procediamo con la generazione delle chiavi e dei certificati. Lato server i comandi da lanciare sono i seguenti:

nightfly@nightbox:~$ cd /usr/share/doc/openvpn/examples/easy-rsa/2.0/

nightfly@nightbox:~$ sudo nano vars

che andrà editato customizzando i campi indicati

nightfly@nightbox:~$ source ./vars

nightfly@nightbox:~$ ./clean-all

nightfly@nightbox:~$ ./build-ca

per la generazione del certificato relativo alla CA

nightfly@nightbox:~$ ./build-dh 

per lo scambio delle chiavi mediante l’algoritmo Diffie-Helman (leggasi premaster-key)

nightfly@nightbox:~$ ./build-key-server server

per la generazione della chiave privata associata al server, con relativa richiesta *.crt

nightfly@nightbox:~$ openvpn --genkey --secret ta.key

per la generazione della chiave segreta.

Una volta generate le chiavi ed i certificati copiamoli nella directory /etc/openvpn/certificati:

nightfly@nightbox:~$ sudo cp ca.crt server.crt server.key dh1024.pem ta.key /etc/openvpn/certificati

A questo punto possiamo avviare il demone OpenVPN:

nightfly@nightbox:~$ sudo service openvpn start

Verifichiamo che l’interfaccia tun0 sia attiva mediante il comando ifconfig:

nightfly@nightbox:~$ ifconfig

tun0     Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
indirizzo inet:192.168.110.1  P-t-P:192.168.110.2  Maschera:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisioni:0 txqueuelen:100
Byte RX:0 (0.0 B)  Byte TX:0 (0.0 B)

Configurazione di IPTABLES

Il bastion host ha DROP come azione di default per la chain FORWARD. Proprio per questo motivo, occorre consentire il transito del traffico dall’interfaccia tun0 a quella TRUST (eth0) e viceversa. Le regole da definire sono le seguenti:

 iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT
 iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT

Configurazione del client

A questo punto è possibile installare e configurare il client, che però dovrà utilizzare lo stesso file ca.crt del server, la medesima chiave ta.key del server ma una propria chiave privata (client.key) ed un proprio certificato (client.crt).

Per ambienti Windows vi consiglio questo client Openvpn (se utilizzate Windows 7 fate riferimento a questa pagina per la procedura di installazione).

Quest’altro client, invece, evitatelo come la peste (in quanto bacatissimo, d’altronde è una versione alpha).

Ad installazione completata potrete utilizzare il seguente file di configurazione:

remote <indirizzo IP pubblico del server OpenVPN> 5002
client
tls-client
nobind
dev tun
ifconfig 192.168.110.2 192.168.110.1
pull

ca key/ca.crt
cert key/client.crt
key key/client.key
tls-auth key/ta.key 1

persist-key
persist-tun

cipher BF-CBC

comp-lzo

keepalive 10 120

scrip-security 2

verb 3

Da notare che gli algoritmi di compressione e cifratura devono essere i medesimi di quelli utilizzati dal server, pena l’impossibilità di connettersi allo stesso.

Diagnostica

A connessione VPN avvenuta, potete verificare la raggiungibilità attraverso il tunnel degli host della LAN mediante un seplice ping, ad esempio:

ping 192.168.1.2

Se il ping dovesse andare in timeout, provate a mettere in ascolto tcpdump sulle interfacce tun0 ed eth0 e controllate la tabella di routing del vostro bastion host (attraverso il comando route).

Verificate inoltre che la rotta verso la LAN sia stata correttamente iniettata sul client (Windows), digitando:

route print

Fine del post, bye.

SSL e Diffie-Helman

Il protocollo SSL (Secure Socket Layer) prevede l’uso di alcuni algoritmi di cifratura nell’ambito dell’handshake. Uno di questi è il Diffie-Helman, che può essere adoperato in 3 varianti:

1) Anonymous Diffie-Helman (A_DH): il numero primo p e la radice primitiva a ad esso associata vengono inviati senza autentica. In realtà questa è la modalità di funzionamento “standard” del Diffie-Helman;

2) Fixed Diffie-Helman (F_DH): p ed a del server https vengono firmati mediante un certificato X.509 trusted. La chiave segreta è statica, ovvero è la medesima per ogni sessione.

3) Ephemeral Diffie-Helman (E_DH): viene generata una secret key (relativa al protocollo DH) temporanea (one-time o ephimeral), ovvero valida per una sola sessone, firmata mediante la propria chiave segreta RSA (o DSS). La controparte ne verificherà l’autenticità attraverso la chiave pubblica del mittente. Anche in questo caso si possono utilizzare dei certificati X.509 contenenti i paramentri a e p per semplificare le operazioni di autentica dei due peer.

Fine del post, alla prossima.

Apache 2: abilitare SSL

In questo post abbiamo visto come configurare LAMP. Adesso vedremo come abilitare SSL sul nostro server Web Apache.

Per prima cosa è necessario generare un certificato SSL. A tale scopo possiamo utilizzare openSSL:

nightfly@nightbox:/etc/apache2$ sudo openssl req $@ -new -x509 -days 365 -nodes -out /etc/apache2/apache.pem -keyout /etc/apache2/apache.pem

dove il parametro -x509 indica proprio che si tratta di un certificato appartenente allo standard X.509 ed il parametro -days specifica la durata della validità relativa al certificato stesso.

Una volta digitato il comando sopra citato verrà richiesto l’inserimento di tutta una serie di parametri necessari per caratterizzare il certificato stesso, quali nazione, città, nome della società, indirizzo e-mail, ecc.

Ora occorre abilitare il modulo SSL di Apache, sfruttando il tool a2enmod (apache2 enable module):

nightfly@nightbox:/etc/apache2$ sudo a2enmod ssl

E’ necessario, inoltre, abilitare il virtual host SSL:

nightfly@nightbox:/etc/apache2$ sudo a2ensite default-ssl

Fatto ciò sarà necessario mettere in ascolto Apache sulla porta 443 (HTTPS), editando il file ports presente nella DIR /etc/apache2/ports. Il file in questione dovrà apparire nel seguente modo:

nightfly@nightbox:/etc/apache2$ cat ports.conf
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default
# This is also true if you have upgraded from before 2.2.9-3 (i.e. from
# Debian etch). See /usr/share/doc/apache2.2-common/NEWS.Debian.gz and
# README.Debian.gz

NameVirtualHost *:80
Listen 80

<IfModule mod_ssl.c>
# SSL name based virtual hosts are not yet supported, therefore no
# NameVirtualHost statement here
Listen 443
</IfModule>

Posizioniamoci ora sulla cartella available-sites:

nightfly@nightbox:/etc/apache2$ cd sites-available/

ed editiamo il file default-ssl aggiungendo le seguenti righe di codice:

 SSLEngine On
 SSLCertificateFile /etc/apache2/apache.pem

e rimuovendo:

SSLCertificateFile /etc/ssl/private/ssl-cert-snakeoil.key

Riavviamo Apache mediante il comando:

nightfly@nightbox:/etc/apache2$ /etc/init.d/apache2 restart

E colleghiamoci al nostro server anteponendo HTTPS (e non HTTP) all’indirizzo. Accettiamo il certificato (anche se il browser lo segnala come inattendibile)… et voilà, siamo dentro il nostro sito Web.

A presto.