Archivo de la etiqueta: linux

Otras utilidades para un servidor de correo

A veces un servidor de correo puede sernos de muchísima utilidad si sabemos cómo manejarlo correctamente. En el artículo de hoy veremos como utilizar nuestro MTA para ejecutar automáticamente acciones cuando se recibe un determinado email o con los parámetros que definamos. Una vez conozcamos la teoría plantearemos dos casos prácticos como ejemplo.

Para comenzar necesitaremos un servidor Linux con Qmail como MTA. Supongo que cualquier otro servidor de correo servirá (Sendmail, Postfix), yo lo personalizo en Qmail porque es el que conozco y utilizo, pero estoy seguro de que con los demás se puede hacer lo mismo.

La teoría

Para entender cómo funciona la idea debemos entender primero cómo Qmail realiza la entrega de mensajes en los buzones locales. Es un tema sobre el que hay bastante literatura buscando en Google pero que puede no quedar muy claro en una lectura rápida. Es el famoso lío de los archivos .qmail.

Cada vez que se crea un usuario del sistema se debe crear, si va a recibir emails, un archivo .qmail-default en su directorio de usuario.

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-default
./Maildir/

Normalmente este archivo se crea automáticamente ya que al instalar qmail se habrá creado una copia de este archivo en el esqueleto de creación de usuarios /etc/skel:

[jiglesias@lerez ~]# ls -la /etc/skel
total 56
drwxr-xr-x  4 root root  4096 Jul 29 18:29 .
drwxr-xr-x 97 root root 12288 Dec 30 04:08 ..
-rw-r--r--  1 root root    33 Feb  1  2008 .bash_logout
-rw-r--r--  1 root root   176 Feb  1  2008 .bash_profile
-rw-r--r--  1 root root   124 Feb  1  2008 .bashrc
drwx------  5 root root  4096 Aug 29  2007 Maildir
-rw-r--r--  1 root root    12 Jan  2  2008 .qmail-default
-rw-r--r--  1 root root   658 Sep 12  2006 .zshrc

Para el que no lo sepa, el esqueleto son los archivos que se copiarán al directorio de usuario (con los permisos de éste) cada vez que se crea uno nuevo. Si quieres que todos tus usuarios tengan algún archivo automáticamente, éste es tu sitio. En nuestro caso vemos que además del .qmail-default está el directorio Maildir, el de entrega por defecto del correo en qmail. Teniendo un usuario este archivo y este directorio, podrá recibir correo.

Vale vale, vas muy deprisa. Todavía no has explicado para que sirve el .qmail-default ese.  Cierto. Los archivos .qmail indican las reglas de entrega de los mensajes en base a dos parámetros:

  • El nombre del archivo .qmail-xx hace referencia a la cuenta de correo sobre la que actúa.
  • El contenido indica qué hacer con el correo.

Supongamos un usuario (jiglesias)  que recibe el correo de dos cuentas distintas (jiglesias@… y osus@…).

Por defecto todo su correo irá a su buzón ya que es lo que indica el archivo .qmail-default. Queremos ahora que el comportamiento sea distinto dependiendo de la cuenta a la que vaya dirigido, creamos entonces los archivos .qmail para las direcciones:

  • .qmail-jiglesias : controla el correo que vaya a jiglesias@…
  • .qmail-osus : controla el correo que vaya a osus@…

Podemos incluso ir un poco más lejos con un archivo .qmail-jiglesias-default, y controlaríamos el correo que vaya a cualquier dirección del tipo jiglesias-XXXX@…, es decir, cualquier dirección que comience por jiglesias- será controlada por este archivo .qmail.

En el caso básico, que es el que veíamos, la entrega se realiza al buzón de correo del usuario (el directorio Maildir) pero podríamos hacer otras cosas en función del contenido del archivo .qmail encargado de procesar la entrega del correo:

Reenvío a otra cuenta:

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-jiglesias
[email protected]

Reenvío a un programa/script:

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-osus
|preline /usr/bin/programa

Combinación de los anteriores

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-jiglesias
./Maildir/
[email protected]
|preline /usr/bin/programa

El caso que nos interesa es el segundo, es decir, pasar la entrega del email a un script que se encargará de analizar el email y tomar decisiones.

Te habrá llamado la atención el |preline del archivo .qmail. Es el sistema que prepara un email para ser procesado y entregado a otro script añadiendo algunas cabeceras. La salida del script que reciba el email es importante ya que de ella depende el resultado de la entrega final de email, en concreto es importante saber que si queremos rechazar un email habrá que terminar el script con un exit (100), esto indicará a qmail que debe rechazar ese mensaje. Veremos más adelante la utilidad de esta salida.

La práctica

Una vez sabemos cómo pasar el control de un email recibido a un script, veamos como tratarlo. Lo haremos con un script PHP. En nuestro archivo .qmail haremos algo como:

[jiglesias@lerez ~]# more .qmail-jiglesias
|preline /usr/bin/php /home/jiglesias/prueba.php

Con esto hemos terminado el trabajo en el servidor de correo. Veamos ahora como parsear el email desde PHP.

Lo primero que debemos hacer es recoger el contenido del email desde el script a través de la entrada estándar,  después ya podemos procesar el email como una cadena de texto.

<?php
$email=file("php://stdin");
$email=implode("", $email);
?>

Con estas sencillas dos líneas de código tendremos en nuestro script el contenido del email. Ahora sólamente debemos procesarlo. Podemos hacerlo línea por línea por nuestra cuenta o apoyarnos en alguna librería. Yo utilizo Mail_mimeDecode de Pear. La ventaja de esta librería es que podemos obtener, además del texto del email, los archivos adjuntos.

<?php
$email=file("php://stdin");
$email=implode("", $email);
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['input'] = $email;
$structure = Mail_mimeDecode::decode($params);
$subject = trim($structure->headers['subject']);
$ddate = trim($structure->headers['date']);
$from = addslashes(trim($structure->headers['from']));
if(ereg("<(.*)>", $from, $p)) $from=$p[1];
if(ereg(""(.*)"", trim($structure->headers['from']), $pp))
    $nombre=$pp[1];
?>

Así podemos ya procesar el email y tomar las decisiones que consideremos oportunas. Podremos insertarlo en una base de datos, lanzar otros procesos automatizados, enviar avisos por SMS… lo que se nos ocurra.

Casos prácticos

La pregunta clave, después de ver la teoría, sería ¿para qué me sirve esto?.  Os propongo dos aplicaciones que yo he hecho.

Sistema de soporte

