19 años en Internet
Mostrando entradas con la etiqueta tutoriales. Mostrar todas las entradas
Mostrando entradas con la etiqueta tutoriales. Mostrar todas las entradas

15 enero 2014

Be "nice", asignar prioridades en shells de Unix

     El estándar POSIX permite la definición de prioridades para que ciertos procesos tengan mayor o menor probabilidad de acceso al quantum de la CPU. En esta entrada no voy a entrar en explicar el funcionamiento de dicho estándar, pero a estas alturas queda claro que todos sabemos que si un procesador tiene uno, dos, cuatro o incluso ocho núcleos, pues resulta físicamente imposible que dicho procesador pueda procesar a la vez todos los procesos y aplicaciones que gestiona un sistema operativo.

A groso modo, podemos afirmar que realmente un sistema operativo no es multi proceso, si no que lo emula de forma bastante eficaz. Por un lado, nuestro núcleo realiza una lista con procesos que debe de procesar y durante un periodo de tiempo reducido (conocido como "quantum") ejecuta uno (o varios, si el procesador es multi hilo) dejando durmiendo al resto. Como es de esperar, al igual que en la justicia española no todos los procesos son iguales, siendo unos más importantes que otros y por consiguiente durante un mismo periodo de tiempo ciertos procesos tendrán mayor número de accesos a la CPU que otros.

Esto se puede observar fácilmente a través del comando "top", donde podemos apreciar distintos asignaciones en la columna PR (siendo la columna NI su inversa), con valores que en condiciones normales por lo general van desde el 39 (poco importante) al 0 (muy importante), siendo 20 la prioridad por defecto de todo nuevo proceso. Por lo general, el estándar dicta que un proceso puede tener prioridad del 1 al 32, menos para Linux, el cual puede definirse del 1 al 99 (1). No obstante estos valores pueden variar dependiendo de las distribuciones (el proyecto GNU recomienda hacer uso de las variable sched_get_priority_max si vas a programar algo que requiera modificar prioridades), además de que en la práctica se suele emplear el sistema del 0 (más prioritario) al 39 (menos prioritario).

La cuestión es que a nivel de programación, los lenguajes compatibles con POSIX traen las distintas herramientas necesarias para pelearte con este sistema de prioridades, pero si no queremos tocar código, siempre podemos hacer uso de nice y renice para modificar a placer nuestros propios scripts. El comando nice está pensado para crear una tarea nueva y asignarle una prioridad distinta a la que traiga por defecto, mientras que renice está pensado para cambiar la prioridad de un proceso ya existente a través de su PID, usuario o grupo.

La sintaxis básica de nice es bien simple, puesto que recibe dos únicos argumentos: un número entero que dicta la cantidad de prioridad a restar y la orden a ejecutar. Tengan en cuenta que un proceso normal tiene asignado valor 20. Es decir, que en caso de pasar los valores 5 o 10, estos se sumarán al 20, obteniendo prioridades de 25 y 30 respectivamente... o lo que es lo mismo, con ello hacemos que el proceso a ejecutar pase a ser menos prioritario. En caso de querer dar más prioridad, habrá que poner un valor negativo, o lo que es lo mismo, escribir "--" antes del número entero. Hay que destacar que un usuario puede hacer que su proceso tenga menor prioridad (un valor superior a 20), pero sólo los usuarios con permisos de administrador son capaces de asignar una prioridad inferior a 20. Aquí tenemos un par de ejemplos prácticos:

Comprimir un archivo tgz con 39 de prioridad (la más baja):
~$ nice -19 tar czvf tar.tgz tmp_folder/
Comprimir el mismo archivo tgz utilizando el máximo de CPU:
~$ sudo nice --20 tar czvf tar.tgz tmp_folder/
El primer caso da a entender que la compresión del fichero no es prioritaria, por lo que tendrán prioridad de acceso a CPU el resto de procesos del sistema. La tarea acabará haciéndose porque los núcleos Unix tienen algoritmos para evitar la "inanición", pero se resolverá en un tiempo mayor de lo que tardaría normalmente.

Mientras, en el segundo ejemplo pasa lo contrario, esta compresión pasa a ser uno de los procesos más importantes del sistema, por lo que en la práctica algunos recursos podrían mostrar ralentizaciones hasta que ésta acabe. Está claro que habrán cambios de contexto en el procesador y que se irán abordando y acabando otras tareas mientras ésta se esté realizando... pero ésta tendrá un mayor número de accesos a CPU que el resto durante el mismo periodo de tiempo. No obstante se recomienda no otorgar nunca una prioridad alta a un proceso pesado que vaya a consumir un número elevado de accesos a CPU, puesto que podría causar una sensación de bloqueo o cuelgue del sistema.

