Archivi tag: centos

CentOS 6 e PHP 5.3.3: cifratura del codice sorgente mediante BLENC (Blowfish Encoder for PHP source scripts)

A quanti di voi sarà capitato di dover cifrare del codice PHP scritto di vostro pugno, magari perchè contenente informazioni sensibili, quali, ad esempio, username e password di accesso al database?

Ebbene, un metodo semplice per ottenere quanto sopra consiste nell’installare il modulo PHP BLENC (che è ancora in versione beta – qui trovate il manuale). Tale operazione può essere effettuata in modo semiautomatico utilizzando il tool pecl presente sulla nostra macchina.

PHPPrima di procedere, è necessario verificare che il suddetto applicativo sia già installato, digitando il comando:

[root@linuxbox ~]# which pecl

il cui output dovrebbe essere:

/usr/bin/pecl

Successivamente, potremo procedere con l’installazione vera e propria del modulo BLENC, lanciando il comando:

[root@linuxbox ~]# pecl install -f blenc

A questo punto potremo creare il file blenc.ini da posizionare all’interno della dir /etc/php.d, il cui contenuto dovrà  essere simile al seguente:

; Enable blenc extension module
extension=blenc.so

All’interno del file /etc/php.ini, utilizzando la entry blenc.key_file, possiamo definire il percorso in cui trovare le chiavi per decifrare gli scrip criptati, ovvero:

[blenc]

blenc.key_file = /usr/local/etc/blenckeys

Ora passiamo alla creazione del file encrypt.php, il cui contenuto dovrà essere:

<?php

$file_to_crypt=file_get_contents("security.php");

$key=blenc_encrypt($file_to_crypt, "connect.php");

$key_file = ini_get('blenc.key_file');

file_put_contents($key_file, $key."\n", FILE_APPEND);

?>

il quale ci consentirà di cifrare gli scrip di nostro interesse.

In particolare, security.php contiene il codice sorgente che vogliamo criptare, connect.php sarà il nostro file cifrato e la chiave generata mediante la funzione blenc_encrypt() verrà salvata all’interno dell’apposito file /usr/local/etc/blenckeys, ricavato mediante il parsing della direttiva blenc.key_file presente in /etc/php.ini.

Da notare che all’interno della funzione file_put_contents(), il secondo argomento contiene il carattere \n (newline), poichè per il corretto funzionamento del modulo è necessario che per ogni riga sia presente una sola chiave (dove ogni chiave si riferisce univocamente ad un file cifrato). Tale “accortezza” non è presente all’interno del manuale ufficiale.

Eseguiamo, quindi, il file encrypt.php da linea di comando:

[root@linuxbox ~]# php encrypt.php

il quale genererà il file cifrato connect.php.

Come ultimo step riavviamo httpd:

[root@linuxbox ~]# service httpd reload

ed abbiamo finito.

Alla prossima.

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.

CentOS e Postfix: utilizzare smtp.gmail.com come smarthost

Premessa

In questo post ho mostrato come configurare uno degli MTA più utilizzati dalle distro *buntu (ovvero exim4) affinchè riesca a sfruttare un server SMTP pubblico (smarthost o relayhost) per l’invio delle nostre email.

Tale configurazione si rende necessaria nel caso in cui la nostra linea ADSL non sia dotata di un indirizzo IP pubblico statico da associare ad un FQDN (pubblico).

postfixConfigurazione di Postfix

La configurazione del nostro MTA si suddivide in 2 fasi: la prima riguarda la procedura di autentica allo smarthost; la seconda fa in modo che tutti gli indirizzi email locali vengano “tradotti” in indirizzi leciti (@gmail.com).

Per prima cosa creiamo il file relay_passwd all’interno della directory /etc/postfix:

[root@linuxbox postfix]# touch relay_passwd

il cui contenuto dovrà avere la seguente struttura:

[smtp.gmail.com]  username:password

A questo punto possiamo “postmappare” il suddetto file:

[root@linuxbox postfix]# postmap relay_passwd

Ora possiamo editare il file /etc/postfix/main.cf, aggiungendo le seguenti direttive:

relayhost = [smtp.gmail.com]
smtp_use_tls=yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/relay_passwd
smtp_sasl_security_options =

