Información blog

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

jueves, 21 de diciembre de 2017

Cómo calcular el CRC de un fichero en Linux

Las redes de datos se van haciendo progresivamente más complejas y más difíciles de administrar; ya sea por que las infraestructuras crecen o debido a que existen más factores que pueden conducir a un error o una corrupción en la comunicación; desde fallos a nivel de diseño hasta ataques de man in the middle. Afortunadamente a medida que la tecnología evoluciona, también lo hacen los mecanismos de verificación, y hoy vengo a hablaros de uno de ellos que se usa en todas las redes de una forma imperceptible para los usuarios. Se trata del CRC o Código de Redundancia Cíclica.

CRC_portada

CRC no es ni más ni menos que una mecanismo de verificación de integridad de datos, el cual realiza un checksum al enviar un paquete a otro punto de la red, y luego es realizado otro más al llegar a su destino. Ambos checksum serían comparados y en caso de que fuesen iguales, el paquete se daría por válido, mientras que en caso contrario se consideraría que el paquete se encuentra corrupto o infectado. A modo aclaratorio, he aquí un pequeño diagrama que puede resultar de ayuda:

Diagrama_CRC

El mecanismo de comprobación del CRC es ejecutado automáticamente por el sistema sin intervención alguna, pero eso no significa que no haya métodos para comprobar el CRC de un fichero manualmente sin necesidad de tener que enviarlo a ningún lado. La herramienta en cuestión, obviamente para entornos GNU/Linux, se llama cksum.

Cksum es una herramienta cuya dificultad de uso, afortunadamente, es nula y que ya viene pre-instalada en el sistema, con lo que su uso está al alcance de cualquiera. Su utilidad es el calcular el checksum CRC de un fichero y para usarla simplemente habría que escribir:

cksum fichero

Ejemplo:
cksum testCRC.txt
935282863 5 testCRC.txt

La salida de este comando estaría dividida en tres columnas: La primera correspondería al checksum CRC, la segunda al tamaño del fichero y la tercera al nombre de éste. Tanto el CRC como el tamaño son importantes, pero a nivel de red el que se tomaría en cuenta sería el CRC. Dicho checksum CRC cambiaría en caso de que el contenido de éste cambiase; veamos un pequeño ejemplo:

root@debian:/tmp# cat testCRC.txt
test
root@debian:/tmp# cksum testCRC.txt
935282863 5 testCRC.txt
root@debian:/tmp# echo 'Cambio de contenido' > testCRC.txt
root@debian:/tmp# cat testCRC.txt
Cambio de contenido
root@debian:/tmp# cksum testCRC.txt
301284194 20 testCRC.txt

En el ejemplo anterior se puede apreciar que cuando el contenido del fichero testCRC.txt es test, el checksum es 935282863 mientras que cuando el contenido es Cambio de contenido, sería 301284194. Dicho checksum también variaría con cambios más sutiles, tales como la sustitución de todas las i por u (mediante el comando sed por ejemplo).

root@debian:/tmp# sed -i 's/i/u/g' testCRC.txt
root@debian:/tmp# cat testCRC.txt
Cambuo de contenudo
root@debian:/tmp# cksum testCRC.txt
1797125648 20 testCRC.txt

Se podría considerar como una comprobación de checksum parecida a la usada con md5 (mediante md5sum) pero con un enfoque mucho más orientado a las redes; es más en este caso se podría decir que se hacen comprobaciones de integridad de forma automática sin que nosotros nos percatemos de ello; haciendo que el Código de Redundancia Cíclica sea usado por todo tipo de usuarios todos los días sin que la mayoría de ellos lo sepan, con lo que en ocasiones puede sernos interesantes conocer el CRC de un fichero con el fin de tener un mayor conocimiento de la información transmitida y/o recibida.

Espero que os haya parecido interesante.

Saludos.

martes, 24 de octubre de 2017

Cómo emular comportamientos de red en Linux con Net Emulation

Tras una buena temporada sin escribir, por fin vuelvo con las pilas cargadas y con un artículo que, en mi opinión puede serle muy útil a muchos. En la ocasión de hoy quiero poneros en un situación hipotética. Tenemos un equipo con un sistema operativo Linux (no importa que sea Debian o Red Hat) con el que queremos emular comportamientos "extraños" de red... Lo normal, y deseado, es que una red esté correctamente gestionada, que el tráfico de red fluya con fluidez, que todos los elementos de la red se comuniquen entre sí sin problemas, etc... Esto es lo deseable, pero no siempre lo que ocurre debido a un sin fin de factores, entre los cuales se encontraría, por supuesto, el factor humano. La cuestión está en que a veces, la mejor forma de resolver el problema, es emulándolo primero para después ver cómo solventarlo, y he ahí donde entra en juego la herramienta tc (Traffic Controller); más concretamente uno de sus módulos llamado netem, más conocido como Net Emulation.

Tux_Net_Emulation

Net Emulation permite crear situaciones que nos permiten emular ciertos comportamientos indeseados, tales como retardos, paquetes erróneos, duplicidades, etc... Es decir que nos permite crear situaciones indeseadas de forma controlada con el fin analizar posibles reacciones por ciertos equipos ante dichas situaciones. Tanto la herramienta Traffic Control como el módulo netem vienen por defecto incluidos en Linux, pues ambos vienen incluidos por defecto con la paquetería de iproute2 con lo que afortunadamente no requieren instalación alguna. 

El uso de este módulo en sí es bastante sencillo, si bien es importante conocer la estructura general que es necesaria conocer:

tc qdisc acción dev nombre_interfaz usuario netem acción_netem