Para verificar esta teoría, vamos a proceder a ejecutar un script con tres tipos de prioridades: 20 (por defecto, 39 y 0. Concrétamente, el contenido del script será el siguiente:
#!/bin/bash
for i in `seq 1 10000`; do touch /tmp/lolo.txt; done
Prioridad 20 (proceso normal):
~$ time ./prueba.sh
real 0m20.507s
user 0m3.490s
sys 0m17.750s

Prioridad 39 (baja):
~$ time nice -19 ./prueba.sh
real 0m20.579s
user 0m3.377s
sys 0m18.005s

Prioridad 0 (alta)
~$ time sudo nice --20 ./prueba.sh
real 0m19.945s
user 0m3.894s
sys 0m16.743s

Como era de esperar, a mayor prioridad antes acaba realizándose nuestra tarea, puesto que esto garantiza un mayor número de accesos a CPU. Tras la ejecución de estas pruebas con "nice" queda aclarado que es posible asignar mayor o menor prioridad a nuevas tareas que queramos ejecutar, ¿pero qué pasa con las ya existentes?

Bueno, para ello podemos hacer uso del comando renice, el cual puede asignar cambios de prioridad a procesos a través de su PID (número identificador de proceso), del grupo o incluso del usuario al que pertenence. El funcionamiento es parecido a nice, por un lado indicaremos el incremento de la prioridad a través de su símbolo (esta vez haciendo uso de + y -, en vez de - y -- de nice), mientras que por otro pondremos el PID del proceso a alterar o bien definiremos el grupo o usuario al que pertenece dicho proceso. Hay que aclarar que pese a que el sistema "habla" de prioridades que van del 0 al 39, renice informa en escala que va del -20 (más prioritaria) al +19 (menos prioritaria). Es decir, para renice la prioridad inicial de un proceso no es 20, es 0.

Ejemplo 1, obtenemos el PID de nuestra shell actual y le damos la máxima prioridad:
~$ echo $$
15708 
~$ sudo renice -19 15708
15708 (ID de proceso) prioridad antigua 0, prioridad nueva -19
Ejemplo 2, incrementamos la prioridad para todos los procesos pertenecientes a los usuarios MySQL y Apache:
~$ renice -n -19 -u mysql
107 (user ID) old priority 0, new priority -19 
~$ renice -n -19 -u www-data
107 (user ID) old priority 0, new priority -19

09 enero 2014

Inyección de ficheros en código HTML

     Desde HTML 4.01 se hace referencia al esquema "Data: URL", el cual permite embeber código en base 64 dentro de una definición de ruta para acceder a él como si tratara de una referencia externa (12, 3). Esta versatilidad permite la creación de documentos html compatibles con los navegadores actuales que puedan hacer uso de hojas de estilo, clases de javascript o de cualquier otro recurso externo sin necesidad de emplear más de un documento.

No obstante, es una táctica sólo recomendada para ficheros ligeros, puesto que este mecanismo de inyección de recursos realmente vuelca el contenido del fichero como una URL a resolver y determinados navegadores como Opera truncan las URL que poseen más de 4.000 carácteres (lo que vendría siendo un fichero de texto plano de 4KB)... además de que resulta poco práctico porque siempre que queramos modificar el recurso tendremos que reconvertirlo a base 64 y editar manualmente el html.

Para hacer uso de este esquema símplemente debemos convertir nuestro fichero en una cadena de texto base 64. Esto puede hacerse en Unix a través del binario base64. Ejemplo:
sebas@MacBookPro:~$ echo "hola qué tal?" > hola.txt 
sebas@MacBookPro:~$ cat hola.txt
hola qué tal? 
sebas@MacBookPro:~$ base64 hola.txt
aG9sYSBxdcOpIHRhbD8K
Ahora bien, esto no se limita a cadenas de texto. También nos sirve cualquier clase de archivo que queramos. En el siguiente ejemplo convierto en base 64 un código QR almacenado en una imagen de formato PNG de 0,6KB:
sebas@MacBookPro:~$ ls -lh Ubuntu\ One/QR/blog.png
-rw-r--r-- 1 sebas sebas 655 ene  9 20:16 Ubuntu One/QR/blog.png 
sebas@MacBookPro:~$ base64 Ubuntu\ One/QR/blog.png
iVBORw0KGgoAAAANSUhEUgAAAGMAAABjCAIAAAAAWSnCAAAAA3NCSVQICAjb4U/gAAAACXBIWXMA
AAsSAAALEgHS3X78AAACMklEQVR4nO2bS27DMAwF66L3v3K6UxZCieHHcgPMLBNFEh74Qou0rtfr
9SWA76c38DGoFEWlKCpFUSmKSlFUiqJSFJWiqBRFpSg/ZNB1XZ011tFyzRN8si9KTqZTOwwwpigq
RUHuW6RKNLsjdtORtVKebe4wwJiiqBQl575FELep+Cd+JKa7b4cLY4qiUpSi+8a5z4ZTGFMUlaI8
477ANc0D4H0YUxSVohTdN26E4JBYW2t8h8YURaUoOfc1S4ukfrIvcbL4GWBMUVSKgtx34JGvZszF
gR0aUxSVoly1hBI4ItXLC0h1HGpnw9SvjCmKSlGQ+96j//baPiaY+UD7PjUPwZiiqBQl5773z4Ye
C4mdyeBmCjb3TaJSlLE3zZ59H4z8CTRXN6YoKkUpPnmmkk7wFRm8rx7gue95VIqSy32pBLc7q1Zs
IYPJNshXAcYURaUoxZrnTjOL1dJis6wa7HDHmKKoFGX+jkMqH6XSIlmr6esAY4qiUpTuHYdm0tkh
Zhl/5YZgTFFUilLsOBQXO9gl99z3GCpFGTv3BTRrNfs8tdfS7PcdQqUoR++2B2OavbwDV5aMKYpK
UY7ebU+92TLVQJ+6HmhMUVSK8l/utjf7+Dsp8/rkOYlKUY66b+oAGMy810Vr/YUdY4qiUpSjd9vJ
k2etpXjfpdqFMUVRKcrRu+3BhEH1MlU2SQ22234LKkU52u/7aIwpikpRVIqiUhSVoqgURaUoKkVR
KYpKUVSK8gskWJXNsyhgrgAAAABJRU5ErkJggg==
Una vez tengamos el contenido en base 64 haremos uso de la etiqueta HTML correspondiente a nuestro recurso ("img" para imágenes, "a" para realizar una descarga, "audio" para reproducir un sonido, etcétera). Después definiremos en la url (ojo, dependiendo la etiqueta esto se hace en href o src)  el tipo de fichero (precedido por la cabecera "data:"), el tipo de decodificación (en este caso ";base64,") y todo el contenido que hemos calculado anteriormente.

Por ejemplo, la definición del PNG que contiene mi código QR quedaría así:

<img border="0" height="97" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGMAAABjCAIAAAAAWSnCAAAAA3NCSVQICAjb4U/gAAAACXBIWXMA
AAsSAAALEgHS3X78AAACMklEQVR4nO2bS27DMAwF66L3v3K6UxZCieHHcgPMLBNFEh74Qou0rtfr
9SWA76c38DGoFEWlKCpFUSmKSlFUiqJSFJWiqBRFpSg/ZNB1XZ011tFyzRN8si9KTqZTOwwwpigq
RUHuW6RKNLsjdtORtVKebe4wwJiiqBQl575FELep+Cd+JKa7b4cLY4qiUpSi+8a5z4ZTGFMUlaI8
477ANc0D4H0YUxSVohTdN26E4JBYW2t8h8YURaUoOfc1S4ukfrIvcbL4GWBMUVSKgtx34JGvZszF
gR0aUxSVoly1hBI4ItXLC0h1HGpnw9SvjCmKSlGQ+96j//baPiaY+UD7PjUPwZiiqBQl5773z4Ye
C4mdyeBmCjb3TaJSlLE3zZ59H4z8CTRXN6YoKkUpPnmmkk7wFRm8rx7gue95VIqSy32pBLc7q1Zs
IYPJNshXAcYURaUoxZrnTjOL1dJis6wa7HDHmKKoFGX+jkMqH6XSIlmr6esAY4qiUpTuHYdm0tkh
Zhl/5YZgTFFUilLsOBQXO9gl99z3GCpFGTv3BTRrNfs8tdfS7PcdQqUoR++2B2OavbwDV5aMKYpK
UY7ebU+92TLVQJ+6HmhMUVSK8l/utjf7+Dsp8/rkOYlKUY66b+oAGMy810Vr/YUdY4qiUpSjd9vJ
k2etpXjfpdqFMUVRKcrRu+3BhEH1MlU2SQ22234LKkU52u/7aIwpikpRVIqiUhSVoqgURaUoKkVR
KYpKUVSK8gskWJXNsyhgrgAAAABJRU5ErkJggg==" width="97" />

Y se visualiza así:
 


qrencoder + zbar: Generación y lectura de códigos QR en Linux

Los famosos códigos QR, que a día de hoy vemos en todas partes, fueron inventados en 1994 por Denso Wave (del grupo Toyota) y su nombre es el acrónimo de Quick Response. El motivo del nombre se debe a la necesidad imperiosa de mostrar una gran cantidad de información en un código de barras y que éste tenga una lectura casi inmediata. De hecho, un código QR alfanumérico puede contener nada menos que 4296 caracteres. Esta versatilidad le ha llevado a ser ahora mismo el código de barras más famoso de Japón, hasta el punto de que su gobierno lo emplea incluso a la hora de emitir sus visados.

Gran parte de este éxito se debe a que, a pesar de provenir de un laboratorio de empresa privada, los códigos QR están libres de toda licencia y además cuentan con su propio estándar ISO. Realmente esto es una mentira a medias, puesto que Denso Wave tiene la patente del código QR pero ha decidido no hacer uso de sus derechos de autor. Digamos que, por un lado, la renuncia de sus derechos de autor permite que todo el mundo pueda explotarlo como le parezca, mientras que por otro lado la existencia de un estándar ISO garantiza la existencia de una serie de reglas que todo el mundo debe de cumplir.

Esta flexibilidad, el de tener una serie de normas bien definidas y una licencia de libre uso, permite la profelización de aplicaciones para todo sistema operativo, siendo sobre todo notable en el gran ecosistema actual de smartphones. No obstante, al igual que en los teléfonos inteligentes también existen herramientas de creación y de descifrado de imágenes QR en los sistemas operativos de ordenador, siendo qrencoder y zbar los referentes más famosos de Linux. Ambos suelen estar presentes en los repositorios de toda distribución actual, pudiéndose instalar fácilmente a través de apt-get, yum o yast2.

El uso de qrencoder es bien sencillo:
qrencoder -o imagen.png 'texto a añadir'
Por ejemplo, escribiendo "qrencoder -o imagen.png 'http://www.elgeneralfailure.com'" crearíamos el siguiente código QR:
Al igual que en toda aplicación Unix, podemos jugar con las tuberías o redirigir la entrada estándar para elaborar toda clase de scripts de bash:
echo "http://www.elgeneralfailure.com" | qrencode -o imagen.png
echo "http://www.elgeneralfailure.com" > /tmp/texto.txt
qrencode -o imagen.png < /tmp/texto.txt
echo "http://www.elgeneralfailure.com" > /tmp/texto.txt
cat /tmp/texto.txt | qrencode -o imagen.png
El segundo y tercer ejemplo son bastante útiles si queremos hacer textos complejos con retornos de carro inclusive. No obstante, si desconocemos las bondades de las que permite hacer uso los códigos QT, resultaría útil darle un vistazo a la aplicación QtQR, una aplicación gráfica escrita en Qt que simplifica labores como automatizar el envío de un e-mail o crear una entrada de agenda de contactos:


Para leerlos no hace falta mostrar estos códigos por webcam, también existen aplicaciones que permiten la decodificación de QR a través de imágenes PNG.  El más práctico que he probado es zbarimg (se instala a través del paquete zbar-tools) y requiere cómo único parámetro la imagen a analizar. Ahora bien, habría que aclarar que se trata de una utilidad que permite analizar toda clase de códigos de barra, no sólo los QR.

Ejemplo de uso:
sebas@MacBookPro:~$ zbarimg imagen.png
QR-Code:http://www.elgeneralfailure.com
scanned 1 barcode symbols from 1 images in 0.04 seconds
Por desgracia, a diferencia de qrencode para la entrada de datos no se nos permite jugar a placer con redirecciones ni con tuberías. No obstante, si tenemos instalado el paquete imagemagick, con la instrucción "import :- | zbarimg :-" podemos capturar una región de pantalla para analizar el código QR que contenga (1).

09 agosto 2012

Montar una unidad Linux LVM desde un Live CD


- Arranca un live CD de Ubuntu, Debian, Mint o cualquier otro de familia debianita.

- Revisa que tengas conexión a internet.

- Abre una shell y nos convertimos en root:
sudo bash
- Instalamos las utilidades de volúmenes lógicos para Debian:
apt-get install lvm2
- Arrancamos el módulo de kernel necesario para trabajar con estos volúmenes:
modprobe dm-mod
- Pedimos al sistema que detecte los volúmenes lógicos:
vgscan
- Listamos los volúmenes lógicos detectados:
pvs
- Activamos cada volumen que nos interese:
vgchange -ay VolGroup00

- Dentro del volumen, buscamos la partición que contiene el directorio raíz "/":
lvs

- Creamos un punto de montaje:
mkdir /media/LogVol00
- Montamos la unidad en modo sólo lectura:
mount /dev/VolGroup00/LogVol00 /media/LogVol00 -o ro,user 

25 julio 2011

Grabando sesiones de X11 con ffmpeg y x11grab


Si en GNU/Linux queréis grabar una sesión de escritorio la mejor herramienta que podéis utilizar es ffmpeg (haciendo uso el parámetro x11grab como fichero de entrada). Podéis instalarlo en entornos Ubuntu/Debian con un simple "sudo apt-get install ffmpeg".

No obstante mi experiencia dice que el ffmpeg compilado por Ubuntu es lentísimo a más no poder. Para haceros una idea, con dicho ffmpeg apenas superaba los 15 frames por segundo en un netbook que compré el año pasado por 230 euros. Está claro que es hardware barato, pero ni mucho menos es obsoleto o anticuado.

Aquí mi netbook: procesador Atom de doble núcleo a 1.6GHz... 
¿¡Y no pasa de 15 frames con el ffmpeg de Ubuntu!?

Puede que 15 fps dentro de lo que cabe sea una velocidad aceptable, pero me extraña muchísimo el malísimo rendimiento que obtiene esta aplicación sobre mi netbook. Me puse a googlear y vi que no era el único al que le pasaba y en una de las soluciones que observé se aseguraba que compilando tu mismo el ffmpeg conseguías un rendimiento muy superior. 

Así que hice eso y creo que el siguiente vídeo habla por sí sólo. Aquí me veis grabando una sesión de X11 mientras juego con mi PSP Go al MotorStorm:


Comando empleado para hacer la grabación:
~$ ffmpeg -f alsa -ac 1 -i pulse -f x11grab -r 30 -s 1024x600 -i :0.0 -s 480x272 -acodec pcm_s16le -vcodec libx264 -vpre lossless_ultrafast -threads 0 video_psp.mkv


Bueno, por si os pudiera interesar aquí os dejo los pasos que he seguido para compilar por mi cuenta el ffmpeg:

# Nos hacemos root e instalamos las dependencias que usaremos para compilar:
~$ sudo bash
~$ apt-get update 
~$ apt-get install build-essential checkinstall git checkinstall libfaac-dev libjack-jackd2-dev libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libsdl1.2-dev libtheora-dev libva-dev libvdpau-dev libvorbis-dev libx11-dev libxfixes-dev libxvidcore-dev texi2html yasm zlib1g-dev

# Compilamos e instalamos el códec x264
~$ cd /tmp 
~$ git clone git://git.videolan.org/x264 
~$ cd x264 
~$ ./configure --enable-static 
~$ make 
~$ checkinstall --pkgname=x264 --pkgversion="3:$(./version.sh | awk -F'[" ]' '/POINT/{print $4"+git"$5}')" --backup=no --deldoc=yes --fstrans=no --default

