Información blog

Linux, tutoriales, noticias, sistemas, redes y seguridad informática, entre otras cosas.

lunes, 19 de febrero de 2018

Cómo registrar qué USBs se han conectado en Linux

La recopilación de información es cada día más importante; en servidores siempre lo ha sido, pero en los equipos de sobremesa ha ido cobrando importancia paulatinamente  y hoy en día se ha demostrado que es importante tener registrada toda la información por si en un futuro nos pudiese llegar a ser útil. Hoy quiero hablar sobre el registro de USBs en Linux. Linux tiene la gran ventaja de tener una enorme cantidad de logs, pero los logs no siempre te registran todos los datos que uno desea y corremos el peligro de que dichos logs sean borrados o se roten mediante utilidades tales como logrotate; utilidades especialmente comunes en servidores. Por eso puede ser interesante tener una herramienta corriendo en segundo plano cuya única función sea registrar qué USBs se han conectado/desconectado, la hora en la que lo han hecho, datos útiles tales como el número de serie y de paso, registrar su actividad a ser posible.

portada_registro_usb

En este caso no optaremos a herramientas de terceros sino que tiraremos de conceptos de Linux y los aglutinaremos en un script que automatice todo ello... Además, en este caso, no almacenaremos la información en un fichero en texto plano, sino que vamos a almacenarla en una mini base de datos en formato SQLite... El motivo por el cual se ha optado por dicha opción es simple: SQLite requiere instalar pocas herramientas en el equipo lo cual es perfecto en equipos de sobremesa; además es una base de datos que consume muy pocos recursos por parte del equipo y al mismo tiempo tiene la gran ventaja de seguir siendo una base de datos basada en el lenguaje SQL, con lo cual podríamos hacer consultar de su información mediante consultas SQL mediante SELECTs con sus respectivos filtros, etc... Haciendo que la consulta de la información en el futuro sea muchísimo más eficiente, con la contrapartida de que se requieren unos conocimientos básicos de SQL para poder sacarle partido.

Teniendo esto en cuenta, comencemos con la instalación de las dependencias necesarias para que nuestra solución funcione... Dependiendo de qué distribución tengamos y las herramientas que tengamos instaladas, puede ser que ya tengamos todo lo necesario, pero para asegurarnos haremos:
apt-get install sqlite
Teniendo todo preparado, pasaríamos a crearnos primero la mini base de datos que almacenará la información de los USBs. En mi caso en particular he creado la base de datos en /var y la he llamado listaUsbs.db con lo que su creación sería tal que así:
sqlite /var/listaUSBs.db
Esto no solo crearía la base de datos, sino que estaríamos dentro de ella, con lo que pasaríamos a crear la tabla en cuestión. En mi caso he llamado a la tabla listausbs y su creación (junto con sus respectivas columnas) sería la siguiente:
CREATE TABLE lista_usbs (NOMBRE,FECHA_DETECT,FECHA_DESCONEX,
IDVENDOR,IDPRODUCT,MANUFACT,IPRODUCT,SERIAL);
En esta base de datos guardaremos toda la información importante tal como el nombre completo, la fecha de detección del USB, el fabricante y el numero de serie... Información que puede sernos útil en el futuro. Con esto claro, podemos crear un script en el que por un lado monitoricemos en tiempo real toda la actividad USB mediante tcpdump, y por otro lado también podemos tener constancia de qué USB se ha conectado en qué momento; esto lo podemos lograr mediante el siguiente script, al cual yo he llamado registrador_usbs.sh y que ha sido probado en Debian 8:


#!/bin/bash