El típico sistema de tickets de soporte. En el asunto del email se arrastra el identificador del ticket, por ejemplo [#123445]. Tendremos que analizar el asunto y comprobar si aparece el patrón predefinido. Si no existe estamos ante un nuevo ticket e insertamos los datos en nuestra base de datos, en caso contrario es una respuesta a una incidencia anterior y ahí tendremos el identificador. Sencillo ¿no?. Podemos incluso adjuntar a nuestras indicendias archivos que puedan llegar en el email.

Envío de archivos desde el movil

El segundo ejemplo sería semejante al anterior técnicamente pero distinto conceptualmente. La mayoría de los móviles (salvo los de gama alta) no pueden enviar archivos desde los formularios wap (<input type=”file”>). La alternativa es que el usuario envie un email o un MMS (la mayoría de operadoras permiten el envío a direcciones de email) con sus archivos. Nuestro script procesará el contenido del mail recibido, decodificará los archivos y los tratará como sea oportuno.

Son dos sencillos ejemplos de cómo utilizar el email para automatizar tareas, pero, como he comentado, podríamos hacer todo lo que se nos ocurra, desde enviar un SMS de aviso hasta incluso reiniciar nuestro servidor o lanzar cualquier otra tarea.

Conversión de vídeos a 3gpp bajo demanda en un entorno web

Hoy veremos como aplicar conversiones de vídeo en un entorno web donde los usuarios suben sus vídeos en cualquier formato. Sí, tienes razón, ni más ni menos que lo que hace YouTube, de hecho utilizaban un sistema muy similar a lo que veremos ahora y basado en el mismo software. En realidad el artículo se podría aplicar a prácticamente cualquier tipo de conversiones, incluso para iPod o PSP, sólo hay que utilizar los parámetros adecuados. Nosotros nos centraremos en la conversión a 3gp para después poder hacer streaming con ellos además de permitir su descarga.

Nuestro entorno se basará en un servidor con Linux (Centos5 en mi caso). Como software sólamente necesitaremos ffmpeg, herramienta imprescindible para cualquier aplicación (tanto web como de escritorio) bajo Linux.

¿Qué es ffmpeg?

Hace unas semanas veíamos cómo utilizar ffserver para convertir flujos de vídeo y hacer streaming. ffmpeg es la utilidad en la que se apoya ffserver para realizar las conversiones.

ffmpeg es una herramienta de software libre que permite realizar conversión entre la mayoría de formatos de vídeo más utilizados. Una inmensa mayoría de programas de conversión bajo Windows no son más que frontends gráficos para ffmpeg.

Normalmente los paquetes precompilados de ffmpeg no vienen con las extensiones adecuadas para las conversiones que necesitamos, así que deberemos compilar nuestra propia versión.

Necesitas instalar los siguientes paquetes, son más de los imprescindibles, pero así nos ahorraremos problemas futuros:

libmp4v2
libvorbis
libvorbis-devel
lame
lame-devel
faac
faac-devel
faad2
x264
x264-devel
xvidcore
xvidcore-devel

Debo decir que para mi Centos no están todos disponibles como rpm con lo cual habrá que compilar manualmente algunos de ellos. No deberías tener ningún problema para localizar cada librería.

Finalmente habrá instalar los siguientes paquetes:

http://ftp.penguin.cz/pub/users/utx/amr/amrnb-7.0.0.2.tar.bz2

http://ftp.penguin.cz/pub/users/utx/amr/amrwb-7.0.0.3.tar.bz2

Con esto tenemos ya todo preparado para compilar nuestro propio ffmpeg. Descarga el paquete desde la web oficial, descomprímelo y:

./configure --enable-libmp3lame --enable-libvorbis --enable-libogg --enable-libamr-nb --enable-libamr-wb --enable-libfaac --enable-gpl --enable-libxvid --enable-libx264 --enable-libfaad --enable-shared
make
make install

Si has seguido bien todos los pasos tendrás el software de conversión preparado.

Convirtiendo a 3gp

Para realizar las conversiones algo tan sencillo como:

ffmpeg -y  -i original.avi -s qcif -r 12 -b 30 -ac 1 -ar 8000 -ab 12200 video.3gp
/usr/local/bin/MP4Box -3gp -mtu 1450 -hint video.3gp

Cuando vimos cómo hacer streaming a móviles veíamos como utilizar la utilidad MP4Box para que nuestros vídeos 3gp se puediesen utilizar para emitir en vivo.

Para nuestro proyecto es probable que necesitemos algo más. Si queremos que también se puedan visualizar los vídeos desde una web necesitaremos convertirlos a flv:

ffmpeg -y -i original.avi -acodec libmp3lame -ac 2 -ar 22050 -r 12 -b 196 -s 176x144 video.flv
/usr/bin/flvtool2 u video.flv

Flvtool es una herramienta que inserta en los flv los metadata necesarios para que funcione bien en el reproductor flash.

Y necesitaremos capturar algún fotograma del vídeo para mostrar como demo:

ffmpeg -i original.avi -y -ss 00:00:01 -vframes 1 -an -sameq -s 160x120 -f image2 thumbnail.jpg

Ahora ya sabemos:

  • cómo crear nuestro vídeo 3gp que nos sirva tanto para descarga como para streaming.
  • cómo crear el vídeo flv que nos sirva para ver vía web.
  • cómo generar thumbnails de escenas del vídeo.

Creando el entorno web

Supongamos que queremos desarrollar una web al estilo Youtube donde el usuario sube vídeos desde un formulario y posteriormente se desea mostrarlos categorizados, con un buscador, una preview (flv), descarga para móviles, etc.

La parte de la web propiamente dicha resulta obvia, no vamos a entrar en detalles. Lo que nos ocupa aquí es como realizar la conversión según las instrucciones que hemos visto. Obviamente no podemos ejecutar la conversión cada vez que un usuario sube un vídeo, sería un proceso lento y pesado. La mejor manera es crear una cola de conversión. Cuando se sube un nuevo vídeo no esta disponible públicamente (no se ha convertido todavía). Podríamos definir tres estados diferentes para un video:

  1. Sin procesar (convertir).
  2. Procesando.
  3. Procesado.

Según esto sería sencillo tener en una tabla el estado de los vídeos. Sólo los procesados se listarían públicamente.

Por otro lado tendríamos una tarea encargada de buscar vídeos en estado “sin procesar” y realizar su conversión. Esta tarea podría ser o bien un demonio residente que está permanentemente buscando vídeos o bien, si el tráfico de nuevos vídeos no va a ser elevado, podría ejecutarse periódicamente para realizar las conversiones.

Si el tráfico fuese muy elevado podría incluso haber varias tareas en paralelo ya que una vez una de ellas encuentra un vídeo “no procesado” actualiza su estado a “procesando” de manera que la siguiente tarea ya no escogerá ese mismo vídeo.

La teoría es mucho más sencilla de lo que parece. La tarea que realiza la conversión creará los formatos que estimemos oportunos según ya hemos visto antes.

Convirtiendo a otros formatos

Con ffmpeg se pueden generar los vídeos para casi cualquier formato existente incluyendo PSP, iPod/iPhone/iTouch… Googleando un poco encontrarás los parámetros adecuados para cada formato.

Conclusiones

En este artículo he explicado cómo realizar conversiones de vídeos a distintos formatos y cómo aplicarlo a un entorno web dónde los usuarios suben sus propios vídeos. He dado los pasos básicos para entender el sistema, vosotros tendríais que adaptarlo a vuestras necesidades.

Depurando condiciones Rewrite en Apache

Últimamente he tenido que utilizar bastante ModRewrite de Apache en distintos proyectos, algunos de migración y otros de Internet Móvil donde se necesitan ciertas URL’s para que las operadoras lancen sus sitemas de billing.

Con ModRewrite es muy fácil convertir URLs antiguas al formato nuevo en caso de migración (de manera que no pierdes el SEO que tengas). En el caso del billing de las operadoras móviles es todavía más sencillo puesto que simplemente lo usamos para simular una URL premium. Por ejemplo:

http://tudominio.com/premium.php?noticia=3

Se convierte en:

http://tudominio.com/index.php?noticia=3

De este modo no es necesario hacer scripts independientes y separados por operadora sino que todo queda integrado en nuestro framework.

El problema es que es bastante tedioso depurar lo que hace ModRewrite para ver si las condiciones que has escrito son correctas o si algún filtro no se está ejecutando correctamente. Para solucionarlo lo mejor que podemos hacer es decirle a Apache que “logee” todas las decisiones que va tomando sobre nuestros filtros. Tan fácil como esto:

RewriteEngine on
RewriteLog "/logs/rewrite.log"
RewriteLogLevel 4

RewriteCond %{REQUEST_URI} ^/premium.php$
RewriteRule ^/premium.php /index.php

Este sencillo ejemplo nos permite comprobar qué veremos en el log:

213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (3) applying pattern '^/premium.php' to uri '/premium.php'
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (4) RewriteCond: input='/premium.php' pattern='^/premium.php$' => matched
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (2) rewrite /premium.php -> /index.php
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (2) local path result: /index.php
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (2) prefixed with document_root to /var/www/tudominio.com/index.php
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (1) go-ahead with /var/www/tudominio.com/index.php [OK]

Por cada URL que se solicita al servidor, Apache va comprobando las posibles condiciones que has escrito por si coincide con el patrón (matched) o no (not matched). Si la URL cumple los requisitos, la reescribe según se indica en la regla, en caso contrario la deja pasar tal cual. Con toda esta información podemos hacer un completo seguimiento de las reglas que queremos aplicar para comprobar si hay algún error o por qué determinada URL no hace lo que esperamos que haga.

El nivel del log indica la cantidad de información que se guardará, de 0 a 9. A  mayor valor, más información.

Algo muy sencillo pero que pasa desapercibido para la mayoría y acabamos perdiendo muchísimo tiempo probando reglas sin saber qué está ocurriendo en el interior de nuestro servidor web.

Cómo emitir webcams en vivo con un servidor Linux y en formato FLV

Hace un tiempo tuve un cliente que quería visualizar las cámaras que había instalado en su oficina desde su casa. En aquel momento eran cámaras IP cableadas. Este tipo de cámaras tienen un servidor web integrado desde donde puedes configurar su dirección IP además de ver el vídeo que transmiten. Por lo general estas cámaras transmiten en formato mjpeg, un flujo de imágenes JPG normales, aunque también podría ser que lo hiciesen directamente en mpeg. Firefox es capaz de reproducir este formato directamente, no así Internet Explorer que necesita de un applet Java para hacerlo.

El reto consistía, por tanto, en transmitir las cámaras en un formato que se pudiese reproducir fácilmente y que se pudiese integrar en una página web, sin programas de terceros para visualizarlas. Así llegamos a la solución que hoy os presento y que no es exclusiva de cámaras IP sino que puedes utilizar cualquier webcam USB que tengas por casa. El formato que hemos escogido para el flujo de vídeo será flv,  con lo que con una sencilla aplicación Flash podremos visualizar cada una de nuestras cámaras evitando, de paso, los applets Java.

Para el proyecto de hoy necesitaremos:

  • Servidor Linux, imprescindible, en mi caso Centos5
  • Apache instalado en el servidor.
  • ffmpeg, para hacer la conversión de formatos
  • ffserver, parte del paquete ffmpeg, para transmitir elvídeo

Si no tienes el software necesario, el primer paso es instalarlo. En mi caso:

yum install ffmpeg apache

Comenzaremos configurando ffserver desde /etc/ffserver.conf:

Port 8090
BindAddress 0.0.0.0
MaxClients 1000
MaxBandwidth 10000            

<Feed feed1.ffm>
  File /tmp/feed1.ffm
  FileMaxSize 5M
</Feed>
<Feed feed2.ffm>
  File /tmp/feed2.ffm
  FileMaxSize 5M
</Feed>            

<Stream camara1.flv>
  Feed feed1.ffm
  Format flv
  VideoCodec flv
  VideoBitRate 128
  VideoBufferSize 500
  VideoFrameRate 5
  VideoSize 320x240
  NoAudio
  Preroll 5
</Stream>            

<Stream camara2.flv>
  Feed feed2.ffm
  Format flv
  VideoCodec flv
  VideoBitRate 128
  VideoBufferSize 500
  VideoFrameRate 5
  VideoSize 320x240
  NoAudio
  Preroll 5
</Stream>            

<Stream stat.html>
  Format status
</Stream>

ffserver funciona como un servidor, transmitiendo en el puerto que le indiquemos lo que recibe en los archivos que se indican en los feed’s. A continuación se indican los flujos de vídeos que vamos a utilizar (feed’s), en mi caso dos cámaras. Finalmente definimos los streams que será a lo que realmente nos conectaremos nosotros, un stream por cada feed en formato flv. Podríamos crear tantos streams de cada feed como creamos oportuno en distintos formatos, consulta la ayuda de ffserver y ffmpeg.  Finalmente creamos un stream especial, el de status, donde veremos información sobre el estado del servidor y clientes contectados.

Lanzamos el servidor ffserver tal cual.

ffserver

Con esta configuración tendremos acceso a dos flujos de vídeo en formato FLV en las direcciones:

http://localhost:8090/camara1.flv
http://localhost:8090/camara2.flv

Vale, sí, aún no hemos transmitido el vídeo, eso viene ahora. Debemos enlazar el flujo de vídeo de nuestras cámaras con ffserver para que éste haga la conversión de formato y lo emita por las urls indicadas. Para hacerlo recurrimos al inseparable compañero de ffserver, ffmpeg.

Si tus cámaras están conectadas al servidor bien sea por USB o por capturadoras de vídeo (debes tenerlas configuradas previamente):

ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video0 http://localhost:8090/feed1.ffm
ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video1 http://localhost:8090/feed2.ffm

Si tus cámaras fuesen IP habría que capturar primero el vídeo desde la cámara y pasárselo a ffmpeg, por ejemplo:

curl http://ip-de-tu-camara:8080 | ffmpeg -er 4 -y -v quiet -an -f mjpeg -r 4 -i - http://localhost:8090/feed1.ffm

Sea como sea el formato de tus cámaras, y si todo ha ido bien, tendrás a ffmpeg enviando el vídeo a ffserver para que lo convierta a FLV y lo publique en las URL’s anteriormente indicadas.

Debes tener cuidado con un detalle importante si conectas más de una cámara USB, debes enchufarlas a concentradores distintos de tu equipo para diversificar adecuadamente los flujos de vídeo y el ancho de banda que ocupan, si conectases dos cámaras al mismo concentrador no tendrían ancho de banda suficiente y se bloquearían entre ellas.

Veamos ahora como visualizar los flujos de vídeo. Necesitaremos el editor de Flash de toda la vida. Creamos un nuevo documento y arrastramos al escenario un objeto FLVPlayback. Como nombre de instancia le ponemos “video” y en sus propiedades ponemos isLive a true. En el primer frame añadimos el siguiente código:

video.contentPath=path_video;

De esta manera podremos indicar desde html la url del flujo de vídeo sin tener que tener un swf por cada cámara. Compila y publica el proyecto puesto que ya hemos terminado.

No me voy a preocupar ahora del código html generado, eso es cosa tuya. Dentro del archivo html que te genera el editor de flash tendrás algo como:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="webcam1" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="webcam.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="webcam.swf" mce_src="webcam.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="webcam1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

Vamos a convertirlo en esto:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="webcam1" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="webcam.swf?path_video=http://192.168.90.1:8090/camara1.flv" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="webcam.swf?path_video=http://192.168.90.1:8090/camara1.flv" mce_src="webcam.swf?path_video=http://192.168.3.2:8090/camara1.flv" quality="high" bgcolor="#ffffff" width="550" height="400" name="webcam1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

Es decir, añadimos al parámetro de la película la variable path_video con la url del flujo tal y como lo hemos generado antes. Debe ser una URL a la que se pueda acceder externamente pues el swf se conectará a ella para recoger el flujo flv generado por ffserver, si tienes un router o un firewall de por medio, deberás abrir ese puerto. Copiaremos este código tantas veces como cámaras tengamos modificando la URL del flujo de vídeo.

Voila! esto es todo, ya tienes publicadas tus cámaras en un formato sencillo de visualizar como es el FLV.  Abre la página html que has creado y comenzarás a visualizar tus cámaras.

Pero… vayamos un paso más allá. Con lo que hemos visto hasta ahora tendremos que abrir el puerto 8090 de buestra máquina para permitir el acceso a los flujos de vídeo, pero nosotros no queremos hacer esto, ya tenemos el puerto 80 con Apache a la escucha. ¿Qué hacemos? Sencillo, mod_proxy viene a nuestro rescate. Asegúrate de tenerlo habilitado en la configuración de tu Apache. Después simplemente habrá que añadir las siguientes líneas a la configuración del virtual host en el que quieres publicar los streams:

ProxyPass /camara1.flv http://127.0.0.1:8090/camara1.flv
ProxyPassReverse /camara1.flv http://127.0.0.1:8090/camara1.flv        

ProxyPass /camara2.flv http://127.0.0.1:8090/camara2.flv
ProxyPassReverse /camara2.flv http://127.0.0.1:8090/camara2.flv

Con esto estaremos enviando automáticamente cada stream desde su url original en el puerto 8090 a una url de tu virtual host del tipo http://tudominio.com/camaraX.flv. Esa ruta no existe físicamente, es virtual y Apache sabe que debe redirigirla al stream del ffserver.

Finalmente queda un detalle importante. Hasta ahora habíamos incializado ffserver por un lado y los flujos de las cámaras por otro con ffmpeg. Esto es un rollo y no nos gusta, queremos que ffserver lo inicie todo automáticamente. Pues vale, añadamos una directiva Launch a cada feed de la configuración de ffserver con los parámetros que utilizábamos para llamarlo desde la línea de comandos. En el ejemplo de nuestra primera cámara quedaría:

<Feed feed1.ffm>
  File /tmp/feed1.ffm
  FileMaxSize 5M
  Launch  ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video0
</Feed>

A partir de ahora, cada vez que lancemos ffserver tecleando simplemente ese comando, se inciarán, además del propio servidor, los streams que hayamos definido.

Hasta aquí hemos llegado por hoy. De una manera extremadamente sencilla hemos publicado nuestras webcams. Es responsabilidad tuya y sólamente tuya el asegurar el acceso a los flujos y a la página de visualización que hemos creado. Otro día quizás veamos cómo montar un sistema de videovigilancia casero con detector de presencia 😉 .

Os dejo el código de la aplicación Flash de visualización de los streams.

Usando certificados SSL de cliente como sistema de autenticación web

A menudo creamos aplicaciones web con un backend de gestión que, por ser también web, exponemos públicamente a cualquiera que consiga averiguar la URL. Habitualmente estos sistemas son de acceso restringido, sólo un pequeño grupo de usuarios lo utiliza.

En escenarios donde tenemos un número de usuarios acotado y se necesita autentificación, se puede utilizar un mecanismo de certificados que aporten mayor seguridad al sistema, de esta manera solo aquellos usuarios que tengan el certificado en cuestión tendrán acceso a la máquina.

Hoy veremos como permitir el acceso a nuestra aplicación a aquellos usuarios que dispongan de un certificado que previamente les habremos enviado mientras que si no lo tienen no podrán acceder de ningún modo. Este método se puede combinar, además, con el tradicional usuario/clave para dar mayor seguridad. Podremos incluso verificar que el nombre de usuario que se intenta utilizar se corresponde con el certificado de usuario que le hemos enviado y no intenta autentificarse con otro.

Conceptos básicos sobre certificados SSL

El método que vamos a ver se basa en certificados SSL. Se utilizan para asegurar la información entre un cliente y el servidor y prevenir escuchas ya que la información viaja encriptada. Ésta es su función y la hace aunque no esté firmado por una autoridad certificadora (CA) oficial o, incluso, aunque esté caducado. Sigue asegurando las comunicaciones.

Los navegadores web reconocen, por defecto, una serie de autoridades certificadoras como Verisign o Thawte, aunque hay muchas más. Puedes verlas todas en las opciones de tu navegador. Pero, ¿qué es realmente lo que hace una Autoridad Certificadora? Firmar. Firma tu certificado SSL asegurando que os pertenece a ti y a tu dominio. Cuando un cliente accede a tu dominio y descarga el certificado SSL, busca dentro de sus certificados de CA‘s si hay alguno que lo haya firmado. Si lo encuentra, acepta tu certificado y no ocurre nada especial, pero si no encuentra la CA lanza un aviso indicando que no se reconoce la autoridad que lo firma. Esto no quiere decir que el certificado no sea válido, lo único que ocurre es que no sabe quien lo firma. Esto significa, por tanto, que tú mismo puedes ser tu propia autoridad certificadora y firmar tus certificados, funcionarán perfectamente y cumplirán su cometido de asegurar las comunicaciones cliente/servidor.

Comercialmente o en sistemas de acceso público en general, no se recomiendan certificados autofirmados ya que el aviso de autoridad de certificación no reconocida generará desconfianza entre tus usuarios, pero en entorno intranet o de paneles de adminsitración es un método ideal.

El servidor puede requerir, además, otro certificado al cliente, de manera que ámbos extremos autentifiquen la comunicación. Esto es precisamente lo que vamos a hacer hoy en este artículo.

Según lo que hemos explicado, los certificados autofirmados son igual de seguros que los firmados por una autoridad certificadora. Como en el ejemplo que estamos viendo estamos asegurando el acceso a nuestra aplicación para un grupo reducido de usuarios, no hay ningún problema en utilizar un certificado firmado por nosotros mismos ya que nuestros usuarios sabrán que no hay ningún problema. Pero esto no es todo, por esta misma razón podemos decir a los usuarios que se instalen el certificado público de nuestra CA, tal y como hacen las CA oficiales, y automáticamente el navegador comenzará a confiar en nuestros certificados ya que, ahora sí, tiene un certificado de una CA que firma los certificados SSL.

Como resumen, nuestro trabjo consistirá en:

  • Crear nuestra autoridad certificadora y su certificado.
  • Crear el certificado SSL para nuestro servidor web firmado por nuestra CA.
  • Crear los certificados de cliente para nuestros usuarios.
  • Habilitar la lectura de los datos SSL desde PHP.

Servidor web SSL

Utilizaremos el paquete Openssl para generar los certificados. Si aún no lo tienes instalado en tu servidor, es el momento. Explicaré de manera rápida como crear crear certificados SSL para asegurar las comunicaciones ya que es el primer paso necesario para añadir certificados de cliente, sin embargo no es el objeto principal de este artículo y hay mucha documentación, googlea un poco 😉 .

Primero creamos el certificado y la clave privada de nuestra autoridad de certificación:

openssl req -x509 -newkey rsa:2048 -days 3650 -keyout CAXplotaKey.pem -out CAXplotacert.pem

Lo más importante de este comando es el parámetro days, ya que no queremos que dentro de un año nos caduque el certificado de nuestra propia entidad. Yo le pongo 10 años. Este comando genera dos archivos, la clave privada con la que firmaremos nuestros futuros certificados y el certificado con la clave pública que instalaremos, si queremos no recibir avisos, en el navegador. Este comando te pedirá algunos datos (nombre de empresa, país…) y, sobre todo, una contraseña. Deberás recordarla cada vez que vayas a firmar un certificado SSL, así que no la olvides. Ya tenemos nuestra CA creada.

Creamos ahora el certificado SSL para nuestro dominio:

openssl genrsa -des3 -out claveprivada.pem 2048
openssl req -new -key claveprivada.pem -out certificado.pem

El primer comando crea la clave privada de nuestro certificado. Te pedirá otra contraseña, esta vez para la clave privada. Recuérdala también.

El segundo comando genera la petición de certificado sobre la clave privada anterior. Te pedirá la contraseña de la clave privada anterior.

En este punto tenemos cuatro archivos, la clave y el certificado de tu CA y la clave y la solicitud de tu certificado SSL. Sólo queda firmar el certificado con nuestra CA y ya podremos utilizarlo.

Para poder firmarlo debemos generar primero un fichero de texto con algunos parámetros de configuración:

cat configservidor.cnf

basicConstraints = critical,CA:FALSE
extendedKeyUsage = serverAuth

Y firmamos el certificado.

openssl x509 -CA CAXplotacert.pem -CAkey CAXplotaKey.pem -req -extfile configservidor.cnf -in certificado.pem -days 3650 -CAcreateserial -sha1 -out certificado-servidor.pem

Ya tenemos un certificado SSL preparado para utilizar en nuestro servidor web. En nuestro caso es certificado-servidor.pem. Vamos a configurar Apache para que lo utilice.

Habrá que editar httpd.conf y añadir

LoadModule ssl_module modules/mod_ssl.so
Listen 443

Es probable que, si no has compilado tu propio Apache y has instalado un paquete precompilado, tengas ya algún ssl.conf. Activándolo tendrás este paso preparado. En mi caso, un CentOS5, sólo hay que incluir /etc/httpd/conf.d/ssl.conf.

Finalmente hay que indicar en el virtual host que quieres asegurar que use nuestro nuevo certificado. Para hacerlo añadimos un nuevo virtual que escuche en el puerto 443 y añadimos las siguientes líneas:

SSLEngine on
SSLCertificateFile /ruta/a/certificado-servidor.pem
SSLCertificateKeyFile /ruta/a/claveprivada.pem

Si ahora reinicias Apache y accedes a tu dominio con https verás que en tu navegador aparece el candado indicando que la información es segura. Verás también que te aparece un aviso de que no se confía en la autoridad certificadora. Veremos más adelante como solucionarlo.

Añadiendo certificados de cliente

Ahora que ya tenemos nuestro servidor web seguro con nuestros certificado autofirmado llega el momento de crear certificados para nuestros clientes de manera que si alguien intenta acceder a nuestra aplicación sin uno de ellos se le prohíba el paso.

Crearemos primero un archivo de configuración con los parámetros que necesitaremos.

cat configcliente.cnf

basicConstraints = critical,CA:FALSE
extendedKeyUsage = clientAuth

Con esto daremos instrucciones de que es un certificado cliente a la hora de firmar el certificado.

Creamos ahora, igual que hacíamos antes, la clave privada y la solicitud de certificado.

openssl genrsa -des3 -out clave-cliente.pem 2048
openssl req -new -key clave-cliente.pem  -out certificado-cliente-req.pem

Como antes, al generar la clave te pedirá una contraseña que deberás introducir después, al hacer la solicitud de certificado. Los datos que te pide esta solicitud, como ocurría antes, los podrás leer posteriormente para comprobar datos o lo que estimes oportuno, así que es importante que prestes atención.

Firmamos ahora el certificado con nuestra CA:

openssl x509 -CA CAXplotacert.pem -CAkey CAXplotaKey.pem -req -in certificado-cliente-req.pem -days 3650 -extfile configcliente.cnf  -CAcreateserial -sha1 -out certificado-cliente.pem

Como véis, el proceso es el mismo para generar el certificado de servidor, pero cambiamos el contenido del archivo de configuración que le indica que es un certificado cliente, no servidor.

Vale, bien, pero ¿no quedamos que es el cliente el que debe instalar el certificado? Sí, ahí vamos ahora. El certificado que acabamos de generar lo debes instalar en tu navegador web, no en el servidor, así que habrá que convertirlo a algún formato que puedan entender. Para esto hacemos lo siguiente:

openssl pkcs12 -export -in certificado-cliente.pem -inkey clave-cliente.pem -certfile CAXplotacert.pem -out cliente.p12

Nos pedirá la contraseña de la clave privada del certificado y nos solicitará otra para el que va a generar. Es importante poner contraseña al certificado final ya que es el que vas a enviar a tus usuarios y pretendes que sólo estos puedan utilizarlo, así que poner una contraseña nunca está demás.

Ya tienes tu certificado cliente.p12. Puedes probarlo tu mismo. Ah no, espera, que no hemos configurado el servidor web para que solicite sí o sí un certificado al cliente. Añade a la configuración SSL de tu virtual host de Apache:

SSLCACertificateFile /path/a/CAXplotacert.pem
SSLVerifyClient require

Y reinicia tu servidor web. Si ahora intentas acceder a tu dominio verás como salta una ventana solicitando un certificado que no tienes. Desde las opciones de tu navegador, busca la sección de certificados SSL y añade un nuevo certificado. Selecciona tu cliente.p12. Te pedirá la contraseña que pusiste al convertirlo a pk12. Ya está. Accede ahora y verás como te aparece el certificado en la ventana de certificiados disponibles y, si lo seleccionas, te deja acceder a tu aplicación.

Revocar certificados

A veces puede ser necesario prohibir el paso con determinados certificados, bien porque el usuario ya no colabora o trabaja contigo, bien porque hay posibilidades de que el certificado haya sido robado, etc. Una opción es, a la hora de crear el certificado, y si sabes de antemano que el usuario lo necesitará pocos días (cuenta de prueba, usuario esporádico o cualquier razón similar), generarlo con una validez limitada (10 días, un mes, etc.), de modo que pasado este tiempo el certificado caduca y el usuario no puede entrar. El problema más importante lo tendrás cuando necesites prohibir el acceso a usuarios con certificados a largo plazo. Para hacerlo debemos crear una lista de revocación de certificados.

En mi caso (CentOS5 y RHEL5) tuve algunos problemas de configuración. Openssl debería de mantener una lista de los certificados que emite y sus números de serie sin embargo tal como hemos generado nuestros certificados no lo hace. Para solucionarlo, creamos a mano la lista.

touch /etc/pki/CA/index.txt

Ahora debemos editar el archivo de configuración de Openssl para reflejar algunos parámetros. Por defecto indica una ruta relativa hacia los directorios donde dejará los certificados, pero no me funcionó bien hasta que no le puse rutas absolutas. Modificamos pues la ruta al directorio y le indicamos donde están la clave privada y el certificado de nuestra CA.

Editar /etc/pki/tls/openssl.cnf

dir             = /etc/pki/CA              # Where everything is kept
certificate     = /path/a/CAXplotacert.pem    # The CA certificate
private_key     = /path/a/CAXplotaKey.pem # The private key

Ahora sí, podemos revocar un certificado de manera tan sencilla como:

openssl ca -revoke certificado-cliente.pem

A partir de la lista con el estado de los certificados debemos crear una lista de revocación que usaremos para indicar al servidor web los certificados que no debe permitir.

openssl ca -gencrl -out recovados.crl

Es posible que al lanzar este comando te apareza otro error relacionado con crlnumber. Esto viene relacionado con lo que os indicaba antes y es simplemente que no existe el archivo de números de serie. Para solucionarlo, simplemente lo creamos:

echo "01" > /etc/pki/CA/crlnumber

Y volvemos a generar la lista de revocación. Cada vez que revoques un certificado deberás repetir esta operación para tener la nueva lista.

Ya sólo falta indicarle a Apache qué certificados no debe permitir. Añadimos a la configuración de nuestro virtual host, en el mismo sitio donde configuramos anteriormente el certificado SSL, la siguiente línea:

SSLCARevocationFile path/a/revocados.crl

Eso es todo, ya tenemos nuestro servido SSL funcionando, nuestra aplicación protegida con certificados de cliente y la opción para revocar certificados.

Comprobando los certificados con PHP

Queremos ir un poco más allá. Queremos que, desde nuestra aplicación, podamos leer el certificado SSL del cliente y comprobar quién es. Seguro que a ti se te ocurren mejores cosas que hacer con estos datos 😉 . Lo primero que tendremos que hacer será configurar Apache para que pase los datos del certificado. En la configuración de tu virtual host añades:

<Files ~ ".(cgi|shtml|phtml|php?)$">
 SSLOptions +StdEnvVars +ExportCertData
</Files>

Ya está. Si ahora haces un print_r($_SERVER) en la aplicación donde tienes instalado tu certificado SSL de cliente verás entre todos los datos algo como:

[SSL_CLIENT_S_DN] => /C=ES/ST=Valencia/L=Valencia/O=Osusnet/CN=blog.osusnet.com/emailAddress=osusENosusnet.com
[SSL_CLIENT_S_DN_C] => ES
[SSL_CLIENT_S_DN_ST] => Valencia
[SSL_CLIENT_S_DN_L] => Valencia
[SSL_CLIENT_S_DN_O] => Osusnet
[SSL_CLIENT_S_DN_CN] => blog.osusnet.com
[SSL_CLIENT_S_DN_Email] => osusENosusnet.com

Tienes toda la información que necesitas, son los datos que se solicitaban al crear la solicitud de certificado. Generando estos parámetros de manera adecuada podrás saber quién entra a tu aplicación a partir del certificado cliente que usa.

Eso es todo por hoy. De una manera muy sencilla hemos añadido un poco más de seguridad a una aplicación web de acceso privado y con un número de usuarios acotado. También hemos aprendido y poco más sobre certificados SSL y hemos visto cómo firmar nuestros propios certificados, igual de fiables que los comerciales. Espero que os sirva de algo 😛 .

Error con el smtp autentificado con los clientes de Nokia a través de Qmail

Como os decía hace unos días, recientemente he cambiado el Nokia N70 que llevaba desde hace algo más de dos años por un Nokia E51. La característica más importante de este nuevo terminal es la disponibilidad de conexión WIFI, con lo cual, si tienes un poco de suerte y encuentras un punto de acceso abierto, puedes tener acceso a tu correo desde cualquier punto. No he tenido muchos problemas para encontrar puntos de acceso y habitualmente he podido leer el correo. El problema llega a la hora de enviarlo ya que siempre obtengo un error en el envío. Cansado de no saber qué ocurría y dado que voy a estar unos días fuera de casa, decidí investigar un poco.

Comencé por ver cual era la comunicación del cliente de correo con mi servidor. Para ello lancé un sniffer en mi máquina, en este caso Wireshark, nombre actual del Ethereal de toda la vida. Para no volverme loco con todo el tráfico que iba a ver añadí un filtro, de manera que sólo se mostraría el tráfico que venía desde la IP de mi conexión ADSL (11.22.33.44), así sólo vería lo que estaba generando mi propio móvil.

tshark -i2 -f "src host 111.222.333.444"

Y este fué el resultado:

3.197223 11.22.33.44 -> 55.66.77.88 TCP 49537 > smtp [SYN] Seq=0 Win=64240 Len=0 MSS=1460 TSV=1218449730 TSER=0 WS=0
3.197263 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=389435772 TSER=1218449730 WS=7
3.347274 11.22.33.44 -> 55.66.77.88 TCP 49537 > smtp [ACK] Seq=1 Ack=1 Win=64240 Len=0 TSV=1218663480 TSER=389435772
3.356257 55.66.77.88 -> 11.22.33.44 SMTP Response: 220 servidor ESMTP
3.509426 11.22.33.44 -> 55.66.77.88 SMTP Command: EHLO
3.509451 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [ACK] Seq=29 Ack=22 Win=5888 Len=0 TSV=389436084 TSER=1218828105
3.509490 55.66.77.88 -> 11.22.33.44 SMTP Response: 250-servidor
3.663467 11.22.33.44 -> 55.66.77.88 SMTP Command: AUTH CRAM-MD5
3.813384 11.22.33.44 -> 55.66.77.88 SMTP Command: amlxxxxxxWFzIDkxNTM4MxxxxxxyYjVjOTdmYxxxxxxxDFhZDg2MjEw
3.853554 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [ACK] Seq=197 Ack=95 Win=5888 Len=0 TSV=389436428 TSER=1219132230
8.817212 55.66.77.88 -> 11.22.33.44 SMTP Response: 535 authorization failed (#5.7.0)

Es decir, me estaba fallando la autentificación a través de CRAM-MD5 a pesar de que en el programa de correo del móvil tenía correctamente introducidos el usuario y la clave y el servidor, obviamente, se presentaba diciendo que soportaba este método.

[osus@servidor ~]# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 servidor ESMTP
ehlo
250-servidor
250-AUTH LOGIN CRAM-MD5 PLAIN
250-PIPELINING
250 8BITMIME

Investigando un poco averigué que para habilitar la autentificación CRAM-MD5 en Qmail, el MTA de correo que utilizo, además de instalar el parche qmail-smtp-auth es necesario sustituir el original checkpasswd  por cmd5checkpw. Con este argumento caí en la cuenta de que nunca jamás he tenido esta autentificación funcionando en mis servidores 😀 , el servidor indicaba que sí pero la realidad era que no 😛 . Ocho años utilizando Qmail y ahora me doy cuenta de que no funcionaba.

No quería complicarme mucho así que, si hasta ahora no la tenía, tampoco me importaba seguir así. Pensé entonces que, si en vez de responder a los clientes que soportaba CRAM-MD5, no se lo decía, el cliente del E51 escogería el AUTH LOGIN de toda la vida y funcionaría. ¿Cómo hacer esto? Sencillo, Qmail es de código abierto y tenemos el código fuente a nuestra disposición.

Parcheando Qmail

En los fuentes de mi Qmail (con todos sus parches) busqué la cadena en cuestión:

grep  CRAM-MD5 *

Y ahí me apareció el objetivo de nuestro parche, qmail-smtpd.c.

Dicho y hecho, abrimos el archivo con vi y buscamos la cadena en cuestión (:/CRAM-MD5) llegando a:

out("rn250-AUTH LOGIN CRAM-MD5 PLAIN");

Esto va a ser sencillo. Modificamos esa/s línea/s de manera que quede/n:

out("rn250-AUTH LOGIN PLAIN");

De este modo, cuando el cliente pregunte le diremos que soportamos LOGIN y PLAIN, pero no CRAM-MD5. He explicado esto en plural porque es más que probable que tengas dos líneas iguales y no sólo una. Puedes dejar sólo una modificada como he indicado o modificar las dos.

Compilamos ahora con un simple make. Como sólo hemos  modificado este archivo únicamente se compilará este. Hacemos una copia de seguridad de nuestro /var/qmail/bin/qmail-smtpd (por si algo no va bien) y copiamos el nuevo a esta ruta dejando los permisos, owner y group igual que tenía el que había.

Si ahora pruebas a enviar un email desde el Nokia E51 comprobarás que funciona correctamente, el cliente del teléfono escoge otro de los sistemas. Utilizando de nuevo el sniffer verás que ahora escoge AUTH LOGIN como método de autentificación.

Ya puedo enviar correo desde mis servidores con el teléfono. Gracias a un sencillo ejercicio de escucha del tráfico de red hemos averiguado cual era el problema.

Conexiones VPN entre máquinas Linux y Windows con autentificación a través de servidor Radius y MySQL

Un día, harto de tener que abrir puertos y más puertos para cada cosa que quería hacer con el servidor Linux que tengo en casa, se me ocurrió hacer algo para poder conectarme directamente a ésa máquina y tener todos los servicios a mi disposición sin tener que crearlos uno a uno. La solución estaba clara, establecer una VPN contra ese servidor y automáticamente tendría acceso a todos los servicios disponibles. El problema principal a la hora de establecer esta red virtual era que tenía que ser sencilla, rápida de crear y, sobre todo, no necesitar software adicional ya que así podría utilizarla desde cualquier ordenador en cualquier localización. Es más, si me lo montaba bien tendría un sistema perfecto para dar soporte remoto a mis hermanas a través de VNC sin tener que abrirles esos puertos 😉 .

Por defecto todos los equipos Windows traen de serie un cliente VPN que puede contectarse a redes privadas virtuales, pero no de cualquier tipo, sólo PPTP. Podríamos haber escogido otro sistema basado en IPsec o incluso OpenVPN, pero necesitaríamos software adicional además de cerficados en ámbos extremos de la VPN, lo que no cumpliría los requisitos que nos habíamos impuesto. PPTP es un protocolo desarrollado por Microsoft (por eso viene de serie con Windows) y, gracias a eso, ha tardado mucho en haber un cliente (y más aún un servidor) que funcione bajo Linux. No es el más seguro de los protocolos de VPN 😛 pero dejémoslo en que cumple sus funciones.

La idea es, por tanto, montar un servidor PPTP bajo Linux. Una vez tengamos el servidor veremos como conectar tanto desde Linux como desde Windows. Aunque la idea de este artículo parte de un entorno doméstico es completamente aplicable a pequeñas empresas que necesiten dar acceso remoto a sus empleados sin complicarles la vida ni realizar grandes desembolsos en routers dedicados o en un servidor Windows (entendiendo esto como una máquina con algún Windows Server 😉 .

Servidor PPTP bajo Linux

Vamos a enrevesar un poco más nuestro servidor. Para da más versatilidad haremos que la autentificación de los usuarios se realice a través de un servidor Radius que posteriormente podemos utilizar para autenticar cualquier otro servicio que se nos ocurra (ftp, email, hotspot inalámbrico…).

Creo que aún es muy sencillo, vamos a complicarlo más aún. El servidor Radius autenticará, así mismo, contra una base de datos MySQL, con lo que tendremos un sistema muy facil de administrar sin tener que estar tocando archivos de texto para crear usuarios nuevos. El escenario es, por tanto PPTP+Radius+MySQL.

Como software vamos a utilizar:

  • Servidor Linux, Centos 5.2 en mi caso.
  • PopTop, servidor PPTP bajo Linux.
  • PPTP Client, cliente PPTP bajo Linux.
  • FreeRadius como servidor Radius.
  • Radiusclient como cliente Radius, para que PopTop pueda consultar el servidor Radius.

Para instalar el software, en mi caso, nada más sencillo. Primero instalamos el repositorio yum de PopTop:

rpm -Uvh http://poptop.sourceforge.net/yum/stable/fc7/pptp-release-current.noarch.rpm

Y ya podemos instalar todo el software:

yum --enablerepo=poptop-stable install freeradius freeradius-mysql radiusclient pptp pptpd

Creo que con eso sería suficiente y tendríamos todo lo necesario. Asumimos, por supuesto, que ya tienes MySQL instalado.

Antiguamente era más complicado instalar PopTop ya que había que parchear el kernel, pero hoy en día, si tu núcleo es superior a 2.6.15 (si no lo es, ¿a qué esperas para actualizarlo?), no es necesario este paso. De todos modos, si tuvieses que hacerlo, es totalmente seguro, yo mismo lo hice durante mucho tiempo. En la web de PopTop tienes las instrucciones para hacerlo.

Suponiendo que hemos instalado correctamente todo el software sin ningún problema, ya sólo nos queda configurar todos y cada uno de los pasos que conforman nuestro servidor VPN.

Configurando PopTop

Lo primero que debes hacer es decidir que direccionamiento utilizarás en tu VPN. En mi caso tengo uno independiente de todo lo demás (192.168.3.0), así puedo gestionarlo a mi antojo, permitiendo o denegando lo que se me ocurra de una manera sencilla. A continuación indico los archivos de configuración a toquetear y como tengo los míos.

/etc/pptpd.conf

[osus@servidor ~]# cat /etc/pptpd.conf
option /etc/ppp/options.pptpd
localip 192.168.3.1-5
remoteip 192.168.3.6-10

El parámetro localip tendrá las IP’s que utilizará tu servidor como locales cada vez que reciba una conexión mientras que en remoteip indicarás las que va a dar dinámicamente a los clientes. Tendrás que poner un rango lo suficientemente amplio como para cubrir las posibles conexiones simultáneas que puedas tener.

/etc/ppp/options.pptpd

[osus@servidor ~]# cat /etc/ppp/options.pptpd
name pptpd
refuse-pap
refuse-chap
refuse-mschap
require-mschap-v2
require-mppe-128
proxyarp
lock
nobsdcomp
novj
novjccomp
nologfd
plugin radius.so

Sería recomendable que consultases las páginas man para saber qué hace cada parámetro.

Como véis, al final de todo indicamos al servidor PPTP que utilice el pluggin para Radius, así que configuremos todo lo relativo a Radius.

Configurando FreeRadius

/etc/raddb/clients.conf

[osus@servidor ~]# cat /etc/raddb/clients.conf
client 127.0.0.1 {
        secret          = TUCLAVESECRETA
        shortname       = localhost
}

Debemos indicar, para cada servidor Radius que tenemos, una clave de conexión y el nombre del mismo. La clave deberán utilizarla los clientes radius para consultar al servidor. Lo normal en un entorno como el nuestro es que el servidor Radius sea el mismo donde reside el servidor VPN.

El siguiente archivo es el más importante ya que se indica a FreeRadius que autentifique las peticiones contra un servidor MySQL. Como es muy largo pondré solamente las partes relevantes:

/etc/raddb/radiusd.conf

mschap {
 authtype = MS-CHAP
 use_mppe = yes
 require_strong = yes
}

authorize {
        preprocess
        mschap
        suffix
        eap
        sql
}

authenticate {
        Auth-Type MS-CHAP {
                mschap
        }
        eap
}

preacct {
        preprocess
        suffix
        files
}

accounting {
        detail
        acct_unique
        sql
}

session {
       sql
}

En /etc/raddb/sql.conf  debes configurar correctamente el acceso a tu base de datos MySQL, servidor, usuario, clave y base de datos que veremos un poco más adelante.

Configurando el cliente Radius

/etc/radiusclient/servers

[osus@servidor ~]# cat /etc/radiusclient/servers
localhost       TUCLAVESECRETA

Donde la clave es la misma que pusiste en /etc/raddb/clients.conf y localhost la dirección de tu servidor si es distinto al que tiene el pptpd.

En /etc/radiusclient debes tener el archivo dictionary.microsoft. Creo recordar que tuve algunos problemas con él, por si acaso dejo el que tengo ahora mismo que no es el que venía por defecto.

En /etc/radiusclient/radiusclient.conf asegurate que tienes los siguientes parámetros apuntando a la IP de tu servidor Radius si no es el mismo donde reside el servidor VPN:

authserver      localhost
acctserver      localhost

Si son distintos a localhost, no te olvides de configurarlos aquí.

Creando la base de datos MySQL

Llegamos al último paso.

Crea una nueva base de datos (create database radius) y un usuario con permisos sobre ella. Recuerda configurar ahora /etc/raddb/sql.conf  con estos datos.

Ahora crea la estructura de la base de datos. Con el paquete FreeRadius viene la estructura que necesitas.

mysql radius < /usr/share/doc/freeradius-1.1.3/examples/mysql.sql

El paso que viene a continuación siempre lo ignoran cuando alguien explica cómo configurar FreeRadius contra MySQL y creedme que no es nada intuitivo.

¿Cómo se rellenan las tablas de autentificación?

Buena pregunta Manel 😛 .

  • Tabla radcheck: mantiene las cuentas de usuario con los siguientes campos:
    • UserName: nombre de usuario.
    • Attribute: Password (literalmente, no la clave del usuario sino la palabra Password).
    • op: == (dos signos de igual).
    • Value: clave del usuario.
  • Table radreply: contiene parámetros de inicialización de los clientes que se conectan. Aquí yo configuro las IP’s que quiero dar a determinados clientes por cuestiones de comodidad.  Además indico que sólo voy a permitir una conexión simultánea con el mismo usuario.
    • UserName: nombre de usuario que estás configurando (según lo introducido en la tabla radcheck).
    • Attribute: la palabra Framed-IP-Address ó Simultaneous-Use, según indiques la IP a asignar a ese usuario o el número máximo de sesiones con el mismo nombre.
    • op: = (un sólo signo igual)
    • Value: 192.168.3.99 (la IP que quieras) ó el número máximo de conexiones simultáneas con el mismo usuario.
  • Table usergroup: agrupa los usuarios en grupos.
    • UserName: nombre de usuario
    • GroupName: nombre de grupo.

La tabla radact contiene el log de actividad del servidor, sesiones iniciadas, duración, etc.

Últimos pasos

Recuerda que debes abrir en tu router y/o firewall el puerto 1723 para que permita conexiones entrantes ya que es el usado por el protocolo PPTP.

Aunque como veremos más adelante podemos auditar las conexiones al servidor Radius (y por ende al servidor VPN) puede ser interesante tener un mecanismo de aviso de que un cliente se ha conectado. Una forma de hacerlo es consultando las interfaces de red disponibles en el servidor (ifconfig), habrá tantos pppX como usuarios activos. Pero hay otro método que te permite recibir, por ejemplo por email, un aviso cada vez que un usuario conecta o desconecta.

Cada vez que se levanta una interfaz ppp se ejecuta el script /etc/ppp/ip-up.local con todos los parámetros relativos a esa conexión, ip remota, local, interfaz… Igualmente cuando se desconecta se lanza /etc/ppp/ip-down.local. Solo debemos adaptar este script a nuestras necesidades. Estos scripts reciben todos los parámetros necesarios para identificar al usuario. Haríamos algo así, por ejemplo para ip-up.local.

#!/bin/sh
if [ "$5" == "192.168.3.10" ]
then
        cliente="pepito"
fi
echo  "Conexion VPN
Interfaz: $1
VPN Local: $4
VPN Remota: $5
IP Remota: $6
1: $1
2: $2
3: $3
4: $4
5: $5
6: $6
"  | mail -s "Conexion VPN - $cliente" [email protected]

De este modo recibirías un email cada vez que un usuario  levanta un tunel VPN con tu servidor y sabrías qué ip tiene el usuario y, si le has otorgado una IP fija en la configuración de Radius, sabrás qué usuario es, en este caso “pepito“.

Con ip-down.local harías un script semejante sólo que en vez de Conexión VPN en el asunto del email pondríamos Desconexión VPN. Los parámetros son exactamente los mismos.

Estos scripts podemos aprovecharlos también para crear/modificar/eliminar determinadas rutas en función de los túneles creados.

El propio paquete pptpd te habrá instalado el script de inicio necesario, en mi caso /etc/init.d/pptpd. Sólo debo añadirlo a la secuencia de arranque del runlevel de mi servidor y automáticamente estará siempre disponible el servicio.

En teoría, todas las vpn’s que se hagan contra el servidor tienen enrutamiento entre sí, es decir, podrías llegar desde un cliente a otro pasando por el servidor sin toquetear nada más. Digo en teoría porque esa es la función del parámetro proxyarp que configuramos hace un rato. Puede que este enrutamiento no sea suficiente y que necesites que los clientes VPN puedan acceder a otras subredes de tu infraestructura. Puedes hacerlo como quieras, incluso configurar un bridge, pero para estas cosas nada mejor que iptables.

Supongamos un escenario en el que tenemos una lan local de la que forma parte nuestro servidor (direccionamiento 192.168.0.0) y la nueva red que creamos para las VPN’s (direccionamiento 192.168.3.0). Para permitir el enrutamiento completo entre las dos redes haríamos algo como:

#!/bin/sh
echo 1 >/proc/sys/net/ipv4/ip_forward
LAN="192.168.0.0/16"
VPN2="192.168.3.0/24"
iptables -A FORWARD -s $LAN -d $VPN2 -j ACCEPT
iptables -A FORWARD -s $VPN2 -d $LAN -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -d $VPN2 -j MASQUERADE

Sencillo. Si sólo quisiésemos que ciertos usuarios accediesen a la red local modificaríamos la variable VPN2 por la IP que radius le da al usuario.

FreeRadius desde un  entorno web, dialup_admin

Vale, es cierto, es un auténtico coñazo gestionar FreeRadius y todos sus parámetros, así que, que mejor que un sencillo entorno web para la gestión de usuarios y visualización de actividad y log del sistema. Esta utilidad es dialup_admin. No me detendré en su instalación ya que creo que se sale fuera de este artículo y, además, es una sencilla aplicación web sin mucha dificultad.

 radiusweb.gif

Con esta herramienta será mucho más sencillo crear usuarios y sus propiedades y hacer un seguimiento de los que están conectados, periodos de conexión que han tenido, etc.

Estableciendo la VPN desde Windows

Sencillísimo. Desde Conexiones de red, se crea una conexión nueva, escoges Conectarse a la red de mi lugar de trabajo y prácticamente sólo queda introducir el host o IP de tu sevidor VPN y  conectarse. Recomiendo desmarcar la opción Usar puerta de enlace predeterminada en la red remota en las propiedades de esta nueva conexión, Funciones de red, Protocolo TCP/IP, Opciones avanzadas, de otro modo todo el trafico de Internet normal lo harás a través de la VPN.

Si todo va bien conectarás a tu servidor y tendrás acceso al mismo como si estuvieses en tu propia red local.

Estableciendo la VPN desde Linux

Desde Linux es un pelín más complicado ya que, como casi siempre, hay que hacer la configuración a mano. Existe una utilidad gráfica que permite crear las conexiones de un modo similar a Windows, pero prefiero explicar cómo hacerlo desde la consola por si tu máquina no tiene entorno gráfico.

[osus@servidor ~]# cat /etc/ppp/options.pptp
lock
noauth
refuse-eap
refuse-chap
refuse-mschap
nobsdcomp
nodeflate

Ahora indicamos el usuario y la contraseña que se utilizará para conectar. IdentificadorRed será el nombre que le demos a la conexión, puede ser lo que quieras.

[osus@servidor ~]# cat /etc/ppp/chap-secrets
usuario IdentificadorRed clave *

Ahora creamos la configuración para la conexión que vas a crear con el IdentificadorRed que comentamos antes. En TUIP debes poner el host o ip de tu servidor VPN.

[osus@servidor ~]# cat /etc/ppp/peers/IdentificadorRed
remotename IdentificadorRed
linkname IdentificadorRed
ipparam IdentificadorRed
pty "pptp TUIP --nolaunchpppd "
name usuario
require-mppe
require-mschap-v2
refuse-eap
refuse-pap
refuse-chap
refuse-mschap
#demand
holdoff 5
persist
maxfail 0
ipcp-accept-remote
ipcp-accept-local
noauth
192.168.3.1:192.168.3.254

Aquí hay dos opciones interesantes:

  • persist: vuelve a crear el tunel automáticamente si se cortase por alguna razón de manera que permanece siempre activo.
  • demand: crea automáticamente el tunel cuando se accede a la IP del servidor o a alguna otra que esté enrutada a través de este tunel,  mientras no se necesite permanece inactivo. Obviamente no debe estar en modo persist, de otro modo estará siempre activa.

Finalmente creamos un sencillo script de arranque para poder lanzarla automáticamente o simplemente para no tener que recordar los parámetros.

[osus@servidor ~]# cat /etc/init.d/IdentificadorRed
#!/bin/sh
  case "$1" in
      start)
            echo -n "Iniciando VPN IdentificadorRed "
            echo
            touch /var/lock/subsys/pptpd
            /usr/sbin/pppd call IdentificadorRed logfd 1 updetach &
            ;;
      stop)
            echo -n "Parando VPN IdentificadorRed: "
            echo
            kill -TERM `head -n 1  /var/run/ppp-IdentificadorRed.pid`
            ;;
      *)
            echo "Usage: $0 {start|stop}"
            exit 1
  esac
