Pagine

mercoledì 10 agosto 2011

A tutto mod ... parte prima

Al giorno d'oggi qualsiasi apparato che svolga una qualsivoglia operazione monta linux. Non è un'ottima notizia? :) Se c'è linux allora ci si può installare qualsiasi cosa, a patto ovviamente di rispettare i limiti fisici (processore, storage e memoria) del nostro apparato. Ad esempio potrebbe essere carino installare direttamente sul router amule oppure un client per torrent... importare sul router magari via iscsi una partizione da un altro pc... oppure inserire un client vpn diverso da quelli gestibili dalla configurazione del router stesso... si possono fare un sacco di cose :).

La cosa è relativamente semplice (... :) ) se l'apparato in questione garantisce un accesso interattivo al sistema operativo, ovvero permette di accedere con telnet, rsh o ssh o attraverso una console seriale. Spesso questa modalità di connessione non è garantita, infatti i produttori si "proteggono" da modifiche più o meno volontarie bloccando la possibilità di accedere direttamente al sistema operativo dell'apparato stesso.
Questa cosa non è nuova, più o meno una decina di anni fa venivano installati i bot per irc direttamente sui router D-link, sfruttando quella che ormai spero sia una vulnerabilità chiusa del software .
Ho acquistato, qualche tempo fa, un media player. Precisamente il media titan CMT2DW.

Dopo averlo usato per un po' mi sono accorto che andava un po' stretto rispetto alle mie esigenze. Ad esempio il trasferimento al disco interno del mediaplayer è una operazione un po' rognosa, dipende dal sistema operativo del pc che si usa e, in genere, ha delle performance scadenti. Ovviamente l'apparecchio, per quanto dicevo prima, non ha la possibilità di ricevere connessioni via telnet o ssh direttamente.
L'approccio iniziale è stato di togliere l'hard disk interno dal mediaplayer e di connetterlo ad un pc per capire cosa ci fosse dentro. Dato che per me è stato impossibile riuscire decomprimere il fs che porta il sistema operativo del media player ho googolato un po' fino a trovare il wydev-mod4.1. Dopo un po' di smadonnamenti sono riuscito ad applicare il firmware modificato, riuscendo così ad aprire il telnet, l'ftp e altri servizi di base.
A questo punto ci si può divertire, sempre tenendo conto delle limitazioni sopra esposte.
La mia idea, per questo articolo, è di far diventare il mediaplayer un mediacenter. Ovvero voglio che tutti i media risiedano direttamente sull'apparato e che questo non si occupi solo di fare da player per i flussi esterni. In parte il firmware installato è servito a questo scopo, infatti tra le cose che vengono rese disponibili c'è anche la possibilità di riprodurre flussi internet (tramite servizi di sottoscrizione tipo webservices wyplay). Inoltre il fw modificato permette di gestire tutti i servizi installati sul player attraverso una comoda interfaccia web.
Il progetto prevede:
*) iscsi client. Per poter condividere attraverso la lan fs con contenuti multimediali e farli vedere come locali sul player
*) amule server. (che si può controllare con una gui da un pc collegato alla stessa lan)
*) web radio player
... e altre cosucce che eventualmente mi verranno in mente.
Intanto per iniziare è bene conoscere il sistema sul quale dovremo lavorare.
Collegandosi in telnet facciamo alcuni test per determinare correttamente l'hw:


karmam@backup:~$ telnet 10.0.0.15
Trying 10.0.0.15...
Connected to 10.0.0.15.
Escape character is '^]'.


wydev-mod-v4.1 (Future is ours!)
/ $ id
uid=0(root) gid=0(root)
/ $ uname -a
Linux (none) 2.6.23.17_stm23_0118-MDBOXA_V0100_sti7109-26 #1 PREEMPT Wed Sep 2 20:20:45 CEST 2009 sh4 unknown
/ $ df -h
Filesystem Size Used Available Use% Mounted on
/dev/sda6 249.8M 169.6M 80.2M 68% /
tmpfs 512.0k 12.0k 500.0k 2% /dev
none 34.1M 0 34.1M 0% /dev/shm
tmpfs 1.0M 4.0k 1020.0k 0% /tmp
tmpfs 1.0M 200.0k 824.0k 20% /var
tmpfs 512.0k 0 512.0k 0% /media
/dev/sda3 464.0G 227.6M 463.8G 0% /wymedia
/dev/sda6 249.8M 169.6M 80.2M 68% /proc/wybox/WC
/ $ free
total used free shared buffers
Mem: 69784 64460 5324 0 0
Swap: 257032 96 256936
Total: 326816 64556 262260
/ $ cat /proc/cpuinfo
machine : Wyplay MediaBox Prototype Board
processor : 0
cpu family : sh4
cpu type : STb7109
cut : 3.2
cpu flags : fpu
cache type : split (harvard)
icache size : 16KiB (2-way)
dcache size : 32KiB (2-way)
bogomips : 263.16

Vediamo subito che ci sono poco più di 80MB liberi sulla partizione destinata al SO e circa 5MB liberi di memoria... pochino.
Proseguendo l'analisi:

/ $ fdisk -l

Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sda1 1 122 979964+ 83 Linux
/dev/sda2 123 154 257040 82 Linux swap
/dev/sda3 155 60735 486616882+ 83 Linux
/dev/sda4 60736 60801 530145 5 Extended
/dev/sda5 60736 60736 8032 83 Linux
/dev/sda6 60737 60768 257039+ 83 Linux
/dev/sda7 60769 60769 8032 83 Linux
/dev/sda8 60770 60801 257039+ 83 Linux

Disk /dev/sdb: 256 MB, 256901120 bytes
16 heads, 32 sectors/track, 980 cylinders
Units = cylinders of 512 * 512 = 262144 bytes

Device Boot Start End Blocks Id System
/dev/sdb1 1 77 19711+ 83 Linux
/dev/sdb2 78 980 231168 83 Linux

Nella partizione /dev/sdb2 ci sono i due file core e kernel che rappresentano la versione originale del "firmware" e del SO, quella per intenderci che viene ripristinata in caso di hard reset del player.
Il kernel, come ci si poteva attendere, è una immagine uboot:

karmam@sphynx:~/temp/test$ file kernel
kernel: u-boot legacy uImage, MDBOXB_V0100_7109, Linux/SuperH, OS Kernel Image (gzip), 1612455 bytes, Thu Sep 3 11:07:48 2009, Load Address: 0x84001000, Entry Point: 0x84002000, Header CRC: 0x1C6A1A4A, Data CRC: 0x9AE4C8C9
karmam@sphynx:~/temp/test$ mkimage -l kernel
mkimage: ERROR: "kernel" has bad header checksum!

Ho l'impressione che qualcosa sia cambiato (...o sia stato customizzato) nella struttura dell'immagine per uboot.
Praticamente il file "kernel" dovrebbe contenere (a occhio) il kernel e il l'immagine iniziale per poter caricare il file "core", che poi è l'intero rootfs (anche questa è una mia idea... di documenti precisi in merito ce ne sono pochi, per cui bisogna fare delle prove...)
Non c'è niente di specifico in internet... o almeno non l'ho trovato... però ci sono spiegazioni per altre architetture o formati... un po' di fantasia :)
alcuni link:
qui [link https://oss.renesas.com/modules/document/?Getting%20Started%20with%20SH4%20and%20QEMU ] c'è l'esempio di come creare una macchina virtuale con qemu che emuli il processore sh4
questo è per la medesima piattaforma che uso io... ma non ha funzionato :| [link http://en.wikibooks.org/wiki/Media_Centers_Based_on_Wyplayer/Firmware#Extraction_Script] però mi ha dato delle idee...
in questo [link http://balau82.wordpress.com/2010/04/12/booting-linux-with-u-boot-on-qemu-arm/] invece trattano più o meno la stessa cosa per piattaforma ARM e qemu.
L'idea generale è di creare una macchina virtuale con qemu nella quale compilare i vari sw che andrò ad installare, poi, nel player. L'idea folle che mi è venuta in mente è di ricostruire tutto il sistema... ovvero ricompilare tutto in una nuova versione e soprattutto ricompilare tutto col supporto delle uclib invece delle gclib. Questo dovrebbe garantire una maggiore disponibilità di spazio ma, soprattutto, di memoria. poi vedrò come installare il tutto nel player ;)

fine prima parte :) alla seconda ci sto lavorando


Read More...

venerdì 5 agosto 2011

Programmare per flash - Parte seconda: implementare una mappa

Passiamo ora alla realizzazione del primo step: implementare una mappa di google all'interno di un swf.
Apriamo flashdevelop, dal menù Project selezioniamo new project. Ci sono diversi tipi di progetto che si possono realizzare, nel nostro caso scegliamo Flex 4 project. Nella parte destra della finestra di flashdevelop dovrebbe esserci tutta l'alberatura del progetto, clickando su src si apre la struttura della directory. Clickiamo su Main.mxml due volte per aprire il file. Verrà visualizzato qualcosa del tipo:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Application>
Questo è lo scheletro applicativo, prima di iniziare a spiegare un po' aggiungo un paio di righe...il tutto diventa così:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
]]>
</fx:Script>
</s:Application>
Rispetto al template proposto ho aggiunto quanto compreso tra i tag <fx:script> e </fx:script>. All'interno di questi tag inseriremo la parte di AS3, ovvero il codice vero e proprio.
All'esterno dei tag fx:script andremo invece ad inserire la parte visuale (bottoni, label e quant'altro necessario). La parte visuale si potrebbe realizzare direttamente in as3, quindi alla fine si potrebbe usare solo as3 per realizzare il progetto... tuttavia preferisco tenere separata la parte di script da quella visuale.
Detto questo... si inizia :-)
Nella pagina di google map api c'è indicato come usare le varie funzioni all'interno di flash. Scarichiamo l'sdk di google da qui, decomprimiamo il file e copiamo nella cartella lib del nostro progetto la libreria di funzioni (ad oggi l'ultima versione disponibile, è la map_flex_1_20.swc). Questa è l'interfaccia verso le funzioni per la gestione delle mappe. Bisogna, quindi, indicare a FlashDevelop di utilizzare la libreria appena inserita. Dal menù project -> properties -> Compiler options inserire nell'opzione "SWC Include libraries" il path completo dove è stato inserito il file swc precedentemente estratto.
Inseriamo nel sorgente del nostro programma le direttive per includere le funzioni di google maps:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<maps:Map xmlns:maps="com.google.maps.*"
id="map"
width="100%"
height="100%"
sensor="false"
key="ABQIAAAAGLRIbGJ9z9R5jwNg-BFqjRT4o7HUAS9HNfPTIbLfF9Tm_VUYmxQvVVnUnJt-sc5edCcZvK41Q2S1wQ" />