#CAPTURAMOS TODO EL TRAFICO USB
modprobe usbmon
nohup tcpdump -i usbmon1 -w /tmp/captura.cap &
while true;
do
        #COMPROBAMOS SI SE HA CONECTADO ALGUN USB NUEVO
        if [ ! -f /tmp/listausbs2.txt ];
        then
                touch /tmp/listausbs2.txt
        fi
        lsusb > /tmp/listausbs.txt
        diff /tmp/listausbs.txt /tmp/listausbs2.txt
        if [ $? -eq 1 ]
        then
                diff /tmp/listausbs.txt /tmp/listausbs2.txt > /tmp/cambio.txt
                cat /tmp/cambio.txt |tail -n +2 > /tmp/cambio2.txt
                while read CAMBIO;
                do
                echo ${CAMBIO} > /tmp/cambio3.txt
                cat /tmp/cambio3.txt |grep "<"
                if [ $? -eq 0 ];
                then
                        #SI ES UNA CONEXION NUEVA LA REGISTRAMOS
                        NOMBRE="$( cat /tmp/cambio3.txt |cut -d '<' -f 2 |cut -d ':' -f 2- | cut -d 'D' -f 2- )"
                        echo $NOMBRE
                        lsusb -D /dev/bus/usb/$(lsusb |grep "${NOMBRE}" |awk {'print $2'})/$(lsusb |grep "${NOMBRE}" |awk {'print $4'} |cut -d ":" -f 1) > /tmp/usb_detallado.txt
                        FECHA_DETECT=$(date)
                        FECHA_DESCONEX=''
                        IDVENDOR=$(cat /tmp/usb_detallado.txt |grep idVendor |awk '{for (i=2; i<NF; i++) printf $i " "; if (NF >= 2) print $NF; }')
                        IDPRODUCT=$(cat /tmp/usb_detallado.txt |grep idProduct |awk '{for (i=2; i<NF; i++) printf $i " "; if (NF >= 2) print $NF; }')
                        MANUFACT=$(cat /tmp/usb_detallado.txt |grep iManufacturer |awk '{for (i=2; i<NF; i++) printf $i " "; if (NF >= 2) print $NF; }')
                        IPRODUCT=$(cat /tmp/usb_detallado.txt |grep iProduct |awk '{for (i=2; i<NF; i++) printf $i " "; if (NF >= 2) print $NF; }')
                        SERIAL=$(cat /tmp/usb_detallado.txt |grep iSerial |awk '{for (i=2; i<NF; i++) printf $i " "; if (NF >= 2) print $NF; }')
                        sqlite /var/listaUSBs.db "INSERT INTO lista_usbs VALUES ('${NOMBRE}','${FECHA_DETECT}','${FECHA_DESCONEX}','${IDVENDOR}','${IDPRODUCT}','${MANUFACT}','${IPRODUCT}','${SERIAL}');"
                        rm /tmp/usb_detallado.txt
                fi
                cat /tmp/cambio3.txt |grep ">"
                if [ $? -eq 0 ];
                then
                        #SI ES UNA DESCONEXION ACTUALIZAMOS LOS DATOS CORRESPONDIENTES
                        NOMBRE="$( cat /tmp/cambio3.txt |cut -d '>' -f 2 |cut -d ':' -f 2- | cut -d 'D' -f 2- )"
                        FECHA_DESCONEX=$(date)
                        sqlite /var/listaUSBs.db "UPDATE lista_usbs SET FECHA_DESCONEX='${FECHA_DESCONEX}' WHERE FECHA_DESCONEX='' AND NOMBRE='${NOMBRE}';"
                fi
                done < /tmp/cambio2.txt
                rm /tmp/cambio.txt
                rm /tmp/cambio2.txt
                rm /tmp/cambio3.txt
        fi
        mv /tmp/listausbs.txt /tmp/listausbs2.txt
done

Con este script, solamente ejecutándolo en segundo plano estaríamos monitorizando en tiempo real toda la actividad mediante tcpdump, y registrándola en nuestra base de datos SQLite. Solamente habría que escribir:
./registrador_usbs.sh >/dev/null &
Dicha información podría ser consultada en el futuro mediante consultas SQL tales como:

Para ver todos los registros:
sqlite /var/listaUSBs.db "SELECT * FROM lista_usbs;"
Para ver un numero de serie concreto:
sqlite /var/listaUSBs.db "SELECT * FROM lista_usbs WHERE 
SERIAL='3 20060413092100000';"
Para eliminar todos los registros:
sqlite /var/listaUSBs.db "DELETE FROM lista_usbs;"

Con ello tendríamos todo monitorizado y registrado en nuestro equipo ganando un gran control sobre la actividad USB en nuestro equipo. Obviamente dicho script sería solamente un ejemplo teórico que puede ser modificado a nuestro gusto para que se adapte lo mejor posible a nuestras necesidades.

Espero que os haya resultado útil.

Saludos.

jueves, 8 de febrero de 2018

Cómo crear un RAID por software mediante mdadm en Linux