# Compilamos e instalamos libvpx
~$ cd /tmp 
~$ git clone git://review.webmproject.org/libvpx 
~$ cd libvpx 
~$ ./configure 
~$ make 
~$ checkinstall --pkgname=libvpx --pkgversion="1:$(date +%Y%m%d%H%M)-git" --backup=no --deldoc=yes --fstrans=no --default 

# Compilamos e instalamos la última versión de ffmpeg
~$ cd /tmp 
~$ git clone git://git.videolan.org/ffmpeg 
~$ cd ffmpeg 
~$ ./configure --enable-gpl --enable-libfaac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-nonfree --enable-postproc --enable-version3 --enable-x11grab 
~$ make 
~$ checkinstall --pkgname=ffmpeg --pkgversion="5:$(date +%Y%m%d%H%M)-git" --backup=no --deldoc=yes --fstrans=no --default 
~$ hash x264 ffmpeg ffplay ffprobe 

# En el mismo directorio que compilamos ffmpeg, compilamos la herramienta qt-faststart:
~$ make tools/qt-faststart 
~$ checkinstall --pkgname=qt-faststart --pkgversion="$(date +%Y%m%d%H%M)-git" --backup=no \--deldoc=yes --fstrans=no --default install -D -m755 tools/qt-faststart /usr/local/bin/qt-faststart