exit 0

Y eso es todo amigos. Me ha costado bastante más de lo previsto escribir este artículo ya que a medida que lo iba redactando me iba saltando nuevos recuerdos sobre detalles que se deberían nombrar.

Tened en cuenta que mi experiencia con PPTP se remonta cinco años atrás con lo que puede ser que algún detalle haya cambiado los últimos años. Después de aquel primer servidor VPN el año pasado migramos el sistema operativo a Centos5 y al configurar de nuevo el servidor VPN dejamos prácticamente todos los parámetros como estaban. No creo que tengáis ningún problema a la hora de solucionar algún pequeño detalle que pueda surgir.

Haciendo streaming de vídeos a móviles

Antes de comenzar este artículo, y para que nadie se haga ilusiones, aclararé que no es posible hacer streaming por tu cuenta a través de las conexiones de datos de las operadoras tradicionales. Tienen esos puertos bloqueados para que sólo ellas o quien ellas quieran pueda ofrecer este servicio, de ahí la exclusividad de los canales de televisión en el móvil que tanto promocionan y que no son más que servicios de streaming. Si cualquiera pudiese hacerlo se les terminaría el chollo, aunque siempre queda el asunto del tráfico de datos, a ti te lo cobrarían íntegramente mientras que en sus canales no, ya pagas la suscripción mensual.