<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[

//import minimi per google maps
import com.google.maps.LatLng;
import com.google.maps.Map;
import com.google.maps.MapType;

]]>
</fx:Script>
</s:Application>
La parte in rosso indica che l'oggetto mappa, con le caratteristiche indicate, sarà referenziato attraverso l'id "map". Quindi l'oggetto map a tutti gli effetti rappresenterà la mappa sulla quale lavoriamo. La parte in blu, invece, serve ad importare le varie classi per accedere, nella parte di scripting, alle funzioni per operare sulla mappa.

Questo è la schermata di flashdevelop con il codice...

e questo è quello che si ottiene facendolo eseguire (in questo caso all'interno di flash player)
Ecco la terra :-D facile no?

Aggiungiamo qualcosa... quando viene caricata la mappa viene notificato all'applicazione che la mappa è caricata e quindi è possibile effettuare le operazioni seguenti. Per far si che l'evento sia intercettato dal programma aggiungiamo le righe seguenti (in verde)
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<maps:Map xmlns:maps="com.google.maps.*"
id="map"
mapevent_mapready="onMapReady(event)"

width="100%"
height="100%"
sensor="false"
key="ABQIAAAAGLRIbGJ9z9R5jwNg-BFqjRT4o7HUAS9HNfPTIbLfF9Tm_VUYmxQvVVnUnJt-sc5edCcZvK41Q2S1wQ" />

<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

<fx:Script>
<![CDATA[
//import per google maps
/import minimi per google maps
import com.google.maps.LatLng;
import com.google.maps.Map;
import com.google.maps.MapType;

private function onMapReady(event:Event):void
{

}

]]>

</fx:Script>
</s:Application>
In questo modo quando google comunica all'applicazione che il caricamento delle mappe è completo possiamo procedere con le nostre attività.

Abbiamo detto che vogliamo usare una mappa dell'Italia. Con google maps andiamo a vedere le coordinate da usare come centro e il fattore di zoom da utilizzare per visualizzare lo stivale per intero.

Diciamo di scegliere come centro un punto tra Roma e L'Aquila con coordinate (42.309815,13.027954). Usiamo 6 come fattore di zoom. Andiamo ad inserire il codice per centrare la mappa dove vogliamo noi:
private function onMapReady(event:Event):void

{

map.setCenter(new LatLng(42.309815,13.027954), 6, MapType.NORMAL_MAP_TYPE);

}

Resta fuori un po' di Sicilia... bisogna allargare il canvas (ovvero lo spazio nel quale è alloggiata la mappa). Trovate le dimensioni giuste si inseriscono nelle dichiarazioni della applicazione:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="570"
height="680"
;>

Ed ecco l'Italia nella nostra app :-)




Come è possibile vedere mancano i controlli tipici delle mappe di google, prima di implementarli è bene notare che, comunque, funziona ad esempio il pan tenendo premuto il tasto sx e trascinando la mappa, oppure il doppio click per zoomare.

Andiamo ad inserire i controlli standard, ho evidenziato in grassetto le parti aggiunte. Alla fine il sorgente della nostra app sarà:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="570"
height="680">
<maps:Map xmlns:maps="com.google.maps.*"
id="map"
mapevent_mapready="onMapReady(event)"
width="100%"
height="100%"
sensor="false"
key="ABQIAAAAGLRIbGJ9z9R5jwNg-BFqjRT4o7HUAS9HNfPTIbLfF9Tm_VUYmxQvVVnUnJt-sc5edCcZvK41Q2S1wQ" />
<fx:Declarations>

<!-- Place non-visual elements (e.g., services, value objects) here -->

</fx:Declarations>
<fx:Script>

<![CDATA[
//import per google maps
import com.google.maps.LatLng;
import com.google.maps.Map;
import com.google.maps.MapType;
import com.google.maps.controls.*;

private function onMapReady(event:Event):void

{
map.setCenter(new LatLng(42.309815, 13.027954), 6, MapType.NORMAL_MAP_TYPE);
map.addControl(new ZoomControl());
map.addControl(new PositionControl());
map.addControl(new MapTypeControl());

}

]]>

</fx:Script>
</s:Application>



Nella prossima puntata vedremo delle personalizzazioni e delle operazioni possibili sulla mappa.


domenico .-. www.mentularia.info

Read More...

Programmare per flash - Parte prima: intro

Avrete sicuramente notato che in rete ci sono migliaia di giochi in flash che possono essere giocati o nel browser oppure, mediante il supporto AIR, sul proprio desktop. Ho cominciato ad interessarmi alla cosa quando mi è venuto in mente che si poteva realizzare una mappa/guida interattiva per un forum che seguo.Quindi è possibile utilizzare flash per qualcosa di un po' più utile dei giochetti... senza, peraltro, arrivare ad alcune esagerazioni che ho visto, interi siti web realizzati in flash, che se qualcuno ha una connessione un po' lenta diventano quasi impossibili da caricare.
Per approfondimenti rimando, come al solito, a wiki.
Ci sono diversi tool per lo sviluppo del codice flash (che è interpretato, un po' come java, per cui è eseguito indipendentemente dalla piattaforma sottostante), quasi tutti programmi proprietari o a pagamento. Io, come sempre, cerco di indirizzarmi alle opzioni open o free.
La scelta è caduta sul sdk rilasciato da Adobe (flex sdk) e su un ambiente di programmazione (un ide per intenderci) che si chiama FlashDevelop.
Qui c'è la home di flashdevelop, l'installazione del prodotto permette anche di scaricare ed installare il flex sdk.
Come forse saprete un file swf altro non è se non una sequenza di frame, una animazione insomma. Utilizzare un swf per contenere una applicazione vuol dire, fondamentalmente, utilizzare solo il primo frame di questa animazione, ovvero eliminare l'aspetto di "animazione" e dare invece una forma di "applicazione".
Useremo, allo scopo, due linguaggi differenti: actionscript 3 e mlxml. La combinazione dei due linguaggi ci permetterà di realizzare e di gestire il programma.
Utilizzeremo, poi, le API che google ha sviluppato per permettere l'interazione tra google maps e l'applicazione.
Nell'esempio che verrà sviluppato visualizzeremo in una swf la mappa dell'Italia, quindi aggiungeremo un overlay (una maschera) che indichi le varie regioni. Passando sopra a ciascuna regione si aprirà un popup con delle informazioni.
E' bene che dica due cose prima di passare all'azione :-)
1) sto imparando anche io, passo passo, l'uso di actionscript, mlxml,... per cui ci possono essere codici non ottimizzati, grossolanità,... portate pazienza
2) non è (e non vuole essere) una serie di articoli o tutorial su come sviluppare per flash. Come ho scritto in altre parti di questo, chiamiamolo, blog qui ci finiscono le mie idee, soprattutto a futura memoria... poi se c'è qualche richiesta specifica basta postare un commento e vediamo che si può fare :-D

Terminata questa breve intro passiamo alla realizzazione. Per chi volesse o avesse bisogno di un tutorial per actionscript3 (d'ora in poi AS3): qui c'è qualcosa

Nel prossimo post iniziamo i lavori.

Read More...

rieccolo...

Dopo un lungo silenzio... dopo le promesse di ritorno... rieccomi :-)
Non tratterò più, per il momento, esperimenti con virtualbox... se c'è qualcuno che è ancora interessato posti un commento con la richiesta o mi invii una mail e vediamo cosa si può fare. Inizierò, invece, una serie di post riguardanti la programmazione per flash. L'idea è che si possa utilizzare flash per qualcosa di più interessante ed utile dei milioni di giochetti che sono sparsi in giro per la rete. Io sto realizzando una mappa/guida interattiva per un forum che seguo, vedremo di fare qui un esempio analogo.

Read More...

domenica 20 settembre 2009

Esperimenti con VirtualBox: creare una lan virtuale - 2/3 - SAN

Nella puntata precedente abbiamo realizzato il fw per la nostra piccola lan virtuale.
Andiamo ora a realizzare la SAN, cioè quel sistema che esporterà via rete i dischi verso il cluster. In effetti questo è un artificio, in un contesto reale i dischi delle SAN sono visibili contemporaneamente a tutti i sistemi e sono normalmente connessi ai server attraverso schede a fibra ottica per ottimizzare i trasferimenti.
Con vmware, ad esempio, è possibile armeggiare un po' con la configurazione ed avere un disco realmente condiviso tra due (o più, credo, ma ho sperimentato solo con 2) server diversi, con virtualbox questo non è possibile, allora proviamo ad aggirare il problema.
La macchina virtuale che farà da SAN, nel mio caso, ha le seguenti caratteristiche:
1 scheda di rete sulla lan-net
1 scheda di rete sulla san-net
Ricordo che la lan-net è la rete interna al firewall (10.100.100.0/24 nel mio caso) mentre la san-net è la rete che useremo per accedere ai dischi. Si potrebbe, in effetti, utilizzare una unica scheda di rete per entrambe le funzioni (cioè raggiungibilità del server ed esportazione dei dischi) però così è formalmente più corretto ed è una configurazione auspicabile quando si abbia una grossa mole di dati che passano sulla rete.
Ci sono diversi standard per il trasporto dei dati su una rete: nfs, sshfs, AoE, iscsi,... I due più interessanti, secondo me, sono AoE (cioè Ata over Ethernet) e iScsi. AoE è forse più semplice da configurare, però non permette né l'autenticazione dei client che si collegano al server per accedere ai dischi né supporta il fatto che client e server stiano su lan diverse. Al contrario iScsi offre sia l'autenticazione che la ruotabilità, permette cioè di esportare dischi anche attraverso lan diverse. Inoltre c'è una interessante feature sperimentale di virtualbox che usa iscsi, poi vedremo meglio.
Nello screenshot qui sotto c'è la configurazione del sistema virtuale.


Ho aggiunto 3 dischi con controller SATA (dovrebbero fornire prestazioni migliori, anche se in effetti in questa applicazione non dovrebbe influire molto) che esporteremo verso il cluster. Questi dischi sono vuoti. Ciascun disco va inizializzato con fdisk. Ad esempio il primo:
san1:~# fdisk /dev/sda
fdisk vi comunicherà che la tabella delle partizioni non è valida. Premendo w fdisk scriverà una tabella valida. Non sono necessari per ora altri interventi sui dischi.
C'è inoltre un disco "boot generico" che useremo per l'esperimento :)
In questo caso abbiamo bisogno di due schede di rete, una verso la lan interna (lan-net) e una per esportare i dischi (san-net).
Il disco di boot è quello clonato dal template, per cui come per il fw ci sono delle modifiche da fare. Una nota: se avete configurato in precedenza il fw con le regole postate potete accedere al sistema via ssh dalla macchina host. Per accedere via ssh alla san1 bisogna collegarsi dalla shell del sistema fw in quanto san1 non ha schede di rete che possono accedere alla rete host.
Come fatto in precedenza annotiamo i mac delle schede di rete.
Modifica del file /etc/hosts
127.0.0.1 localhost
#ip sulla lan locale lan-net
10.100.100.1 fw.rebeldia-lab.local fw
10.100.100.10 san1.rebeldia-lab.local san1
#ip sulla lan dello storage san-net
10.120.32.1 san1.san-net.local
modifica del file /etc/hostname
Riavvio...
san1:~# ifconfig -a
lan0 Link encap:Ethernet HWaddr 08:00:27:b9:c4:d0
inet addr:10.100.100.10 Bcast:10.100.100.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:feb9:c4d0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:70 errors:0 dropped:0 overruns:0 frame:0
TX packets:62 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:7644 (7.4 KiB) TX bytes:8060 (7.8 KiB)
Interrupt:11 Base address:0xd020