23 julio 2011

Ya juego a la PSP en mi netbook ;)


Tras patearme todos los centros comerciales de mi ciudad, finalmente he encontrado una capturadora AV por USB que tiene soporte oficial del fabricante para los sistemas GNU/Linux. Se trata de la AVerMedia Volar HD Video Capture M y la broma tiene un coste aproximado de 80 euros. Eso sí, aparte de capturar AV me permite utilizar el dispositivo como decodificador DVB-T.

El driver es descargable para las arquitecturas x86 y x64 desde la web del fabricante, pero el contenido del paquete sólo posee shellscripts y fuentes en C, por lo que debería de ser compilable desde otras arquitecturas. Según el "Readme" el driver ha sido testeado por el fabricante para la distrubición Ubuntu en su versión 10.04, 10.10 y 11.04, bajo los programas MPlayer, VLC y MythTV. A pesar de ser testeado sólo en Ubuntu, entiendo que el driver debería de funcionar para cualquier sistema con kernel superior al 2.6.29 (inclusive).

La instalación es sencilla, basta con descomprimir el paquete y ejecutar el H830_LinuxDrv_x86_V1.0.28-beta_Install_OEM.sh. Si ves que la instalación falla revisa que tengas instalado el paquete build-essential y las cabeceras de los módulos (del kernel de Linux) videodev, videobuf-core, v4l2-common, videobuf-vmalloc, dvb-core, i2c-core, tda18271, snd_pcm, snd_timer, snd_page_alloc, snd y soundcore.

En cuanto a su función de capturadora AV notarás que se han instalado dos nuevos dispositivos: /dev/video1 (o /dev/video0 si no tienes webcam) que funciona con el driver v4l2 y el hw:1,0 de alsa (siendo el 0,0 tu tarjeta de sonido). Vale, suena a chino, pero en la práctica se traduce en esto:

#Visualizar AV
~$ vlc v4l2:///dev/video1

#Activar sonido
~$ cvlc alsa://hw:1,0

¿Por qué ejecutamos dos comandos distintos para audio y video? Porque alguien de VLC andaba carente de ideas diabólicas y ha decidido que en las últimas versiones del reproductor sea muy chungo el poder ejecutar distintas interfaces de hardware en una única instancia. Vamos, que con esto ejecutamos dos instancias de vlc donde cada una reproduce una interfaz distinta (video por v4l2 y audio por alsa).

Si lo que quieres es meterle mano al sintonizador DVB-T, lo que tienes que hacer es abrir el VLC y seleccionar "Medio>Abrir dispositivo de captura", seleccionar el modo de captura DVB y DVB-T como tipo de captura.

14 julio 2011

Apuntes de shell script

- Emplear argumentos:
Al igual que en C o en Java, a la hora de invocar una shell puedes pasar argumentos. Por defecto en Bash  la variable $0 equivale al nombre del binario, el $1 al primer argumento, el $2 al segundo... y así hasta $n. Ejemplo:
./fprintf.sh Hola
Donde el código del script sería:
#!/bin/bash
echo $1

- Definir y emplear variables:
Para crear una variable basta con poner el operador "=" después del nombre. Por defecto todos los valores que asignes serán strings, pero Bash es lo suficientemente inteligente para saber diferenciar strings de enteros:
nombre=Sebas
edad=28
argumento=$1
No obstante, siempre que queramos emplearlas deberemos de poner delante el símbolo $:
echo "Hola ${nombre}. Tienes $edad años"
Cabe destacar que por defecto en un script de Bash todas las variables son locales. Es decir, distintas instancias de una shell pueden tener distintos valores para una misma variable. Además, una vez finalice el script esta variable no aparecerá en la lista de variables inicializadas en el sistema. Si necesitas crear una variable "global" que pueda ser empleado en otros scripts del sistema, tendrás que usar el comando export:
export nombre=Sebas 

- Realizar cálculos:
Para realizar operaciones simples (como sumar o restar enteros) podemos emplear el comando let. Su funcionamiento es simple:
operador_A=1
operador_B=2
let resultado_suma=${operador_A}+${operador_B}
Si por el contrario necesitas emplear decimales, o realizar mutiplicaciones o divisiones, tendrás que emplear awk:


division=`awk -v dividendo=$dividendo -v divisor=$divisor  'BEGIN{printf "%d",dividendo/divisor}'`
division_con_decimales=`awk -v dividendo=$dividendo -v divisor=$divisor  'BEGIN{printf "%.2f",dividendo/divisor}'`


- Manejar strings:

Si os fijáis en el ejemplo echo "Hola ${nombre}. Tienes $edad años", he puesto nombre entre llaves. ¿Por qué? Esto es porque Bash piensa que la varaible que quieres emplear no es "nombre", si no "nombre." y al no existir esta variable Bash nos printaría sólo por pantalla "Hola  Tienes 28 años". Es decir, cuando veas que el nombre de una variable pueda estar comprometido debes emplear llaves para que Bash sepa cuando empieza y cuando acaba el nombre de variable. Esto nos permite, por ejemplo, componer nuevos strings a partir de otros ya existentes:

nombre_maximizado="Hola ${nombre}món"

También podemos recortar un string ponínendolo entre llaves e indicando dos valores: el caracter inicial que nos interesa y la longitud que tendrá el substring:
nombre_recortado=${nombre:0:3}
iniciales=${nombre:0:1}.${apellido1:0:1}.${apellido2:0:1}

- Utilizar condiciones:
Todo el que sepa programar no tendrá problemas con esto:
if [ condición ]
then
(...)
else
(...)
fi
Bash emplea muchos operadores para analizar condiciones. Os resumo los más comunes: -eq para comprobar que un entero es igual a otro; -lt para comprobar que un entero es menor a otro; -gt para comprobar que el entero es mayor al otro; == para comprobar que dos strings son iguales; Y por último ! al principio para negar el resultado. Unos ejemplos:

if [ $edad -gt 100 ]
then
echo "Es un milagro que sigas vivo"
fi


if [ ! $nombre == "Sebas" ]
then
echo "No eres Sebas"
fi


Si elaboras scripts de gran tamaño, a lo mejor te conviene revisar que las variables que empleadas están inicializadas. Esto se puede hacer con una negación lógica del contenido de la variable:
if [ ! $edad ]
then
echo "La varibale edad no está inicializada"
edad=18
fi
Nota: si trabajas con variables que contienen decimales puedes tener problemas a la hora de manejar condiciones. O bien empleas awk (como hicimos para calcular divisiones), o bien la entrecomillas la variable para tratarla como un string:

if [ ! "${nota_media}" ]
then
nota_media=0.00
fi