No lo he probado con las nuevas operadoras virtuales ni con Yoigo, aunque es posible que con éstas sí que funcione. De todos modos, con la proliferación de terminales con wifi se abre un nuevo mundo de posibilidades sin límite, entre ellos el streaming.

En esta ocasión utilizaremos un servidor Linux (Centos5.2) y como servidor de streaming, Darwin Stream Server.

Darwin es la versión Open Source del QuickTime Streaming Server de Apple, sí, habéis oído bien, Apple, la del iPod y la del iPhone, y permite enviar streamings a través de los protocolos estandar RTP y RTSP. Pero lo que hace más interesante a Darwin sobre otros servidores de streaming es que maneja perfectamente archivos MPEG4 y 3gpp, algo imprescindible si queremos enviarlos a dispositivos móviles

Puedes descargar aquí el archivo de instalación para Linux. Una vez lo descomprimes, la instalación es extramadamente sencilla, sólo debes ejecutar el archivo Install y un asistente te guiará. Cuando haya terminado tendrás tu servidor de streaming funcionando. Vamos a probarlo

La instalación deja todos los archivos de configuración en /etc/streaming y algunos vídeos de ejemplo en /usr/local/movies, ruta por defecto para los archivos. Puedes modificar esta ruta en el archivo /etc/streaming/streamingserver.xml buscando el parámetro de nombre “movie_folder”. Lo dejaremos como viene por defecto.