san0 Link encap:Ethernet HWaddr 08:00:27:93:f0:08
inet addr:10.120.32.1 Bcast:10.120.32.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe93:f008/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:120 (120.0 B) TX bytes:468 (468.0 B)
Interrupt:10 Base address:0xd240

Verifichiamo se il link sulla lan-net funziona. Per fare questo dobbiamo avere acceso anche il fw. Il fw non risponde, nella configurazione attuale, al ping, però se il fw ha l'interfaccia esterna connessa ad internet allora il ping a un sito web dalla san1 dovrebbe, passando attraverso il fw, dare esito positivo:
san1:~# ping www.yahoo.it
PING www.euro.fyeu.b.yahoo.com (217.146.186.51) 56(84) bytes of data.
64 bytes from www.vip.ird.yahoo.com (217.146.186.51): icmp_seq=1 ttl=50 time=56.0 ms
64 bytes from www.vip.ird.yahoo.com (217.146.186.51): icmp_seq=2 ttl=50 time=55.0 ms
^C64 bytes from www.vip.ird.yahoo.com (217.146.186.51): icmp_seq=3 ttl=50 time=54.4 ms

--- www.euro.fyeu.b.yahoo.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 10191ms
rtt min/avg/max/mdev = 54.464/55.176/56.066/0.718 ms
un po' lento... ma funziona :) del resto al momento ci interessa la visibilità nella lan privata, non la velocità di connessione.
Prima di proseguire con l'installazione di iscsi potrebbe, per chi non conoscesse il protocollo, dare una occhiata qui
Dunque, san1 sarà il terminatore della connessione (il server) mentre i vari client che dovranno accedere ai dischi saranno gli iniziatori.
Installiamo il software necessario (per questo è necessario che san1 possa accedere ad internet):
san1:~# aptitude install iscsitarget
dobbiamo configurare il terminatore.
nel mio esempio il layout dei dischi è il seguente:
/dev/sda è un disco di boot generico
/dev/sdb,sdc,sdd sono dischi vuoti da 5 GB
Ogni entità iscsi, che per semplicità chiameremo disco :), è rappresentata nella seguente maniera:
iqn.yyyy-mm.[:identificatore]
iqn è il formato di rappresentazione più usato. In pratica i vari campi hanno il seguente significato:
yyyy-mm : anno e mese. A piacimento... ma non nel futuro.
: il nome del dominio della san invertito. Nel mio caso il nome del dominio è rebeldia-lab.local quindi l'inverso sarà local.rebeldia-lab
[:identificatore] : il nome che vogliamo dare alla risorsa per riconoscerla meglio.
La configurazione del terminatore si effettua modificando il file /etc/ietd.conf (questa serie di post è solo a scopo di test, la configurazione di iscsi non sarà autenticata e avrà la configurazione minima per funzionare):

Target iqn.2010-09.local.rebeldia-lab:bootdisk
Lun 0 Path=/dev/sda,Type=fileio

Target iqn.2010-09.local.rebeldia-lab:SANdisk1
Lun 0 Path=/dev/sdb,Type=fileio

Target iqn.2010-09.local.rebeldia-lab:SANdisk2
Lun 0 Path=/dev/sdc,Type=fileio

Target iqn.2010-09.local.rebeldia-lab:SANdisk3
Lun 0 Path=/dev/sdd,Type=fileio
Bisogna modificare nel file /etc/default/iscsitarget da false a true la riga:
ISCSITARGET_ENABLE=false
Infine c'è da installare il modulo nel kernel.
san1:~# apt-get install iscsitarget-modules
Lettura della lista dei pacchetti in corso... Fatto
Generazione dell'albero delle dipendenze in corso
Lettura informazioni sullo stato... Fatto
Il pacchetto iscsitarget-modules è un pacchetto virtuale fornito da:
iscsitarget-modules-2.6.26-2-xen-686 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-vserver-686-bigmem 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-vserver-686 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-openvz-686 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-amd64 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-686-bigmem 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-686 2.6.26+0.4.16+svn162-6+lenny1
iscsitarget-modules-2.6.26-2-486 2.6.26+0.4.16+svn162-6+lenny1
Bisogna esplicitamente sceglierne uno da installare.
E: Il pacchetto iscsitarget-modules non ha candidati da installare
si copia il nome del pacchetto per la vostra architettura. Nel mio caso:
san1:~# apt-get install iscsitarget-modules-2.6.26-2-686
...
san1:~# /etc/init.d/iscsitarget start
Starting iSCSI enterprise target service: succeeded.
Per testare se funziona il terminatore facciamo l'esperimento di cui parlavo prima. In pratica sfruttiamo una feature sperimentale di virtualbox che permette, al sistema virtuale, di vedere una risorsa iscsi come se fosse un disco locale. Realizziamo, cioè, una macchina virtuale diskless.
nell'help di virtualbox c'è la voce relativa alla procedura per consentire all'ambiente virtuale (cioè alla macchina virtuale prima del boot) di fungere da iniziatore (client) verso la san.
Per fare questo esperimento però bisogna spegnere il sistema san1, aggiungere nella configurazione una scheda di rete in bridge o in nat. Questo perchè il meccanismo di routing delle vlan non permette di accedere ad una rete interna prima del boot della macchina.
Creiamo una macchina virtuale con le caratteristiche che vogliamo, non aggiungiamo dischi al sistema e rimuoviamo anche la scheda di rete che di default viene aggiunta alla creazione. Avremo quindi un sistema diskless e nicless.

Le informazioni da annotare prima di cominciare sono
1) ip di san1 della interfaccia (nuova) aggiunta = 10.19.74.75 (lan non interna)
2) un mac address, generato ad esempio dalle proprietà delle schede di rete
3) un indirizzo ip da assegnare alla scheda di rete del sistema virtuale = 10.19.74.251


Ora sul sistema HOST con l'utenza che usate per avviare virtualbox:
domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/Trusted 1
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/Config/MAC 08:00:27:ee:9a:45
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/Config/IP 10.19.74.251
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/Config/Netmask 255.255.255.0
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/LUN#0/Driver IntNet
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/LUN#0/Config/Network privata1
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

domenico@cerbero:~$ VBoxManage setextradata TEST VBoxInternal/Devices/IntNetIP/0/LUN#0/Config/IsService 1
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

A questo punto dovrebbe essere correttamente configurata l'interfaccia sulla san-net. Attiviamo l'iniziatore e facciamo connettere il disco:
domenico@cerbero:~$ VBoxManage addiscsidisk --server 10.29.74.75 --target iqn.2010-09.local.rebeldia-lab:bootdisk --intnet
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.
iSCSI disk created. UUID: c75b6257-5dbe-4d4d-9b99-ad0b2b2184e1
Torniamo alla configurazione della macchina TEST, nella sezione dischi fissi aggiungere un disco ide primario e selezionare dalla lista il disco che come nome ha server|target, come si vede dalla figura qui sotto:

A questo punto si riavvia il sistema, se è andato tutto bene, il sistema diskless farà il boot.
Il boot è chiaramente un po' più lento.
E' importante, affinché questa prova abbia buon esito, che il sistema san1 sia acceso e che la scheda di rete in bonding o in nat sia correttamente configurata prima di avviare la macchina di test.
A questo punto è possibile spegnere la san1, rimuovere la scheda in bridge o in nat e riavviare san1.

Altra prova possibile, si clona un'altra immagine del disco template, si construisce una macchina di prova con una sola scheda di rete sulla lan interna san-net, si assegna il disco clonato come primario e la si avvia.
una volta avviato il sistema installiamo quanto necessario per l'iniziatore (client)
template-debian:~# apt-get install open-iscsi
e, infine, verifichiamo che la connessione con san1 funzioni:
template-debian:~# iscsiadm -m discovery
10.120.32.1:3260 via sendtargets
template-debian:~# iscsiadm -m discovery -t st -p 10.120.32.1
10.120.32.1:3260,1 iqn.2010-09.local.rebeldia-lab:bootdisk
10.120.32.1:3260,1 iqn.2010-09.local.rebeldia-lab:SANdisk2
10.120.32.1:3260,1 iqn.2010-09.local.rebeldia-lab:SANdisk1
10.120.32.1:3260,1 iqn.2010-09.local.rebeldia-lab:SANdisk3
e questi sono, appunto, i target che avevamo impostato in precedenza su san1.

Questi dischi importati possono essere facilmente utilizzabili:
template-debian:~# iscsiadm -m node --targetname "iqn.2010-09.local.rebeldia-lab:SANdisk2" --portal "10.120.32.1:3260" --login
Logging in to [iface: default, target: iqn.2010-09.local.rebeldia-lab:SANdisk2, portal: 10.120.32.1,3260]
Login to [iface: default, target: iqn.2010-09.local.rebeldia-lab:SANdisk2, portal: 10.120.32.1,3260]: successful
leggendo i messages:
[ 442.020178] scsi0 : iSCSI Initiator over TCP/IP
[ 442.357305] scsi 0:0:0:0: Direct-Access IET VIRTUAL-DISK 0 PQ: 0 ANSI: 4
[ 442.490153] Driver 'sd' needs updating - please use bus_type methods
[ 442.504137] sd 0:0:0:0: [sda] 10485760 512-byte hardware sectors (5369 MB)
[ 442.509827] sd 0:0:0:0: [sda] Write Protect is off
Il disco è stato importato come /dev/sda. Adesso è possibile compiere qualsiasi operazione su questi device così come fareste se fosse un disco connesso direttamente.

Attraverso il protocollo iscsi è possibile esportare anche partizioni o file system invece che dischi interi. L'importante è configurare correttamente il terminatore e, soprattutto, che il fs che stiamo esportando non siano montati sul sistema terminatore.

Nella prossima puntata il cluster.

domenico .-. www.mentularia.info

Read More...

giovedì 17 settembre 2009

Esperimenti con VirtualBox: creare una lan virtuale - 1/3 - Firewall

Pur non essendo, forse, VirtualBox il miglio prodotto per la virtualizzazione tuttavia è quello che mi piace di più, anche se prodotti concorrenti come VMware offrono funzionalità che non sono presenti in VB.
Lo scopo di questo post è di raccogliere una serie di esperimenti fatti con VB per creare una lan interamente virtualizzata.