- Asignar valores a variables a partir de resultados de comandos:
Con la comilla invertida podemos invocar comandos dentro de una shell y asignar a una variable el valor de su salida estándar. En este ejemplo la variable resultado_ls tendrá como valor el archivo txt más reciente del directorio actual:
resultado_ls=`ls -lrt *txt | tail -1`
Si necesitamos almacenar un valor de un comando dentro de una comilla invertida, podemos utilizar la comilla simple:
resultado_ls=`ls -lrt *'echo ${fecha}'.txt | tail -1`

- Arrays:
Sí, en Bash también existen vectores. No hace falta definir ninguna talla, símplemente defines índices al vuelo:
palabra[0]=Hola
palabra[1]=estimado
palabra[2]=lector
numero=2
echo "${palabra[${numero}]}"

- Bucles:
Bash emplea los dos bucles más famosos: while y for.
while [ condición ]
do
(...)
done


for ((  i = 0 ;  condición ;  i++  ))
do
(...)
done

Ejemplos:


while [ ! "$j" == "AAAA" ]
do
j=${j}A
done



for ((  i = 0 ;  i <= 50 ;  i++  ))
do
echo  "-"
done

while true
do
df -h
sleep 1
clear
done



- Leer un fichero línea a línea:
Basta con generar un bucle while facilitando la ruta del archivo como entrada estándar.
while read linea
do
nombre=`echo ${linea}`
(...)
done < /tmp/mi_lista_de_nombres


- Manejar señales:
Si eres un entendido en señales posix entonces quiere decir que eres un maestro del C puro. Que sepas que todo el tema de máscaras y manejadores en shell script se resume en un sólo comando: trap. Esta sentencia te permite invocar comandos o funciones cuando el proceso recibe la señal que le indiques:
trap "echo Noooooooo" INT
trap "rm $archivos_temporales" EXIT

En este caso, cuando recibimos un control+c de teclado nuestro shell script mostrará por su salida estándar el texto Noooooooo. Si por el contrario finaliza el script este borrará antes todos los archivos temporales que tenemos definidos en la variable archivos_temporales.


- Lanzar comandos en segundo plano:
Pon entre paréntesis la lista de comandos que quieres que trabajen en segundo plano. Esto significa que el "proceso hijo" definido entre paréntesis trabajará de forma concurrente con el proceso padre (tu shell script). Detrás del paréntesis de cierre debes poner un &. Ten en cuenta que si no aplicas redirecciones la salida estándar de los procesos hijo serán la misma que la del proceso padre.
echo "Calculamos las notas de 2009 y 2010"
(calcular_notas.sh 2009 ; echo "FIN calculo notas 2009") &
(calcular_notas.sh 2010 ; echo "FIN calculo notas 2010") &

13 mayo 2011

Aplicar ttysnoop sin recompilar ssh:


Renombramos el /bin/login:
root@MacBook:~$ mv /bin/login /bin/login.original
Reemplazamos el ttysnoops por el sistema de login del sistema:
root@MacBook:~$ which ttysnoops
/usr/sbin/ttysnoops
root@MacBook:~$ mv /usr/sbin/ttysnoops /bin/login
Modificamos el /etc/snooptab. Donde pone "* socket login /bin/login", lo reemplazamos por "* socket login /bin/login.original".

reamos un backup del inittab, lo modificamos y recargamos la configuración:
root@MacBook:~$ cp /etc/inittab /etc/inittab.original
root@MacBook:~$ perl -p -i,orig -e 's#getty#getty -l /bin/login.original#g' /etc/inittab
root@MacBook:~$ init q
Revisad que esté sin comentar la línea "UseLogin yes" del /etc/ssh/sshd_config.

Relanzamos el proceso ssh:
root@MacBook:~$ /etc/init.d/ssh restart

¿Problemas? ¿Necesitas hacer una marcha atrás?
root@MacBook:~$ mv /bin/login /usr/sbin/ttysnoops
root@MacBook:~$ mv /bin/login.original /bin/login
root@MacBook:~$ cp /etc/inittab.original /etc/inittab
root@MacBook:~$ init q
root@MacBook:~$ /etc/init.d/ssh restart

11 mayo 2011

Modificando el ttysnoops

Ayer os enseñé a hacer uso del ttysnoop y ttysnoops para poder monitorizar sesiones de security shell (SSH). Uno de los puntos más molestos es que cada vez que hacemos uso del ttysnoop la aplicación nos consulta por el password de root. Y encima no podemos hacer uso de un script de "spawn/expect/send" para saltarnos este paso (ya lo he probado).

Parece ilógico, ¿verdad? Ya me he logueado como root para poder hacer uso de esta aplicación, ¿para qué voy a querer volverme a validar? Realmente lo que sucede es que a nivel de código fuente, dentro del config.h, se define a root como el usuario snoop predeterminado. Tras analizar el código del ttysnoops, entendí que esta solicitud de contraseña está puesta por mero capricho del programador.

En el bucle principal, a través del método authenticate, el programa crea un proceso hijo encargado de pedir al usuario la contraseña del usuario "snoop". A continuación he remarcado en rojo los valores de retorno y en verde el texto printado para solicitar el password del usuario. A primera vista el código asusta, pero veréis que es bastante inteligible.


    311 void authenticate (int fd)
    312 {
    313         struct passwd *pw;
    314
    315 #ifdef SHADOW_PWD
    316         struct spwd *spw;
    317 #endif
    318
    319         int ret = 0;
    320         char buff[16], *pwbuff;
    321
    322         if ((authpid = fork()) == 0)    /* authentication child */
    323         {
    324                 dup2 (fd, STDIN_FILENO);
    325                 dup2 (fd, STDOUT_FILENO);
    326                 dup2 (fd, STDERR_FILENO);
    327                 if (fd > STDERR_FILENO)
    328                         close (fd);
    329
    330                 if ((pw = getpwnam(SNOOPUSER)) == NULL)
    331                         exit (1);
    332
    333 #ifdef SHADOW_PWD
    334                 if ((spw = getspnam(SNOOPUSER)) == NULL)
    335                     exit(1);
    336 #endif
    337
    338                 printf ("Connected to %s snoop server...\r\n", ptynam);
    339                 printf ("%s (ASCII %d) to suspend, %s (ASCII %d) to terminate.\r\n",
    340                         SC_STRING, SUSP_CHAR, TC_STRING, TERM_CHAR);
    341                 printf ("Snoop password:"); fflush (stdout);
    342                 if (inputs(buff, 16, stdin) == 0)
    343                 {
    344 #ifndef SHADOW_PWD
    345                         if (strcmp(pw->pw_passwd, crypt(buff, pw->pw_passwd)) == 0)
    346 #else
    347                         if (strcmp(spw->sp_pwdp, crypt(buff, spw->sp_pwdp)) == 0)
    348 #endif
    349                         {
    350                                 printf ("\r\nVerified OK... Snoop started.\r\n");
    351                                 ret = 1;
    352                         }
    353                         else
    354                                 printf ("\r\nPassword incorrect.\r\n");
    355                 }
    356
    357                 fflush (stdout);
    358                 exit (ret);
    359         }
    360 }

En pocas palabras, el proceso ttyspoons crea un hijo para solicitar al usuario la contraseña de root. Este proceso verifica que el usuario root exista y que la contraseña sea válida para dicho usuario.