Si no has iniciado todavía tu servidor, hazlo con el siguiente comando:

/usr/bin/perl /usr/local/sbin/streamingadminserver.pl

Si todo ha ido bien puedes probar los vídeos de ejemplo. Abre tu Quicktime Player y abre la url:

rtsp://tuip/sample_h264_300kbit.mp4

Si has seguido bien los pasos verás un vídeo de muestra de QuickTime. Tu servidor de streaming ya está funcionando.

Prueba ahora alguno de tus vídeos 3gpp. Copialos a la ruta correspondiente de tu servidor, /usr/local/movies y llámalos con la url anterior cambiando el nombre del archivo:

rtsp://tuip/nombre.3gp

Deberías ver tu vídeo en el Quicktime Player. Prueba ahora desde tu móvil. Abre el Real Player (o el software que traiga para visualizar vídeos por streaming) e introduce esa dirección.

Cierto, no funciona 😛 . No podía ser tan simple 🙂 …

Preparando los archivos para visualizar en el móvil

Para visualizar los vídeos 3gpp y mp4 en el móvil a través de Darwin han de someterse a un proceso denominado hint.

Necesitaremos una nueva herramienta que haga este proceso, en este caso será el paquete gpac. Descargamos e instalamos el software y tendremos el programa que necesitamos, MP4Box. Ahora simplemente debemos ejecutar la siguiente instrucción en todos nuestros vídeos:

/usr/local/bin/MP4Box -3gp -mtu 1450 -hint video.3gpp

Ya está. Si vuelves a intentar ver el vídeo en tu móvil comprobarás como ahora sí que se visualiza.

Creando gráficas del servidor Darwin en Cacti

Hace algunos meses os conté como monitorizar tu servidor con Cacti. Vamos pues a añadir gráficas de nuestro servidor de streaming para poder monitorizar la actividad del mismo.

Graficas Cacti Darwin Stream Server

Lo primero que debemos hacer será habilitar el acceso remoto desde la IP de nuestro monitor Cacti a la consola de administración del servidor, el “remote admin”. Para ello editamos /etc/streaming/streamingserver.xml, localizamos el módulo QTSSAdminModule y en el parámetro IPAccessList introducimos la IP correspondiente, ponemos a false LocalAccessOnlyenable_remote_admin y a true. Habrá que reiniciar el servidor. Quedaría tal que así.

<MODULE NAME="QTSSAdminModule" >
<PREF NAME="IPAccessList" >XXX.XXX.XXX.XXX</PREF>
<PREF NAME="Authenticate" TYPE="Bool16" >true</PREF>
<PREF NAME="LocalAccessOnly" TYPE="Bool16" >false</PREF>
<PREF NAME="RequestTimeIntervalMilli" TYPE="UInt32" >50</PREF>
<PREF NAME="enable_remote_admin" TYPE="Bool16" >true</PREF>
<PREF NAME="AdministratorGroup" >admin</PREF>
</MODULE>