Aquí podemos ver cuatro partes en cursiva, que serían las que variarían dependiendo de nuestras necesidades, cada parte tendría el siguiente significado:

  • Acción: Cuando escribimos el comando siempre hay que especificar si se va a añadir un comportamiento personalizado, si se va a modificar o eliminar. Es decir que en esta parte podemos optar por hacer: add, change o del. Es importante tener en cuenta que una vez añadido una emulación a la interfaz de red, no podremos hacer otro add, sino que tendremos que hacer un change para modificarla o un del para eliminarla.
  • Nombre_interfaz: Simplemente le decimos sobre qué interfaz de red queremos aplicar la emulación de red.
  • Usuario: El usuario que realizará el cambio de comportamiento. Por lo general siempre será root.
  • Comportamiento: Finalmente diríamos que comportamiento queremos emular; es decir qué emulación de red queremos hacer. Esta parte al ser algo extensa la explicaré con más detalle más adelante.

Teniendo clara esta estructura base, podemos empezar a realizar las primeras emulaciones de red.

Comenzaremos con una emulación muy sencilla pero muy útil, que es la de la implantación de retardos. Esto hace que cualquier interacción realizada con esta interfaz tardará un tiempo extra en ser realizada. Esto se logra gracias al comportamiento delay, cuyo comando completo sería:

tc qdisc acción dev nombre_interfaz usuario netem delay retardo_en_milisegundos

He aquí un comando a modo de ejemplo en el que vamos a añadirle un retardo de 200 ms a la interfaz de red eth0, con el usuario root:

tc qdisc add dev eth0 root netem delay 200ms

Una forma muy fácil de comprobar el efecto de este cambio es haciéndole un simple ping a la IP de dicha interfaz de red, tal y como podéis ver en la siguiente imagen:

delay_1

Además de poner un retardo de Xms, podemos también hacer que dicho valor no siempre sea fijo, sino que oscile entre ese valor y Xms arriba o abajo de forma aleatoria. Por ejemplo si pusiésemos el mismo comando de antes con una oscilación de 10ms, la latencia del ping podría ser hasta 10ms mayor o menor.  He aquí el comando que haría referencia a exactamente dicho ejemplo:

tc qdisc add dev eth0 root netem delay 200ms 10ms

En la imagen de a continuación podemos apreciar esa oscilación:

delay_2

Obviamente podemos hacer más cosas aparte de crear retardos; otra funcionalidad muy útil de este módulo sería la de la posibilidad de que los paquetes recibidos se consideren como "perdidos", es decir que hayan paquetes que no hayan llegado a su destino. Para ello, en vez de recurrir al comportamiento delay recurriremos a loss, comportamiento en el que especificaremos qué % de paquetes queremos que se "pierdan". Por ejemplo podemos hacer que la mitad de los paquetes no lleguen a su destino, para lo cual el comando sería el siguiente:

tc qdisc add dev eth0 root netem loss 50%

Al igual que antes, con un simple ping también podemos verificar que ahora alrededor de la mitad de los paquetes enviados se perderán; he aquí una pequeña muestra a modo de ejemplo:

loss

Casi tan grave como la perdida de paquetes es la duplicidad de éstos, cosa que afortunadamente también es emulable en Linux. Dicha emulación es posible gracias al comportamiento duplicate y usa la misma sintaxis que el anterior comando, sustituyendo, obviamente, loss por duplicate.

tc qdisc add dev eth0 root netem duplicate 50%

En este caso algunos de los pings que mandemos al equipo recibirán como respuesta que hay una duplicidad; es decir que respondería como si hubiesen dos equipos con la misma IP en la misma VLAN. Dichos pings con respuesta de duplicidad en la red tendrían la etiqueta DUP!, tal y como se puede ver en la siguiente captura:

duplicate

Otro problema que puede surgir en una red es que los paquetes de red se encuentren corruptos, cosa que también podemos emular gracias corrupt que tiene la misma sintaxis que el anterior comando, sustituyendo duplicate por corrupt.

tc qdisc add dev eth0 root netem corrupt 50%

En este caso, la respuesta que recibiremos al hacerle ping será que se esperaba un valor determinado pero que han recibido uno diferente al que se debía, mostrándonos algo como lo de la captura mostrada aquí abajo:

corrupt

Estas serían las diferentes emulaciones que se pueden hacer gracias a esta herramienta; emulaciones que, en mi opinión, no son pocas y pueden ser de gran ayuda a la hora de intentar reproducir problemas reales para después encontrarles solución. La potencia de el módulo Net Emulation queda patente ya que es increíblemente flexible y versátil. Obviamente las funciones pueden combinarse; es decir que se puede hacer que haya un delay determinado, con un % determinado de paquetes erróneos otro % determinado de paquetes duplicados, etc... Por ejemplo, podemos querer editar una de las reglas que hemos creado atrás para que tenga toda esta combinación de comportamientos; con lo que en vez de usar add, usaremos change para obtener el siguiente comando:

tc qdisc change dev eth0 root netem delay 100ms 20ms \
loss 1% duplicate 0.5% 

Como podéis observar, el comando no es más que una combinación de los atrás vistos, con el cual se puede combinar una latencia alta con un pequeño porcentaje de paquetes perdidos y uno aun menor de duplicados. Un detalle importante a la hora de hacer uso de la función change; si nos fijamos en el valor anterior, se hace un change a los valores delay, loss y duplicate. Si anteriormente hubiese sido especificado un valor para corrupt, la acción change no modificaría su valor ni lo eliminaría a menos que se especificase específicamente. Es decir que si por ejemplo hubiésemos hecho un corrupt del 50% y luego hubiésemos introducido este comando, el valor de corrupt sería siendo el mismo.

Finalmente, para eliminar estos comportamientos especiales por completo, podemos recurrir a la acción del, para lo cual simplemente habría que escribir:

tc qdisc del dev eth0 root netem

Tal y como se puede ver en este artículo, la emulación de diferentes situaciones "conflictivas" es más sencillo de lo que parece, y afortunadamente, todo ello se puede hacer desde cualquier equipo con Linux instalado y sin herramienta especial alguna, estando dicha emulación al alcance de todo el mundo.

Espero que os haya resultado útil.

Saludos.

martes, 1 de agosto de 2017

Predictable Network Interface Names: Qué es y cómo deshabilitarlo