Cuando hablamos de servidores y/o equipos que tienen que almacenar un gran volumen de datos importantes, nos vemos obligados a usar tecnologías que aseguren la integridad de nuestros datos en caso de desastre, mediante la tecnología RAID, pues es gracias a ésta que la rotura de un disco duro no es tan "grave" como debería. Generalmente la configuración de discos en RAID se suele hacer desde la propia controladora que incluye el hardware, pero no siempre podemos trabajar en las circunstancias ideales, y ahí es donde debemos de optar por una solución algo menos cómoda, pero igual de útil. La creación de RAID por software, que en este caso realizaremos gracias a la herramienta mdadm.

RAID_portada

Antes de proseguir, creo que es interesante que todo aquel que no esté familiarizado con la tecnología RAID, y con los diferentes tipos, tenga una pequeña noción tanto del RAID como de los distintos tipos de éste. 

RAID (Redundant Array of Independent Disks) permite usar un espacio de almacenamiento repartido entre varios discos para aumentar el rendimiento y/o la tolerancia a fallos. Dependiendo del tipo de RAID escogido, cuya elección haríamos dependiendo de nuestras necesidades, el RAID nos otorgaría unas ventajas u otras, con lo que es interesante conocer los tipos RAID más comunes con el fin de saber qué elección es la más adecuada para nosotros.


RAID 0:

De entre todos los RAID, probablemente sea el más "inútil", pues su única ventaja es que reparte toda la información en todos los discos que componen el RAID... Esto tiene como ventaja que si tenemos dos discos duros de 1 TB en RAID 0, tendríamos 2TB disponibles para almacenar lo que queramos, si bien tiene una gran desventaja y es que al repartir todo el contenido entre los dos discos, en caso de que uno de los dos fallase, toda la información se perdería, con lo que podríamos decir que el objetivo de este RAID se basa en la rapidez y la eficiencia, dejando de lado cualquier medida que pueda ayudarnos a no perder la información (más allá de las típicas, y al mismo tiempo infravaloradas, copias de seguridad).

RAID_0

RAID 1:

Este sería justo lo contrario al RAID 0, pues así como antes la única función del RAID 0 es la rapidez de acceso a los datos y la capacidad de usar varios discos como si fueran uno solo, en este caso el objetivo del RAID 1 es la tolerancia a fallos. En este caso, RAID 1 lo que haría sería clonar lo guardado en un disco en otro, con el fin de que en caso de que el "primer" disco falle, tengamos el segundo con la información íntegra disponible.

RAID_1

RAID 5:

Probablemente el más famoso de todos en el ámbito de servidores. En este caso no podemos realizar un RAID 5 con solo 2 discos, sino que harían falta un mínimo de 3. En este caso, el RAID 5 combina capacidad y tolerancia a errores, pues se trata de un RAID que usa tres cuartas partes de cada disco para el espacio en sí, mientras que una cuarta parte de cada uno de ellos es dedicada para la tolerancia a fallos. Además, en caso de que uno de los discos se averiase, tan pronto como se sustituyese, el nuevo disco se llenaría automáticamente con la información que le corresponde gracias a que los otros dos discos saben qué información le falta. Estos gracias a que distribuye cada ciertos bloques de información un bit de paridad permite saber qué información sería necesaria "rellenar" en caso de fallar un disco duro. En caso de que un segundo disco fallase (sin haber llegado a sustituir el primero), se perdería toda la información.

RAID_5



Teniendo estos conceptos claros, podemos pasar a la creación y/o configuración de RAIDs; para ello, lo primero e indispensable sería tener instalada la herramienta mdadm. Dicha herramienta, si bien viene incluida por defecto en los repositorios, no está instalada en el sistema, con lo que si queremos instalarla deberíamos hacer:
apt-get install mdadm
Una vez tengamos la herramienta instalada; la creación de volúmenes RAID es bastante sencilla; el único requisito es que tengamos dos discos duros (que pueden ser discos duros virtuales) o más para RAID 0 o RAID 1 y tres discos o más para RAID 5. Teniendo esto en cuenta, la sintaxis para la creación de un nuevo RAID por software sería:

mdadm -C nombre_raid -l tipo_raid -n número_discos discos_duros_a_usar nombre_discos

Si esto se desgranase significaría lo siguiente:

  • -C implica que se va a crear un nuevo RAID, al cual se le va a poner un nombre identificativo que el sistema pueda reconocer como si fuese un dispositivo más. Por norma general se usa como nombre /dev/mdx; por ejemplo /dev/md0.
  • Con el parámetro -l le estaríamos diciendo qué tipo de RAID queremos... Aquí solamente habría que indicarselo de forma numérica. Es decir que si quisiésemos un RAID 0 solamente especificaríamos el número 0, RAID 1 con el número 1, etc...
  • Gracias al parámetro -n estaríamos indicando el número de discos que van a componer el RAID, tras lo cual indicaríamos el nombre de las unidades que van a componer el susodicho.