Il meccanismo di autentica utilizzato è SASL (vedi qui per approfondire). Inoltre, poichè le credenziali di autentica devono essere inviate allo smarthost tramite canale cifrato (STARTTLS), è stato necesario abilitare l’uso di tale protocollo da parte del client SMTP di Postfix.

A configurazione dell’autentica ultimata, possiamo editare il file /etc/postfix/generic, aggiungendo delle entry simili alle seguenti:

root@hostname.local.loc        vostro.indirizzo@gmail.com
nagios@hostname.local.loc       vostro.indirizzo@gmail.com

e più in generale:

username@hostname.local.loc        vostro.indirizzo@gmail.com

“Postmappiamo” anche il suddetto file:

[root@linuxbox postfix]# postmap generic

Ed aggiungiamo la seguente direttiva al file /etc/postfix/main.cf:

smtp_generic_maps = hash:/etc/postfix/generic

Ricarichiamo la configurazione di Postfix:

[root@linuxbox postfix]# service postfix reload

ed abbiamo finito.

Alla prossima.

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

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

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

router_ciscoInstallazione

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

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

[root@linuxbox opt]# yum install rancid

Configurazione

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

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

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

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

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

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

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

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

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

il cui contenuto dovrà essere:

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

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

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

il cui contenuto dovrà essere:

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

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

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

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

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

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

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

Il cui contenuto dovrà avere il seguente formato:

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

ad esempio:

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

Assegniamo i giusti permessi al file appena creato:

[rancid@linuxbox opt]$ chmod 700 .cloginrc

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

[rancid@linuxbox opt]$ locate clogin

il cui output potrebbe essere:

/usr/libexec/rancid/clogin

quindi digitiamo:

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

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

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

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

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

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

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

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

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

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

[root@linuxbox ~]# rancid -V

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

IP;marca;stato

anzichè:

IP:marca:stato

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

192.168.1.1;cisco;up
192.168.1.2;cisco;up

mentre per il gruppo branch-office avremo:

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

Testiamo RANCID sui gruppi:

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

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

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

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

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

[root@linuxbox rancid]# newaliases

e passiamo all’integrazione con tac_plus.

Integrazione tra RANCID e tac_plus

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