Ahora veamos el manejador del proceso padre, encargado de gestionar si la autenticación (realizada en el proceso hijo) ha sido correcta:


    404 void sigchld (int sig)
    405 {
    406         int status, pid;
    407         sig = sig;
    408
    409         if ((pid = wait(&status)) == authpid)
    410         {
    411                 if (((status >> 8) & 0xff) == 1)
    412                 {
    413                         snoopfd = authfd;
    414                         fdmax = max(fdmax, snoopfd);
    415                         syslog (LOG_INFO, "snoop on %s (%s)", ttyname(STDIN_FILENO),
    416                                                 leafname(sockname));
    417                 }
    418                 else
    419                         close (authfd);
    420
    421                 authpid = authfd = -1;
    422         }
    423         else if (pid == pgmpid)
    424                 raise (SIGHUP);
    425
    426         signal (SIGCHLD, sigchld);
    427 }


Según el código de este manejador, para cumplir la verificación deberemos de hacer que el proceso hijo finalice con valor 1. Cualquier otro valor devolverá error de autenticación.

Visto esto, he modificado el método authenticate para que JAMAS DE LOS JAMASES se nos solicite la contraseña y que además la verificación del padre se pase de forma correcta:

    357 void authenticate (int fd)
    358 {
    359          if ((authpid = fork()) == 0)    /* authentication child */
    360          {
    361                  dup2 (fd, STDIN_FILENO);
    362                  dup2 (fd, STDOUT_FILENO);
    363                  dup2 (fd, STDERR_FILENO);
    364                  if (fd > STDERR_FILENO)
    365                          close (fd);
    366                  exit(1);
    367          }
    368 }

Como veis el método ha adelgazado bastante... y encima funciona: Si eres root no te solicitará ninguna contraseña de usuario snoop, y si no eres root no podrás clonar ninguna terminal. Por cierto, si queréis recompilar las fuentes para Ubuntu o Debian, vais a tener que realizar las siguientes modificaciones.

10 mayo 2011

Espiar las sesiones de SSH


Una imagen vale más que mil palabras =)

Debido al grado de cifrado del ssh, resulta bastante complejo poder conocer los comandos realizados en una de sus sesiones remotas. Instalar un sniffer no es una solución puesto que toda comunicación que captemos no lo podremos leer "en claro" y tampoco nos sirve instalar un keylogger, puesto que en una conexión remota no se hace uso del teclado físico del servidor.

Está claro que siempre podemos consultar el bash_history del usuario, pero por defecto suelen venir sin el date-stamp activo, suele estar limitado por un número concreto de líneas ($HISTSIZE) y además al ser un archivo de texto es susceptible de ser modificado o borrado. Para más inri, con este método nos resulta imposible observar los datos que se han modificado dentro de un vi o las acciones que se han tomado dentro de cualquier otro binario.

Con todo esto queda demostrado que para poder realizar un análisis de calidad deberemos de pensar en una solución algo más elaborada. La solución que propongo en esta entrada es funcional y no muy complicada de llevar a cabo: Instalar un monitor de terminales y recompilar el servicio sshd para obligarle a hacer uso de él.

Lo primero es lo primero, vamos a instalar un monitor de terminales. El más famosillo es ttysnoop y consta de una aplicación cliente y una aplicación servidor. Ambas se instalarán en la máquina servidor:

sebas@MacBook:~$ sudo apt-get install ttysnoop
Y por último editaremos el /etc/snooptab para revisar que la siguiente línea no está comentada:
* socket login /bin/login

Nuestra idea es simple, obligar al demonio local de SHH a loguear todas las peticiones a través del ttysnoop. ¿Cómo haremos esto? Bajándonos las fuentes, configurar manualmente nuestro programa de login y recompilar el demonio. Los puristas de Ubuntu para hacer eso usuarían seguramente el apt-build (un híbrido de apt-get y el merge de Gentoo), pero yo soy más tradicional...

Primero averiguaremos la versión de nuestro sshd:
root@MacBook:~# sshd -v
sshd: illegal option -- v
OpenSSH_5.8p1 Debian-1ubuntu3, OpenSSL 0.9.8o 01 Jun 2010
Acto seguido nos metemos en este repositorio y nos bajamos el código fuente de nuestra versión de OpenSSH. En este caso habría que bajarse el openssh-5.8p1.tar.gz. Una vez descargado configuraremos una variable de sesión, descomprimiremos el archivo y ejecutaremos el ./configure para crear el makefile necesario para compliar nuestro demonio:
root@MacBook:~# which ttysnoops
/usr/sbin/ttysnoops (Hacemos esto para localizar el binario)
root@MacBook:~# export LOGIN_PROGRAM="/usr/sbin/ttysnoops"
root@MacBook:~# cd Descargas/
root@MacBook:~# tar xzvf openssh-5.8p1.tar.gz
root@MacBook:~# cd openssh-5.8p1/
root@MacBook:~# ./configure --prefix=/usr --sysconfdir=/etc/ssh --without-zlib-version-check --with-pam --with-tcp-wrappers

Durante la configuración nos irá dando una serie de errores referentes a la ausencia de librerías necesarias para la compilación de la aplicación. Tendremos que hacer uso del apt-get para ir instalándolas. En mi caso tuve que realizar la siguiente instalación de paquetes:
sebas@MacBook:~$ sudo apt-get install zlib1g-dev libwrap0-dev libssl-dev libpam0g-dev
Una vez finalizado sin errores el configure, compilaremos la aplicación y la instalaremos:
root@MacBook:~# make
root@MacBook:~# make install
Una vez instaldo editaremos el /etc/ssh/sshd_config para verificar que estas dos líneas no estén comentadas y tengan asignado el valor "yes":
PasswordAuthentication yes
UseLogin yes
Por último reiniciamos el servicio sshd:
root@MacBook:~# /etc/init.d/ssh restart
(En otras distribuciones es /etc/init.d/sshd restart)

Vale, ya tienes todo listo para monitorizar las sesiones SSH de tu servidor. Ahora pasemos al siguiente nivel, ¿cómo utilizamos esta herramienta? Lo primero que nos hace falta saber es en qué terminal se ha conectado el usuario que nos interesa. Si en una terminal haces un who verás que las terminales remotas vienen con apodo (definido en el /etc/hosts) o con una dirección IP. En mi ejemplo el usuario remoto está conectado en el "pts/3":


pts/3 = terminal 3


Ahora que ya hemos localizado que estamos en el terminal 3, siendo root (o haciendo uso de sudo) invocamos el ttysnoop con el número de terminal como único parámetro. Acto seguido, por motivos de seguridad, la aplicación nos solicitará la contraseña de root. Realmente te pide la contraseña de la aplicación snoop, pero si no la defines (se hace a través del código fuente original) se empleará la de root. Si no tienes password de root te va a tocar darlo de alta tecleando "sudo passwd" en otra shell:


Si no queréis usar el password de root, cread un usuario específico en vuestro sistema (similar a root pero más restrictivo) y modificar el config.h del código fuente de ttysnoop para que este sea el nuevo "snoop user".

Disfruta. Ahora verás en vivo todo lo que está haciendo el otro usuario. En tiempo real verás todo lo que teclea, todo lo que abre y todo lo que modifica en su shell. Por cierto, es una monitorización bidireccional, si escribes algo en "tu" shell también aparecerá en la shell original.

