Archivo de la categoría: Sistemas

Monitorizando aplicaciones web con rrdtool

Vamos con uno de esos artículos que le gustan a Álvaro basados en experiencias del trabajo.

En uno de los proyectos en los que estamos trabajando actualmente y del que ya os he hablado, teníamos un problema de rendimiento en una aplicación web, pero no teníamos localizado el punto donde se nos iba el tiempo. Incluso había momentos que la aplicación iba bien pero en otros era lamentable. Se nos ocurrió, entonces, hacer un sencillo sistema de monitorización de los distintos puntos sensibles que manejábamos y crear con ellos una serie de gráficas que nos permitiese hacer un seguimiento visual de los potenciales problemas.

Nuestra aplicación se basa en llamadas a distintos webservices a partir de las cuales se genera un XML al que se le aplican transformaciones XSL para obtener el código html que se muestra al usuario. Algunas de esas llamadas se guardan en la sesión del usuario, con lo que en sucesivos accesos a la aplicación el rendimiento es infinitamente mejor, así que necesitábamos que el control se hiciese con sesiones limpias para que no se nos desvirtuasen las gráficas.

Nuestro sistema consiste en generar por un lado, en la aplicación web, una serie de datos estadísticos que nos ayuden a buscar los puntos conflictivos. Estos datos se insertarán periódicamente en una base de datos rrd (round robin database) que finalmente utilizaremos para generar las gráficas.

Preparando la aplicación web

El primer paso es, entonces, modificar el código de la aplicación para que vaya generando los tiempos de respuesta en los distintos puntos que queremos controlar. Para facilitar las cosas decidimos que cada vez que se pase un parámetro predefinido en la url, en vez de mostrar la página html correspondiente mostraremos las estadísticas que después controlaremos. En nuestro caso decidimos que la url de estadísticas fuese:

http://tudominio.com/index.php?stats

Es decir, añadiendo el parámetro stats a cualquiera de las urls del site obtendremos los tiempos de respuesta que necesitamos.

Tenemos una función que genera el tiempo exacto del servidor cada vez que se llama, así que la utilizaremos después de ejecutar cada uno de los webservices y guardaremos esos datos. En mi caso tengo que obtener ocho puntos de control además del tiempo de inicio y el tiempo final, algo así:

function getTime() {
 $mtime2 = explode(" ", microtime());
 $endtime = $mtime2[0] + $mtime2[1];
 return $endtime;
}
$isStats=isset($_GET['stats']);
if($isStats)
    $time_inicio=getTime();
....
if($isStats)
    $time_ws1=getTime();
.....
if($isStats)
    $time_ws2=getTime();
....
if($isStats)
    $time_ws3=getTime();
....
if($isStats)
    $time_ws4=getTime();
....
if($isStats)
    $time_ws5=getTime();
....
if($isStats)
    $time_ws6=getTime();
....
if($isStats)
    $time_ws7=getTime();
....
if($isStats)
    $time_ws8=getTime();
....
if($isStats)
    $time_final=getTime();
......

Finalmente devolvemos una línea con todos los valores intermedios, es decir, para cada indicador sería el valor del tiempo de ese indicador menos el del anterior, esto nos dará el valor real del tiempo que ha pasado entre el indicador anterior y el actual. Para facilitar la posterior integración de estos valores con rrdtool lo que haremos será generarlos ya en el formato adecuado, separando cada valor con “:”.

if(isset($_GET['stats'])){
	echo number_format($time_ws1-$time_inicio, 7).":".
			number_format($time_ws2-$time_ws1, 7).":".
			number_format($time_ws3-$time_ws2, 7).":".
			number_format($time_ws4-$time_ws3, 7).":".
			number_format($time_ws5-$time_ws4, 7).":".
			number_format($time_ws6-$time_ws5, 7).":".
			number_format($time_ws7-$time_ws6, 7).":".
			number_format($time_ws8-$time_ws7, 7).":".
			number_format($time_final-$time_inicio, 7);
}

Si ahora llamamos a nuestra url obtendremos los valores de nuestros indicadores. El último valor nos devolverá el tiempo completo de ejecución del script entre los valores $time_inicio y $time final.

0.0281749:0.5443010:0.3132501:2.9015441:0.0000241:0.6517198:5.5171580:0.0000379:10.0677590

Vemos cómo esta llamada ha tardado diez segundos, de los cuales 5,5 corresponden al indicador ws7.

Tenemos el primer paso preparado, vamos ahora a crear la base de datos donde almacenaremos toda la información estadística de la url. Tendremos una tarea que se ejecutará cada minuto y que recuperará los valores en ese momento de los identificadores que se han definido.

Creando la base de datos

Para guardar la información he escogido rrd, el mismo que utilizan aplicaciones como mrtg o Cacti y el utilizado en la mayoría de sistemas de monitorización. Según reza en su propia web, es el sistema estándar para gráficas y logs de series de datos temporales.

Para que todo vaya bien nuestra base de datos rrd debe tener la misma cantidad de fuentes de datos que el sistema de estadísticas, nueve en este caso, los ocho puntos de control más el total. Además tenemos que pensar qué graficas vamos a crear. Yo quiero cuatro: última hora, últimas seis horas, últimas 24 horas y última semana. No necesito hacer un sistema con histórico avanzado, lo que quiero en este caso es controlar el rendimiento, así que no necesitaré gráficas mensuales ni anuales, pero se podrían generar de igual modo.

Para crear nuestro archivo rrd ejecutamos el comando siguiente:

rrdtool create estadisticas.rrd --step 60
DS:ws1:GAUGE:120:0:U
DS:ws2:GAUGE:120:0:U
DS:ws3:GAUGE:120:0:U
DS:ws4:GAUGE:120:0:U
DS:ws5:GAUGE:120:0:U
DS:ws6:GAUGE:120:0:U
DS:ws7:GAUGE:120:0:U
DS:ws8:GAUGE:120:0:U
DS:total:GAUGE:120:0:U 

RRA:AVERAGE:0.5:1:60
RRA:AVERAGE:0.5:1:360
RRA:AVERAGE:0.5:5:144
RRA:AVERAGE:0.5:10:202
RRA:MAX:0.5:1:60
RRA:MAX:0.5:1:360
RRA:MAX:0.5:5:144
RRA:MAX:0.5:10:202
RRA:MIN:0.5:1:60
RRA:MIN:0.5:1:360
RRA:MIN:0.5:5:144
RRA:MIN:0.5:10:202

Vamos a explicarlo un poco.

  • –step=60: indica que los datos los actualizaremos cada 60 segundos. Nuestro sistema guardará estadísticas cada minuto.
  • DS:ws1:GAUGE:120:0:U: Fuentes de datos, hay una línea de este tipo para cada valor que vamos a mantener.
    1. wsX es el nombre que tendrá esa fuente.
    2. GAUGE es el tipo de fuente de datos, puedes consultar la documentación completa para tener más idea.
    3. 120 es el parámetro heartbeat, tiempo máximo entre dos actualizaciones antes de que se asuma que es desconocido. He puesto el doble de step, si pasado ese tiempo no hay una actualización es que se ha perdido por cualquier razón.
    4. Los últimos datos son los valores mínimo y máximo que habrá en la fuente de datos. En nuestro caso el mínimo será cero y el máximo no lo sabemos, le ponemos U de unknown.
  • RRA:AVERAGE:0.5:1:60: Round robin archives. El objetivo de una rrd es guardar datos en los rra que mantienen valores y estadísticas para cada una de las fuentes de datos definidas.
    1. AVERAGE/MAX/MIN/LAST: la función utilizada para consolidar los datos. Como su propio nombre indica son la media, máximo, mínimo y el último valor.
    2. 0.5; el xff (xfiles factor). De 0 a 1, indica el número de datos UNKNOWN que se permiten por intervalo de consolidación.
    3. 1: define el número de datos utilizados para crear un intervalo de consolidación.
    4. 60: Cuantos datos se mantienen en cada RRA. Esto depende de la gráfica que vamos a generar y se relaciona con el dato anterior.