La tecnología va evolucionando constantemente y siempre en cada versión de cada sistema operativo, se van apreciando cambios; cambios que pueden ser desde parches de seguridad a otros más "tangibles" tales como nuevas versiones de software, cambios en el init (el famoso systemd) o pequeños, pero en parte importantes, cambios en la nomenclatura, tales como los realizados en las interfaces de red. Cualquiera que haya estado usando GNU/Linux durante años sabe que "normalmente", a la hora de llamar a una interfaz de red, se le llamaba eth0, eth1, etc... o en caso de ser una tarjeta de red inalámbrica, se le llamaba wlan0, wlan1, etc... La cuestión está que esa nomenclatura ya no se mantiene y las interfaces de red reciben un nombre distinto, dicha nomenclatura no era del todo fiable ni robusta...

PNIN_portada

La explicación técnica sería que si bien los nombre usados antiguamente era fáciles de recordar, la "relación" entre el nombre de la interfaz de red y la MAC no siempre era del todo fiable, pues dicha MAC podía cambiar (en un entorno virtualizado es algo relativamente común), y a veces debido a problemas con drivers podía haber problemas. Para solucionar dicho problema, desde systemd han creado la funcionalidad: Predictable Network Interface Names.

Dicho tipo de nomenclatura se basa en nombrar las interfaces de red basado en su firmware, el tipo de interfaz y su MAC, haciendo que el proceso sea completamente automático y que se evite cualquier tipo de conflicto con los nombres y/o MACs, con el fin de evitar duplicidades y/o problemas de configuración con las interfaces de red... Este nuevo tipo de interfaces tienen 3 tipos de prefijos, seguidos de unas letras y números:

  • en: Este prefijo significaría que la interfaz de red es del tipo Ethernet (LAN)
  • wl: En este caso se haría referencia a un interfaz del tipo inalámbrica (WLAN).
  • ww: Aquí se haría referencia a una interfaz de red de área amplia inalámbrica (WWAN)

Hasta aquí la parte que más fácil se puede recordar, pues a partir de aquí la nomenclatura se torna bastante peculiar, y si bien es una nomenclatura "predictiva" que el sistema puede detectar y reconocer correctamente, a nivel humano no es, ni de lejos, tan predecible. Esta funcionalidad intentará ejecutar una serie de políticas para nombrar la interfaz de red, primero intentará una política y en caso de no poder aplicarla lo intentará con la siguiente y así sucesivamente... Las políticas a aplicar (en orden) serían:

  • Política 1: Se intentará incorporar al nombre de la interfaz, la letra o más el número de indexación que le ha dado la BIOS/firmware para los elementos integrados en la placa. Por ejemplo: eno33. Si la tarjeta de red no fuese integrada, no fuese reconocida como tal o simplemente no se pudiese aplicar dicha política, se pasaría a la política 2.
  • Política 2: Se intentará incorporar al nombre de la interfaz, la letra s más el número de indexación que le ha dado la BIOS/firmware para los elementos hotplug. Por ejemplo: ens33. Si la tarjeta de red no fuese un elemento hotplug, no fuese reconocida como tal o simplemente no se pudiese aplicar dicha política, se pasaría a la política 3.
  • Política 3: Se intentará incorporar al nombre de la interfaz de red la letra p junto con la localización física ésta. Por ejemplo: enp2s0. En caso de que tampoco se pudiera, se pasaría a la política 4.
  • Política 4: Se añadirá la letra x más MAC al nombre de la interfaz; por ejemplo: enx78e7d1ea46da
  • Política 5: Si no se hubiese podido adoptar ninguna de las anteriores políticas, se usaría la nomenclatura tradicional; por ejemplo: eth0.

Como podéis ver, las nomenclaturas usadas tienen una estructura muy bien definida, pero desgraciadamente no es del todo "amigable" pues podemos encontrarnos con casos tales como la política 3 o la política 4 en los que el nombre de nuestra interfaz es largo y difícil de recordar... Esto no significa que esa desventaja haga que el uso de las políticas antiguas sea mejor, ya que generalmente las ventajas superan a las desventajas... 

El problema del uso de esta nomenclatura viene cuando tenemos multitud de scripts "antiguos", que están preparados para trabajar con los nombres de interfaz clásicos... Pues si bien es cierto que podemos ir sustituyendo los nombres mediante el comando sed... No siempre es la mejor opción, ya que basta que la Predictable Network Interface Names (PNIN para acortar), use un nombre distinto para la interfaz para que el script no funcione... Es por ello que a veces nos puede ser más beneficioso usar las nomenclaturas antiguas en nuestro sistema, si bien ¿Cómo podemos hacerlo?

Afortunadamente el cambio es muy sencillo; cambio que además puede revertirse fácilmente por si en el futuro nos hiciesen falta las nuevas nomenclaturas. Este cambio se ha aplicado en Debian 9, pero debería ser válido en cualquier sistema similar. Dicho cambio se realiza a nivel de GRUB y para ello editaremos el fichero /etc/default/grub y editaremos la línea GRUB_CMDLINE_LINUX.

Su aspecto original sería el siguiente:
GRUB_CMDLINE_LINUX=""

Dicha línea debería ser editada para que tenga el siguiente aspecto:
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"

Con esto haríamos que tan pronto como se cargue el GRUB, la asignación del nombre se realice a nivel de kernel y con mediante PNIN, si bien para aplicar los cambios tendríamos que ejecutar dos comandos:
sudo grub-mkconfig
sudo update-grub

Ahora bien, antes de reiniciar el sistema sería necesario entrar sí o sí dentro del fichero /etc/network/interfaces para cambiar las nomenclaturas a las interfaces, pues en caso contrario la interfaz de red en cuestión, cuando obtuviese el "nuevo" nombre, no podría asociarse a una IP. Esto lo podemos hacer o bien a mano o bien mediante el comando sed, aunque para esto último tendríamos que tener muy claros los nombres. Supongamos que tenemos una sola interfaz de red en el sistema, llamada ens33... Al ser la única interfaz y ser ethernet sabemos que se convertirá en eth0 con lo que podemos ejecutar el siguiente comando:
sed -i 's/ens33/eth0/g' /etc/network/interfaces