Se si dispone di un pc abbastanza "carrozzato" (difficile in questi giorni trovare un pc con meno di 4GB di ram e qualche centinaia di GB di spazio disco) è possibile creare una lan composta da 4-5 computer virtuali.
L'idea generale è quella di realizzare una lan secondo lo schema sotto riportato:




Le macchine virtuali da creare sono quindi quattro: un firewall, due nodi per il cluster e una SAN.
Se non è chiaro cosa sia un cluster si può avere una idea leggendo qui, invece per una descrizione sulla SAN si può leggere qui.

Come dicevo in precedenza useremo VirtualBox per questo esperimento. Conviene, per velocizzare le operazioni, costruire un sistema base che poi personalizzeremo a seconda delle esigenze. Questo ci permette di dover installare una sola volta il sistema operativo.
Creiamo una macchina virtuale di base, potete impostare a piacimento i parametri. L'importante è dare un nome al disco fisso che sia facilmente identificabile (una buona idea ptrebbe essere template.vdi :) ).Tale disco sarà utilizzato per produrre tutti i dischi di avvio dei vari sistemi sulla lan.Dopo aver configurato tutti i parametri della macchina virtuale nel riepilogo dovreste avere una cosa del genere:




accendiamo la macchina "template", installiamo debian lenny e quindi spegnamo la macchina virtuale.Siccome il disco di questa macchina ci servirà per costruire i dischi di tutti gli altri sistemi è bene rimanere, nella fase di installazione, il più generico possibile. Quindi imposteremo un hostname generico, tipo "template" ed utilizzeremo una sola scheda di rete, connessa ad internet, che in questa fase ci serve tanto per l'installazione che per l'aggiornamento del sistema.
Importante prima di procedere con i passi successivi bisogna rimuovere, dal gestore supporti virtuali, il disco template (nel mio caso template.vdi):




Selezionare il disco da rilasciare, premere "Rilascia" e confermare nella schermata successiva. Selezionare nuovamente il disco, che ora risulterà Connesso a: Non Connesso e premere "Rimuovi". Confermare con "Rimuovi" nella schermata successiva e "Mantieni" nell'ultima schermata. Questo permette di avere il disco tolto dal controllo del gestore supporti virtuali, ma di non cancellare il disco fisicamente
Diversamente da quanto avviene con vmware non basta copiare il disco e rinominarlo per importarlo su una macchina virtuale. Bisogna utilizzare il seguente comando:

Sistemi HOST windows:
C:\Program Files\Sun\VirtualBox>VBoxManage.exe clonevdi template.vdi fw-boot-disk.vdi
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone hard disk created in format 'VDI'. UUID: f548d710-fcea-4118-8353-e21cf6794de6
Sistemi HOST linux 
domenico@cerbero:~$ VBoxManage clonevdi template.vdi /vm/rebeldia-lab.local/fw-boot-disk.vdi
VirtualBox Command Line Management Interface Version 3.0.6
(C) 2005-2009 Sun Microsystems, Inc.
All rights reserved.

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone hard disk created in format 'VDI'. UUID: 1d8d923e-e42a-4b82-819c-f3ec285ffe5b


Analogamente vengono clonati i dischi di boot per il cluster (Consiglierei, per motivi di opportunità e di tempo, di non creare entrambi i dischi per i due nodi del cluster. Conviene farne uno solo e poi, dopo i vari aggiustamenti, clonare quello in modo da avere il sistema sui due nodi del cluster assolutamente allineato) e per la SAN.
Importiamo i vari dischi creati all'interno del gestore dei supporti virtuali, premendo il pulsante "Aggiungi" e selezionando, quindi, i dischi da porre sotto il controllo del gestore.
A questo punto creiamo le macchine virtuali che faranno parte della nostra lan, avendo cura di impostare, quando richiesto, i dischi clonati in precedenza. Verifichiamo che tutto funzioni accendendoli (uno alla volta o tutti assieme, in questa fase non importa). Se tutti i sistemi caricano il sistema operativo correttamente si può passare al punto successivo. Spegniamo tutti i sistemi virtuali e iniziamo a riconfigurare, prima però è necessario avere ben chiaro come deve essere la configurazione globale della lan. Abbiamo necessità di una lan di gestione, utilizzando la quale si riesca ad accedere ai sistemi, una lan privata per il cluster e una privata per la SAN. Lo specchietto seguente riporta le varie esigenze:

Lan di gestione: 10.100.100.0/24 (lan-net)
Lan del cluster: 192.168.200.0/24 (cluster-net)
Lan della SAN: 10.120.32.0/24 (san-net)

1) Il Firewall
Rispetto al template il firewall necessita di una scheda di rete aggiuntiva. La prima scheda sarà attestata sulla rete esterna (WAN) ed avrà l'ip assegnato tramite DHCP. La seconda scheda sarà invece sulla lan di gestione, avrà ip 10.100.100.1
Passiamo a modificare la configurazione della macchina virtuale. Aggiungiamo una scheda di rete. La prima interfaccia (WAN) sarà in bridge su una scheda di rete del sistema host che possa essere raggiunta dal DHCP server. La secondo interfaccia sarà, invece, dichiarata come  connessa alla rete interna lan-net:




Premendo il pulsantino indicato dalla freccia si apre una finestra che contiene il MAC per la scheda. 


Prendere nota del mac

Questa dichiarazione fa si che la scheda possa essere vista solo dagli altri appartenenti alla rete interna lan-net.
Anche per questa scheda bisogna annotare il mac

I mac per le due schede sono (le lettere in minuscolo):
Scheda 1 = eth0 = 080027786f18 che, nella notazione normale diventa, 08:00:27:78:6f:18
Scheda 2 = eth1 = 0800277cbd05 e cioè 08:00:27:7c:bd:05
Regoliamo, ora, a nostro piacimento la memoria da assegnare alla macchina virtuale. E' bene tenere conto che la somma della memoria virtuale di tutti i sistemi non dovrebbe andare oltre la metà (i più avventurosi dicono non oltre i 2/3) della ram totale del sistema. Impostati tutti i parametri avviamo il firewall
Chiaramente, siccome tutti i dischi sono clonati da un master avremo il medesimo hostname, le medesime regole udev, le medesime chiavi ssh...
Bisogna dunque effettuare le seguenti modifiche:
modificare il file /etc/hostname inserendo il nome del server (nel mio caso fw). La modifica avrà effetto al boot successivo.
Modificare il file /etc/hosts inserendo il nome dell'host e l'eventuale dominio.
fw:~# cat /etc/hosts
127.0.0.1 localhost
10.100.100.1 fw.rebeldia-lab.local fw
Rigenerare le chiavi rsa e dsa:
fw:~# rm /etc/ssh/ssh_host_*
fw:~# dpkg-reconfigure openssh-server
Creating SSH2 RSA key; this may take some time ...
Creating SSH2 DSA key; this may take some time ...
Restarting OpenBSD Secure Shell server: sshd.
Assegnare staticamente i nomi delle nic.
Al boot, oppure quando si connette una periferica al sistema viene creato un nodo nell'alberatura di /dev per ogni periferica. Udev è il tool di sistema che si occupa di questo. Anche per le schede di rete il meccanismo è analogo. Il file /etc/udev/rules.d/70-persistent-net.rules (su debian, in linea di massima il nome del file da modificare contiene persistent-net.rules) contiene le regole statiche per la creazione dei device ethX (ma, come si vede sotto, non è obbligatorio chiamare le schede ethX). Per quanto non dovrebbe variare l'ordine delle interfacce ethX (ovvero ciò che era eth0 all'installazione rimane eth0 per sempre) in alcuni casi può succedere che venga modificato l'ordine delle schede. Questo può essere un problema, specie se si è configurato il sistema per assegnare staticamente un ip ad una determinata scheda.
Ad esempio nel caso del mio fw il file /etc/udev/rules.d/70-persistent-net.rules contiene:
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:78:6f:18", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

# PCI device 0x1022:0x2000 (pcnet32)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:7c:bd:05", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="lan0"
In questo caso, invece di chiamare la scheda di rete sulla lan-net l'ho chiamata lan0.
Basta quindi inserire il mac address corretto per ciascuna interfaccia. Questa operazione è piuttosto importante in quanto tutti sistemi sono cloni di una unica macchina per cui avranno registrate le interfacce del template in posizione eth0, ad esempio. Per cui quando voi create la nuova macchina virtuale avrete delle schede con, ovviamente, mac diversi e quindi sul sistema troverete eth1 dove vi attendevate eth0.
IMPORTANTE: ogni regola (nell'esempio ci sono due regole) deve stare su una linea unica, senza a capo.
Per impostare gli ip statici bisogna editare /etc/network/interfaces inserendo e/o modificandone il contenuto. Sempre nel mio sistema:
auto lo
iface lo inet loopback

# eth0: scheda sulla wan
allow-hotplug eth0
iface eth0 inet dhcp

# lan0: scheda verso la lan
allow-hotplug lan0
auto lan0
iface lan0 inet static
address 10.100.100.1
netmask 255.255.255.0
broadcast 10.100.100.255
network 10.100.100.0
A questo punto un bel reboot per vedere se funziona tutto. Se la macchina torna su :) verifichiamo che le modifiche siano state acquisite.
fw:~# ifconfig -a
eth0 Link encap:Ethernet HWaddr 08:00:27:78:6f:18
inet addr:10.19.74.73 Bcast:10.19.74.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe78:6f18/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:383 errors:0 dropped:0 overruns:0 frame:0
TX packets:208 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:33317 (32.5 KiB) TX bytes:30148 (29.4 KiB)
Interrupt:11 Base address:0xd020

lan0 Link encap:Ethernet HWaddr 08:00:27:7c:bd:05
inet addr:10.100.100.1 Bcast:10.100.100.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe7c:bd05/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:468 (468.0 B)
Interrupt:10 Base address:0xd240
Tutte le modifiche sono state correttamente implementate.
Adesso il sistema è pronto per diventare il nostro firewall. Data la natura del post io effettuerò una configurazione di base, chi vuole la può comodamente ampliare :)
In questo caso utilizzo FwBuilder per creare lo script di configurazione iptables:
#!/bin/sh
#
# This is automatically generated file. DO NOT MODIFY !
#
# Firewall Builder fwb_ipt v2.1.19-1
#
#
#
#


PATH="/sbin:/usr/sbin:/bin:/usr/bin:${PATH}"
export PATH

LSMOD="/sbin/lsmod"
MODPROBE="/sbin/modprobe"
IPTABLES="/sbin/iptables"
IPTABLES_RESTORE="/sbin/iptables-restore"
IP="/sbin/ip"
LOGGER="/usr/bin/logger"


#
# Prolog script
#

#
# End of prolog script
#