Supongamos que vamos a crear un RAID 0 y que tenemos dos discos listos para ser usados llamados /dev/sdc y /dev/sdd. Para componer dicho RAID escribiríamos:
mdadm -C /dev/md0 -l 0 -n 2 \
/dev/sdc /dev/sdd
Dependiendo del espacio de cada disco, el RAID tardaría desde segundos hasta varios minutos. Tras crear dicho RAID podemos activarlo o desactivarlo a nuestro gusto mediante los siguientes comandos:

Para pararlo:
mdadm -S /dev/md0
Para arrancarlo:
mdadm -A /dev/md0 /dev/sdc /dev/sdd
Gracias a ello podemos tener activo el RAID solo cuando nosotros lo veamos conveniente, si bien obviamente el tener inactivo el RAID haría que no pudiésemos acceder a los discos.

En caso de tener el RAID activo, podríamos consultar el estado de este gracias al comando:
mdadm -D /dev/md0
En mi caso en particular, la información mostrada sería la de a continuación:

mdadm_D

Es importante recalcar que aunque el RAID esté creado, este carece de partición ni formato alguno... Con lo que con el fin de que sea usable habría que usar la utilidad fdisk, y crear una nueva partición:
fdisk /dev/md0
En mi caso habría creado una sola partición cuyo nombre sería md0p1. A dicha partición habría que darle formato, ya que por defecto no posee ninguno y no puede ser montado en GNU/Linux sin él. Así que le asignaríamos un nuevo formato mediante la herramienta mkfs; por ejemplo ext4:
mkfs.ext4 /dev/md0p1
Gracias a dicho comando, el RAID tendría una partición plenamente funcional que podría ser montada en el sistema como una partición más mediante el comando mount. Por ejemplo:
mount /dev/md0p1 /mnt
Obviamente dicha partición podría ser montada durante el arranque si se incluyese en /etc/fstab.

Como podéis ver la creación y gestión de RAIDs, y el convertirlos en volúmenes funcionales es más sencillo de lo que parece; simplemente habría que tener conocimiento de cada tipo de RAID y la ventaja que ofrece cada tipo.

Espero que os haya resultado útil.

Saludos.

jueves, 1 de febrero de 2018

Cómo monitorizar eventos de un directorio en tiempo real con inotify en Linux

En ocasiones podemos vernos en la situación de querer tener un control de lo que ocurre dentro de un directorio; tal es así el control que queremos que podemos llegar a desear ver todos los eventos en tiempo real que ocurren dentro de un directorio, eventos tales como el listado de archivos, la creación y modificación de estos e incluso la eliminación. Generalmente podemos intentar jugar el comando history para ver los últimos comandos, examinar las últimas fechas de modificación de los ficheros, e incluso examinar sus metadatos, con el fin de averiguar lo máximo posible de lo que ocurre en nuestro equipo; especialmente cuando éste está siendo accedido por varios usuarios a la vez o simplemente tenemos miedo de que lo esté siendo. Es por ello que para ocasiones como esas puede ser muy interesante recurrir a herramientas de observación de eventos en tiempo real para Linux tales como inotify.

Inotify_portada

Inotify es una herramienta con la capacidad de observar eventos en tiempo real dentro de un directorio, lo que nos otorga un gran conocimiento en comparación con otras herramientas, si bien, tal y como he matizado al principio, requiere monitorizar en tiempo real para poder ser funcional. Esta utilidad no está instalada por defecto en el sistema, sino que requiere ser instalada, si bien afortunadamente es realmente sencillo pues está incluida dentro de los repositorios oficiales del sistema:
apt-get install inotify-tools
Esto hará que se instalen dos herramientas: Inotifywait e Inotifywatch. El que nos interesa en concreto es Inotifywait pues ofrece una enorme información.

Un modo muy básico de usar Inotifywait sería de la siguiente forma:
inotifywait /tmp
El problema que tiene usar este comando directamente es que efectivamente escuchará los eventos de dicho directorio, pero no será capaz de escuchar los eventos de los subdirectorios de éste y además al recibir un solo evento inotifywait se cerraría automáticamente. Es por ello que conviene conocer cómo usarla para poder sacarle de verdad provecho.