Por último, pero no por ello menos importante, sería necesario reiniciar, con el fin de poder aplicar la nueva configuración introducida en el GRUB y poder hacer referencia a nuestra interfaz tal y cómo siempre hemos hecho.

Espero que os haya resultado útil.

Saludos.

miércoles, 26 de julio de 2017

Conociendo iproute en Linux

Finalmente el uso del comando ip se ha vuelto una necesidad; hace muchos años se lleva diciendo que el comando ip iba a sustituir al famoso ifconfig, entre otros, pero siempre ha habido gente que conociendo el archi-conocido comando, no necesitaba ahondar más y recurrir a su "primo" iproute (que contiene el comando ip, entre otros) para manipular la configuración de red en Linux. La cuestión es que ahora las nuevas versiones, tales como Debian 9, ya no usan el comando ifconfig, con lo que lo que antes era cuestión de gustos, ahora se ha vuelto necesario aprender a usar. En su momento expliqué las ventajas del comando ip para la manipulación de la tabla ARP, pero ahora es necesario dar un paso más y dominarlo un poco más a fondo para que, al menos, podamos defendernos con iproute, concretamente con el comando ip.

iproute_portada

El comando ip agrupa una serie de "subcomandos" que pueden controlar desde la propia IP, valga la redundancia, hasta otros factores relacionados con ésta, tales como la tabla ARP, la conectividad y la puerta de enlace. Englobar todos los parámetros de todas las opciones es una tarea prácticamente imposible, con lo que nos centraremos en aquellas opciones y/o comandos que, en mi opinión, son los más útiles e importantes en el día a día, pues serán los que más posibilidades tienen de ser usados.

Empecemos con el comando más básico e importante de todos, el comando: el comando ip addr. Este comando sería el equivalente al clásico ifconfig y es sin lugar a dudas el que más probabilidades tiene de ser usado. Dicho comando tiene diferentes opciones que permitirán tanto mostrar como modificar y/o eliminar nuestra ip actual. Veamos cómo hacerlo:

Para mostrar nuestra IP podemos usar uno de los siguientes comandos:
ip addr
ip addr show
ip addr show dev ens33

Los dos primeros comandos mostrarían la IP de todas las interfaces de red, mientras que el tercero mostraría la IP de la interfaz con nombre ens33 (algo equivalente a hacer un ifconfig de una interfaz de red concreta).

Obviamente podemos hacer más cosas con este comando, tales como la asignación o eliminación de IP a una interfaz de red, cosa tan sencilla como hacer:
ip addr add 192.168.1.2/24 dev ens33
ip addr del 192.168.1.2/24 dev ens33

Otra funcionalidad muy útil sería la manipulación de la interfaz de red en sí; es decir la activación y desactivación de la interfaz de red o la activación y desactivación de algunas características de dicha interfaz... Dicha funcionalidad se denominaría ip link, y con ella podríamos hacer cosas tan interesantes como las siguientes:

Activación de la interfaz de red ens33:
ip link set dev ens33 up

Desactivación de la interfaz de red ens33:
ip link set dev ens33 down

Obviamente podemos manipular más características de la interfaz de red... No todas son usadas habitualmente pero hay algunas que nos pueden resultar realmente interesantes:

Activación/Desactivación de modo promiscuo en interfaz de red ens33:
ip link set dev ens33 promisc on
ip link set dev ens33 promisc off

Activación/Desactivación del arp en la interfaz de red ens33:
ip link set dev ens33 arp on
ip link set dev ens33 arp off

Activación/Desactivación de la gestión del tráfico multicast en la interfaz ens33;
ip link set dev ens33 multicast on
ip link set dev ens33 multicast off

También podemos usar la herramienta ip link para añadir interfaces de red virtuales para luego asignarles una ip y activarlas... Obviamente dichas interfaces virtuales también pueden ser eliminadas. Esto requiere una combinación de las utilidades ip link e ip addr, pero el proceso es relativamente sencillo:

Para crear una interfaz de red virtual plenamente funcional, lo primero que haremos será crear dicha interfaz virtual. Al ser una interfaz virtual, ésta dependerá de una interfaz física (en este caso ens33):
ip link add link ens33 name ens33.1 type vlan id 100

Luego únicamente habría que asignarle una dirección IP a dicha intefaz, con nombre ens33.1, y activarla, cosa que ya hemos visto antes y que sería tan sencillo como hacer:
ip addr add 192.168.1.10/24 dev ens33.1
ip link set dev ens33.1 up

Por último podemos también eliminar las interfaces de red que hayamos creado mediante este método, mediante el comando:
ip link delete ens33.1 

No podríamos repasar el comando ip sin mencionar la definición de puertas de enlace mediante el comando ip route. Lo primero que tendríamos que conocer de dicho comando sería la tabla de enrutamientos que se tiene actualmente, lo cual se puede hacer de dos formas:
ip route 
ip route show

Además de dicho listado, también podemos usar el comando ip route para denegar una IP de forma parecida como haríamos con iptables; si bien esta utilidad tiene muchísimas más limitaciones que iptables. Podemos denegar el acceso a una IP de forma simple o también podemos especificar de qué IP denegar el acceso a otra IP. Por ejemplo:

Para denegar el acceso a una IP:
ip route add prohibit 192.168.1.1

Para denegar el acceso a una IP desde una IP de origen determinada:
ip route add prohibit 192.168.1.1 from 192.168.1.2

Además de estas dos funcionalidades, tendríamos la posibilidad de establecer la puerta de enlace, lo cual se puede añadir de esta forma:
ip route add default via 192.168.1.1

Para eliminar una regla añadida simplemente habría que recurrir al parámetro: del; tal y como veis a continuación:
ip route del prohibit 192.168.1.1
ip route del default via 192.168.1.1