log() {
echo "$1"
test -x "$LOGGER" && $LOGGER -p info "$1"
}

check_file() {
test -r "$2" || {
echo "Can not find file $2 referenced by AddressTable object $1"
exit 1
}
}

va_num=1
add_addr() {
addr=$1
nm=$2
dev=$3

type=""
aadd=""

L=`$IP -4 link ls $dev | head -n1`
if test -n "$L"; then
OIFS=$IFS
IFS=" /:,<" set $L type=$4 IFS=$OIFS if test "$type" = "NO-CARRIER"; then type=$5 fi L=`$IP -4 addr ls $dev to $addr | grep inet | grep -v :` if test -n "$L"; then OIFS=$IFS IFS=" /" set $L aadd=$2 IFS=$OIFS fi fi if test -z "$aadd"; then if test "$type" = "POINTOPOINT"; then $IP -4 addr add $addr dev $dev scope global label $dev:FWB${va_num} va_num=`expr $va_num + 1` fi if test "$type" = "BROADCAST"; then $IP -4 addr add $addr/$nm dev $dev brd + scope global label $dev:FWB${va_num} va_num=`expr $va_num + 1` fi fi } getInterfaceVarName() { echo $1 | sed 's/\./_/' } getaddr() { dev=$1 name=$2 L=`$IP -4 addr show dev $dev | grep inet | grep -v :` test -z "$L" && { eval "$name=''" return } OIFS=$IFS IFS=" /" set $L eval "$name=$2" IFS=$OIFS } getinterfaces() { NAME=$1 $IP link show | grep ": $NAME" | while read L; do OIFS=$IFS IFS=" :" set $L IFS=$OIFS echo $2 done } # increment ip address incaddr() { n1=$4 n2=$3 n3=$2 n4=$1 vn1=`eval "echo \\$$n1"` R=`expr $vn1 \< 255` if test $R = "1"; then eval "$n1=`expr $vn1 + 1`" else eval "$n1=0" incaddr XX $n4 $n3 $n2 fi } if $IP link ls >/dev/null 2>&1; then
echo;
else
echo "iproute not found"
exit 1
fi



MODULES_DIR="/lib/modules/`uname -r`/kernel/net/"
MODULES=`find $MODULES_DIR -name '*conntrack*'|sed -e 's/^.*\///' -e 's/\([^\.]\)\..*/\1/'`
MODULES="$MODULES `find $MODULES_DIR -name '*nat*'|sed -e 's/^.*\///' -e 's/\([^\.]\)\..*/\1/'`"
for module in $MODULES; do
if $LSMOD | grep ${module} >/dev/null; then continue; fi
$MODPROBE ${module} || exit 1
done


# Using 0 address table files


INTERFACES="eth0 lan0 lo "
for i in $INTERFACES ; do
$IP link show "$i" > /dev/null 2>&1 || {
log "Interface $i does not exist"
exit 1
}
done


# Configure interfaces
$IP -4 neigh flush dev lan0 >/dev/null 2>&1
$IP -4 addr flush dev lan0 secondary label "lan0:FWB*" >/dev/null 2>&1


add_addr 10.100.100.1 24 lan0
$IP link set lan0 up
add_addr 127.0.0.1 8 lo
$IP link set lo up

getaddr eth0 i_eth0

# Add virtual addresses for NAT rules


log 'Activating firewall script generated Thu Sep 17 23:04:29 2009 by domenico'

$IPTABLES -P OUTPUT DROP
$IPTABLES -P INPUT DROP
$IPTABLES -P FORWARD DROP
ip6tables -L -n > /dev/null 2>&1 && {
ip6tables -P OUTPUT DROP
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
}



cat /proc/net/ip_tables_names | while read table; do
$IPTABLES -t $table -L -n | while read c chain rest; do
if test "X$c" = "XChain" ; then
$IPTABLES -t $table -F $chain
fi
done
$IPTABLES -t $table -X
done


$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

#
# Rule 0 (NAT)
#
echo "Rule 0 (NAT)"
#
#
$IPTABLES -t nat -A POSTROUTING -o eth0 -s 10.100.100.0/24 -j MASQUERADE
#
# Rule 0 (eth0)
#
echo "Rule 0 (eth0)"
#
# anti spoofing rule
#
$IPTABLES -N In_RULE_0
test -n "$i_eth0" && $IPTABLES -A INPUT -i eth0 -s $i_eth0 -m state --state NEW -j In_RULE_0
$IPTABLES -A INPUT -i eth0 -s 10.100.100.1 -m state --state NEW -j In_RULE_0
$IPTABLES -A INPUT -i eth0 -s 10.100.100.0/24 -m state --state NEW -j In_RULE_0
test -n "$i_eth0" && $IPTABLES -A FORWARD -i eth0 -s $i_eth0 -m state --state NEW -j In_RULE_0
$IPTABLES -A FORWARD -i eth0 -s 10.100.100.1 -m state --state NEW -j In_RULE_0
$IPTABLES -A FORWARD -i eth0 -s 10.100.100.0/24 -m state --state NEW -j In_RULE_0
$IPTABLES -A In_RULE_0 -j LOG --log-level info --log-prefix "RULE 0 -- DENY "
$IPTABLES -A In_RULE_0 -j DROP
#
# Rule 1 (lo)
#
echo "Rule 1 (lo)"
#
#
#
$IPTABLES -A INPUT -i lo -m state --state NEW -j ACCEPT
$IPTABLES -A OUTPUT -o lo -m state --state NEW -j ACCEPT
#
# Rule 2 (global)
#
echo "Rule 2 (global)"
#
# SSH Access to firewall is permitted
# only from internal network
#
$IPTABLES -N Cid4AB2AEFA15114.0
$IPTABLES -A OUTPUT -p tcp -m tcp --dport 22 -m state --state NEW -j Cid4AB2AEFA15114.0
test -n "$i_eth0" && $IPTABLES -A Cid4AB2AEFA15114.0 -d $i_eth0 -j ACCEPT
$IPTABLES -A Cid4AB2AEFA15114.0 -d 10.100.100.1 -j ACCEPT
$IPTABLES -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
#
# Rule 3 (global)
#
echo "Rule 3 (global)"
#
# Firewall uses one of the machines
# on internal network for DNS
#
$IPTABLES -N RULE_3
$IPTABLES -A OUTPUT -p tcp -m tcp -d 10.100.100.0/24 --dport 53 -m state --state NEW -j RULE_3
$IPTABLES -A OUTPUT -p udp -m udp -d 10.100.100.0/24 --dport 53 -m state --state NEW -j RULE_3
$IPTABLES -A RULE_3 -j LOG --log-level info --log-prefix "RULE 3 -- ACCEPT "
$IPTABLES -A RULE_3 -j ACCEPT
#
# Rule 4 (global)
#
echo "Rule 4 (global)"
#
# All other attempts to connect to
# the firewall are denied and logged
#
$IPTABLES -N RULE_4
test -n "$i_eth0" && $IPTABLES -A OUTPUT -d $i_eth0 -m state --state NEW -j RULE_4
$IPTABLES -A OUTPUT -d 10.100.100.1 -m state --state NEW -j RULE_4
$IPTABLES -A INPUT -m state --state NEW -j RULE_4
$IPTABLES -A RULE_4 -j LOG --log-level info --log-prefix "RULE 4 -- DENY "
$IPTABLES -A RULE_4 -j DROP
#
# Rule 5 (global)
#
echo "Rule 5 (global)"
#
#
#
$IPTABLES -A INPUT -s 10.100.100.0/24 -m state --state NEW -j ACCEPT
$IPTABLES -A OUTPUT -s 10.100.100.0/24 -m state --state NEW -j ACCEPT
$IPTABLES -A FORWARD -s 10.100.100.0/24 -m state --state NEW -j ACCEPT
#
# Rule 6 (global)
#
echo "Rule 6 (global)"
#
#
#
$IPTABLES -N RULE_6
$IPTABLES -A OUTPUT -m state --state NEW -j RULE_6
$IPTABLES -A INPUT -m state --state NEW -j RULE_6
$IPTABLES -A FORWARD -m state --state NEW -j RULE_6
$IPTABLES -A RULE_6 -j LOG --log-level info --log-prefix "RULE 6 -- DENY "
$IPTABLES -A RULE_6 -j DROP
#
#
echo 1 > /proc/sys/net/ipv4/ip_forward


#
# Epilog script
#


# End of epilog script
#
Questo script deve poi essere invocato in fase di boot. Per debian si può far richiamare all'interno di /etc/rc.local
Alternativamente si può usare arno-iptables-firewall, nella modalità che ho già illustrato in un altro post.

Nel prossimo post la configurazione della SAN e del cluster.
Postate nei commenti se avete considerazioni o domande

domenico .-. www.mentularia.info

Read More...

martedì 18 agosto 2009

Attacco e difesa...un esempio pratico: attacco e analisi

Supponiamo che, nonostante la "blindatura", il sistema sia stato compromesso e (la cosa peggiore che possa capitare :) ) l'attaccante sia riuscito a scalare i privilegi diventando root. [Ho volutamente evitato di inserire informazioni su come fare a... è fuori dagli scopi di questo blog :D ]

Normalmente, se un attacco è stato portato "bene" le tracce lasciate sono poche. Però spesso i sistemi non si comportano esattamente come ci si aspetterebbe. Se l'amministratore del sistema analizza con una certa frequenza i log si può accorgere di qualcosa di strano. Nel nostro caso si vedono comparire nei messages una serie di segnalazioni del tipo:


sandbox:/var/tmp# tail -f /var/log/messages
Aug 17 13:47:17 sandbox kernel: [249600.574872] netstat[8292]: segfault at 0 ip b7e8fe90 sp bfd9403c error 4 in libc-2.7.so[b7e1a000+155000]
Aug 17 13:47:25 sandbox kernel: [249608.839057] netstat[8648]: segfault at 0 ip b7e1ce90 sp bfc216cc error 4 in libc-2.7.so[b7da7000+155000]
Aug 17 13:47:25 sandbox kernel: [249608.922034] netstat[8651]: segfault at 0 ip b7e95e90 sp bfc9c73c error 4 in libc-2.7.so[b7e20000+155000]
Aug 17 13:47:25 sandbox kernel: [249608.983258] netstat[8654]: segfault at 0 ip b7e6be90 sp bf97041c error 4 in libc-2.7.so[b7df6000+155000]

tutte queste segnalazioni di segmentation fault di netstat sono tutt'altro che normali. Infatti:

sandbox:/tmp/shv6/conf# netstat -an
Segmentation fault

Questa cosa decisamente dovrebbe mettere in guardia qualsiasi amministratore.
Come è possibile che un binario che fino a ora ha sempre funzionato ora, invece, senza alcun motivo apparente va in errore (...non stiamo parlando di winzozz :D ).
Verifichiamo i processi che stanno girando sul sistema:
altro fatto strano: risulta avviato inetd che normalmente rimane non attivo sui sistemi critici:

root 7884 1 0 13:45 ? 00:00:00 /usr/sbin/inetd
root 8809 3381 0 13:54 pts/0 00:00:00 ps -ef
sandbox:/proc# date
lun ago 17 13:54:53 CEST 2009

una rapida occhiata al file di configurazione di inetd non rivela niente di rilevante.
Provando ad utilizzare lsof per verificare quali processi siano attivi si verifica un altro comportamento anomalo, cioè lsof non restituisce alcun output:

sandbox:# lsof -i:22
sandbox:# lsof
sandbox:#

In condizioni normali questi eventi sono sufficienti per convincere un amministratore a mettere offline il nodo e a tenerlo in quarantena.
Una rapida occhiata ai processi di sistema toglie ogni dubbio:

sandbox:/var/log# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Aug14 ? 00:00:15 init [2]
root 2 0 0 Aug14 ? 00:00:00 [kthreadd]
root 3 2 0 Aug14 ? 00:00:00 [migration/0]
root 4 2 0 Aug14 ? 00:00:05 [ksoftirqd/0]
root 5 2 0 Aug14 ? 00:00:00 [watchdog/0]
root 6 2 0 Aug14 ? 00:01:09 [events/0]
root 7 2 0 Aug14 ? 00:00:00 [khelper]
root 39 2 0 Aug14 ? 00:00:03 [kblockd/0]
root 41 2 0 Aug14 ? 00:00:00 [kacpid]
root 42 2 0 Aug14 ? 00:00:00 [kacpi_notify]
root 85 2 0 Aug14 ? 00:00:00 [kseriod]
root 118 2 0 Aug14 ? 00:00:00 [pdflush]
root 119 2 0 Aug14 ? 00:00:13 [pdflush]
root 120 2 0 Aug14 ? 00:00:00 [kswapd0]
root 121 2 0 Aug14 ? 00:00:00 [aio/0]
root 560 2 0 Aug14 ? 00:00:00 [ksuspend_usbd]
root 561 2 0 Aug14 ? 00:00:00 [khubd]
root 605 2 0 Aug14 ? 00:00:00 [ata/0]
root 606 2 0 Aug14 ? 00:00:00 [ata_aux]
root 706 2 0 Aug14 ? 00:00:04 [kjournald]
root 782 1 0 Aug14 ? 00:00:02 udevd --daemon
root 1117 2 0 Aug14 ? 00:00:00 [kpsmoused]
root 1395 2 0 Aug14 ? 00:00:00 [kjournald]
root 1398 2 0 Aug14 ? 00:00:00 [kjournald]
root 1400 2 0 Aug14 ? 00:00:02 [kjournald]
root 1401 2 0 Aug14 ? 00:00:05 [kjournald]
daemon 1453 1 0 Aug14 ? 00:00:00 /sbin/portmap
statd 1469 1 0 Aug14 ? 00:00:00 /sbin/rpc.statd
root 1665 1 0 Aug14 ? 00:00:03 /usr/sbin/rsyslogd -c3
root 1676 1 0 Aug14 ? 00:00:00 /usr/sbin/acpid
Debian-e 1943 1 0 Aug14 ? 00:00:01 /usr/sbin/exim4 -bd -q30m
daemon 1961 1 0 Aug14 ? 00:00:00 /usr/sbin/atd
root 1981 1 0 Aug14 ? 00:00:12 /usr/sbin/cron
root 2002 1 0 Aug14 tty2 00:00:00 /sbin/getty 38400 tty2
root 2004 1 0 Aug14 tty3 00:00:00 /sbin/getty 38400 tty3
root 2005 1 0 Aug14 tty4 00:00:00 /sbin/getty 38400 tty4
root 2008 1 0 Aug14 tty5 00:00:00 /sbin/getty 38400 tty5
root 2009 1 0 Aug14 tty6 00:00:00 /sbin/getty 38400 tty6
root 3364 1 0 Aug14 ? 00:00:00 /usr/sbin/sshd
root 7706 1 0 Aug17 tty1 00:00:00 /sbin/getty 38400 tty1
root 7884 1 0 Aug17 ? 00:00:00 /usr/sbin/inetd
root 9074 3364 0 13:31 ? 00:00:10 sshd: root@pts/0
root 9077 9074 0 13:31 pts/0 00:00:01 -bash
root 9136 9077 0 15:09 pts/0 00:00:00 ps -ef

Il comando ps restituisce 44 processi. Però andando a vedere il contenuto di /proc

sandbox:~# ls /proc/ |grep '[0-9]'|wc -l
46

I processi sono 46. Quindi ps non restituisce info almeno su due processi, le probabilità che il sistema sia compromesso sono molto elevate.
Un ultimo test taglia la testa al toro. Verifichiamo la tabella degli hash md5 che avevamo in precedenza creato:

sandbox:~# debsums -c -m /mnt/sda1/md5_hash_table
/var/lib/aspell/it.compat
/bin/ls
/usr/bin/md5sum
/usr/bin/find
/bin/login
/bin/netstat
/sbin/ifconfig
/bin/ps
/usr/bin/top
/usr/bin/pstree

Come si vede sono cambiati gli hash di alcuni file fondamentali, come netstat che, abbiamo visto, non si riesce ad eseguire.... giustamente è cambiato anche md5sum, il che fa supporre che il rootkit cerchi di bypassare questo genere di controllo, alterando gli hash del sistema operativo.

Prima di isolare il sistema, da un'altra macchina, verifichiamo se qualcosa è cambiato nel networking dato che netstat non funziona:

root@slax:~# nmap -sS -sV -p1-65535 10.41.113.99

Starting Nmap 4.60 ( http://nmap.org ) at 2009-08-18 14:30 GMT
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns_servers
Stats: 0:00:45 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 66.67% done; ETC: 14:31 (0:00:05 remaining)
Interesting ports on 10.41.113.99:
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh (protocol 2.0)
111/tcp open rpcbind 2 (rpc #100000)
37599/tcp open status 1 (rpc #100024)
37998/tcp open ssh SCS sshd 2.0.13 (protocol 1.5)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at http://www.insecure.org/cgi-bin/servicefp-submit.cgi :
SF-Port22-TCP:V=4.60%I=7%D=8/18%Time=4A8ABB42%P=i486-Slackware-linux-gnu%r
SF:(NULL,20,"SSH-2\.0-OpenSSH_5\.1p1\x20Debian-5\r\n");
MAC Address: 08:00:27:B3:D3:4E (Cadmus Computer Systems)

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 46.056 seconds


Sorpresa sorpresa :) una seconda istanza di un ssh su 37998. Vediamo un po':

root@slax:~# nc 10.41.113.99 37998
SSH-1.5-2.0.13

Quindi il rootkit ha installato anche una backdoor. A questo punto ogni ulteriore analisi compiuta col sistema attivo e online è inutile e potenzialmente dannosa. Inutile perchè il rootkit è fatto apposta per nascondere le proprie tracce (infatti ad una prima analisi non è possibile determinare nè dove risieda questo secondo demone ssh, nè dove sia la corrispondente configurazione), dannosa perchè l'attaccante, se il sistema è online, può ancora connettersi e rimuovere eventuali tracce.

Supponiamo, quindi, di mettere offline il sistema. Se dobbiamo raccogliere informazioni utili per una analisi forense ci sono alcune attenzioni da avere. Prima di tutto bisogna effettuare una copia del o dei dischi del sistema impattato. Conviene prendere un hd nuovo da collegare al sistema della stessa dimensione del disco presente. Il boot va effettuato in modo "pulito", cioè o da un cdrom o da una chiave usb. L'importante è non far riavviare il sistema dal proprio disco "contaminato". Questo per evitare ulteriori eventuali modifiche dei file che impediscano di stabilire una linea temporale precisa.
Effettuiamo, quindi, il boot da una live.
Prima di tutto una copia fisica. Supponiamo che il disco incriminato sia /dev/hda e il nuovo disco sia /dev/hdb:

sandbox:~# dd if=/dev/zero of=/dev/hdb
sandbox:~# dd if=/dev/hda of=/dev/hdb

Il primo dd serve a "ricoprire" di zero tutto il disco in modo da evitare che qualche informazione spuria presente su hdb vada ad inficiare la validità della copia.
Verifichiamo che le copie siano consistenti:

sandbox:~# md5sum /dev/hda
b335358b2054cb9d68325d3fa4687531 /dev/hda
sandbox:~# md5sum /dev/hdb
b335358b2054cb9d68325d3fa4687531 /dev/hdb
sandbox:~# openssl sha1 /dev/hda
SHA1(/dev/hda)= 159ef7f052e1579b527a582cfb8ad4e62cdbc94c
sandbox:~# openssl sha1 /dev/hdb
SHA1(/dev/hdb)= 159ef7f052e1579b527a582cfb8ad4e62cdbc94c

Perchè usare due algoritmi di hash? Se si stanno compiendo analisi per l'utilizzo forense è necessario tutelarsi dalla possibilità che, nel frattempo, si scopra una possibile collisione nei protocolli di hashing. Così facendo se anche uno dei protocolli dovesse venir bucato, esiste sempre l'altro che fa fede. Se non ricordo male qualcuno ha dimostrato la possibilità di generare due hash uguali per file diversi sfruttando una collisione della routine di compressione dell'MD5.
L'uguaglianza degli hash implica che i due dischi sono uguali, per quanto riguarda i contenuti, al byte.
Infine facciamo una terza copia in modo del tutto analogo al precedente, utilizzando però un file e non un supporto fisico:

sandbox:~# dd if=/dev/zero of=/tmp/dump_hd
...
sandbox:~# md5sum /tmp/dump_hd
...

In questo modo i due hd fisici rimarranno come reperti, mentre l'analisi verrà effettuata sul dump appena creato. Chiaramente se non ci sono necessità legali non c'è bisogno di questo... anche se magari avere una immagine può aiutare nel caso si corrompa l'hd.
A questo punto, per eseguire una vera e propria analisi forense ci sarebbero altre cose da fare, a noi interessa capire solo cosa sia successo al sistema.
Come dicevo in precedenza io stesso mi sono auto attaccato, però l'ho fatto alla maniera "lamer" cioè ho banalmente applicato un rootkit, senza curarmi troppo di cosa facesse. Per cui lo scopro ora in diretta :D
Lavoriamo sull'immagine precedentemente raccolta. Supponiamo di montarla su /tmp/dump
Attenzione a montare l'immagine. Bisogna assicurarsi che il fs non venga modificato, non venga modificata l'ora di accesso agli inode dei vari file, chiaramente bisogna evitare che si eseguano file infetti. In sostanza l'immagine va montata cone le opzioni ro,noatime,noexec,nodev
Importante: da ora in poi assumiamo che il boot sia stato effettuato da un sistema pulito.
Prima di tutto un controllo anti rootkit:

box:~# chkrootkit -q -r /tmp/dump
Checking `ifconfig'... INFECTED
Checking `netstat'... INFECTED
Checking `pstree'... INFECTED
Checking `top'... INFECTED
/etc/ld.so.hash
Possible t0rn v8 (or variation) rootkit installed
The following suspicious files and directories were found:
/lib/init/rw/.ramfs

Warning: Possible Showtee Rootkit installed
/usr/include/file.h /usr/include/proc.h
Possible ShKit rootkit installed
You have 3 process hidden for readdir command
You have 5 process hidden for ps command

Mi pare che sia abbastanza significativo l'output :)
Vengono segnalati 5 processi nascosti. In effetti 5 nascosti al ps, 3 al readdir, risulta la differenza di 2 che avevamo visto prima facendo il confronto tra ps e il contenuto di /proc.

Ora con l'immagine montata su un altro sistema si riesce ad accedere a quei punti del fs che erano stati "oscurati" dal rootkit. Troviamo lo script per rimuovere l'utente dai log, i tools per bypassare i normali ids... si può, quindi, avere una idea generale del funzionamento:
1) vengono installate versioni trojanized dei principali comandi di sistema.
2) si determina, mediante il check di un paricolare file (penso /lib/init/rw/.ramfs) se il rootkit è installato, in modo da evitare sovrainstallazioni
3) viene creata una backdoor utilizzando un demone ssh dedicato
4) vengono installati tutti i tools necessari a nascondere le proprie tracce
Generalmente, ma non è stato questo il caso, il trojan tende a mantenere delle copie "sane" dei file che infetta, in modo che l'attaccante possa controllare effettivamente il sistema. Per esempio il comando ls non restituisce nulla nella directory nella quale si trova il trojan, così come ps maschera i processi relativi al rootkit. Le versioni non compromesse, invece, funzionano correttamente.