Hay diferentes parámetros y opciones, pero los más importantes serían los de a continuación:

Comencemos con el parámetro -m; La inclusión de este parámetro hará que inotify entre en modo monitorización, con lo que podrá recibir eventos continuamente sin llegar a cerrarse a menos que nosotros le mandemos la señal necesaria para que se detenga. Dicho parámetro también podría especificarse también mediante --monitor. Otro parámetro muy útil sería el -r; Al usar este parámetro estaríamos indicando que se monitorizase todo el directorio en cuestión y también todos los subdirectorios. El parámetro en cuestión también podría especificarse mediante --recursive. El último que en mi opinión es también importante tener en cuenta sería el -e; Este último es más "opcional" que los dos anteriores, pero puede ser de una enorme utilidad, ya que sirve de filtrador de eventos; es decir que con dicho parámetro veríamos los eventos que nosotros queramos y no todos, que sería lo que se nos mostraría por defecto. El parámetro también puede ser llamado mediante --events. Obviamente, para poder filtrar los eventos, habría que conocer cuales son... Son un número considerable y, desgraciadamente, no todos son fáciles de recordar, pero es bueno conocer al menos su existencia:

  • access: Este sería uno de los más veríamos, ya que indica que un fichero o el contenido de un directorio ha sido leído. 
  • modify: Este parámetro haría que se nos notifique de los cambios que se realicen en el entorno que estamos monitorizando.
  • attrib: Este es ligeramente más raro que el resto, pues solamente notificaría los cambios en los atributos de los ficheros o directorios.
  • close_nowrite: Se notifica cuando se ha cerrado un fichero o directorio que ha sido abierto en modo solo lectura.
  • close_write: Se notifica cuando se cierra un fichero o directorio en modo escritura.
  • open: Especifica que un fichero o directorio ha sido abierto; este mensaje también es de los que más aparecen junto con access.
  • moved_to: Este evento implica que algo ha sido movido hacia el directorio que estamos monitorizando.
  • moved_from: Este evento indica que algo ha sido movido desde el directorio que estamos monitorizando.
  • move: Este evento indica que va a haber una actividad de movimiento dentro del directorio que monitorizamos, ya sea desde o hacia el directorio en cuestión.
  • create: En este caso se indicaría que algo (ya sea fichero o directorio) ha sido creado dentro del lugar que estamos monitorizando.
  • delete: Al contrario que create, en este caso se detectaría que algo se ha eliminado.
  • delete_self: Esto indica que el fichero o directorio que estamos monitorizando acaba de ser eliminado.
  • umount: Aquí se detectaría que el sistema de ficheros en que se aloja aquello que estamos monitorizando, acaba de ser desmontado.

Es conveniente resaltar que generalmente querremos ver todos los eventos, pero es importante conocer qué eventos se monitorizan para así poder hacer filtrados en caso de tener una necesidad muy concreta...

Teniendo estos conceptos claros podemos hacer cosas como la siguiente:
inotifywait -r -m /tmp
Con dicho comando estaríamos monitorizando todos los eventos dentro de la carpeta /tmp y todos sus subdirectorios; además estaría recibiendo constantemente eventos hasta que se le indique lo contrario, con lo que si abriésemos otra terminal podríamos realizar las acciones de a continuación:
cd /tmp && ls && touch test && echo prueba > test && rm test
Si viésemos los eventos visualizados por inotifywait veríamos lo siguiente:

inotifywait

Al final se estarían viendo en tiempo real todas las acciones realizadas, desde la creación del archivo hasta la eliminación de éste. Además si queremos podemos dejar que el proceso inotifywait esté corriendo en segundo plano y que vuelque todo el contenido en fichero para poder analizarlo más tarde con tranquilidad, extrayendo las conclusiones que deseemos. Esto requeriría hacer simplemente la siguiente modificación en el anterior comando:
inotifywait -r -m /tmp > /var/inotify.log &
Dicho proceso tendría que ser finalizado mediante un kill, o llevado a primer plano para poder finalizarse.

Como podéis ver el tener controlado un directorio o un grupo de éstos puede llegar a ser útil pues gracias a ello podemos tener un mayor conocimiento de lo que ocurre dentro de nuestro sistema.

Espero que os haya resultado útil.

Saludos.