La última parte es la más complicada de comprender. Intentaré explicarlo lo mejor que pueda para que se entienda. En nuestro caso tomamos un dato cada 60 segundos y queremos tener la gráfica de la última hora (60 segundos x 60 minutos, 3600 segundos). Necesitamos, por tanto, 60 puntos de control (3600/60 segundos de intervalo), utilizaremos cada uno de los datos que se toman. Esto es lo que indica la creación RRA:AVERAGE:0.5:1:60, queremos utilizar 60 puntos de control tomados cada 60 segundos. Este es bastante fácil, veamos el siguiente.

RRA:MIN:0.5:1:360

Ahora estamos guardando 360 puntos generados cada 60 segundos, es decir, cubrimos 60×360=21600 segundos=6 horas. Era sencillo.

RRA:AVERAGE:0.5:5:144

En la siguiente gráfica queremos obtener datos de las últimas doce horas (12 horas x 60 minutos x 60 segundos, 43200 segundos). Ahora ya no necesitamos saber el comportamiento exacto en cada momento sino que necesitamos una visión global de lo que ha ocurrido durante esas doce horas, tomamos, por tanto, un punto de control cada cinco datos de registro, es decir, estamos manteniendo un dato cada 5×60 segundos, 300 segundos. Necesitaremos por tanto 43200/300=144 valores para obtener nuestra gráfica de las últimas 12 horas

RRA:MIN:0.5:10:202

La última gráfica es semanal, 7x24x60x60=604800 segundos. Acepto que con tener un valor cada 10 minutos será suficiente, tendré entonces un valor cada 10×300=3000 segundos. 604800/3000=~202.

Es algo difícil de entender al principio, pero una vez lo piensas, todo tiene sentido.

Guardando las estadísticas

Tenemos ya nuestra base de datos preparada, creemos ahora la tarea que guardará en este archivo rrd los datos estadísticos. En mi caso es una tarea PHP que lo hace todo muy sencillo.

tarea.php

$url="http://dominio.com/?stats";
$cont=file_get_contents($url);
system("rrdtool update estadisticas.rrd N:".$cont);

Añadimos la tarea al cron para que se ejecute cada 60 seguntos.

*/1 * * * * /usr/bin/php /path/to/tarea.php > /dev/null 2>&1

¡Y ya está! Nuestra base de datos de estadísticas se está alimentando automáticamente.

Creando las gráficas

Lo más importante a fin de cuentas, lo que realmente vamos a utilizar. Rrdtool viene con un script de ejemplo que debes colocar en el directorio cgi-bin de tu servidor web y realizar los ajustes adecuados. Yo he puesto como ejemplo la gráfica de la última hora, las demás se harían igual variando el parámetro “–start” a los segundos anteriores que queremos mostrar.

/cgi-bin/stats.cgi

#!/usr/bin/rrdcgi

Tu Dominio - Home Stats

<RRD::GRAPH /usr1/www/www.genteirc.com/htdocs/imagenes/daily-visitas.png --imginfo '<img src="http://tudominio.com/%s" alt="" width="%lu" height="%lu" />' --start -3600 --end -0 -h 400 -w 600 --vertical-label="Tiempo" --title="Last Hour" -a PNG -l 0 DEF:ws1=/path/to/estadisticas.rrd:ws1:AVERAGE LINE1:ws1#00FF00:WebSer1t GPRINT:ws1:AVERAGE:"Media : %.4lft" GPRINT:ws1:MAX:"Max : %.4lft" GPRINT:ws1:MIN:"Min : %.4lfl" DEF:ws2=/path/to/estadisticas.rrd:ws2:AVERAGE LINE1:ws2#800000:WebSer2t GPRINT:ws2:AVERAGE:"Media : %.4lft" GPRINT:ws2:MAX:"Max : %.4lft" GPRINT:ws2:MIN:"Min : %.4lfl" DEF:ws3=/path/to/estadisticas.rrd:ws3:AVERAGE LINE1:ws3#FF8000:WebSer3t GPRINT:ws3:AVERAGE:"Media : %.4lft" GPRINT:ws3:MAX:"Max : %.4lft" GPRINT:ws3:MIN:"Min : %.4lfl" DEF:ws4=/path/to/estadisticas.rrd:ws4:AVERAGE LINE1:ws4#400080:WebSer4t GPRINT:ws4:AVERAGE:"Media : %.4lft" GPRINT:ws4:MAX:"Max : %.4lft" GPRINT:ws4:MIN:"Min : %.4lfl" DEF:ws5=/path/to/estadisticas.rrd:ws5:AVERAGE LINE1:ws5#0000FF:WebSer5t GPRINT:ws5:AVERAGE:"Media : %.4lft" GPRINT:ws5:MAX:"Max : %.4lft" GPRINT:ws5:MIN:"Min : %.4lfl" DEF:ws6=/path/to/estadisticas.rrd:ws6:AVERAGE LINE1:ws6a#00FFF0:WebSer6t GPRINT:ws6a:AVERAGE:"Media : %.4lft" GPRINT:ws6:MAX:"Max : %.4lft" GPRINT:ws6:MIN:"Min : %.4lfl" DEF:ws7=/path/to/estadisticas.rrd:ws7:AVERAGE LINE1:ws7#FF00FF:WebSer7t GPRINT:ws7:AVERAGE:"Media : %.4lft" GPRINT:ws7:MAX:"Max : %.4lft" GPRINT:ws7:MIN:"Min : %.4lfl" DEF:ws8=/path/to/estadisticas.rrd:ws8:AVERAGE LINE1:ws8#FFFF00:WebSer8t GPRINT:ws8:AVERAGE:"Media : %.4lft" GPRINT:ws8:MAX:"Max : %.4lft" GPRINT:ws8:MIN:"Min : %.4lfl" DEF:total=/path/to/estadisticas.rrd:total:AVERAGE LINE2:total#FF0000:'Total t' GPRINT:total:AVERAGE:"Media : %.4lft" GPRINT:total:MAX:"Max : %.4lft" GPRINT:total:MIN:"Min : %.4lfl" >

Para cada línea que queremos mostrar en la gráfica le indicamos de qué archivo rrd saldrán los datos y la fuente (DS) que vamos a utilizar. Añadimos además como leyenda los datos de media, máximo y mínimo de cada fuente, de manera que en una sola gráfica tenemos toda la información necesaria. Si ahora vas a la url donde has dejado el cgi tendrás:

http://tudominio.com/cgi-bin/stats.cgi

stats

Y tu te preguntarás, con lo pesado que te pones con Cacti, ¿por qué no meterla ahí también? Buena pregunta :P. Si tengo un ratito os enseño cómo integrarlo también con Cacti :).

Me he pasado con este artículo, no se lo va a leer nadie :P.

Monitorizando Qmail con Cacti

Hace ya mucho tiempo que no hablamos de Cacti, mi sistema de monitorización preferido. Hoy veremos como controlar con él la actividad de nuestro Qmail.

Habitualmente la instalación de Qmail se realiza utilizando las daemonTools, pero nunca me han gustado mucho así que lanzo el servicio qmail-send con un script propio, mientras que los servidores smtpd y pop3d los integro con xinetd. Ya sé que a muchos no les gustará esto último, pero para mi es muy cómodo :P. El caso es que con este sistema, estos últimos demonios escapan del control de multilog, el sistema de logging de las daemontools, mala suerte. Veremos en este artículo cómo monitorizar el servicio de envío y recepción de Qmail pero no veremos nada relativo a smtp o pop3 debido a que no tengo esos logs. Si los tuvieses sería muy sencillo añadir estas gráficas de actividad, incluso las de spamassasin o clamav.

Supondremos que nuestro multilog deja sus archivos en /var/log/qmail, sustituye esto por tu ruta real, esa es la mía :P.

Lo primero que tendremos que hacer será instalar el paquete qmailmrtg7. Este software permite crear gráficas mrtg a partir de los logs de multilog. Será la herramienta que utilicemos para obtener los datos. NO es necesario que configuremos todo el qmailmrtg7, sólo necesitamos el ejecutable. Podemos comprobar que funciona bien haciendo:

[osus@servidor ~]#/usr/local/bin/qmailmrtg7 q /var/qmail/queue
636
0

Como se puede ver, nos devuelve dos líneas, en la primera los mensajes en cola y en la segunda los mensajes sin procesar. Esto marcha bien.

Configuremos ahora el servidor snmp para que podamos obtener estos valores a través de Cacti, para ellos simplemente añadimos al archivo de configuración las siguientes líneas:

/etc/snmpd/snmpd.conf

extend .1.3.6.1.4.1.18689.1.3 qmail-message-status /usr/local/bin/qmailmrtg7 s /var/log/qmail
extend .1.3.6.1.4.1.18689.1.3 qmail-bytes-transfer /usr/local/bin/qmailmrtg7 b /var/log/qmail
extend .1.3.6.1.4.1.18689.1.3 qmail-smtp-concurrency /usr/local/bin/qmailmrtg7 t /var/qmail/supervise/qmail-smtpd/log/main
extend .1.3.6.1.4.1.18689.1.3 qmail-smtp-sessions /usr/local/bin/qmailmrtg7 a /var/qmail/supervise/qmail-smtpd/log/main
extend .1.3.6.1.4.1.18689.1.3 qmail-queue /usr/local/bin/qmailmrtg7 q /var/qmail/queue
extend .1.3.6.1.4.1.18689.1.3 qmail-messages /usr/local/bin/qmailmrtg7 m /var/log/qmail
extend .1.3.6.1.4.1.18689.1.3 qmail-concurrency /usr/local/bin/qmailmrtg7 c /var/log/qmail

Ahora podemos probar que funciona buscando, por ejemplo, el tamaño de la cola que veíamos antes:

[osus@servidor ~]# snmpwalk -Os -c COMUNIDAD -v 2c SERVIDOR  .1.3.6.1.4.1.18689.1.3.4.1.2.11.113.109.97.105.108.45.113.117.101.117.101.1
enterprises.18689.1.3.4.1.2.11.113.109.97.105.108.45.113.117.101.117.101.1 = STRING: "636"

Debes sustituir COMUNIDAD y SERVIDOR por los datos de tu servidor snmpd. Ya tenemos todo preparado, vamos a por las gráficas.

Cuando comencé a buscar plantillas de Cacti para Qmail encontré bastantes referencias, pero no conseguí que funcionasen, unas simplemente me daban errores al importarlas y otras tenían los OID del snmp incorrectas y no recopilaban nada, así que lo que hice fue una mezcla de todas ellas modificadas por mi. Aquí podéis descargar mis cacti_qmail_templates, espero que os sirvan a todos. Lo importante es que los “data templates” se importen bien, a partir de ellos puedes crear tus propias gráficas. Yo os adjunto una plantilla que crea una gráfica común de todos los indicadores, cacti_graph_template_qmail_-_statistics.xml, cuyo resultado es lo que véis:

qmail_cactiPero también podéis crear gráficas independientes para cada indicador, mucho más claras, sin duda, que todas juntas. Debéis importar, en este caso, todas las demás plantillas que aparecen en el archivo.

qmail_cacti2Eso es todo, ahora podemos ver el tráfico de nuestro MTA visualmente y buscar potenciales problemas.

Descarga los templates de cacti para Qmail.

Cómo comprobar SMTP autentificado

Siempre me pasa lo mismo, cada vez que hago algún cambio en el servidor de correo (Qmail) me vuelvo tonto para probar que funciona bien ya que con el SMTP autentificado no es tan sencillo como introducir tu usuario y tu contraseña.

Tenemos dos métodos para probarlo, el primero requiere de una sola cadena en base64 con el usuario y la clave del usuario. Para sacarlo utilizamos una pequeña expresión en PERL:

perl -MMIME::Base64 -e 'print encode_base64("00USUARIO00CLAVE")'
AFVTVUFSSU8AQ0xBVkU=

Debes sustituir USUARIO y CLAVE por los datos adecuados. Presta atención a los 00 al inicio de cada parámetro, deben seguir ahí. Si tu usuario lleva el dominio completo debes añadir una barra inversa a la @, por ejemplo [email protected].

Con este dato ya podemos probar el servidor:

[osus@host ~]# telnet dominio.com 25
Trying 10.10.10.10...
Connected to dominio.com (10.10.10.10).
Escape character is '^]'.
220 dominio.com ESMTP
EHLO dominio.com
250-dominio.com
250-AUTH=LOGIN PLAIN
250-PIPELINING
250 8BITMIME
AUTH PLAIN AFVTVUFSSU8AQ0xBVkU=
235 ok, go ahead (#2.0.0)

Como véis, después de saludar al servidor (ehlo dominio.com) le decimos en una sóla línea que queremos autentificarnos y cuales son nuestros datos (AUTH PLAIN tucadenaBase64). Eso es todo. Si la autentificación no fuese correcta recibiríamos la respuesta adecuada.

AUTH PLAIN AFVTVUFSSU8AQ0xBVkU=
535 authorization failed (#5.7.0)

En el segundo método enviamos el usuario y la claves por separado, también en base64.

perl -MMIME::Base64 -e 'print encode_base64("usuario")'
dXN1YXJpbw==
perl -MMIME::Base64 -e 'print encode_base64("clave")'
Y2xhdmU=

Para probarlo debemos enviar por un lado el nombre de usuario y por otro su contraseña:

[osus@servidor ~]# telnet dominio.com 25
Trying 10.10.10.10...
Connected to dominio.com (10.10.10.10).
Escape character is '^]'.
220 dominio.com ESMTP
ehlo dominio.com
250-dominio.com
250-AUTH=LOGIN PLAIN
250-PIPELINING
250 8BITMIME
AUTH LOGIN
334 VXNlcm5hbWU6
dXN1YXJpbw==
334 UGFzc3dvcmQ6
Y2xhdmU=
235 ok, go ahead (#2.0.0)

Como véis es parecido al primer sistema pero aquí enviamos el usuario y la contraseña. Después de saludar (ehlo dominio.com) le indicamos que queremos autentificarnos (AUTH LOGIN). Ahora nos pide el usuario (VXNlcm5hbWU6 es Username: en base64) y la clave (UGFzc3dvcmQ6 es Password:). Hemos terminado.

El resto del protocolo SMTP funciona como si estuviese sin autentificar.

Problemas con checkpassword

Otro de los puntos que suele darme algún tipo de problema es la configuración del sistema que utilizan tanto qmail-smtpd como qmail-pop3d para autentificarse: checkpassword. Por defecto no se puede probar directamente ya que lo importante es el código de error que devuelve. Para comprobar su funcionamiento debemos hacer:

perl -e 'printf"%s%sY123456","USUARIO","CLAVE"' | /bin/checkpassword id 3<&0
uid=511(USUARIO) gid=100(users) groups=100(users)

Si la autentificación es correcta nos devolvera algo similar a lo que se indica, si no, no obtendremos respuesta.

Es importante tener en cuenta que el usuario que ejecuta los demonios de Qmail (normalmente qmaild) debe poder ejecutar checkpasswd y que éste debe tener permiso setuid de manera que se ejecute siempre como root independientemente del usuario que lo haga en realidad. Esto debe ser así ya que tiene que acceder a /etc/passwd.

chmod 4755 /bin/checkpassword

Espero que a alguien le sirvan estos apuntes :P, a mi seguro que me ahorrarán tiempo dentro de algunos meses cuando lo necesite de nuevo.

Vsftpd con autentificación a través de servidor Radius y MySQL

Le he cogido gusto a lo del Radius y centralizar todos los temas de autentificación bajo su paraguas 😛 .

Por un lado hemos visto como crear vpn’s y puntos de acceso wireless con Radius y por otro cómo integrar vsftpd con MySQL utilizando usuarios de ftp virtuales. ¿Por qué no combinarlos? ¿Para qué vamos a querer mantener una nueva base de datos para ftp si ya tenemos nuestro Radius preparado para realizar autentificaciones? Está casi todo hecho, sólo nos falta tocar las teclas adecuadas.

Vamos a suponer que tu servidor Radius ya está funcionando y que ya has montado tu sistema vsftpd con usuarios virtuales, en los links anteriores se explica todo. Configuraremos ahora nuestro servidor ftp para que se autentique contra Radius en vez de contra MySQL directamente.

Como vsftpd utiliza PAM para autenticarse, deberemos solamente cambiar el módulo MySQL por el correspondiente a Radius. En aquél momento utilizábamos pam_mysql. ahora haremos lo mismo con pam_radius, así de sencillo.

En mi caso, como sabéis, utilizo CentOS5, y curiosamente no está disponible un rpm con este módulo, así que toca prepararlo a mano, la suerte es que en los fuentes viene el archivo .spec adecuado para crearlo directamente. Si para vuestro sistema tenéis el instalable adecuado podéis saltaros este paso.

Podríamos compilar e instalar directamente el tar, pero prefiero crear un rpm que permita actualizarlo y desinstalarlo a posteriori. Descargamos el código fuente desde aquí, lo desempaquetamos en una ruta temporal y copiamos el propio tar a la ruta adecuada para compilar el rpm:

#cp pam_radius-1.3.17.tar.gz /usr/src/redhat/SOURCES/pam_radius_auth-1.3.17.tar.gz
#tar xvfz pam_radius-1.3.17.tar.gz
#cp pam_radius-1.3.17/pam_radius_auth.spec /usr/src/redhat/SPECS

Ahora debemos hacer un pequeño cambio en este archivo spec. Deberemos editarlo y modificar la línea 2

%define version 1.3.15

actualizando el número de versión correspondiente:

%define version 1.3.17

Y añadimos la línea

BuildRequires: pam pam-devel

Después de la línea 15, de manera que queda

%define name pam_radius_auth
%define version 1.3.17
%define release 0

Name: %{name}
Summary: PAM Module for RADIUS Authentication
Version: %{version}
Release: %{release}
Source: ftp://ftp.freeradius.org/pub/radius/pam_radius_auth-%{version}.tar
URL: http://www.freeradius.org/pam_radius_auth/
Group: System Environment/Libraries
BuildRoot: %{_tmppath}/%{name}-buildroot
License: BSD-like or GNU GPL
Requires: pam
BuildRequires: pam pam-devel

Ya tenemos todo preparado para compilar el módulo

#rpmbuild -ba /usr/src/redhat/SPECS/pam_radius_auth.spec

Si todo va bien tendremos en /usr/src/redhat/RPMS/x86_64 el rpm del paquete preparado para instalar. En mi caso, como es un sistema de 64bits está en la carpeta x86_64, en tu caso puede estar en i386. Sólo nos queda instalarlo.

rpm -ivh /usr/src/redhat/RPMS/pam_radius_auth-1.3.17-0.x86_64.rpm

Vamos ahora a configurar lo poco que hay que configurar 😛 . Indicamos la dirección del servidor Radius y la clave de acceso al mismo:

/etc/raddb/server

127.0.0.1       claveSecretaRadius             1

Cambiamos la configuración PAM de vsftpd para que en vez autenticarse con MySQL utilice Radius:

/etc/pam.d/vsftpd

auth required /lib/security/pam_radius_auth.so debug
account required /lib/security/pam_radius_auth.so debug

Eso es todo. Nuestro servidor FTP se autenticará contra el servidor Radius. Hemos añadido la opción de depuración (debug) para comprobar el funcionamiento, cuando estemos seguros de que todo funciona bien podemos eliminarla.

Si tienes algún problema, para verificar qué hace el módulo pam_radius debemos comprobar el log estándar de debug. Por defecto no viene activado en el demonio syslog, así que lo añadimos:

/etc/syslog.conf

*.debug       /var/log/debug.log

Intentamos acceder al servidor FTP y comprobamos qué está haciendo el módulo de autentificación en este log:

cat /var/log/debug.log

Sep 28 17:11:09 osiris vsftpd: pam_radius_auth: Got user name XXX
Sep 28 17:11:09 osiris vsftpd: pam_radius_auth: Sending RADIUS request code 1
Sep 28 17:11:09 osiris vsftpd: pam_radius_auth: DEBUG: getservbyname(radius, udp) returned 1214042784.
Sep 28 17:11:09 osiris vsftpd: pam_radius_auth: Got RADIUS response code 2
Sep 28 17:11:09 osiris vsftpd: pam_radius_auth: authentication succeeded

Si hubiese algún problema deberíamos verlo ahí, pero si ya tenías Radius y vsftpd funcionando, todo debería ir bien.

Hasta aquí hemos llegado, hemos integrado un nuevo servicio de nuestra red en el mismo sistema de autentificación. Cualquier demonio que utilice PAM para autenticarse se debería configurar del mismo modo. Ssh es el ejemplo más claro, se haría prácticamente igual.

Punto de acceso inalámbrico con autentificación a través de servidor Radius y MySQL

Si hace unos meses os mostraba cómo crear conexiones VPN entre máquinas Linux y Windows con autentificación a través de servidor Radius y MySQL hoy veremos cómo aprovechar la misma estructura para autentificar usuarios que se conecten a nuestra red wifi de manera que tengamos todo el entorno de acceso a nuestra red integrado. En capítulos posteriores puede que veamos cómo añadir más servicios.

Ante todo aviso que no soy ningún experto en la materia y puede que diga cosas que no son correctas 😛 , por favor, no dudéis en corregirme.

La parte más importante del sistema la tenemos ya preparada, si necesitas montar todo el servidor Radius puedes leer el artículo anterior donde se explica bien el procedimiento. En esta ocasión nos centraremos en hacer que el servidor Radius que ya teníamos autentifique a los usuarios que se conecten a nuestro punto de acceso inalámbrico.

En un escenario normal es el punto de acceso el que se encarga de autentificar al cliente que intenta conectarse a través de las conocidas contraseñas (técnicamente shared-keys) con el inconveniente de que todos los usuarios deben utilizar la misma y si hay que cambiarla hay que comunicar a todos ellos cual es la nueva.

Hay dos conceptos dentro de todo este sistema que van siempre juntos pero son distintos:

  1. La encriptación de la comunicación entre el cliente y el punto de acceso, las conocidas WEP, WAP y WAP2.
  2. La shared-key de acceso al router.

Estos dos elementos son completamente independientes. No me voy a meter técnicamente en los distintos tipos de encriptación ya que no son el objetivo del artículo y hay toda la documentación que quieras al respecto. Para nuestro ejemplo vamos utilizar WPA2 con cifrado AES. Recuerda que estos parámetros sólo importan al punto de acceso y al cliente que se conecta, se utiliza para asegurar el canal inalámbrico, el servidor Radius es completamente ajeno a estos mecanismos.

En nuestro escenario el procedimiento descrito anteriormente quedaría como muestra este gráfico.

Dibujo1

Es decir, para que un cliente se conecte a nuestro punto de acceso necesitará las credenciales adecuadas. Una vez el AP las recibe, consulta al servidor Radius si son correctas y debe dejarle pasar, en cuyo caso el cliente ya puede acceder a nuestra red. Cómo os habréis dado cuenta, entre el AP y Radius no he nombrado en ningún momento WAP2 o AES, éstos se utilizan sólamente en lo que en el gráfico superior es el rayo amarillo, el medio inalámbrico. En resumen, la diferencia entre la configuración habitual con shared-key estática y ésta es que permitimos que cada usuario tenga su login independiente, pudiendo cancelar su acceso en cualquier momento, algo que puede ser muy útil en ciertos ambientes, sobre todo corporativos.

Configurando el servidor Radius

Nuestro servidor Radius ya estaba configurado, sólo deberemos hacer un par de modificaciones. Primero añadimos permiso para que se conecte el punto de acceso y pueda autentificar a los clientes, para ello agregamos lo siguiente al archivo /etc/raddb/clients.conf:

client 192.168.100.100 {
	secret = clavesecreta
	shortname = osusnet
}

Donde la ip es la del APsecret es la contraseña que utilizará el AP para conectarse a Radius y shortname un identificador interno. A continuación editamos el archivo /etc/raddb/eap.conf dejando las siguientes secciones de este modo:

default_eap_type = peap

tls {
        private_key_password = whatever
        private_key_file = ${raddbdir}/certs/cert-srv.pem
        certificate_file = ${raddbdir}/certs/cert-srv.pem
        CA_file = ${raddbdir}/certs/demoCA/cacert.pem
        dh_file = ${raddbdir}/certs/dh
        random_file = ${raddbdir}/certs/random
}

peap {
        default_eap_type = mschapv2
}

Eap es el método de autenticación que se utilizará, en el caso que tratamos será PEAP (conocido también como EAP-MSCHAPv2) y necesita además TLS, por eso debemos añadir los dos en la configuración de Radius. En mi caso, la instalación de FreeRadius crea los certificados necesarios así que no entraré más en detalle sobre este tema, supondremos que existen y funcionan bien. Ya solo queda reiniciar el demonio Radius para que los cambios comiencen a estar visibles.

Configurando el punto de acceso

En este punto hemos terminado la configuración en nuestro servidor Linux. Configuraremos ahora el router, en mi caso un SMC. Tendremos que utilizar un cable ethernet para acceder al router ya que no podremos entrar por wifi hasta que lo tengamos bien configurado. La configuración en tu router será muy parecida a ésta. En las opciones “Wireless” le indicamos que la seguridad será WPA:

router4

En la configuración WPA especificamos los parámetros que hemos acordado, WPA2 y AES. Además le indicamos que no utilizaremos pre-shared key para autenticación sino que se hará a través del protocolo 802.1x. Los demás parámetros no nos importan.

router1Finalmente configuramos las opciones de autenticación 802.1x habilitándola e indicando la dirección IP de nuestro servidor Linux con Radius.

router2Ya está el router configurado. Sólo nos queda hacer lo propio con nuestro cliente inalámbrico. Como hemos escogido WAP2 supondremos que la tarjeta wireless del portátil la soporta, si no ¿por qué la has escogido? 😛 .

Configurando los clientes inalámbricos

Al buscar las redes inalámbricas disponibles en el portátil veremos la del router que hemos configurado. Por defecto no podremos acceder a ella, debemos indicarle cómo debe autenticarse, para ello vamos a “Configuración avanzada” y en la lista de redes seleccionamos la que nos ocupa y vamos a propiedades.

En la venta que se abre seleccionamos, en la pestaña “Asociación“,  los métodos de autenticación y cifrado que hemos definido:

wifi3En la pestaña “Autenticación” seleccionamos “PEAP” como tipo de EAP y vamos a “Propiedades“:

wifi1En la ventana que se abre eliminamos la selección de “Utilizar certificado cliente” y en el método de autenticación “EAP-MSCHATP v2” vamos a “Configurar”, eliminando en la ventana que se abre la opción que viene señalada por defecto.

wifi0Aceptamos todos los cambios hacia atrás y guardamos la configuración. A los pocos segundos veremos este aviso:

wifi4Parece que funciona 😛 . Pinchamos en el aviso y nos salta la venta que buscamos:

wifi5Introducimos nuestro usuario y clave de Radius y padentro!

Eso es todo, hemos conseguido el objetivo del artículo, utilizar la misma infraestructura de autentificación para el entorno inalámbrico que la que teníamos para el acceso remoto por VPN.

Cómo realizar backups centralizados de sistemas Windows desde Linux

Hoy voy a contar cómo tengo montado en mi casa el sistema de copias de seguridad para los equipos de escritorio basados en Windows, concretamente para mi portátil.

Lo primero, obviamente, es compartir las carpetas o unidades de las que vas a querer hacer copia, en mi caso la unidad d: completa y mi escritorio, tengo todos los documentos y archivos centralizados en esas dos rutas. Lógicamente no debes hacerlas públicas, sino cualquiera que tenga acceso por red a tu equipo podrá entrar a todo, hay que compartirlas y punto, el que quiera entrar que se autentifique como hará nuestro sistema.

Las copias están centralizadas desde el servidor Linux del que ya os he hablado otras veces, basado en CentOS5. En este equipo tengo un disco duro USB con dos particiones, una ext3 para copias del propio servidor y otra NTFS para las de los equipos Windows. Para evitar pérdidas por cortes, el disco se monta y desmonta automáticamente cuando se necesita a través del sistema autofs, de este modo cada vez que se acceda a la ruta predefinida se montará el disco y pasado un tiempo de inactividad se desmonta. Veamos primero cómo preparar este sistema.

Lo primero que necesitaremos son los drivers para acceder a partificiones NTFS desde Linux, yo utilizo ntfs-3g. En mi caso es tan sencillo como hacer:

yum instal ntfs-3g

Mi equipo tiene varias rutas de automontaje, vamos a ver las que nos interesa que son las del disco usb. Primero configuramos los dos archivos necesarios:

/etc/auto.master

/mnt/backup /etc/auto.backup

/etc/auto.backup

backupusb       -fstype=auto    :/dev/sdb1
backupntfs      -fstype=ntfs-3g,rw,fmask=666,dmask=777,gid=users,nls=iso8859-1,locale=es_ES     :/dev/sdb2

En el primero indicamos que nuestras unidades se van a montar en /mnt/backup y que lea la configuración del segundo archivo mientras que en éste especificamos qué montar y cómo hacerlo.

Como veis he definido dos puntos de montaje correspondientes a las dos particiones. El primer parámetro indica la “carpeta” dónde los montará dentro de la definida en master, en este caso /mnt/backup/backupusb y /mnt/backup/backupntfs. La importante en nuestro caso es la segunda donde indicamos el tipo de archivos y el lenguaje en el que están los nombres, imprescindible para que se lean bien los acentos y eñes del castellano.

Ya sólo debemos iniciar el demonio autofs y probar las rutas.

service autofs status

Si ahora intentamos acceder a /mnt/backup/backupntfs deberíamos poder entrar a la unidad del disco correspondiente y ver su contenido. Ya tenemos la mitad del trabajo resuelto.

Vayamos ahora con el backup propiamente dicho. Creamos el punto de montaje de la unidad que vamos a copiar, en este caso /mnt/portatil. Ahí montaremos temporalmente la unidad remota para hacer las copias. Creamos además en la partición del disco USB el directorio donde meteremos el backup, en mi caso /portatil. Ya estamos preparados para copiar. Ah no, falta un detalle. ¿Cómo lo hacemos si no queremos copiar los tropecientos Gb cada vez que se haga la copia?. El socorrido rsync viene en nuestra ayuda. Rsync permite sincronizar datos entre dos rutas sin copiarlo todo, simplemente elimina lo que ya no existe  y copia lo nuevo y lo que ha cambiado sin tocar lo que no ha variado, con lo que en un ambiente normal será poco lo que hay que copiar.

Este sería mi script.

#!/bin/sh
PORTATIL=192.168.0.79
fecha=`date +%Y-%m-%d`

#MONTAMOS EL USB
if test -d "/mnt/backup/backupntfs"
	then
		echo "Dispositivo USB correcto"
	else
		echo "Error en dispositivo de backups NTFS $fecha" | mail -s "Error montando dispositivo USB backup NTFS" [email protected]
		exit;
	fi
echo "MONTAMOS EL PORTATIL"
mount -t cifs -o iocharset=utf8,username=usuario,password=clave  //$PORTATIL/D /mnt/portatil
if [ $? -ne 0 ]
	then
		echo "ERROR MONTANDO EL PORTATIL/D"
	else
		#copiamos
		echo "COPIAMOS"
		/usr/bin/rsync ----alv ----modify-window=1 ----delete ----recursive ----exclude "RECYCLER" ----exclude="$RECYCLE.BIN" ----exclude "System Volume Information"  /mnt/portatil/ /mnt/backup/backupntfs/portatil/
fi
echo "DESMONTAMOS"
umount //$PORTATIL/D

Lo primero que hago es comprobar que el disco USB está disponible, básico para poder hacer el backup :P. A continuación montamos la unidad remota en el directorio que creamos antes, /mnt/portatil. Esto lo hacemos con:

mount -t cifs -o iocharset=utf8,username=usuario,password=clave  //$PORTATIL/D /mnt/portatil

Los parámetros importantes son los del medio, le indicamos que el juego de caracteres sea UTF-8 y los datos con los que autentificarse en el portátil.  En este punto podríamos hacer un ls -l /mnt/portatil y deberíamos ver los archivos de la unidad D: del portátil.

A continuación ejecutamos la sincronización. La primera vez que se lance tardará bastante ya que ahí sí que debe copiar todos los archivos de la unidad origen en el portátil a la del backup. Cuando termine de sincronizar el script desmontará la unidad del portátil y el autofs hará lo propio automáticamente con la del USB. En el rsync añadimos los parámetros adecuados para que excluya algunas carpetas predefinidas de Windows, como la papelera, y muy importante, el parámetro –modify-window=1 para salvar la diferencia que tiene Windows con la fecha de modificación de los archivos, Windows utiliza sólo valores pares, lo que generaría la copia de muchos más archivos de los que realmente han sido modificados.

Vayamos un poco más lejos. Quiero poder recuperar algún archivo borrado, no sea que me equivoque y me cargue algo. Con rsync todo es muy sencillo, le indicamos que los archivos que vaya a eliminar los mueva a otra ruta. Para ello añadimos los parámetros :

----backup ----backup-dir=/mnt/backup/backupntfs/incremental/portatil/$fecha

Con esto conseguimos que en la carpeta /incremental/portatil de la partición NTFS del disco USB se mantenga un histórico por fecha de los archivos eliminados. El comando entero quedaría del siguiente modo:

/usr/bin/rsync ----alv ----modify-window=1 ----delete ----recursive ----backup ----backup-dir=/mnt/backup/backupntfs/incremental/portatil/$fecha ----exclude "RECYCLER" ----exclude="$RECYCLE.BIN" ----exclude "System Volume Information"  /mnt/portatil/ /mnt/backup/backupntfs/portatil/

De este modo tenemos en el disco todo, por un lado la instantánea de la unidad al momento de hacerla y en otro un histórico diario por día, pudiendo recuperar todo lo que queramos.

Sólo nos queda añadir el script al crontab para que se ejecute cuando creamos oportuna, una vez por semana por ejemplo. Yo crearía también un script que limpie las copias de los archivos eliminados cada cierto tiempo, cada mes o cada dos meses, para que no crezca en exceso el espacio ocupada por la “basura“.

Siguiendo este mismo ejemplo podemos ahora hacer copias de las copias para tener varias copias distribuidas no sea que se nos incendie el piso y lo perdamos absolutamente todo 😛 .

Cómo desmontar una unidad ocupada bajo Linux

Esto es algo que siempre me pasa y nunca me acuerdo de cómo solucionarlo. Hoy he recibido una alerta de uno de mis servidores, MySQL se había parado y no podía reiniciarse. Al entrar a la máquina para hacerlo manualmente, en efecto, me decía que no podía, que los archivos eran de sólo lectura 😐 . Después de hacer alguna comprobación más me doy cuenta de que la unidad entera se había quedado en algún estado extraño de sólo lectura a pesar de que el mount indicaba lo contrario.

[osus@servidor ~]# mount
/dev/hdb1 on /mnt/unidad type ext3 (rw)

Decido entonces desmontar la unidad y volver a montarla, pero…

[osus@servidor ~]# umount /mnt/unidad
umount: /mnt/unidad: device is busy

Y aquí viene el problema. Había parado, en teoría, todos los servicios que utilizaban esa unidad, pero aún así me daba este error. Podría haber forzado el umount con:

umount -l /dev/hdX

Pero prefiero saber qué es lo que está ocupando la unidad antes de forzarlo, cuestión de precaución sólo. Necesitamos saber, entonces, qué procesos están haciendo uso de la unidad que queremos desmontar, y esto es lo importante de este artículo.

[osus@servidor ~]# fuser -vm /dev/hdb1

                     USER        PID ACCESS COMMAND
/dev/hdb1:           named       456 ..c.. named
                     mysql       587 F.c.. mysqld
                     apache     1113 F.... httpd
                     root       1925 ..c.. screen
                     root       1926 ..c.. bash
                     apache     8009 F.... httpd
                     apache     9267 F.... httpd

Con este sencillo comando de fuser ya sabemos quién accede a la unidad en cuestión. En mi caso era un proceso bash de un screen que estaba abierto y un rsync. Los paré y ya pude desmontar la unidad correctamente. Al volver a montarla todo comenzó a funcionar correctamente.

¿La causa? Ni idea, no había nada raro en los logs, pero me da que el rsync había hecho algo extraño…

Televigilancia con Motion y un servidor Linux

Hoy vamos con algo curioso 😛 . A veces pienso que su utilidad no está muy clara, me refiero a utilidad real 😉 , no sólo como juguete, pero seguro que cuando lo leáis se la encontráis.

Hoy veremos cómo montar un sistema de televigilancia con un servidor Linux y una webcam USB normal y corriente, de las baratitas.

Asumiré que ya tienes tu webcam funcionando bajo Linux, no entraré en detalles puesto que no es el objeto de este artículo y cada webcam tendrá una configuración distinta. Por suerte, hoy en día la mayoría de webcams USB funcionan perfectamente bajo Linux.

La base de este artículo cae sobre el software encargado de monitorizar la webcam en busca de actividad: motion. Lo utilicé por primera vez allá por 2001, hace ya tiempo, y desde luego puedo confirmar es una solución completa y robusta.

Motion funciona como la mayoría de programas de detección de movimiento basados en cámaras, no hay mucho misterio, simplemente comprueba la diferencia de píxeles entre fotogramas consecutivos capturados y si esta diferencia es superior a un umbral predefinido asume que hay movimiento. Este umbral debe ser bien estudiado para que un simple movimiento de cortina o el reflejo del sol que entra por la ventana no salten como un falso positivo. Una de las características que lo diferencian de otros programas similares es que permite monitorizar tantas cámaras como el ancho de banda de tus puertos USB pueda asumir, aunque no tienen porqué ser USB, recuerda que Linux trata igual cualquier flujo de vídeo, venga de donde venga, /dev/videoX. Veremos todo esto más adelante.

Instalación

En mi caso, como ya sabéis los que me seguís, utilizo un servidor con Centos5. No hay un rpm para esta distribución (o al menos yo no lo encontré en su día) y me gusta normalmente instalar las cosas con rpm por sencillez, así que vamos a construirlo desde el fuente, así que, descárgalo y descomprímelo.

Dentro de esta carpeta que has obtenido encontrarás un archivo motion.spec.in, edítalo y cambia las primeras líneas:

Name:           @PACKAGE_NAME@
Version:        @PACKAGE_VERSION@

por

Name:           motion
Version:        3.2.11

Guárdalo. Ahora copia el archivo original que descargaste antes, motion-3.2.11.tar.gz a /usr/src/redhat/SOURCES/. Ya está todo, ahora construímos el RPM.

[osus@servidor motion-3.2.11]# rpmbuild -bb motion.spec.in

Si todo va bien, cuando termine tendremos el RPM en /usr/src/redhat/RPMS/x86_64/motion-3.2.11-1.x86_64.rpm. Si usas una arquitectura de 32bits lo tendrás en una carpeta distinta a x86_64, mira en /usr/src/redhat/RPMS. Instala el rpm generado, rpm -ivh motion-3.2.11-1.x86_64.rpm. Primer paso completado.

Configuración

Motion no tiene interfaz gráfica propiamente dicha aunque sí una sencilla aplicación web para modificar los parámetros. Todas sus opciones se gestionan a través del archivo de configuración /etc/motion/motion.conf. Por defecto, viene bastante bien configurado. El parámetro más importante es:

videodevice=/dev/video1

Ahí debes indicar cuál es el dispositivo de tu cámara.

Con el resto de configuraciones puedes jugar desde la interfaz web, aquí dejo algunas importantes.

Primera opción chula. Activar una interfaz web para ver las capturas directamente desde la cámara, así tienes un sitio desde donde espiar. Obviamente debes tener acceso al puerto desde donde lo quieras ver, es decir, abrirlo en el router.

webcam_port 8001

Conéctate a http://ipdelservidor:8001/. Sorpresa!. Con firefox lo verás como si fuese un vídeo, aunque en realidad es un flujo de imágenes JPG. A Explorer no le gusta mucho ese formato y se suele utilizar un applet para convertirlo.

La siguiente es interesante ya que en vez de mostrar las imágenes muestra los píxeles distintos entre ellas, útil para preparar el sistema y comprobar cómo funciona motion.

setup_mode off

Ahora activamos la gestión web para configurar el sistema y hacer más “cositas“.

control_port 8000
control_localhost off
control_authentication tuusuario:tucontraseña

Pasa lo mismo con el puerto, no olvides abrirlo.

Conéctate a http://ipdelservidor:8001/ y juega con las opciones. Todo lo que toques desde ahí se aplicará en tiempo real sin necesidad de reiniciar motion. Muy útil para la puesta a punto. Recuerda sin embargo que debes guardar los cambios para que se mantengan la próxima vez que reinicies el servidor.

motion

La siguiente nos permite decidir cuántos fotogramas deben cambiar para disparar la alerta. Por defecto viene a 1 y esto nos provocará bastantes avisos falsos, si entra alguien en casa se cambiarán muchos fotogramas 😛 .

minimum_motion_frames 1

Con ésta otra podemos generar automáticamente un frame cada X segundos. Por defecto está desactivado (0), si le das un valor se creará la imagen por cada intervalo, podremos utilizarla para poner una webcam en web “semi-real” 😛 . Cuando estaba en la universidad teníamos una cámara enfocando al comedor que actualizaba una imagen periódicamente y nos servía para controlar la cola del comedor y bajar cuando había poca 😛 .

snapshot_interval X

Motion tiene muchísimas opciones más como para comentarlas todas, en la documentación de la web oficial puedes enterarte de para qué sirven y qué hacen todas ellas.

Comprobando el funcionamiento

Ya tenemos todo listo, hemos configurado todo bien, ¿funcionará?.

Veamos qué ocurre cuando lanzamos el servicio (service motion start). Me muevo un rato delante de la cámara y ocurre esto:

snaps

Sí, motion comienza a grabar imágenes de lo que está ocurriendo. Pero eso no es todo. Cada vez que se detecta movimiento motion graba un vídeo del momento, sonríe ladrón, te están grabando 🙂 :

Enviando alertas

Hay una serie de eventos que puedes utilizar para hacer otras acciones no definidas, como enviarte alertas por email, SMS, etc…

on_event_start value
on_event_end value
on_picture_save value
on_motion_detected value
on_movie_start value
on_movie_end value

Simplemente debes añadir el comando completo que quieres utilizar. Con %f accedes al nombre del fichero generado en los eventos de imagen y vídeo. Por ejemplo, voy a utilizarla para convertir el vídeo generado a 3gp y enviarlo por email para verlo en el móvil.

on_movie_end ffmpeg -i %f -s qcif /ruta/publica/web/video.3gp && echo "puedes verlo aqui http://tuservidor.com/video.3gp" | mail -s "Alertas de movimiento" [email protected]

Incríble pero cierto 😛 . Ahora tendría una tarea que recoge los emails de ese buzón y me los envía por SMS. También podríamos haber enviado el SMS directamente como vimos aquí.

¿Puedo usar más cámaras?

Para lanzar una segunda cámara (o más) añadiríamos al final del archivo de configuración tantas líneas como cámaras quieres poner:

thread /etc/motion/cam2.conf

Donde cam2.conf es un archivo de configuracion como motion.conf donde cambias, además de todos los parámetros que necesites, el videodevice. Recuerda que los puertos de gestión y visualización deben ser diferentes en cada cámara.

Quiero ver mi cámara desde fuera sin abrir ningún puerto más

¡Qué complicado me lo estás poniendo!

Apache viene al rescate. Configuramos los puertos del streaming jpeg y/o la interfaz de administración para que se vean a través de Apache:

ProxyPass /camara.cgi http://127.0.0.1:8001
ProxyPassReverse /camara.cgi http://127.0.0.1:8001
ProxyPass /admincamara.cgi http://127.0.0.1:8000
ProxyPassReverse /admincamara.cgi http://127.0.0.1:8000

Ahora sí, todo perfecto. Aunque pensándolo bien, como comience a recibir alertas me voy a cagar de miedo :S , me estrán robando 😛 .

Usuarios de ftp virtuales con vsftpd y MySQL

Un problema que se me ha presentado más de una vez es dar acceso FTP a determinadas carpetas a usuarios que necesitan actualizar archivos en ellas. Por regla general habría que crear un usuario del sistema que tuviese su “home” en esa carpeta o un enlace simbólico desde otra, pero nunca me ha gustado la idea de crear usuarios a diestro y siniestro, aunque sean sin privilegios. Buscando un día, empecé a encontrar información sobre cómo crear usuarios “virtuales” en vsftpd y me gustó mucho la idea, combinando esto con la gestión de usuarios en MySQL podría tener un sistema bastante sencillo de dar acceso FTP a unos cuantos usuarios sin incrementar los del sistema y, por tanto, sin abrir agujeros de seguridad.

Lo primero será instalar los módulos necesarios, asumiendo que ya tienes el servidor MySQL funcionando. Nos hará falta el paquete vsftpd y el pam_mysql que permitirá hacer autentificaciones contra una base de datos de este tipo.

yum install pam_mysql vsftpd

Ahora configuramos el archivo /etc/pam.d/vsftpd para que quede así;

[osus@servidor vsftpd]# cat /etc/pam.d/vsftpd
auth required /lib/security/pam_mysql.so user=vsftpd passwd=clave host=localhost db=basedatos table=usuarios usercolumn=usuario passwdcolumn=pass crypt=0
account required /lib/security/pam_mysql.so user=vsftpd passwd=clave host=localhost db=basedatos table=usuarios usercolumn=usuario passwdcolumn=pass crypt=0
session required /lib/security/pam_mysql.so user=vsftpd passwd=clave host=localhost db=basedatos table=usuarios usercolumn=usuario passwdcolumn=pass crypt=0

Esto indica a vsftpd que debe autenticarse contra “basedatos” en “localhost” con el usuario “vsftpd” y la clave “clave” y que debe buscar en la tabla “usuarios” con las columnas  “usuario” y “pass“. Qué evidente es todo 😛 .

El último parámetro, “crypt“, indica el modo en que se guardarán las claves:

  • 0 para claves en texto plano sin encriptar
  • 1 para claves encriptadas con la función crypt()
  • 2 para claves generadas con la función PASSWORD() de MySQL
  • 3 para claves en md5

Escoge el sistema que prefieras. Dejaremos ahora el archivo de configuración principal de este modo:

[osus@servidor vsftpd]# cat /etc/vsftpd/vsftpd.conf
ftpd_banner= "Servidor FTP"
anonymous_enable=NO
chroot_local_user=YES
guest_enable=YES
guest_username=ftpoculto
hide_ids=YES
listen=yes
listen_address=192.168.3.254
listen_port=21
local_enable=YES
max_clients=100
max_per_ip=5
pam_service_name=vsftpd
use_localtime=YES
user_config_dir=/etc/vsftpd/usuarios
userlist_enable=YES
userlist_file=/etc/vsftpd/denied_users
virtual_use_local_privs=YES
xferlog_enable=YES
async_abor_enable=YES
connect_from_port_20=YES
dirlist_enable=NO
download_enable=NO
local_umask=000

Con esto le estamos diciendo que no permitiremos el acceso anónimo y que el usuario real que se utilizará será “ftpoculto“, los usuarios virtuales se comportarán como este usuario. Le indicamos, además, que busque los usuarios virtuales en /etc/vsftpd/usuarios y que no deje entrar a ningún usuario real, sólo a los virtuales. Para esto último haremos lo siguiente:

cat /etc/passwd | cut -d ":" -f 1 | sort > /etc/vsftpd/denied_users

Así añadimos a la lista de usuarios denegados a todos los usuarios del sistema, no hay que ponerlos a mano uno a uno 🙂 .

Ahora debemos configurar el acceso para cada usuario dentro de la carpeta “usuarios“. El nombre del archivo debe ser el mismo que el del usuario que se ha añadido a la base de datos, el que utilizará el cliente para conectarse. Yo, por ejemplo, suelo utilizar como nombres el dominio del cliente, así se quién es quién.

[osus@servidor usuarios]# cat tudominio.com
dirlist_enable=YES
download_enable=YES
local_root=/var/www/tudominio.com/
write_enable=YES
anon_world_readable_only=NO

Tendremos que configurar el directorio de este usuario para que pueda escribir en él. Para conseguirlo recordamos que vsftpd utilizaría el usuario real “ftpoculto“. Debemos, por tanto, dar permisos sobre los directorios de los usuarios virtuales a ese usuario:

chown -R ftpoculto.users /var/www/tudominio.com

Con esto, el usuario “tudominio.com” podrá moverse en /var/www/tudominio.com a sus anchas 🙂 .

La verdad es que me ha funcionado muy bien siempre. Si se combina con un sistema de acceso VPN sencillo, de manera que el puerto FTP no tenga que estar abierto púclibamente, queda todo muy robusto y seguro además de que es muy sencillo añadir nuevos usuarios, incluso lo puedes automatizar con un script ya que solamente hay que añadir un registro en la base de datos y crear el archivo de configuración en /etc/vsftpd/usuarios.

Es una buena idea en pequeños entornos ISP no dedicados o especializados, entornos donde los usuarios no van a necesitar habitualmente acceso FTP (cms, blogs, webs corporativas…).

Hay muchos más parámetros en vsftpd, desde limitar el ancho de banda por usuario hasta utilizar SSL en las conexiones, sólo hay que leer un poco la documentación 🙂 .

Acelera tus scripts PHP con eAccelerator

Como ya sabréis la mayoría, PHP es un lenguaje de programación de script, es decir, el código fuente no se compila generando un binario o un bytecode sino que el intérprete de PHP lo lee, compila y ejecuta cada vez que se llama a ese script. No hay que ser muy espabilado para darse cuenta de que este proceso de compilación permanente tiene que tener penalizaciones en cuanto a rendimiento, supongo que se compensa con la facilidad de desarrollo al no tener que compilar cada prueba que se quiere hacer.

Hace ya unos años descubrí eAccelerator, fork del conocido Turck MMCache. La primera vez que lo probé e hice tests de rendimiento no me creía la diferencia que había entre el antes y el después, poco a poco y a base de más pruebas me fui convenciendo y hoy en día es parte fundamental de todos los servidores a los que tengo que meter mano. eAccelerator trabaja compilando los scripts PHP y cacheando esta compilación de manera que la siguiente vez que se solicita está ya compilado y no hay que volver a hacerlo. Es importante hacer notar que maneja automáticamente los cambios de versiones a través de la fecha de modificacion (mtime). Si el script es más nuevo que el que tiene cacheado, lo vuelve a compilar, con lo que, de cara al desarrollador, el sistema sigue teniendo la ventaja de no necesitar compilados ni empaquetados.

La instalación es muy sencilla, descargamos el código fuente desde la web oficial y lo compilamos.

phpize
./configure
make
make install

Ya está el módulo instalado, ahora tendremos que activarlo. En mi CentOS es muy sencillo, simplemente debo añadir un nuevo archivo a /etc/php.d y será leído automáticamente en el siguiente reinicio de Apache. El contenido del archivo será algo así:

[osus@servidor ~]# cat /etc/php.d/eacclerator.ini
zend_extension="/usr/lib/php/modules/eaccelerator.so"
eaccelerator.shm_size="16"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
eaccelerator.allowed_admin_path="/ruta/a/tu/servidor/de/control"

Todos los parámetros están bien explicados en la documentación oficial. El más importante es el último ya que, como veremos más adelante, indica una ruta dentro de tu servidor web desde donde podrás ver un pequeño panel de control de eAccelerator.

Ahora reinicia Apache y comprueba si funciona de verdad:

[osus@servidor ~]# php -v
PHP 5.1.6 (cli) (built: Nov 12 2008 11:22:34)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies
    with eAccelerator v0.9.5.2, Copyright (c) 2004-2006 eAccelerator, by eAccelerator

Parece que sí. Aprovecharemos para configurar la interfaz web que comentaba que nos permite controlar algunas funcionalidades. Para ello copiamos desde la carpeta con el código fuente el archivo control.php a la ruta indicada anteriormente y configuramos los parámetros de autenticación que nos pedirá, usuario y clave, al principio de este archivo.

if (!function_exists('eaccelerator_info')) {
    die('eAccelerator isn't installed or isn't compiled with info support!');
}

/** config **/
$user = "osus";
$pw = "bombona";
/** /config **/

Ya está, sólo queda acceder a esta ruta web para ver algo semejante a esta imagen: información del estado de la caché, posibilidad de desactivar el cacheo, limpiar caché…

Eaccelerator admin

Analizando el rendimiento

Todo esto está muy bien y suena muy bonito, pero ¿mejora de verdad el rendimiento? ¿qué beneficios reales obtenemos? La pregunta del millón.

Utilizaremos Apache Benchmark para hacer pequeñas pruebas de carga sobre el servidor. Esta utilidad viene siempre con Apache, con lo que no tendrás que instalar ningún software adicional. La sintaxis es muy sencilla:

ab -n 100 -c 5 http://dominioentuservidor.com/script.php

Con esto estaremos diciendo a “ab” que lance 100 peticiones contra la url indicada con una concurrencia máxima de 5 solicitudes. El resultado seria algo similar a esto:

[osus@servidor ~]# ab -n 100 -c 5 http://tudominio.com/
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking tudominio.com (be patient).....done

Server Software:        Apache
Server Hostname:        tudominio.com
Server Port:            80

Document Path:          /
Document Length:        125397 bytes

Concurrency Level:      5
Time taken for tests:   52.549041 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      12559900 bytes
HTML transferred:       12539700 bytes
Requests per second:    1.90 [#/sec] (mean)
Time per request:       2627.452 [ms] (mean)
Time per request:       525.490 [ms] (mean, across all concurrent requests)
Transfer rate:          233.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      195  232  45.7    211     417
Processing:  1687 2356 401.0   2257    3609
Waiting:      342  620 240.6    546    1756
Total:       1933 2589 400.7   2487    3815

Percentage of the requests served within a certain time (ms)
  50%   2487
  66%   2611
  75%   2798
  80%   2903
  90%   3090
  95%   3475
  98%   3791
  99%   3815
 100%   3815 (longest request)

Bien, pues a partir de esta información he hecho un pequeño estudio estadístico para valorar el cambio de rendimiento.  Se hicieron dos baterías de pruebas, primero con eAccelerator desactivado y después activado. En cada grupo se lanzaron diez tests utilizando “ab” con 100 peticiones. Vale, sí, la muestra es pequeña, pero veréis que suficiente para ver el cambio. Estos fueron los resultados.

eaceleratorstats.gif

La última fila establece los porcentajes de las pruebas con eAcclerator respecto de no utilizarlo. Interpretemos los resultados:

  • Tiempo total: utilizando el acelerador las pruebas se han ejecutado en el 73% del tiempo empleado al no utilizarlo. Eso supone un 27% menos de tiempo, una cifra nada despreciable.
  • Peticiones por segundo: con eAccelerator se ejecuta un 36% más de peticiones por segundo que sin él. Sobran más comentarios.
  • Tiempo mínimo: El tiempo mínimo empleado para atender una petición desciende un 22% al utilizar eAccelerator.
  • Tiempo máximo: este valor es sumamente llamativo, con eAccelerator se necesita un 42% menos de tiempo en el peor de los casos.

¿Necesitas más argumentos? 😛 . Otro día hablaremos de memcached.