Acabamos de clonar una sesión de SSH, utilizable desde dos shells distintas =)


Podrás ver incluso todos los archivos de texto que modifica.


Ahora bien, ¿te interesa guardar todo lo que hace en un archivo de log? Pues juega con las redirecciónes:

En este ejemplo, todo lo que el usuario vea se almacenará en mi_log.txt. Como vemos en el "cat" se almacenan hasta los colorines ;)

07 mayo 2011

Dante para lerdos

¿Qué es Dante? Un proxy de sockets (socks v5), es decir, un servicio que permite que ordenadores de nuestra red interna puedan abrir sockets a través de un cortafuegos para acceder al exterior. La diferencia con otros proxys, como Squid, es que no está orientado a hacer de webcache, si no de monitorización y gestión de todo tipo de tráfico.

¿Quieres controlar las conexiones ssh de tu red local hacia tus clientes? ¿Ver su hora de conexión, desconexión, puerto empleado, ip local e ip destino? Pues por ejemplo esto no lo puedes hacer con un proxy http, te hace falta un proxy de sockets como pudiera ser Dante. Y quien dice ssh dice http o conexiones de chat o edonkey. Puedes monitorizar cualquier clase de puerto o tipo de conexión (tanto UDP como TCP).

Ok, ¿cómo instalamos un Dante? Pues en Ubuntu y Debian es tan simple como teclear "sudo apt-get install dante-server".

Sólo 147Kb de descarga =)


Ahora pasamos a configurar el servicio: "sudo nano /etc/danted.conf":


Vale, cosas que debemos saber. Por defecto no se crea ningún archivo de log pero podemos definir uno. Para ello vamos a reemplazar la línea:
lougoutput: stderr
Por la siguiente:
logoutput: /var/log/danted.log
También es importante definir la interfaz ethernet que atiende las peticiones de proxy y la que accede al exterior. Puedes editar la misma interfaz para las dos cosas. Por ejemplo, mi configuración de casa quedaría así (siendo 1080 el puerto que usaré para atender las peticiones):
internal: eth1 port = 1080
external: eth1

Si no sabes cuales son tus interfaces, puedes ejecutar el comando ifconfig desde una shell:


Sigamos con el danted.conf. Por defecto no hay métodos de autenticación, pero puedes definir username (nombre de usuario), none (ninguno), pam (autenticación del sistema en GNU/Linux, un usuario existente en esta máquina) y rfc931 (un protocolo de autenticación). Si no quieres autenticación puedes dejar comentada esta línea o bien insertar el parámetro none:
method: none
Si por ejemplo escribiera "method: pam none" daríamos a entender que la autenticación pam es optativa, pero no obligatoria para poder utilizar el proxy.

También puedes definir el tiempo máximo que invertirá el proxy en intentar abrir un socket (el tiempo máximo que tarda en levantar una conexión):
connecttimeout: 30

E incluso definir que los sockets mueran por por falta de uso (por no recibir señales de entrada o salida):
iotimeout: 30000

Vale, ya hemos definido los parámetros básicos. Ahora, como si se tratara de un cortafuegos, vamos a definir los parámetros necesarios para filtrar las conexiones. Por ejemplo, nos interesa sólo que se conecten a este proxy equipos que pertenezcan a nuestra red local:


 #Los equipos de nuestra red interna podrán usar el proxy