user = rancid {

        login = des CjzxbdLRbG6sY

        service = exec {
                priv-lvl = 15
        }

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

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

Ricarichiamo la configurazione del demone tac_plus:

[root@linuxbox rancid]# service tac_plus reload

ed abbiamo finito.

Alla prossima.

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

Scenario

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

Problema

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

Soluzione

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

syslog

Configurazione della Linux box e del syslog server

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

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

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

[root@linuxbox ~]# service iptables restart

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

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

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

*.* -?CiscoVPN

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

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

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

[root@linuxbox ~]# service rsyslog restart

Configurazione dei dispositivi di rete

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

Per configurare la sorgente SNTP è sufficiente lanciare il comando:

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

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

Router(config)# sntp server 193.204.114.232

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

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

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

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

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

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

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

A presto.

CentOS 6 e contromisure agli attacchi DDoS: regole di firewalling basate sui netblock dei provider italiani

Scenario

Server Web Linux (basato su Apache) con CentOS 6 a bordo, dotato di 16 GB di RAM e 2 CPU con 4 core ciascuna. Totale assenza di firewall di frontend, ergo il filtraggio del traffico viene demandato direttamente al server in questione.

Problema

Da N giorni la suddetta macchina è vittima di attacchi di tipo DDoS, provenienti per lo più da IP stranieri (macchine zombie facenti parte di una botnet).

Soluzione

Creare delle regole netfilter ad hoc (mediante iptables), consentendo solo ed esclusivamente i netblock degli ISP italiani. Inoltre, per evitare che l’attaccante possa avvalersi di qualche proxy “aperto” made in Italy, sono stati bloccati ulteriori IP pubblici recuperati da un sito specifico. Infine, sono stati consentiti gli IP pubblici degli spider (aka crawler) utilizzati dai più importanti motori di ricerca (Google, Bing, Yahoo!), in modo tale da impedire che il sito Web hostato sul server venga penalizzato in termini di ranking.

Step 1: consentire gli IP italiani

Lo scrip bash (permit_ita.sh) che esegue tale operazione è il seguente:

#!/bin/bash

iptables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp --dport 443 -j DROP

wget http://www.ipdeny.com/ipblocks/data/countries/it.zone -O ip_italiani.txt

while read line
do
    iptables -I INPUT -s $line -p tcp --dport 80 -j ACCEPT
    iptables -I INPUT -s $line -p tcp --dport 443 -j ACCEPT
done < ip_italiani.txt

iptables-save

Il suo funzionamento è banale: per prima cosa vengono create 2 regole netfilter per droppare tutto il traffico HTTP/HTTPS diretto al sito Web. Successivamente viene scaricato (mediante wget) il contenuto del sito http://www.ip-deny.com/ipblocks/data/countries/it.zone, in cui è presente l’elenco dei netblock italiani. Infine, vegnono consentiti i netblock in questione.

Step 2: bloccare i proxy made in Italy

A questo punto possiamo procedere con il blocco dei proxy “open” italiani. Per fare ciò possiamo utilizzare il seguente scrip bash (block_proxy.sh):

#!/bin/bash

wget --user-agent="Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092416 Firefox/3.0.3"  http://spys.ru/free-proxy-list/IT/ -O lista_proxy.txt

grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" lista_proxy.txt | uniq > ip_proxy.txt

while read line
do
    iptables -I INPUT -s $line -p tcp --dport 80 -j DROP
    iptables -I INPUT -s $line -p tcp --dport 443 -j DROP
done < ip_proxy.txt

iptables-save

Il sito target (ovvero quello che contiene la lista dei proxy da bloccare) è http://spys.ru/free-proxy-list/IT/, il quale effettua un filtraggio dei client che possono accedervi utilizzando lo User Agent come discriminante (quindi non è possibile scaricarne il contenuto se non si effettua uno spoofing di tale parametro tramite wget e l’opzione –user-agent).

Inoltre, poichè il contenuto del sito non è una “lista piatta” di IP pubblici, è stato necessario filtrare il tutto utilizzando un’espressione regolare in grado di individuare i 4 ottetti tipici dell’IPv4.

Step 3: consentire gli spider dei motori di ricerca

Come ultima fase, occorre consentire gli IP degli spider, utilizzando tale scrip (permit_spider.sh):

#!/bin/bash

while read line
do
    iptables -I INPUT -s $line -p tcp --dport 80 -j ACCEPT -m comment --comment "crawler"
done < ip_spider.txt

iptables-save

Il contenuto del file ip_spider.txt potete ricavarlo da qui (Google), qui (Yahoo!) e qui (Bing ed altri).

A configurazione completata, possiamo saggiare il carico sul server (intento a droppare il traffico proveniente dalla botnet) utilizzando htop.

Se la macchina regge e le risorse hardware locali non sono allo stremo potremo finalmente dire di aver vinto.

Alla prossima.

Demone nodejs per CentOS 6

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

nodejs

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

[root@linuxbox ~]# node server.js

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

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

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

 #!/bin/bash

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

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

USER="node"

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

SERVER="$ROOT_DIR/server.js"

for i in {1..8}

do

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

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

        do_status()
        {
                PORT="900$i"

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

done

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

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

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

exit $RETVAL

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

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

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

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

#!/bin/bash

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

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

USER="node"

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

SERVER="$ROOT_DIR/server.js"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

exit $RETVAL

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

Non ci rimane che rendere il demone eseguibile:

[root@linuxbox ~]# chmod +x nodejs

spostarlo nella directory /etc/init.d:

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

ed avviarlo:

[root@linuxbox ~]# service nodejs start

E’ tutto.

checksecurity.sh: script bash per monitorare gli aggiornamenti critici su CentOS 6

Poichè devo gestire un elevato numero di VM *nix, mi è indispensabile riuscire ad automatizzare quanti più task possibili, tenendo sempre a mente tale regola:

                                                                        a good sysadmin is a lazy one

Uno di questi task riguarda le notifiche per gli aggiornamenti critici di sistema, soprattutto per ciò che concerne l’aspetto della cyber security.

A tal proposito, ho realizzato uno scrip bash in grado di verificare la presenza di eventuali aggiornamenti e di inviarmi successiva notifica via email.  Starà a me, in un secondo momento, decidere quando e se installare gli aggiornamenti proposti.

I tool necessari al suo funzionamento sono 2, ovvero yum-security e mutt. Procediamo dunque con la loro installazione mediante yum:

[root@linuxbox ~]# yum install yum-plugin-security mutt -y

bashEd ecco lo scrip:

#!/bin/bash

logfile=/var/log/checksecurity.log

data=`date +%d-%m-%Y`

destinatario=vostro.indirizzo@email.it

echo "$data: Checking for security updates..." >> $logfile

yum clean all

yum list-security >> /root/checksec/tmp

security=`cat /root/chcksec/tmp | grep security | grep -v Loaded`

bugfix=`cat /root/checksec/tmp | grep bugfix`

cve=`cat /root/checksec/tmp | grep cve`

if [ -n "$security" ];then
echo "$data: security updates available" > /root/checksec/out
fi

if [ -n "$bugfix" ];then
echo "$data: bugfix updates available" >> /root/checksec/out
fi

if [ -n "$cve" ];then
echo "$data: cve updates available" >> /root/checksec/out
fi

if [ -s /root/checksec/out ];then

cat /root/checksec/tmp >> /root/checksec/out

cat /root/checksec/out >> $logfile

cat /root/checksec/out | mutt -s "GESTORE01: security, bugfix and cve system report" $destinatario

rm /root/checksec/out

else

echo "$data: Nothing to do, exiting..." | tee -a $logfile

fi

rm /root/checksec/tmp

exit 0;

Ci rimane solo rendere lo scrip eseguibile (chmod +x /root/checksec/checksecurity.sh) ed aggiungerlo a crontab.

Alla prossima.

Modificare l’MTA di default su CentOS 6

Ho già parlato più volte di exim, definendolo come uno degli MTA più performanti in circolazione.

Exim_logo.jpg

Essendo la mia formazione sui sitemi *nix derivata principalmente da Debian, preferisco usare il suddetto MTA piuttosto che postfix e qmail. Badate bene, però, che non sto facendo un paragone tra i software in questione: la mia è semplicemente una scelta di ordine pratico, ovvero preferisco avere a che fare con applicativi che già conosco perchè, in tal modo, posso ridurre notevolmente la percentuale di errori ed i tempi di troubleshooting.

Dopo questa breve premessa veniamo al dunque: da 3 anni a questa parte i server sotto la mia gestione sono principalmente dei CentOS/Red Hat, i quali si avvalgono di postfix come MTA di default.

Per sostituire postfix con exim occorre seguire questi step:

1) scaricare exim mediante il comando:

 [root@server exim]#  yum install exim

2) stoppare postfix e rimuoverlo dalla lista dei demoni da avviare al boot:

[root@server exim]#  service postfix stop 

[root@server exim]#  chkconfig –level 2345 postifx off

3) abilitare exim come MTA di default ed avviarlo:

[root@server exim]#  chkconfig –level 2345 exim on

[root@server exim]#  service exim start

A questo punto possiamo fare una prova di invio email. Per far ciò è sufficiente utilizzare il comando mail e dare un’occhiata al file di log associato ad exim, ovvero /var/log/exim/main.log.

Se effettivamente l’invio avviene mediante exim il suddetto file dovrebbe popolarsi di nuove entry. Nel caso in cui ciò non avvenga possiamo lanciare il comando:

[root@server exim]# alternatives –display mta

per visualizzare gli MTA disponibili e successivamente procedere con la scelta del Mail Transfer Agent da utilizzare:

[root@server exim]# alternatives –config mta

There are 2 programs which provide ‘mta’.

  Selection    Command
———————————————–
*+ 1           /usr/sbin/sendmail.postfix
   2           /usr/sbin/sendmail.exim

Enter to keep the current selection[+], or type selection number: 2

digitando semplicemente 2.

Tentiamo l’invio di una nuova email e tutto dovrebbe funzionare correttamente.

A presto.

Auguri!

Ecco una strofa di “Jingle Bells”, rivista in chiave linuxiana:

  Jingle bells, CentOS swells, when you add new stuff,
  Program here, Program there, never is enough, hey!
  gzip file, bz2, Fun for me and you!
  Time flies by in a bash shell if you've nothing else to do!

Quale modo migliore per augurarvi un felice Natale? 😉