Come dicevo si tratta di un caso semplice (anche perchè l'ho fatto io :D ) e sicuramente ci sarebbero molte più analisi da effettuare. Però questo articolo voleva solo fornire degli spunti e, soprattutto, far considerare che un malfunzionamento di un comando (così come un ouptup strano e non consueto) dovrebbero mettere subito in allarme qualsiasi sysadm e indurlo ad approfondire la cosa. Poi c'è un secondo aspetto: il logging. Spesso i log vengono trascurati e alle volte, come in questo caso, possono venir manomessi privando quindi il sysadm di una preziosissima fonte di informazioni. Si può rimediare mediante logging su un sistema terzo, che introduce una complessità maggiore all'attaccante.
Magari, in un futuro, proverò ad inserire un articolo più completo sulla fase di analisi di un sistema compromesso.
Come al solito, se ci sono questioni o commenti postate pure :)

Read More...

venerdì 14 agosto 2009

Attacco e difesa...un esempio pratico: difesa iniziale

Casualmente sono venuto in possesso di un rootkit/trojan per linux. Voglio provare ad analizzarlo per verificare quanto sia in grado di coprire la propria presenza e quanto un sistema decentemente protetto sia in grado di resistere o di segnalare il problema.
Per fare questo mettiamo in piedi una sandbox pulita e protetta al meglio possibile. Come sempre la partenza è una Debian hardenizzata con bastille.

In particolare:
1) Un utente non root non può eseguire le utilità di amministrazione, come ifconfig, fsck... questo permette di ridurre la possibilità di exploit su programmi setuid.
2) Tutti i servizi insicuri (occhio, però, a considerare troppo sicuro ssh...) sono chiusi
3) è consentito a root di connettersi solo da locale. Viene disabilitata la connessione dall'esterno.
Per proteggersi dai rootkit è bene, oltre a usare un detector specifico, avere una tabella degli hash md5 per tutti i file installati sul sistema. Debian mette a disposizione un tool per fare questo.


sandbox:~# apt-get clean
sandbox:~# aptitude install debsums
...
sandbox:~# debsums_init
...
Finished generating md5sums!
Checking still missing md5files...

In questo modo abbiamo creato una serie di hash di tutti i pacchetti installati sul sistema. Per ogni nuovo pacchetto installato verrà creato l'hash.
Si può, a questo punto, creare una voce in crontab di root che periodicamente effettui il controllo degli hash:

0 0 * * * debsums -c | logger -t "Hash modificato" -p warning >/dev/null 2>&1

in questo modo ogni mezzanotte verrà effettuato un controllo degli hash e, in caso di variazione, nei messages di sistema comparirà una linea del tipo (se si vuole controllare anche gli hash dei file di configurazione basta dare debsums -ca invece che debsums -c):

...
Aug 14 00:00:10 sandbox Hash modificato: /var/lib/aspell/it.compat
...


Ora chiaramente c'è un problema fondamentale... un attaccante può sostituire un eseguibile, magari con una versione troianizzata, ricalcolare l'hash e inserirlo nella tabella. In questo modo il sistema non segnalerà l'anomalia.
Possiamo procedere in questa maniera:
1) creiamo uno script che estrae tutti gli hash e li ingloba in un unico file
2) spostiamo questo file su un supporto read only
3) effettuiamo il controllo degli hash confrontando quelli generati per i vari file con la tabella salvata
4) creiamo uno script che ci permetta di modificare gli hash presenti nella tabella quando, ad esempio, aggiorniamo il sistema.
Un modo più raffinato consiste nell' inserire tutte le info in un db. In tal modo (ammesso che l'attaccante non riesca anche a bucare il db...) sarà più semplice la gestione degli upgrade e della modifica dei singoli hash.
Vediamo la soluzione più grezza ;)
1.
Gli hash si trovano in /var/lib/dpkg/info all'interno della directory ci sono una serie di file, ciascun file rappresenta un pacchetto installato e, per ciascun pacchetto, il file con estensione md5sums contiene l'hash per tutti i singoli file installati.
una prima bozza di script potrebbe essere:

#!/bin/bash
# Script /usr/bin/chkhash
#
SOURCE_DIR="/var/lib/dpkg/info"
TARGET="/var/tmp/md5_hash_table"

if [ ! -e $TARGET ]; then
touch $TARGET
else
rm $TARGET && touch $TARGET
fi

find $SOURCE_DIR -name '*.md5sums' -exec cat {} \; > $TARGET

2.
Il file /var/tmp/md5_hash_table va ora spostato su un supporto protetto da scrittura. A questo proposito magari si può usare una SD che può essere protetta e sprotetta, in modo da non dover poi utilizzare enormi quantità di CD man mano che si modificano gli hash.
3. e 4. :)
supponendo che, ora, il file degli hash sia su /dev/sda1 e che questo device sia montato su /mnt/sda1 andremo a effettuare il controllo di integrità dei file direttamente dalla lista appena prodotta:

sandbox:~# debsums -m /mnt/sda1/md5_hash_table -c
/var/lib/aspell/it.compat

quindi nel crontab andremo a modificare l'istruzione:

0 0 * * * debsums -c -m /mnt/sda1/md5_hash_table | logger -t "Hash modificato" -p warning >/dev/null 2>&1

5.
Come dicevo in precedenza a seguito di un aggiornamento o anche per l'installazione di qualche nuova funzionalità e, comunque, a seguito della modifica anche di un solo byte del file, l'hash cambia. Per fare un esempio, supponiamo di avere un file "a" composto da 1000 caratteri a e un file "b" composto da 999 caratteri a e un carattere b. Proviamo a fare l'hash di ciascun file:

sandbox:~# perl -e 'print "a" x1000' > a
sandbox:~# echo -n "b" > b
sandbox:~# perl -e 'print "a" x999' >> b
sandbox:~# diff a b
1c1
< aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
---
> baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
sandbox:~# md5sum a
d4bce459cbbb478eda5d9bf3bf7e2db3 a
sandbox:~# md5sum b
1760ddef13fa66838b8356333c56ada6 b

Come si può vedere gli hash sono del tutto diversi, anche per la modifica dello 0.1% del contenuto del file.
A questo punto sono possibili due soluzioni. La prima prevede, semplicemente, di verificare tutte le variazioni negli hash che vengono riportate nei messages di sistema, individuando quali possono corrispondere a modifiche non volute o estranee. Questo sicuramente potrebbe essere un approccio "safe" però richiede anche una certa opera di analisi dei log.
Altrimenti possiamo effettuare uno script che aggiorni, dopo installazioni e/o upgrade la lista degli hash. La cosa più semplice che si possa fare è inserire nel file /etc/apt/apt.conf.d/90debsums una chiamata allo script realizzato in precedenza.
90debsums contiene le istruzioni che vengono effettuate da apt-get (e da aptitude) dopo l'installazione del pacchetto. Nel mio sistema il file ha il seguente contenuto:

DPkg::Post-Invoke { "if [ -x /usr/bin/debsums ]; then /usr/bin/debsums --generate=nocheck -sp /var/cache/apt/archives; fi"; };

Volendo integrare lo script che abbiamo scritto prima:

DPkg::Post-Invoke { "if [ -x /usr/bin/debsums ]; then /usr/bin/debsums --generate=nocheck -sp /var/cache/apt/archives && /usr/bin/chkhash; fi"; };

In questo modo ad ogni installazione, update... verrà rigenerata la lista completa di hash del sistema. A questo punto non resta che copiarla sul supporto che poi renderemo protetto da scrittura. Se si sceglie questa strada è bene non sovrascrivere direttamente la versione precedente della lista, in modo da poter eventualmente avere uno storico delle varie modifiche. Il limite di questo sistema è che se un attaccante scala i privilegi e diventa root potrebbe installare un rootkit come fosse un pacchetto, attivando quindi il processo di ricalcolo del checksum.
Il mio consiglio è quello di aggiornare periodicamente la lista degli hash, verificando ogni segnalazione che compaia nei log di sistema per capire se si tratta della modifica di un file innocuo oppure no.
Attenzione che, comunque, questo sistema non calcola l'hash di altri file critici come potrebbe essere /etc/passwd o /etc/shadow e, in genere, di tutto quello che si trova in /etc, salvo pochissimi casi.
Infine trovo sia utile utilizzare, sempre schedulandolo in crontab, anche chkrootkit che, basandosi su firme e/o meccanismi euristici riesce a determinare se i file del sistema siano stati o meno compromessi. E' bene, chiaramente, effettuare da subito la scansione in modo da avere un punto iniziale consistente:

sandbox:/var/tmp# aptitude install chkrootkit
...
sandbox:/var/tmp# chkrootkit -e
ROOTDIR is `/'
Checking `amd'... not found
Checking `basename'... not infected
Checking `biff'... not found
Checking `chfn'... not infected
Checking `chsh'... not infected
...


A questo punto il sistema è abbastanza protetto, nelle prossime puntate ci caleremo nel ruolo dell'attaccante e, nell'ultima, dell'analista forense :D