Además de estas opciones, hay otra que si bien no especialmente utilizada a nivel de escritorio, a nivel de servidor se trata de una herramienta de un valor incalculable... Se trata de ss, el equivalente al siempre útil netstat, cuya función es listar los puertos de red del sistema. No se trata de un parámetro del comando ip y por eso lo he dejado para el final, pero se trata de una de las herramientas otorgadas por iproute que tiene un gran valor en la administración de servidores. Afortunadamente la curva de aprendizaje con esta utilidad es mínima pues el comando ss usa los mismo parámetros que netstat con lo que podremos hacer acciones como las siguientes:

Listar todas las conexiones:
ss -a

Listar todas las conexiones tcp y udp, mostrando el número de puerto en vez de el nombre correspondiente a dicho puerto.
ss -tuna

Con este conjunto de comandos (más los del manejo de arpwatch cuyo enlace se encuentra en el primer párrafo de este artículo) tendríamos un dominio global de las funcionalidades más importantes de iproute, pudiendo desenvolvernos con soltura con esta herramienta para poder manipular adecuadamente nuestra configuración de red.

Espero que os haya resultado útil.

Saludos.

miércoles, 12 de julio de 2017

Variables de entorno y variables de shell en Linux

A la hora de trabajar con la consola de GNU/Linux; ya sea para ejecutar scripts, programas o simplemente ejecutar algunos comandos, a veces necesitamos recurrir a unos comandos de forma recurrente, comandos que generalmente tienen una composición relativamente compleja, con muchos parámetros, argumentos, etc... Esto de por sí generalmente no suele ser demasiado problemático a la hora de ejecutarlo una sola vez, pero cuando queremos hacerlo de forma recurrente es preferible tenerlo almacenado en una variable a la que podamos recurrir cuando queramos sin tener que repetir siempre lo mismo... Además el uso de éstas puede resultarnos muy útil también pues podemos tenerlas incluidas desde el inicio de la sesión, pudiendo ayudarnos en ciertas tareas... Para ello hemos de tener en cuenta que tenemos dos tipos de variables: De entorno y de shell.

Shell_entorno_bash

La diferencia entre ambas es sutil, pero importante, ya que dependiendo de la que elijamos, puede tener una repercusión u otra... Aún así empecemos por lo más básico, el listado de las variables actuales, lo cual se puede hacer mediante dos métodos:

Para listar las variables de entorno haríamos:
env

Mientras que para las variables de shell sería:
set

Dependiendo del comando introducido tendremos un resultado u otro,  y es que cada tipo de variable tiene un propósito muy diferente... Mientras que la variable de shell se encuentra únicamente disponible para dicha shell/consola en concreto, la variable de entorno  se encontraría disponible para dicha shell y cualquier subshell que se haya podido invocar tras la declaración de dicha variable, siendo una variable mucho más accesible que la otra y pudiendo ser usada para facilitar varias tareas. Ahora bien; ¿Cómo crear una variable?

Comencemos con lo más básico, la variable de shell: Declarar una variable de shell es tan sencillo como hacer:

variable='aquello a lo que queramos hacer referencia'

Pongamos un pequeño ejemplo; vamos a crear una variable que liste los ficheros de log del directorio /var/log:

logs='ls -lp /var/log/ |grep -v /'

Esta declaración de variable puede recordarnos a la declaración de una variable dentro de un script de bash; y es que en verdad estaríamos haciendo lo mismo; es decir que estaríamos limitando la validez de la variable a una shell en concreto. En caso de ser en la consola, solamente se limitaría a ésta, mientras que en un script la variable solamente viviría durante la ejecución de éste. Al igual que en un script, la forma de llamar a esta variable sería usando el carácter $ seguido del nombre de la variable; es decir que en este caso sería:

$logs

Generalmente este tipo de variables serán usadas o bien en scripts concretos o en situaciones puntuales en las que en una misma sesión queramos recurrir a un dato/comando recurrentemente, pero si queremos algo más flexible y persistente, este tipo de variables no nos serán especialmente útiles.

Para situaciones en las que queramos que tanto la shell, como subshells como scripts ejecutados posteriormente (desde dicha shell/subshell), puedan recurrir a la variable, necesitaremos algo más persistente; necesitaremos una variable de entorno cuya creación difiere ligeramente de las de shell pues se crea de la siguiente forma:

export variable='aquello a lo que queramos hacer referencia'

Lo cual si aplicásemos a nuestro ejemplo anterior sería:
export logs='ls -lp /var/log/ |grep -v /'

Gracias a esto, dicha variable podría ser accedida por scripts ejecutados posteriormente o por cualquier subshell, dandonos una enorme flexibilidad...

Obviamente, al igual que una variable puede ser creada, también puede ser removida. Para remover una variable, sea del tipo que sea, solamente habría que hacer:

unset variable

Lo que en nuestro caso significaría:
unset logs

Gracias a lo cual la variable habría sido eliminada.

Lo ideal, especialmente con las variables de entorno, es incluirlas dentro de un script de arranque para cerciorarnos que siempre estarán accesibles... Se puede usar cualquier script , pero el script estándar en el que se incluyen este tipo de variables es el script .bashrcincluido dentro de la carpeta home de todos los usuarios, pues se ejecuta automáticamente al iniciar sesión el usuario y sabremos que cualquier función y/o script que éste ejecute podrá recurrir a la variable en cuestión. La única pega que tiene esta medida es que requiere modificar todos los .bashrc de todo los usuarios que necesitemos que usen esta variable, con el fin de que no haya problema alguno, pero una vez hecho ese esfuerzo, tendremos la certeza de que la variable siempre será exportada. Solamente tendremos que añadir la variable de entorno a dicho fichero y con ello ya tendríamos el problema resuelto.

Espero que os haya resultado útil.

Saludos.

sábado, 24 de junio de 2017

Cómo poner una marca de agua desde la terminal Linux con Imagemagick

Hoy en día es realmente común ver imágenes de galerías o de blogs con marcas de agua incluidas con el fin de que dichas imágenes no puedan ser redistribuidas, a menos que, en algunos casos, pagues por el derecho de usarlas sin marca alguna, cosa común en algunas webs dedicadas a la distribución de imágenes. El proceso de generación e incrustación de marcas de agua puede que a primera vista a uno le pueda parecer complejo o que sea tarea de una persona con conocimientos de maquetación o diseño gráfico; pero en realidad es mucho más sencillo de lo que parece, o al menos lo es en GNU/Linux. Esto es gracias a una herramienta de manipulación de imágenes muy potente y conocida llamada Imagemagick.