client pass {
       from: 192.168.0.0/8 port 1-65535 to: 0.0.0.0/0
       log: connect disconnect error
#Los equipos externos no pueden usar nuestro proxy
client block {
      from: 0.0.0.0/0 to: 0.0.0.0/0
       log: connect disconnect error
}
Como veis la sintaxis no es complicada. 192.168.0.0/8 representa nuestra red local y 0.0.0.0/0 representa "todo el rango posible de IPs". Si os fijáis usamos /8 en vez de /24. ¿Por qué? Porque en Dante no se configuran las redes según su máscara de subred, si no por su máscara wildcard. Esta máscara es la inversa de la máscara de subred. Es decir, en vez de utilizar 255.255.255.0 (el /24), se utiliza el 0.0.0.255 (el /8). De igual forma, para definir una IP concreta hay que marcar un /0 y no un /32. También observamos que podemos permitir o bloquear tráfico partiendo de un rango de puertos. Si no se define ningún puerto pues no pasa nada, entiende que se permite (o bloquea) cualquier tipo de conexión.

Si se desea crear más de una regla de acceso o de bloqueo, se debe crear una instancia por norma:
# Permitimos el acceso al cliente externo A
client pass {
       from: 192.168.0.0/8 port 1-65535 to: 10.40.0.0/16
       log: connect disconnect error
}  
# Permitimos el acceso al cliente externo B por ssh
client pass {
       from: 192.168.0.0/8  to: 10.50.2.2/0 port = 22
       log: connect disconnect error

Vale, ya hemos definido la clase de equipos que se pueden conectar al proxy. Pero aún así también nos interesa que sea el proxy quien no pueda conectarse a otros sitios. Para ello aplicaremos las normas pass y block (sin la palabra client) e incluso podemos definirle una serie de comandos predefinidos. Estas normas se aplican de forma genérica a todos los clientes conectados en nuestro proxy:


# Bloqueamos que "hackers" puedan acceder a nuestros recursos a través del loopbak
block { 
       from: 0.0.0.0/0 to: 127.0.0.0/8
       log: connect error disconnect
}


También podemos bloquear páginas webs y dominios:
# Bloqueamos el acceso web a marca.com
block {
       from: 0.0.0.0/0 to: marca.com
       log: connect error disconnect
#Bloqueamos el acceso al dominio facebook.com
block {
       from: 0.0.0.0/0 to: facebook.com
       command: bind connect udpassociate
       log: connect error disconnect
}
Puedes solicitar que todo el tráfico web sea aceptado para cualquier web:

pass { 
       from: 0.0.0.0/0 to: 0.0.0.0/0 port = http
       log: connect error disconnect 
}


pass { 
       from: 0.0.0.0/0 to: 0.0.0.0/0 port = https
       log: connect error disconnect 
}

También puedes habilitar otra clase de puertos conocidos:

pass { 
       from: 0.0.0.0/0 to: 0.0.0.0/0 port = pop3
       log: connect error disconnect 
}


pass { 
       from: 0.0.0.0/0 to: 0.0.0.0/0 port = imap
       log: connect error disconnect 
}


pass { 
       from: 0.0.0.0/0 to: 0.0.0.0/0 port = smtp
       log: connect error disconnect 
}


Por descontado, tendremos que avisar que el resto de tráfico lo denegamos:
block { 
       from: 0.0.0.0/0 to: 0.0.0.0/0 
       log: connect error disconnect 
}
Consideraciones a tener en cuenta: Los bloqueos deben ir antes de las aceptaciones. Si en una norma permites todo el tráfico http y acto seguido bloqueas el acceso a marca.com, tus usuarios entrarán sin problemas en marca.com. Es decir: primero debes indicar las webs que bloqueas y luego habilitar todo el tráfico web. Si por el contrario te interesa tener una lista muy selecta (como la lista de ip's de tus clientes para conectarte sólo a esas máquinas), puedes hacer la inversa. Defines sólo los dominios/ip/redes que das acceso para un puerto determinado (o todos) y al finalizar defines que bloqueas el resto de conexiones para ese puerto (el famoso "from: 0.0.0.0/0 to: 0.0.0.0/0").

Verás que además he puesto como logs las opciones de "connect", "error" y "disconnect". Esto indica que el servicio Dante debe de escribir en el log siempre que salte una conexión, error, o desconexión referente a esa norma. Si te interesa, por ejemplo, que Dante simule ser un sniffer, además deberías de añadir las opciones "data" e "iooperation". No te recomiendo usarlas a no ser que te interese tener un archivo de log gigantesco o violar la privacidad de tus compañeros. Si aún así quieres disponer de aún más información, puedes editar el /etc/init.d/danted para que Dante arranque con el parámetro -d (no confundir con -D). Esto hará que Dante vuelque a los logs toda su información de debug. En caso de tocar el /etc/init.d/danted, acuerdate de que no basta con tocar el método start, también deberás de ponerlo en el restart.

Bueno, ya tenemos un buen servidor Dante montadito. Recuerda que los cambios no son dinámicos, siempre que modifiques el archivo de configuración tendrás que relanzar el servicio para aplicar los cambios:
sudo /etc/init.d/danted restart
Si Dante no estaba en ejecución, el restart fallará y deberás de reiniciar manualmente:
sudo /etc/init.d/danted stop 
sudo /etc/init.d/danted start

Haz un "ps -ef | grep dante" para verificar que el servicio está levantado:



Vale, ya tienes tu proxy Dante levantado. ¿Ahora cómo configuramos los equipos? Fácil, cada sistema operativo suele traer en su configuración de red la opción de añadir un proxy socks. En el caso de Ubuntu basta con buscar la aplicación "Proxy de la red" e insertar la IP y el puerto de nuestro servidor:


Muy importante, Dante es proxy socks. ¡Nunca lo configures como proxy http!

Si en una shell haces un "tail -f /var/log/danted.log" verás en tiempo real todo el tráfico de los equipos que acceden al proxy: Sus conexiones, desconexiones y bloqueos:



¿Es posible monitorizar también el tráfico ssh? Sí, pero para ello debes de usar el comando "ProxyCommand" y bajarte y compilar el binario connect.c:


Una vez tengáis el binario lo guardáis en algún path, por ejemplo en /usr/local/bin. A partir de ahora siempre que tengamos que conectarnos por ssh haremos uso del Proxycommand, facilitando nuestro binario, ip del proxy y puerto de conexión:
ssh -o "ProxyCommand connect sebas@192.168.1.11:1080 %h %p" usuario@equipo
Si os fijáis he puesto "sebas" como usuario de la máquina proxy. Esto no lo he hecho por capricho. En el código fuente del connect.c veréis que no está soportado la conexión a proxys "socks v5" sin autenticación. Si en vuestro Dante teníais configurado el "method: none" deberéis de habilitar el de pam (usuario existente en esa máquina GNU/Linux).

No temas, ambos métodos ("autenticación" y "no autenticación") pueden coexistir escribiendo símplemente "method: pam none" en el /etc/danted.conf. De este modo te garantizas no tener que autenticarte a nivel de conexiones web y sí a nivel de ssh. De todas formas, si deseas un nivel de seguridad básico lo ideal sería que definieras de forma obligatoria el uso de un método de autenticación.

29 abril 2011

Consejos para mejorar la velocidad de navegación

- Haz uso del /etc/hosts

Crea una lista de los servidores web que más visites. Acto seguido, utilizando el comando ping, averigua las direcciones ip que tienen asignadas:
sebas@WarMachine:~$ ping  facebook.com
PING facebook.com (69.63.189.16) 56(84) bytes of data.
64 bytes from facebook.com (69.63.189.16): icmp_req=1 ttl=234 time=234 ms

En los entornos Unix el fichero de configuración /etc/hosts sirve para definir apodos a determinadas direcciones ip. En este caso, puedes predefinir todas esas direcciones ip con sus respectivos dominios. Así, cada vez que escribas en el navegador "facebook.com", el sistema operativo consultará este fichero de texto antes de mandar la petición a los servidores DNS.

El formato del /etc/hosts es bastante sencillo (dirección ip, tabulación, apodo):
root@WarMachine:/etc# head -4 hosts 
199.59.148.82 twitter.com
209.85.143.99 google.com
69.63.189.16 facebook.com
209.85.146.18 gmail.com
Intenta tener una lista de dominios compacta, puesto que no todas las páginas webs tienen por que tener contratada una ip fija. Eso sí, si ahora vuelves a hacer ping verás que el tiempo de respuesta se ha dividido a casi a la mitad.
sebas@WarMachine:~$ ping  facebook.com
PING facebook.com (69.63.189.16) 56(84) bytes of data.
64 bytes from facebook.com (69.63.189.16): icmp_req=1 ttl=238 time=133 ms


- Crea un servidor DNS local

Está claro que tu sabes cuales son las páginas webs que más visitas, ¿pero sabes cuales son las páginas que más visitan tu hermano o tu madre? No vas a entrar en sus perfiles para verificar su historial de navegación, a lo mejor descubres algún oscuro secreto que no deberías de saber... Bromas aparte, lo ideal sería que hicieras un servidor DNS local, de forma que tu sistema operativo memorice las direcciones ip más utilizadas. En Ubuntu es tan sencillo como instalar los siguientes paquetes:
sudo apt-get install dnsmasq dnsutils
Acto seguido, vamos al Network Manager y configuramos las direcciones DNS. Vamos a poner como primaria el localhost (127.0.0.1) y las de tu operadora como secundaria y terciaria. Si tienes configuración DHCP como método deberás de cambiarlo por "sólo direcciones automáticas (DHCP)". Si desconoces las direcciones DNS de tu operadora, te recomiendo utilizar las de Google (8.8.8.8 y 8.8.4.4).


Ahora vamos a testear nuestro nuevo sistema de resolución de dominios. Vamos a realizar dos consultas sobre un mismo dominio. La primera tardará un poco por que se mandará la consulta al DNS secundario, pero el resto de peticiones serán instantáneas al memorizarse los resultados en el caché del sistema:
root@WarMachine:/etc# dig google.com | grep Query
;; Query time: 139 msec
root@WarMachine:/etc# dig google.com | grep Query
;; Query time: 0 msec
*Si no haces uso del Network Manager, deberás de insertar manualmente las direciones DNS en el fichero de configuraicón /etc/resolv.conf. Es un cambio dinámico que no requiere que reinicies el servicio de red.


- Selecciona bien tu navegador web

Ciertos navegadores web como Chromium (Google Chrome), tienen implementado su propio sistema de resolución de dominios conocido como DNS Prefetch. Por lo general este sistema de resolución suele ser bastante básico y su funcionalidad no es la de reemplazar las DNS de tu operadora, si no la de ganar un par de segundos a la hora de cargar las webs más populares de internet.

Pero no es la única tecnología a tener en cuenta. Por ejemplo Chromium tiene la ventaja de dibujar, por defecto, las webs al vuelo. Es decir, las dibuja mientras la web está cargándose. Por otro lado, Opera  Browser tiene la opción de Turbo que permite, mediante la utilización de un sistema proxy propio que reconvierte el formato de los ficheros de imagen, adelgazar a la mitad el peso de las páginas webs.