Etiquetas

martes, 17 de febrero de 2015

En los *nix, los pipelines son un caño

Parte de la "filosofía" de Unix (o de la programación para sistemas *nix) puede resumirse según el principio "escribí programas que hagan una cosa, y que la hagan bien".
Los pipelines, "pipes" son el mecanismo que permite extender este principio combinando y comunicando distintos procesos, para que cada uno haga su tarea, de la mejor forma posible.
En esta entrada, la historia de la noche febril en que Ken Thompson escribió la llamada al sistema pipe() y un par de breves y mundanos ejemplos de uso de pipes en bash.


Una febril noche de 1973 y una orgía de one-liners

Dice Wikipedia ( Pipeline_(Unix) ) que el concepto de pipeline (el paso de la salida de un programa a la entrada de otro) fue inventado por Douglas McIlroy, uno de los autores de los primeros shells. McIlroy notó que buena parte del tiempo estaban dirigiendo la salida de un programa a la entrada de otro.
Sus ideas las implementó Ken Thopson en una "noche febril" de 1973, cuando agregó la llamada al sistema pipe() y el mecanismo de "pipe" | a shell, junto con varias utilitarios en la versión 3 de Unix. Al día siguiente surgieron los "one-liners" (entradas de una líneas que encadenaban varios programas): dice McIlroy "vi una orgía inolvidable de one-liners en la medida que cada uno se sumaba a la diversión de meter todo en un caño".

Comunicar procesos en Bash

Los "pipes" o "pipelines" son una de las formas más simples y básicas de comunicar procesos (IPC: "Inter Process Communication"). Una de las grandes ventajas de los shells *nix es la posibilidad de crear one-liners que son combinaciones de programas, que juntos hacen una tarea en la que cada uno contribuye.
Solo un ejemplo, tomado de www.bashoneliners.com:

$ /usr/bin/printf 'GET / \n' | nc yahoo.com.ar 80

En este ejemplo se puede grabar el html de yahoo.com.ar usando netcat como cliente. Esa era la orgía a la que McIlroy se refería.

Existen pipelines con nombre y pipelines sin nombre, los primeros son básicamente un tipo de archivo especial en el File System al cual cierto/s proceso/s pueden escribir y otros leer. Los pipeline sin nombre son los que más usamos en la cli, cuando se usa para dirigir la salida de un comando a la entrada de otro con el caracter '|'.
Un pipe funciona, oh sorpresa, como un archivo y puede leerse de la misma forma.
Sigue abajo un ejemplo con el que me divertí un rato y con la esperanza que le pueda servir al sr. @esturniolo:

#!/usr/bin/env bash
#### Script for testing porpouses. Showing unnamed pipes in bash
#### imagemagick must be installed, so: # aptitude install imagemagick.


function printUsage {
    echo >&2 'Error: no argument supplied.'
    echo >&2 "Usage $0 "
    echo >&2 "$0 will start looking for image files starting at the initial directory you supply"
exit 1

}

function checkDep {
    type $1 > /dev/null 2>&1
    if [ "$?" -ne 0 ]
    then
     echo >&2 "Dependency $1 not met. Install it!"
    exit 1
    fi
}
function main {
    echo "inicial dir=" $1
    COMMAND="find $1 -iregex .*\.\(jpg\|gif\|png\|jpeg\)$ -type f"
    $COMMAND | while read i
    do
    # Here you do whatever you want with each lined passed throgh the pipe.
    # That's the unix magic.
      echo $i
      # in this test we grab some data from image file properties.
      identify -format "%wx%h %[EXIF:DateTime] " $i
    done
    echo
}

# Test user input: parameter $1, which must be the initial dir.
if [ "$#" -eq 0 ]; then
    printUsage
fi
if [ ! -d $1 ]; then
    echo >&2 "$1 is not a valid directory, so it's none of my bussiness fellow."
    exit 1
fi

# For checking dependency pass bin/script to be checked as checkDep parameter:
checkDep identify
main $1

A este script se le pasa como único parámetro un directorio inicial para que busque recursivamente en él archivos de imágenes:

find $1 -iregex .*\.\(jpg\|gif\|png\|jpeg\)$ -type f

Donde $1 es el primer parámetro que se le pasa al script. Ejemplo:

$ ./test_stu.sh /home/retux/fotos

La magia de pipe:

$COMMAND | while read i
do
    # Here you do whatever you want with each lined passed throgh the pipe.
     # That's the unix magic.
     echo $i
     # in this test we grab some data from image file properties.
     identify -format "%wx%h %[EXIF:DateTime] " $i
done

Cada línea que find en este caso devuelva por su stdout es redirigida por un pipe y almacenada en la var $i. Luego podemos hacer o que querramos, en este caso se llama al programa identify para que imprima algunos atributos de cada imagen, cuando se le pasa su ubicación, contenida en $i.
Continuar »

lunes, 16 de febrero de 2015

Mantener ssh más seguro. Hoy: ssh-agent

Como sysadmins en ambientes corporativos o en nuestras redes hogareñas openssh es el protocolo capaz de encriptar absolutamente todos el tráfico y permitirnos la administración remota de hosts o vps de forma segura.

Usar ssh de manera predeterminada con autenticación por password puede ser peligroso. Por eso, auth asimétrica con keys es la solución.

El desconfiao' puede hacer la prueba. Colocar un host en internet que acepte autenticación por password. En algunas horas los logs evidenciarán que el host fue objeto a algún ataque de diccionario. Si además la config de sshd permite ogin del usuario root (como suele ser el default: PermitRootLogin yes) las consecuencias pueden ser fatales. Si la contraseña es débil y el ataque tiene éxito los miscreants se habrán hecho de acceso al host y con el usuario con mayores privilegios del sistema.
Estas líneas asustadoras sólo pretenden exponer la gravedad. Un protocolo sumamente seguro como ssh versión 2 puede ser vulnerado solo por una configuración "permisiva" del usuario.

En otra entrada de este blog me ocupé sobre cómo usar criptografía asimétrica (par de claves pública/privada) para autenticar ssh:

Aquella vez, para mantener las cosas simples, al momento de crear las claves con ssh-keygen no se establecía una passphrase para el acceso a la clave privada. Eso efectivamente hace las cosas sencillas si se va a usar scp u otro programa en scripts. Pero qué pasa si alguien lograra tener acceso a la clave privada: como no hay passphrase podría tener acceso a los hosts que tienen su clave pública en el archvio authorized_keys.

Para hacer mejor las cosas existe ssh-agent.

Como su manpage explica el objeto de ssh-agent es

ssh-agent es un programa que guarda las llaves privadas que se usan para autentificación por llave pública. En general ssh-agent se inicia como demonio al principio de una sesión X. Otros programas funcionarán como clientes de ssh-agent cuando requieran acceder a otros hosts vía ssh.
Las aplicaciones pueden localizar a ssh-agent a través de dos variables del entorno:

SSH_AGENT_PID

SSH_AUTH_SOCK

Por ejemplo, en mi Debian, el entorno de escritorio Enlightenment inicia el ssh-agent.
Para usarlo con autenticación ssh por clave pública el procedimiento es similar al descripto en pero en el momento de crear las llaves, se debe registrar una contraseña fuerte (passphrase) para el acceso a la llave privada.
Luego, con ssh-add agregaremos la llave privada a la gestión de ssh-agent:

$ ssh-add /home/retux/.ssh/id_rsa

Luego, podremos listar las llaves que ssh-agent gestiona. Obtendremos algo así:

$ ssh-add -l
2048 04:07:3f:a8:28:ca:e7:11:22:33:55:.... .ssh/id_rsa (RSA)

Así las cosas, ssh-agent nos pedirá la primera vez que intentemos acceder a ese host ingresar las passphrase, pero el programa se encargará de gestionarla, y en lo sucesivo, mientras dure la sesión no nos la pedirá nuevamente.
El resultado es similar a no registrar passphrase al crear las llaves con ssh-keygen, pero con el enorme agregado en lo que hace a la seguridad. Si un malhechor se hace de la llave privada deberá conocer la passphrase para tener acceso.
Continuar »