Imagemagick_portada

Imagemagick es un conjunto de utilidades basadas en la terminal que es capaz de mostrar, manipular y convertir imágenes de multitud de formatos, lo hace de ella una utilidad muy versátil y conocida. Dicha herramienta, dependiendo de la distribución que estemos usando no estará instalada por defecto, pero al estar dentro de los repositorios oficiales, es increíblemente sencilla de instalar, pues solamente habría que hacer:

apt-get install imagemagick

Con esto realizado, lo primero que crearíamos sería nuestra marca de agua particular, la cual en nuestro caso sería basada en texto y que sería la que usaríamos para incrustarla a nuestras futuras imágenes. En mi caso particular la marca de agua en sí se llamaría Marca_bytelearning.png pondría bytelearning, pero se podría poner cualquier otro nombre y texto. Para la creación de la marca de agua en sí, solamente requeriríamos de un comando:

convert -size 400x200 xc:none -pointsize 25 -kerning 1 \
-gravity center -fill black \
-annotate 330x330+0+0 "Bytelearning" -fill white \
-annotate 330x330+2+2 "Bytelearning" Marca_bytelearning.png

Este comando crearía una marca de agua blanca con bordes oscuros; en concreto dicha marca tendría el siguiente aspecto:

marca_de_agua
Marca de agua

Obviamente dicha marcha de agua tendría que ser incrustada en una imagen a nuestra elección y además tendría que tener una pequeña degradación para que la marca de agua sea semi-transparente en la imagen en la que queramos añadir nuestra marca. Esto se logra gracias al comando composite, que tendría la estructura de a continuación:

composite  -disolve 20% -tile marca_de_agua imagen_original nombre_imagen_con_marca

Por ejemplo, si nuestra imagen se llamase prueba_marca.png, el comando podría ser:

composite -dissolve 20% -tile Marca_bytelearning.png \
prueba_marca.png prueba_marca_MARCADO.png

Lo cual daría como resultado una imagen como la siguiente:

tux_marca_de_agua

Si tuviésemos la costumbre de incrustar marcas de agua de manera recurrente, si bien el comando es relativamente sencillo, podemos simplificarlo todo en un script que hará que todo resulte mucho más sencillo. Un script como el siguiente, al que podríamos llamar incrustador_marca.sh:


#!/bin/bash

#VARIABLES COLORES
VERDE='\e[0;32m' #Verde
ROJO='\e[0;31m' #Rojo
NC='\e[0m' # No Color