[EDIT]: ovviamente il sistema dovrebbe essere protetto anche dall'esterno, con l'utilizzo di firewall, ids... questa vuole essere solo una dimostrazione :) quindi supporremo che l'attaccante sia già entrato nel sistema :)

Read More...

mercoledì 5 agosto 2009

Toc Toc... bussa e ti sarà aperto...

Spesso capita che, nonostante si cerchi di blindare al massimo un sistema, ci sia il solito furbacchione che, magari con uno 0-day pagato a peso d'oro, riesce a penetrare il sistema, con tutto quello che ne consegue. Infatti, anche mettendo le policy più stringenti sugli accessi (anche in termini di ip) il fatto che un servizio rimanga esposto alla rete 24h/24 amplifica la possibilità che il servizio stesso venga exploitato.
Anche variare le porte sulle quali ascolta il servizio può risultare di scarsa utilità se l'aggressore è motivato e deciso. Analogo discorso per gli IDS, se l'attaccante è abbastanza esperto riuscirà facilmente a bypassare i meccanismi di allarme. L'idea è quella di fare in modo che il servizio necessario sia attivo solo quando serve realmente. Facciamo un esempio concreto: supponiamo di avere un fw perimetrale. Per prima cosa è bene che il fw non esporti nessun servizio, salvo ad esempio dns/dhcp preferibilmente solo verso la rete interna. Per amministrare il fw supponiamo di utilizzare una connessione ssh. Il fatto di permettere la sola autenticazione con chiave pubblica, di limitare il range di ip che possono connettersi e di variare la porta del servizio non mi garantisce che qualche utente possa bucare il sistema anche dal lato "sicuro" (il fatto che l'interfaccia interna di un fw sia "sicura" è del tutto opinabile...).
Dunque cosa possiamo fare? La necessità è che il servizio ssh sia attivo solo quando devo amministrare il fw.
Un approccio potrebbe essere questo: bussare ad una sequenza nota di porte e, come conseguenza, ottenere l'apertura della porta sulla quale ascolta il server.
La tecnica si chiama, appunto, Port Knocking. Un demone ascolta tutte le connessioni in entrata sul sistema, quando riceve la sequenza di SYN sulle porte indicate apre la porta relativa al servizio.
Esempio: supponiamo che il servizio ssh giri sulla porta 22, le porte da bussare siano 5641, 8942, 9265 e 5476. Inviando un pacchetto SYN (cioè di richiesta connessione) sulle porte, nell'ordine impostato, il demone di cui si diceva prima provvederà ad aprire la porta 22 e a rendere accessibile il servizio. Analogamente possiamo definire una sequenza di "bussata" che provveda a chiudere la porta 22 quando non è più necessaria. Credo che il demone più usato per il Port Knocking sia knockd che dovrebbe essere presente su tutte le piattaforme linux (non ho idea per i sistemi m$). Qui c'è il sito di knockd.
Per l'esempio fatto prima il file di configurazione /etc/knockd.conf potrebbe essere


[options]
logfile = /var/log/knockd.log

[openSSH]
sequence = 5641,8942,9265,5476
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

[closeSSH]
sequence = 5476,9265,8942,5641
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

quindi quando il demone intercetta la sequenza corretta di porte apre il servizio ssh. La sintassi della configurazione è piuttosto semplice e permette alcune interessanti variazioni. Avete presente il wake on lan? Bene si può implementare semplicemente il contrario, cioè visto che esiste l'opzione command perché non estendere la cosa implementando una cosa del tipo:

[options]
logfile = /var/log/knockd.log

[openSSH]
sequence = 5641,8942,9265,5476
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

[closeSSH]
sequence = 5476,9265,8942,5641
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

[shutdown]
sequence = 5521,6275,4462,2631
seq_timeout = 10
tcpflags = urg
command = /sbin/shutdown -h now


Come si può vedere in questo caso per spegnere la macchina bisogna inviare 4 pacchetti, uno per porta, con il flag "urg" invece che "syn".
Alcune considerazioni:
1) occhio ai flag che si usano per identificare i pacchetti. Il fw potrebbe essere configurato per droppare i pacchetti non syn che si riferiscono a connessioni non stabilite.
2) Implementare un meccanismo di port knocking non è, di per se stesso, un sistema per mettere in sicurezza una macchina. Un attaccante potrebbe, sniffando il vostro traffico, determinare la sequenza di bussata ed utilizzarla per aprire l'ssh. Quindi questo metodo deve essere usato assieme a tutti gli altri strumenti di protezione.
3) In questi esempi ho sempre usato porte tcp. Per usare porte udp basta modificare la configurazione inserendo, dopo il numero della porta, :udp. Così, ad esempio, potrebbe essere:

[openSSH]
sequence = 5641:udp,8942,9265:udp,5476
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

chiaramente poi il campo tcpflags ha valore solo per i pacchetti inviati alle porte tcp.
4) Siccome l'invio fuori sequenza o il bussare su una porta sbagliata porta al reset (cioè se la sequenza è 5641:udp,8942,9265:udp,5476 e io busso su 5641:udp,8942,9265:udp,5477) è bene evitare porte in uso, utilizzando porte "alte" per il knocking.

Come bussare? Ci sono vari metodi, se la combinazione di porte non è troppo complessa si può usare netcat, hping o, al limite, lo stesso nmap.

Mi pare sia più o meno tutto... provate a sperimentare e fatemi sapere.

Read More...

martedì 16 giugno 2009

Realizzare un personal firewall con Virtualbox - 2/2

Nella prima parte abbiamo realizzato un piccolo fw utilizzando una macchina virtuale interna all'host da proteggere.
Ci sono dei pro e dei contro nell'adottare una soluzione di questo tipo. Sicuramente se volete proteggere un pc non molto dotato dal punto di vista hw questa strada potrebbe non essere ottimale.
Tuttavia le richieste della macchina virtuale non sono così eccessive, la quantità di memoria riservata può essere abbassata a 256 MB, potendo scendere ancora se si vuole utilizzare solo come fw. In questo caso è bene, comunque, non scendere sotto i 128MB.
Facciamo un passo indietro. Come avevo scritto l'esigenza di avere una soluzione di questo tipo nasce dal fatto che non mi fido un granché dei tools di sicurezza per microsoft. Questo vale, quindi, anche per i prodotti antivirus.
Il personal fw, quindi, può essere espanso per diventare anche un proxy server per http e non solo.
La prima prova mettendo insieme squid, clamav, apache e squidguard ha dato esiti altalenanti, in paricolare risultava difficoltosa l'interazione tra squidguard e clamav. La seconda soluzione è molto ma molto più semplice :D
E' bene notare che se viene installato un programma infetto sul pc bisognerà utilizzare un antivirus in locale. Questa soluzione permette solo una protezione dalla rete, non da eventuali virus presenti su dischi esterni, chiavette usb... Pertanto è comunque consigliabile mantenere anche l'antivirus (aggiornato) su pc windows.
Infine io ho il problema di dovermi autenticare su un ulteriore proxy ISA per avere l'accesso ad internet, quindi installerò anche un proxy in grado di autenticarsi vs ISA server. Conosco 2 prodotti, ntlmap in python e cntlmap in c. La versione in c è estremamente stabile e performante, quindi userò quella. Lo schema qui sotto riporta come sarà la funzione di proxy:




Ci serve il seguente sw:
-clamav
-HAVP

Potrebbe essere necessario che il sistema abbia installato anche: il compilatore gcc, make, curl e libcurl (in debian è necessario preinstallare i seguenti pacchetti (non presenti nell'installazione minima): gcc, make, curl e libcurl4-gnutls-dev con i relativi prerequisiti.
Passiamo, quindi, all'installazione:


fwproxy:~# apt-get install clamav
...
apt-get install gcc make curl libcurl4-gnutls-dev
...
fwproxy:~# apt-get install havp

Prima di proseguire è necessario creare un tempfs montato su /dev/shm, cioè un fs allocato in memoria. Questo viene utilizzato per le transazioni di clamav e di havp, in modo da velocizzare i processi di scansione.

fwproxy:~# mkdir /var/tmpmem
fwproxy:~# mount -t tmpfs /dev/shm/ /var/tmpmem/
fwproxy:~# mkdir -p /var/tmpmem/spool/havp
fwproxy:~# chown -R havp:havp /var/tmpmem/
fwproxy:~# mount | grep tmpmem
/dev/shm on /var/tmpmem type tmpfs (rw)
fwproxy:~# umount /var/tmpmem/

La dimensione di questo fs dipende dalla memoria allocata sul sistema.
Ho smontato il fs in quanto è necessario introdurre una entry in /etc/fstab in modo che al reboot sia ricreato e rimontato. Aggiungiamo la seguente riga:

/dev/shm /var/tmpmem tmpfs rw,mand,uid=106,gid=108 0 0

Le opzioni di mount sono: lock mandatorio (come richiesto dalla documentazione) e fs di proprietà dell'utenza applicativa hapv (il gid e l'uid corrispondono all'utente hapv nel mio sistema)
Ok, ora passiamo alla configurazione:
il file da modificare è, nella mia distro, /etc/havp/havp.config. La mia configurazione è la seguente

BIND_ADDRESS 192.168.12.1
SERVERNUMBER 10
MAXSERVERS 100
SCANTEMPFILE /var/tmpmem/spool/havp/havp-XXXXXX
TEMPDIR /var/tmpmem
ENABLECLAMLIB true
PARENTPROXY localhost
PARENTPORT 4444

Ci sono molte altre voci, ma il file di configurazione è molto ben commentato. Le opzioni che ho modificato riguardano l'ip sul quale deve ascoltare il proxy (si può anche mettere in listen su tutti gli indirizzi dato che il firewall protegge l'interfaccia interna, tuttavia è meglio limitare il listen all'interfaccia interna). Ho modificato i valori di base per le connessioni concorrenti, ho indicato di usare libclamv come motore antivirus, tuttavia si possono integrare anche altri antivirus. Il file di configurazione ha già i template inseriti per altri programmi av. Infine ho indicato a havp che non si deve connettere direttamente in uscita, ma deve passare attraverso il proxy cntlm che, come dicevo, serve ad autenticarmi in uscita dal firewall ISA.
Per verificare il funzionamento provo a scaricare un file che contiene la firma di un virus da eicar e qui sotto vedete il risultato :)

Volendo poi fare le cose per bene si può anche personalizzare la pagina che viene restituita in caso di virus...
Ecco, per ora è tutto... se mi viene in mente qualche ulteriore accrocchio provvederò a postarlo.

p.s.
L'ultima versione di Virualbox (2.2.2.xxx) sembra avere dei problemi se la ram assegnata è inferiore a 256 MB. Consiglio almeno 381 MB e max 16 per il video. Se usate solo il fw senza proxy allora 256 possono bastare e se mettete al massimo 16 MB destinati alla scheda video non dovreste avere problemi :)


Read More...