Ya tenemos el servidor de streaming listo, ahora tocaremos en el servidor Cacti. Aquí está todo explicado, pero lo repasaremos. Creo recordar que no me funcionaban tal cual las plantillas, así que os dejo las que tengo en funcionamiento. En el archivo encontrarás dos scripts python y dos plantillas. Los scripts debes copiarlos a la carpeta “scripts” de tu instalación Cacti. Los “templates” debes importarlos desde “Import templates” de la inferfaz web.

Con esto  ya sólo tendremos que añadir las gráficas correspondientes a la máquina que vamos a monitorizar, DSS Current Bandwidth  y DSS – Current Connection Count. Nos solicitará rellenar algunos datos:

  • Servidor: ip de la máquina con el Darwin.
  • Puerto: si no has cambiado nada, el 554.
  • Usuario: usuario de administración del servidor Darwin que hayas configurado al instalarlo.
  • Clave: la contraseña del usuario anterior.

Tendremos dos gráficas, número de conexiones y ancho de banda consumido.

No me hago responsable del gasto  en tráfico de datos con tu operadora que te suponga probar lo expuesto en este artículo 😛 . Otro día veremos como generar vídeos 3gpp bajo demanda a partir de casi cualquier otro formato.

[ERROR] /usr/libexec/mysqld: Incorrect key file for table

O cómo cargarte un servidor de bases de datos por tocar un parámetro de configuracion 😛 .

Hace un par de semanas tuvimos una incidencia con el MySQL de uno de nuestros servidores. Algunas tablas comenzaron a devolver errores extraños referentes a /tmp y al archivo de índices de algunas tablas. Lo siento, no tengo el mensaje concreto. El caso es que la base de datos estaba inutilizada. Tras investigar un poco por Google comencé a probar algunos cambios de parametrización en /etc/my.cnf. Me pareció muy extraño puesto que la configuración que utilizamos lleva muchos años dando buen resultado, de hecho no es ni por asomo hoy por hoy el momento de mayor actividad que hemos tenido.

Tras hacer un poco el tonto caí en la cuenta, ríanse de mi, de que la máquina se había quedado sin espacio en disco 😛 . Era esto lo que provocaba que no se pudieran regenerar los índices de las tablas y demás errores asociados.

Una vez solucionado el error todo comenzó a funcionar con normalidad y parecía que todo iba bien. Craso error.

Hace unos días nos dimos cuenta que el log de MySQL se estaba llenando de mensajes de error como éste sin aparente explicación:

080902  0:51:42 [ERROR] /usr/libexec/mysqld: Incorrect key file for table ‘./basededatos/tabla.MYI’; try to repair it

Lanzando un repair table tabla se solucionaba el error, pero era algo temporal, pronto volvía a aparecer, sino era con esa tabla era con otras. Incluso tablas de bases de datos sin apenas uso.

Googleando de nuevo, el primer resultado me dió la clave. , era uno de los parámetros que había modificado el día que me había quedado sin espacio en disco y no lo había restaurado.

key_cache_block_size=1024

Había leído que aumentando este parámetro se solucionaría el error, así que ni corto ni perezoso lo puse a 10024 y así se quedó aunque el problema no se solucionó 😛 .

Al parecer este parámetro debe tener un valor que sea potencia 2 de 512, sino aparecerán los errores aleatorios en los archivos de índices que nos estaban ocurriendo a nosotros.

Restauramos el valor a 1024, reiniciamos el servidor de bases de datos y, por arte de magia, los problemas desaparecieron.

Como conclusión sacamos que el parámetro más absurdo puede hacernos perder días de investigación y problemas, en mi caso a las cuatro de la madrugada 😛 . Esta vez tuvimos suerte. Para la próxima, pensaré en apuntar cada cambio que haga, por si acaso…

Alertas y acciones en tu servidor a través de SMS

Tal y como he prometido sigo en mi empeño de, como mínimo, dos artículos por semana, así que ahí va el segundo de esta 🙂 .

Hoy veremos cómo aprovechar un móvil que ya no utilicemos para recibir alertas y ejecutar acciones remotamente en nuestros servidores.

Monté un sistema de este tipo por primera vez allá por 2002 y desde entonces lo hago siempre que tengo nuevos servidores que manejar, sobre todo en aquellos que hacen de monitor (Cacti y/o Nagios) y backup.

La idea inicial era recibir alertas de problemas en el servidor sin tener que estar delante de la máquina. En aquél momento no había más opciones que el email, pero si no estabas delante de la máquina no recibirías nada. Posteriormente aparecieron las primeras pasarelas SMS por HTTP (con una llamada HTTP y los parámetros adecuados, envías un SMS), pero aún hoy tienen un inconveniente, si tu máquina se queda sin conectividad (problemas de red, router, ataques DOS..) nunca recibirás alertas y nunca te enterarás de que algo ocurre.

Se me ocurrió entonces que podría utilizar un Nokia 3210 que acababa de jubilar ya que además me había comprado el cable para conectarlo al ordenador por el puerto serie, pero me faltaba lo más importante, el software. Me puse a investigar y encontré lo que buscaba, era lógico, no iba a ser yo el primero al que se le ocurriese esa idea 😉 . La solución se llamaba Gnokii, un software que se comenzó a desarrollar ni más ni menos que a finales de 1998. Por lo que me consta, dejó de actualizarse allá por 2003, aunque probablemente siga funcionando perfectamente en la mayoría de casos, sin embargo hace ya tiempo que apareció un fork de Gnokii, Gammu, que es el que utilizo desde hace ya unos años y sobre el que nos centraremos en este artículo. Además he cambiado la forma de conectar el terminal al ordenador, ya no es por el puerto serie sino por bluetooth.

Podríamos dividir el artículo en cuatro pasos:

  1. Conectando el móvil con el servidor.
  2. Enviando mensajes.
  3. Sistema de mensajería avanzado.
  4. Ejecutando acciones.

Conectando el móvil con el servidor

Comenzaremos por configurar el servidor. No voy a explicar detenidamente los pasos puesto que, aunque hoy en día son sencillos, se salen del alcance del artículo. De todos modos configurar el bluetooth hace cuatro o cinco años era bastante más complicado, hoy todas las distribuciones Linux tienen los paquetes preparados.