#COMPROBAMOS LA CANTIDAD DE ARGUMENTOS
if [[ $# -ne 1 ]];
then
        echo -e "${ROJO}SOLAMENTE HAY QUE INTRODUCIR UN ARGUMENTO${NC}"
        exit
fi
#PREPARAMOS LOS COMPONENTES
IMAGEN_ORIGINAL=$1
IMAGEN_MARCADA=$(echo "${IMAGEN_ORIGINAL}" |cut -d '.' -f 1)"_MARCADA.png"
MARCA_AGUA=/tmp/Marca_bytelearning.png

#EJECUTAMOS EL COMANDO
composite -dissolve 20% -tile ${MARCA_AGUA} ${IMAGEN_ORIGINAL} ${IMAGEN_MARCADA}\
> /dev/null 2>&1

#INFORMA DEL RESULTADO DEL COMANDO
if [[ $? -ne 0 ]];
then
        echo -e "${ROJO}La incrustacion de la marcha ha sido FALLIDA" \
        "revise la imagen original: ${NC}${IMAGEN_ORIGINAL}"
else
        echo -e "${VERDE}La incrustacion de la marca ha sido EXITOSA," \
        "revise la nueva imagen: ${NC}${IMAGEN_MARCADA}"
fi

Con esto haríamos que todo el proceso fuese automático, exigiendo a cualquier persona que use el script el mínimo esfuerzo, ya que solamente habría que indicarle la imagen a la que queremos incrustarle la marca.

Gracias a esto, tendríamos un incrustador automatizado y cualquier marca que queramos poner en nuestras imágenes se haría de forma rápida e intuitiva.

Espero que os haya resultado útil.

Saludos.

viernes, 16 de junio de 2017

Cómo cifrar un disco en Linux con LUKS

La seguridad de nuestros datos es cada vez más preocupante para usuarios de todos los niveles, tanto a nivel de equipos de escritorio como de servidores. Es más, a nivel de equipos escritorio es algo aún más importante, ya que dichos equipos están al alcance de cualquiera y si una buena protección nuestros datos pueden verse expuestos. Existen varios tipos de cifrado que suelen estar orientados para cifrar cierto tipo de 'objetos'... Por ejemplo GPG o PGP son usados para el cifrado de ficheros y eCryptfs para el cifrado de directorios... Pero ninguna de estas herramientas de cifrado sirve para nuestro propósito; para cifrar un disco entero tenemos que optar por otro tipo de cifrado llamado LUKS.

LUKS

LUKS (Linux Unified Key Setup) es una especificación de cifrado de disco creado para GNU/Linux que se usa de forma estándar, lo que hace que dicho cifrado pueda ser usado en cualquier distribución de Linux. Dicho cifrado está específicamente pensado para cifrar discos, lo que lo hace perfecto para nuestro propósito.

Para ello lo primero que habría que hacer sería instalar las dependencias necesarias para posibilitar tanto el cifrado como el descifrado del disco, pues si bien es una implementación estándar, algunas distribuciones, tales como Debian, no la instalan por defecto. El paquete en cuestión se llama cryptsetup, el cual se instala desde los repositorios oficiales, lo que es tan sencillo como:

apt-get install cryptsetup

Por otro lado, vamos a tratar con un disco duro 'limpio', que simplemente está conectado; es decir que no tiene contenido alguno, ni tabla de particiones alguna; solamente está conectado. Con lo que vamos a convertir dicho disco duro en un disco duro con cifrado que además se automontará durante el arranque y que estará siempre disponible para nosotros.

Para ello, lo primero que haremos será fdisk, lo cual de por sí es sencillo, si bien en este caso no le agregaremos formato alguno al disco duro que añadamos, solamente le asignaremos una tabla de particiones muy básica para que nuestro sistemas pueda interactuar con él. Nuestro disco en cuestión es /dev/sdb, con lo que lo que haríamos sería:

fdisk /dev/sdb

Tras lo cual simplemente habría que crear una nueva partición, pulsando la tecla n, haciendo que dicha partición ocupe todo el disco, cosa que ya trata de hacer por defecto, y aplicando los cambios al disco en cuestión mediante la tecla w.

fdisk
Muestra comando fdisk

Esto que acabamos de hacer haría que tuviesemos como resultado un disco duro de nombre /dev/sdb con una partición única llamada /dev/sdb1. Generalmente sería necesario darle formato a dicha partición, pero no lo vamos a realizar aún debido a que cuando cifremos la unidad eliminaremos TODO el contenido de ésta y además dejaremos la unidad sin formato, con lo que en caso de tener contenido dentro de la partición, habría que hacer una copia de seguridad antes de pasar a cifrarla.

Teniendo eso en cuenta lo que haremos será cifrar la partición con formato LUKS, lo cual se hará mediante el comando de a continuación:

cryptsetup luksFormat /dev/sdb1

Para verificar que vamos a cifrar dicha partición, lo que tendremos que escribir será YES, tras lo cual nos pedirá que introduzcamos la contraseña con la que descifraremos la partición. El proceso tardará más o menos tiempo dependiendo de la potencia de nuestro equipo y del tamaño de la unidad que queramos cifrar.

Para abrir, o mejor dicho descifrar, la unidad que recién acabamos de crear, necesitaremos usar el comando cryptsetup de nuevo, pero con una estructura diferente:

cryptsetup open nombre_partición nombre_identificativo

Él nombre identificativo sería el nombre con el cual haremos referencia a la partición tras descifrarla. Es decir que tras descifrar la unidad, no haremos referencia a ésta como dev/sdb1 (en este caso) sino cono el nombre identificativo que le hemos asignado.

cryptsetup open /dev/sdb1 DISCO

Tras introducir el comando, el equipo nos pedirá introducir la contraseña correspondiente, tras la cual el disco quedará disponible, si bien no podremos hacer referencia a éste como habitualmente, sino que tendremos que hacerlo a través de su nombre identificativo; además, no encontraremos dicho nombre dentro del directorio /dev/ como es habitual con los dispositivos físicos, sino que lo haremos a través del directorio /dev/mapper. A sabiendas de que el nombre que le hemos puesto es DISCO, la ruta completa de nuestra unidad descifrada será /dev/mapper/DISCO.

Ahora que tenemos localizada la unidad, pasaremos a darle formato para que podamos usarla con normalidad. En este caso le vamos a asignar formato ext4, el más común de todos, pero podemos darle otros tales como bfs, ext2 o ext3. El comando para darle formato ext4 sería:

mkfs.ext4 /dev/mapper/DISCO

Ahora que la unidad tiene formato, ya puede ser usada por el sistema como si fuese una unidad más sin ningún tratamiento especial. Por ejemplo vamos a crear una carpeta llamada /mnt/NUEVODISCO, para luego montar la unidad dentro de ésta:

mkdir /mnt/NUEVODISCO
mount /dev/mapper/DISCO /mnt/NUEVODISCO

Con esto ya tendríamos la unidad disponible y perfectamente utilizable, lo cual generalmente es lo que deseamos, pero el problema que tiene esto es que al reiniciar el equipo habrá que recurrir de nuevo al comando cryptsetup para después montar de nuevo la unidad... Tarea que si bien no es demasiado complicada, si que es algo tediosa tener que hacerla cada vez que arranquemos el equipo, si tenemos intención de tener la nueva unidad siempre disponible.

Es por eso que vamos preparar el equipo para que cada vez que se inicie éste, realice todos los preparativos y que solamente te pida la contraseña de descifrado durante el arranque. Para ello tendremos que dirigirnos al fichero /etc/crypttab; fichero que ha sido creado durante la instalación de cryptsetup y que trabaja de forma parecida a fstab, pero orientado a discos cifrados. Dicho fichero será modificado para acabar con el siguiente aspecto:

# <target name> <source device> <key file> <options>
DISCO    /dev/sdb1       none luks

Gracias a ésto, el disco sería descifrado durante el arranque. Solamente faltaría montarlo para que sea accesible tan pronto como hemos arrancado el equipo, cosa que se especificaría dentro del fichero /etc/fstab. Aquí añadiremos esta línea al final del todo:

/dev/mapper/DISCO /mnt/DISCONUEVO ext4 rw 0 0

Tras aplicar este cambio, únicamente haría falta actualizar initframs (initial RAM file system) para asegurarnos de que descifre y monte adecuadamente nuestra nueva unidad. La actualización radica en un único comando que sería:

update-initramfs -u -k all

Gracias a esto, tras reiniciar el sistema veremos que pide una contraseña para descifrar la nueva unidad, contraseña que tendremos que introducir para poder acceder a ésta. Dicha contraseña siempre será pedida durante cada arranque, pero al menos evitaremos tener que usar cryptsetup y mount tras cada arranque.

Espero que os haya resultado útil.

Saludos.

miércoles, 7 de junio de 2017

Cómo mandar mensajes del Kernel de Linux por red

Una de las prácticas que más miedo le da a uno a la hora de trabajar con GNU/Linux es la actualización del Kernel... Dicha actualización siempre tiene cierta peligrosidad,especialmente si el kernel ha sido compilado a mano, ya que al final estamos metiendo una nuevo núcleo que va a soportar nuevos componentes de hardware, va a solucionar bugs, etc... pero que también puede generar ciertas incompatibilidades y puede tener algún problema con algún módulo o alguna función, llegando a generar, en casos extremos, los temidos 'Kernel panic'. Es por ello que puede ser interesante capturar los mensajes del kernel con el fin de ver la causa de un problema concreto; especialmente el mencionado kernel panic, ya que si hay un kernel panic y el sistema es reiniciado (cosa normal, ya que los pánicos suelen hacer que el ordenador se apague o reinicie), el problema no se registra en ninguna parte y lo único que ven los logs es el kernel panic en sí y no la causa del error. Además, al ser enviados los mensajes a otra máquina, podremos examinarlos tranquilamente pues la maquina receptora, en un principio, será un equipo estable.

Linux_kernel

Para ello lo que habría que tener claro es la esquemática de red que seguiremos... En este caso querremos que los mensajes a otro equipo que se encuentra en la misma red local; en concreto el escenario sería el siguiente:

escenario_transferencia
Escenario transferencia de mensajes del kernel

Con esto claro, lo primero que tenemos que conocer es la MAC del equipo destino, MAC que usaremos más adelante para el envió de mensajes... Esto se puede conocer gracias a la tabla ARP que se puede consultar desde el equipo que va a enviar los mensajes. Para ello simplemente tendremos que usar dos comandos muy sencillos que se pueden usar en una consecución:

ping -c 2 192.168.1.7 && arp -n |grep 192.168.1.7 |awk '{print $3}'

Con el primer comando, haríamos un simple ping al equipo al que queremos transferir los archivos, ping que haría que la IP y la MAC del equipo de destino se agregasen automáticamente a la tabla ARP (siempre y cuando no existiesen ya dichos datos). Con el segundo comando extraeríamos la MAC correspondiente a dicha IP; extracción que realizaríamos de la tabla ARP.

Ahora que conocemos la MAC del destinatario de nuestros mensajes, pasaremos a modificar el arranque del GRUB en el equipo A para que muestre todos los eventos del kernel durante el arranque, acción que realizaremos como root... Esto se puede hacer de dos formas, o de manera temporal o de manera permanente... En este caso lo haremos de forma permanente, para lo cual iremos al fichero /etc/default/grub y editaremos el parámetro GRUB_CMDLINE_LINUX_DEFAULT, borrando su contenido y haciendo que tenga el siguiente aspecto:

GRUB_CMDLINE_LINUX_DEFAULT="debug ignore_loglevel"

Tras dicho cambio, guardaríamos los cambios en el fichero y luego ejecutaríamos el comando:

update-grub

Tras realizar dicho cambio lo que haremos será que un módulo llamado netconsole cargue durante el arranque, dicho módulo permitirá el envío de eventos del kernel a través de la red, con lo que su importancia es fundamental. Para añadir dicho módulo simplemente habría que incluirlo dentro del fichero /etc/modules. Esto consiste simplemente en escribir el comando de a continuación:

echo 'netconsole' >> /etc/modules

Aún así, con la carga de dicho módulo durante el arranque no vale, pues dicho módulo debe tener una configuración, en la que se especifique de donde a donde van a ir los paquetes, la interfaz por la que van a salir y la MAC del destinatario (para evitar posibles malentendidos o posibles ARP spoofing). Para dicha configuración habría que crear el fichero /etc/modprobe.d/netconsole.conf y añadirle lo siguiente:

options netconsole netconsole=10000@192.168.1.6/eth0,10000@192.168.1.7/08:00:27:e0:ad:30

Tal vez así escrito no quede demasiado claro, pero expuesto de forma genérica vendría a ser la estructura:

options netconsole netconsole=puerto_origen@ip_origen/nombre_interfaz_origen,puerto_destino@ip_destino/mac_destino

Es muy importante que todos los datos estén correctos, ya que en caso contrario el módulo no cargará y no podremos enviar los eventos.

Teniendo el emisor preparado, habría que preparar el receptor de dichos mensajes; preparación que afortunadamente no requiere gran esfuerzo; simplemente requiere tener netcat instalado. En caso de no tenerlo instalado haríamos:

apt-get install netcat

Netcat sería usado en este caso para ponernos a "escuchar" en el puerto que hemos especificado anteriormente en netconsole.conf; escucha necesaria para recibir los paquetes que el emisor va a lanzar. Para ponernos en escucha la sintaxis sería:

nc -l -u -p puerto > fichero &

Con esto no solo estaríamos poniéndonos en escucha, sino que además estaríamos almacenando lo recibido en un fichero y con el símbolo & al final estaríamos haciendo que el proceso corriese en segundo plano para que la consola no se sature con mensajes constantemente y para que simplemente leamos los susodichos cuando queramos. En este caso, al haber escogido el puerto 10000 lo que escribiríamos sería:

nc -l -u -p 10000 > /root/192.168.1.6_kernel.log &

Ahora, con tan solo reiniciar el equipo emisor, éste empezaría a mandar eventos; eventos que serían desde los generados en el propio arranque hasta cualquier evento generado por el kernel mientras el equipo está en marcha; gracias al comando anterior, la consola del receptor no se encontraría saturada de mensajes constantemente, sino que solamente tendríamos que consultar el fichero donde estamos volcando los susodichos... Al ser un fichero que suele recibir información con regularidad, lo ideal sería ver tanto el contenido de por sí añadido como el que se va añadiendo en tiempo real, con lo que la mejor forma de consultarlo sería mediante el comando:

tail -n 100 -f /root/192.168.1.6_kernel.log

A modo de ejemplo, nos podríamos encontrar con el contenido de a continuación:

muestra_netconsole
Ejemplo mensajes del kernel

Los mensajes serían exactamente los mismos que los que mostraría el equipo emisor si introdujésemos en éste el comando dmesg, con la ventaja de que en caso de reiniciar el equipo, no perderíamos la información generada.

Como podéis ver, los mensajes del kernel pueden ser enviados por red con facilidad, mensajes que más adelante pueden ser analizados con tranquilidad para poder ver la causa los problemas, en caso de haberlos.

Espero que os haya resultado útil.

Saludos.