Lo primero, obviamente, será tener una antena bluetooth en el servidor, con una USB será suficiente y funciona perfectamente, yo lo tengo así desde hace años.

El siguiente paso será instalar los paquetes necesarios para tener conectividad bluetooth, normalmente son dos, bluez-libs y bluez-utils. Si tu sistema operativo es medianamente moderno probablemente los tengas ya instalados, sino estarán en cualquier repositorio.

Como supondremos que estamos trabajando con servidores, lo más probable es que no tengas entorno gráfico, así que lo configuraremos todo a mano. Si tuvieses entorno gráfico hay paquetes gnome para hacer el emparejamiento automáticamente.

En mi Centos5.0 los archivos de configuración se encuentran en /etc/bluetooth, en el tuyo pueden variar. Tendremos que configurar tres archivos:

  • hcid.conf: configura los servicios que ofrecerá la antena bluetooth.
  • pin: pin de la SIM del móvil para no tener que introducirlo manualmente. Tambien puedes configurar la SIM para que no solicite pin.
  • rfcomm.conf: configura los canales de comunicación.

La verdad es que no recuerdo mucho las distintas opciones de hcid.conf, así que me limitare a copiar mi configuración para que tengáis un punto de inicio.

options {
        autoinit yes;
        security user;
        pairing multi;
        passkey "123";//aqui el pin de acceso a la antena del servidor
}
device {
        name "%h-%d";
        class 0x120104;
        iscan enable; pscan enable;
        lm accept;
        lp rswitch,hold,sniff,park;
}

El archivo pin es muy sencillo, simplemente debes poner el pin de la SIM de tu teléfono.

Finalmente configuramos el canal de conexión con el móvil. Para ello debemos averiguar la dirección bluetooth de tu móvil, sería algo así como la dirección MAC, de hecho tiene una estructura semejante. Para ello, inicia el servicio bluetooth, o reinícialo si lo tienes ya iniciado,

/etc/init.d/bluetooth restart

y ejecuta

hcitool scan

Esto te devolverá la lista de dispositivos bluetooth a tu alcance con el nombre y la dirección que tienen. Si obtienes algo como Device is not available: No such device, tu antena no está iniciada, quizás no se ha detectado correctamente, consulta el log de inicio con dmesg a ver qué ocurre cuando conectas la antena al USB.

Supondremos que todo ha ido bien y tienes la lista de dispositivos. Busca el del móvil que estás utilizando y apunta la dirección, la necesitarás añadir al siguiente fichero, rfcomm.conf.

rfcomm0 {
        bind yes;
        device 00:0E:07:E7:12:27;//aqui la dirección bluetooth de tu movil
        channel 1;
        comment "linea bluetooth";
}

Si todo ha ido bien tenemos el sistema bluetooth preparado y enlazado con el móvil.

Enviando mensajes

Instalamos el paquete Gammu. De nuevo, lo más probable es que haya un binario para tu sistema en los repositorios habituales, sino siempre puedes descargar el fuente y compilarlo tu mismo. Configurarlo es muy sencillo. Para empezar tenemos un sólo archivo:

/etc/gammurc
[gammu]
port=/dev/rfcomm0
connection = at19200
logfile = /var/log/smsdlog
logformat = textall
startinfo = yes

En la primera línea indicamos el canal que vamos a utilizar, que es el que configuramos en el paso anterior y añadimos un log para tener información.

Comprobamos si todo va bien. Para eso preguntamos a nuestro teléfono la hora que tiene su sistema, por ejemplo:

gammu --getdatetime

Nos devolverá la hora del teléfono y tendremos todo preparado. Ya podemos probar a enviar mensajes.

echo "probando el envio de SMS" | gammu --sendsms TEXT NUMERODESTINO

Con este sencillo comando podemos configurar cualquier aplicación que necesite enviar alertas para que nos haga llegar un SMS. Así de fácil.

Sistema de mensajería avanzado

Una vez teníamos el sistema anterior montado caímos en la cuenta de que podríamos utilzarlo para hacer más cosas de las que habíamos pensado, sólo necesitábamos saber cómo 🙂 .

Una de las máquinas que utilizábamos de monitor se encontraba en la oficina, tras una línea ADSL. El router de esta línea tenía algún problema que hacía que bajo ciertas circustancias se colgase y había que reiniciarlo, con lo que había que ir personalmente a la oficina para hacerlo. Se nos ocurrió entonces que, si ya teníamos un sistema de envío de SMS funcionando, ¿por qué no hacer que pueda recibir y ejecutar acciones en base a lo que reciba? Dicho y hecho. En vez de responder a POLI BULERIA 😉 , nuestro sistema respondería a ROUTER, aunque posteriormente fuimos ampliando las acciones, como REBOOT que permite hacer un reinicio limpio del servidor y todas las que posteriormente se te ocurran.

Investigando la ayuda de Gammu descubrimos que hay una forma de poner el sistema en modo demonio y que automáticamente recoja los SMS que lleguen al terminal y envíe los que se le encolen. Este procedimiento requiere modificar un poco el sistema, pero una vez configurado ganarás en robustez y efectividad.

La clave es la opción -smsd de gammu que lo pone automáticamente en modo demonio. Este servicio lo que hace es recoger los SMS que se reciban en el móvil y dejarlos como archivos de texto en un directorio específico mientras que coge los SMS que se quieren enviar y que previamente hemos dejado en otro directorio y los envía. Es decir, en vez de hacer los envíos directamente como veíamos antes, los dejas en archivos de texto y automáticamente el demonio los envia. Veamos primero la configuración necesaria. El archivo que maneja toda la configuración es /etc/smsdrc. De nuevo os copio mi configuración tal cual puesto que tiene mucho tiempo y no recuerdo los parámetros.

[gammu]
port=/dev/rfcomm0
connection = at19200
logfile = /var/log/smsdlog
startinfo = yes[smsd]
PIN = PINDETUSIM
logfile = /var/log/smsdlog
commtimeout = 1
sendtimeout = 10inboxpath = /var/spool/gammu/inbox/
outboxpath = /var/spool/gammu/outbox/
sentsmspath = /var/spool/gammu/sent/
errorsmspath = /var/spool/gammu/error/

No tiene mucha ciencia, el primer sector es igual que en el apartado anterior mientras que en el segundo configuramos el PIN de tu SIM y los directorios donde dejar y recoger los SMS tal como explicábamos antes.

Es importante puntializar el formato de los archivos de texto que dejaremos para enviar mensajes los que recibiremos.

  • Mensajes salientes: en outboxpath dejaremos los archivos con el nombre siguiento el patrón
    OUT<phone number>.txt,
    por ejemplo con el nombre OUT666777888.txt se enviaría al 666777888 el contenido del archivo de texto.
  • Mensajes entrantes: en inboxpath recibiremos los mensajes con el nombre del archivo siguiendo el patrón
    IN<date>_<time>_<serialno>_<phone number>_<sequence>.txt
    ,
    por ejemplo IN20021130_021531_00_+45409000931640979_00.txt. En este caso habríamos recibido el 30/11/2002 a las 02:15:31 un mensaje desde el +45409000931640979. Creo que sobran detalles adicionales.

Teniendo esto claro, podemos lanzar el demonio. Tan simple como

/usr/bin/gammu  --smsd FILES /etc/smsdrc &

Si algo va mal nos devolverá algún error, si no, tendremos nuestro sistema de envío y recepción funcionando. Para probarlo, comprueba la ruta inboxpath, si tenías SMS’s en tu teléfono empezarás a ver como entran los archivos de texto. También puedes probar a crear uno en el outboxpath siguiendo el patrón descrito, verás como lo recibes en el otro teléfono.

Hasta aquí tenemos el nuevo sistema de envío y recepción terminado. Deberás modificar tu sistema de alertas para crear los archivos de texto tal como hemos explicado y lo tendrías automáticamente funcionando. También puedes crear un sencillo script PHP que te permita enviar SMS de una manera cómoda, simplemente dejarás los mensajes en forma de archivos de texto en la cola del directorio y el demonio se encargará de enviarlos.

Ejecutando acciones remotas

Ahora deberemos hacer el script que compruebe los SMS recibidos y decida qué hacer con ellos. Os pondré un ejemplo básico para que tengáis una idea clara de cómo habría que hacerlo, es responsabilidad vuestra asegurar el acceso al sistema de acciones de manera que sólo determinados teléfonos puedan ejecutarlas. Yo, por comodidad, lo haré en PHP, pero podéis hacerlo como queráis, PERL, Python, bash, C,…

<?
$inbox="/var/spool/gammu/inbox/";
$outbox="/var/spool/gammu/outbox/";
$d = dir($inbox);
while (false !== ($entry = $d->read())) {
    if ($entry != "." && $entry != ".." && substr($entry, 0, 2)=="IN"){
        $contenido=file_get_contents($inbox.$entry)
        $palabras=explode(" ", trim($contenido));
        $mensaje=substr($entry, 2, strlen($entry));
        $datos=split("_", $mensaje);
        $fecha=$datos[0];
        $hora=$datos[1];
        $numero=$datos[3];
	//utiliza el numero para decidir si permites el reinicio desde ese cliente

	switch(trim(strtoupper($palabras[0]))){
            case 'ROUTER':
                system("/usr/bin/router.sh");
                $mensaje="El Router ha sido reiniciado";
                break;
            case 'REBOOT':
                system("reboot");
                $mensaje="El servidor se está reiniciando";
                break;
            default:
                $mensaje="La palabra clave enviada no se corresponde con ninguno de nuestros servicios.";
        }
        $serial=date("YmdHis");
        $arch=$outbox."OUTA_".$numero."_".$serial.".txt";
        $fp=fopen($arch, "w");
        fwrite($fp, $mensaje);
        fclose($fp);
        unlink($inbox.$entry);
    }
}
$d->close();
?>

El script es muy sencillo, buscamos los archivos del directorio INBOX, analizamos el nombre para ver el número de origen y separamos el contenido por palabras para decidir la acción. Fijáos que con este sistema es fácil crear acciones con parámetros, simplemente se añaden palabras y en el switch se opera como se necesite. En mi caso, cada acción recibida genera una respuesta en modo de SMS a enviar para que sepamos que la acción ha funcionado.

Y esto es todo amigos. Si has seguido bien las instrucciones tendrás un precioso sistema de envío y recepción de SMS desde tu servidor sin necesidad de tener conectividad a Internet. A través de Gammu puedes enviar también MMS, por si te apetece montar un sistema de seguridad que te envíe una captura de una webcam 😉 , por ejemplo. El límite está en tu imaginación y en tus necesidades.