Archivo por meses: febrero 2009

Adobe Air I – Tipos de ventanas y cómo hacer aplicaciones con nuestro propio chrome

Aprovechando que los últimos días me he metido de lleno a desarrollar una pequeña aplicación con Adobe Air, comienzo una serie de artículos dónde iré contando algunos de los entresijos de este sistema ya que parece muy sencillo pero cuando quieres hacer cosas concretas parece que se complica un poco.

Hoy veremos la parte fundamental de una aplicación, las ventanas en sí mismas. Se conoce como “chrome” al entorno sobre el que se crea la ventana. Habitualmente es el propio sistema operativo el que lo hace, son las ventanas clásicas de la mayoría de aplicaciones. En AIR se crean como “WindowedApplication” en la propia aplicación (veremos más adelante que éste es un detalle importante) y en el xml descriptor de la aplicación se configuran estos parámetros:

<initialwindow>
    <systemChrome>standard</systemChrome>
    <transparent>false</transparent>
</initialWindow>

El resultado, bajo Windows, sería la clásica ventana con la estética del sistema operativo.

Ventana Standard en Adobe Air

Vale, pero yo quiero crear mi ventana sin el chrome del sistema operativo.

La primera opción es configurar así los parámetros:

<initialwindow>
    <systemChrome>none</systemChrome>
    <transparent>true</transparent>
</initialWindow>

Y obtenemos una ventana con un chrome AIR.

Venatana none en Adobe Air

Queda muy bonita, pero yo lo que quiero es que no tenga nada de chrome, ni barra superior ni barra inferior, quiero definir yo toda la estética y funcionalidad de mi aplicación. Bienvenidos a la madre del cordero. Esto, que debería estar bien explicado en la documentación, no hay manera de entenderlo, de hecho creo que no se llega a explicar la manera concreta de conseguirlo.

La clave del problema es que, en este caso, la aplicación en el MXML no se definde como  “WindowedApplication” sino como “Application” estándar, el de cualquier aplicación Flex, y en el descriptor definimos:

<initialwindow>
    <systemChrome>none</systemChrome>
    <transparent>true</transparent>
    <visible>true</visible>
</initialWindow>

Y conseguimos el resultado esperado, sólo nuestra ventana sin adornos.

Ventana con chromless personal en Adobe Air

Es importante remarcar que en este caso hay que ponerle el parámetro visible=true dentro del xml ya que si no lo hacemos no se verá nada hasta que se ponga a true, perdí varias horas hasta averiguar porqué no veía nada.

Esto es todo por hoy. En el siguiente capítulo veremos cómo crear aplicaciones que se quedan como un icono al lado del reloj en Windows o en el Dock de Mac OSX.

Funciones útiles en XSLT

Una de las tecnologías que más utilizo en el desarrollo de proyectos para móviles son las transformaciones XSL (XSLT) ya que me dan la versatilidad adecuada para adaptar la capa de presentación a las compatibilidades del terminal del cliente (xHTML Mobile, WML, iMode…) o de la operadora (PML de Vodafone por ejemplo).

A lo largo de estos años he ido recopilando algunas funciones (en realidad templates) XSLT que me facilitan la vida a la hora preparar determinadas funcionalidades dentro de un portal wap. Os las dejo por si a alguien le vienen bien.

Capitalize

Un clásico. Convierte una cadena de texto de manera que devuelve el primer carácter en mayúsculas y el resto de la cadena en minúsculas.

<xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>

<xsl:template name="capitalize">
    <xsl:param name="arg"/>
    <xsl:value-of select="concat(translate(substring($arg,1,1), $lcletters, $ucletters), substring($arg,2))"/>
</xsl:template>

Ejemplo de uso:

<xsl:call-template name="capitalize">
     <xsl:with-param name="arg" select="/ruta/al/texto"/>
</xsl:call-template>

 

URL-Encode

Lo que su propio nombre indica. Codifica una cadena de texto para ser enviada como parámetro de una URL. Muy útil en situaciones de integración con sistemas de billing.

<xsl:variable name="ascii"> !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~</xsl:variable>
<xsl:variable name="latin1"> ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ</xsl:variable>
<xsl:variable name="safe">!'()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~</xsl:variable>
<xsl:variable name="hex" >0123456789ABCDEF</xsl:variable>
<xsl:variable name="thestring" select="null" />

<xsl:template name="url-encode">
    <xsl:param name="str"/>
    <xsl:if test="$str">
        <xsl:variable name="first-char" select="substring($str,1,1)"/>
        <xsl:choose>
            <xsl:when test="contains($safe,$first-char)">
                      <xsl:value-of select="$first-char"/>
           </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="codepoint">
                    <xsl:choose>
                           <xsl:when test="contains($ascii,$first-char)">
                                 <xsl:value-of select="string-length(substring-before($ascii,$first-char)) + 32"/>
                           </xsl:when>
                           <xsl:when test="contains($latin1,$first-char)">
                               <xsl:value-of select="string-length(substring-before($latin1,$first-char)) + 160"/>
                           </xsl:when>
                           <xsl:otherwise>
                               <xsl:message terminate="no">Warning: string contains a character that is out of range! Substituting "?".</xsl:message>
                               <xsl:text>63</xsl:text>
                           </xsl:otherwise>
                       </xsl:choose>
                </xsl:variable>
                <xsl:variable name="hex-digit1" select="substring($hex,floor($codepoint div 16) + 1,1)"/>
                <xsl:variable name="hex-digit2" select="substring($hex,$codepoint mod 16 + 1,1)"/>
                <xsl:value-of select="concat('%',$hex-digit1,$hex-digit2)"/>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:if test="string-length($str) > 1">
            <xsl:call-template name="url-encode">
                <xsl:with-param name="str" select="substring($str,2)"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:if>
</xsl:template>

Ejemplo de uso:

<xsl:call-template name="url-encode">
    <xsl:with-param name="str" select="/ruta/al/texto"/>
</xsl:call-template>

 

Search and Replace

Otra vez lo que su propio nombre indica. Reemplaza un texto por otro dentro de una cadena.

<xsl:template name="SEARCH-AND-REPLACE">
	<xsl:param name="string" />
	<xsl:param name="search-for" />
	<xsl:param name="replace-with" />
	<xsl:choose>
		<xsl:when test='contains($string,$search-for)'>
			<xsl:value-of select="substring-before($string,$search-for)"/>
			<xsl:value-of select="$replace-with"/>
			<xsl:call-template name="SEARCH-AND-REPLACE">
				<xsl:with-param name="string" select="substring-after($string,$search-for)" />
				<xsl:with-param name="search-for" select="$search-for" />
				<xsl:with-param name="replace-with" select="$replace-with" />
			</xsl:call-template>
		</xsl:when>
		<xsl:otherwise>
			<xsl:value-of select="$string" />
		</xsl:otherwise>
	</xsl:choose>
</xsl:template>

Ejemplo:

<xsl:call-template name="url-encode">
    <xsl:with-param name="string" select="/ruta/al/texto/original"/>
    <xsl:with-param name="search-for" select="texto a buscar"/>
    <xsl:with-param name="replace-with" select="texto por el que se reemplaza"/>
</xsl:call-template>

Si habéis utilizado XSLT en alguna ocasión os habréis dado cuenta de que se echan en falta ciertas funciones “habituales” y que, tal cual, estás muy limitado. Sin embargo XSLT tienes las funcionalidades básicas a partir de las cuales se puede generar casi cualquier otra cosa que se te ocurra, sólo tienes que tener muy claro con lo que cuentas y qué quieres hacer.

Quiero aclarar que estas funciones NO las he hecho yo, las he ido recopilando los últimos años googleando un poco, a veces algo más que un poco, lástima no recordar de donde las fui sacando.

De momento estas tres son las que he localizado. Tengo otra más que suelo utilizar en integración con sistemas de billing para convertir a Base64, pero no la encuentro por ningún lado.

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.

Cuevas de Canelobre

El pasado fin de semana, y aprovechando una buena oferta que me había pasado David, decidimos pasarlo en Benidorm, no es el mejor sitio para ir pero la oferta de hotel con spa era llamativa. De paso aprovechamos para visitar las Cuevas de Canelobre que tenía apuntadas en mi agenda desde hacía tiempo.

Nos alojamos en el Hotel Dynastic, en la punta más al este de la playa de Levante y dimos buena cuenta del ¿spa?, bueno, dejémoslo en una piscina con algunos chorros de agua. Hoy en día parece que poner “Hotel SPA” es sinónimo de más clase y da igual cómo sean las picisinas.

El domingo por la mañana aprovechamos para pasear por la playa e hicimos estas dos fotos, playa de Levante y playa de Poniente desde la confluencia de ámbas, en lo que es el pueblo original. Cada uno que opine lo que quiera sobre el desarrollo urbanístico y el modelo turístico.

Vista de Benidorm, playa de LevanteVista de Benidorm, playa de Poniente

Era la segunda vez que estaba en Benidorm, la primera fue hace casi 10 años y no es que me acuerde mucho, pero me quedé impresionado con la cantidad de gente que había, la mayoría ingleses. Muy extraña la sensación de estar a 140km de tu casa, en tu mismo país, y tener que hablar en inglés para que te entiendan y te acaben poniendo un café con leche en tazón de desayuno porque no entienden eso de “café largo” 😛 .

Cuevas de Canelobre

El sábado por la mañana nos acercamos a estas cuevas en el municipio de Busot, perfectamente señalizadas desde varios kilómetros antes de llegar a la localidad, a unos 40km de Benidorm.

Según crónicas árabes encontradas, estas cuevas fueron descubiertas allá por el año 740 por los árabes que penetraron en su interior a través del único paso que había hasta la Guerra Civil, situado a la derecha, en un alto de difícil acceso. Hoy se entra por aquél pasadizo de 40m de largo que abrió el ejército republicano.

Cuevas de Canalobre

Esta cueva es la más alta de España, con una bóveda de 70m de altura que la asemeja a una catedral con una acústica natural fantástica, de hecho se utilizan para pequeños conciertos.

El nombre de las cuevas “Canelobre” viene de la mayor de las caprichosas figuras creada por el agua durante más de 100.000 años, un candelabro, canelobre en valenciano.

Cuevas de Canalobre

Desde el s.XIX se realizan ya visitas a la cueva y las crónicas hablan de conjuntos de estalacticas y estalagmitas verdaderamente impresionantes, hoy prácticamente desaparecidos. La apertura del nuevo túnel de acceso con dinamita provocó la caída de miles de conjuntos creados por la acción del agua sobre la roca caliza. Otros fueron directamente cortados por manos del hombre.

 Cuevas de Canalobre

Durante la Guerra Civil las cuevas fueron utilizadas por el ejército republicano como refugio, polvorín y como taller de reparación y montaje de motores del avión Polikarpov I-16 Tipo 5 y 6,  conocido como “Mosca”. Como ya he comentado, fue el ejército republicano quien abrió el actual acceso dinamitando los 40m de roca que se han convertido en el túnel de entrada.

Cuevas de Canalobre

En el interior de la cueva construyeron tres plataformas a distintos niveles, lo que da fé de la altura que tiene la cueva. Todavía pueden verse en algunas paredes los agujeros sobre los que se apoyaban las vigas donde reposaban las plataformas. Tras la contienda fueron destruídas hasta que en 1963 se construyeron las actuales y los accesos adecuados para las visitas turísticas.

Cuevas de Canalobre

La guía nos relataba como cuándo ella era niña, hará unos 30 años, todavía había agua en el interior de la cueva, en la parte inferior, pero que probablemente la perforación de varios pozos de agua hacia la zona de El Campello habría acabado por secarla del todo.

Cuevas de Canalobre

Al parecer los espeleólogos han encotrado otras dos cavidades en la misma montaña de mayor belleza que la visitable, pero habilitar el acceso a turistas implicaría destruir parte de ésta actual, con lo que es poco probable que algún día sean visitables por el gran público ya que se accede tras sortear varios cientos de metros de estrechos pasadizos y una caída final de 60 metros de altura.

 Cuevas de Canalobre

Ahora mi opinión personal. Esperaba mucho más. Impresiona la altura y amplitud de la bóbeda, pero echas en falta conjuntos estalagmíticos. Que sí, está claro, la Guerra Civil acabó con ellos, pero resultan mucho más impresionantes las Grutas de Cristal que visité hace unos meses. Aún así, merece la pena una escapada.

Reiniciar el router automáticamente

Tengo un problema con mi router ADSL de casa. Cada 3 o 4 días se cuelga por alguna extraña razón (¿será saturación? 😛 ) y hay que resetearlo para que vuelva a funcionar. Cansado de estos cuelgues decidí solucionarlo con un pequeño script que mi máquina Linux ejecuta cada dos días de madrugada, así no me entero. Ya no me he vuelto a dar cuenta de los cuelges.
Lo primero que hay que hacer es habilitar el acceso telnet al router desde dentro de la red local, nunca desde el exterior 😛 .

expect es una herramienta que permite automatizar aplicaciones interactivas tipo telnet o ftp, donde tienes que introducir tu usuario y tu clave y alguien tiene que estar ahí para teclearlas. Expect lo que hace es, como su propio nombre indica, esperar determinadas cadenas de texto e introducir los datos que se hayan programado. Pongamos un ejemplo práctico. Cuando haces un telnet tienes la siguiente secuencia de eventos:

[osus@servidor ~]# telnet 192.168.0.100
Trying 192.168.0.100...
Connected to 192.168.0.100.
Escape character is '^]'.

Username:

Password:

Es decir, cuando haces un telnet “esperas” la cadena “Username:” para introducir tu usuario y después introduces tu clave cuando recibes “Password:“.

Esto es exactamente lo que automatiza expect. Veamos cómo con un script práctico.

[osus@servidor scripts]# cat router.sh
#!/usr/bin/expect -f
set force_conservative 1 ;
if {$force_conservative} {
        set send_slow {1 .010}
        proc send {ignore arg} {
                sleep .2
                exp_send -s -- $arg
        }
}
spawn telnet 192.168.0.100
expect "Username :*"
send -- "USUARIOr"
expect "Password :*"
send -- "CLAVEr"
expect "ROOT :>*"
send -- "rebootr"
expect "figuration(y/n) ? :*"
send -- "yr"
expect -gl "\[*]$*"
exit

Creo que habla por sí sólo el script. Esperamos a recibir cada cadena de texto para enviarle nuestros datos. Una vez estamos logueados esperamos la cadena inicial que nos devuelve el router, en mim caso “ROOT :>“. Ahí es cuando le pedimos el reboot y lo confirmamos. Ya está, el router se reinicia sólo. Obviamente todas las cadenas que esperas recibir deben coincidir con las de tu router, las aquí indico son las del mío.

Creando enlaces punto a punto entre servidores Linux con OpenVPN

Si hace unas semanas veíamos como crear un servidor VPN, hoy veremos como crear un túnel seguro dedicado entre dos servidores que esté permanentemente levantado y que se cree automáticamente al reiniciar alguna de las máquinas. ¿La utilidad? Tener una conexión directa con la máquina de backups, con un servidor de bases de datos, con la oficina, con otro centro de datos… seguro que se te ocurren más 😛 .

Como comentábamos entonces, PPTP no es el paradigma de la seguridad, pero la ventaja que tiene es la facilidad con que un cliente se conecta contra el servidor sin apenas conociemientos.  El ejemplo de hoy no nos servirá para crear túneles bajo demanda, pero veremos lo sencillo que puede ser crear un túnel dedicado. Siempre que se habla de VPN se termina en IPSec, pero ¿quién no se vuelto loco con la instalación de un túnel de este tipo? ¿Es realmente necesario utilizar IPSec para cualquier túnel? Yo creo que no, cada cosa para lo que está. Igual que con el primer artículo creábamos un sistema de VPN sencillo para el usuario, ahora que simplemente queremos conectar máquinas entre sí recurrimos a otro software que lo hace sin quebraderos de cabeza.

Necesitaremos, obviamente, máquinas Linux (aunque también se pueden emplear Windows) y el software que utilizaremos en este artículo: OpenVPN.

Tal y como reza en la home de OpenVPN:

Starting with the fundamental premise that complexity is the enemy of security, OpenVPN offers a cost-effective, lightweight alternative to other VPN technologies that is well-targeted for the SME and enterprise markets.

Y además lo cumple, ligero y sencillo.

Configuración

Lo primero será instalar el software. En mi CentOS no tendremos más que hacer

yum install openvpn lzo

Lzo nos proporcionará la compresión a través del túnel.

Aunque no lo parezca, ya queda poco 🙂 . Ahora debemos generar la clave SSL  que asegurará las comunicaciones a través del tunel. Muy sencillo, sólo necesitaremos un comando:

openvpn --genkey --secret clave.key

Esta clave debemos copiarla a /etc/openvpn de los dos servidores que se quieren conectar. Para copiarla al segundo lo puedes hacer con scp:

scp clave.key tuservidor.com:/etc/openvpn

Finalmente sólo nos queda el archivo de configuración. En el primer servidor creo un archivo con el nombre del segundo para identificar fácilmente qué conexión es puesto que podemos haber definido varios túneles.

[osus@servidor1 openvpn]# cat servidor2.conf
port 1194
proto udp
remote servidor2.com 1194
dev tun
ifconfig 192.168.2.2 192.168.2.1
secret clave.key
comp-lzo
keepalive 10 60
ping-timer-rem
persist-tun
persist-key
log /var/log/vpn.log

Como se puede ver, la configuración es muy sencilla, no necesita apenas explicación. En servidor2.com pones la IP del servidor al que se va a conectar, defines el puerto, las IPs privadas del servidor y del cliente y voilà, ya tienes la primera máquina preparada.

Vamos ahora con el segundo servidor.

[osus@servidor2 openvpn]# cat servidor1.conf
port 1194
proto udp
remote servidor1.com
dev tun
ifconfig 192.168.2.1 192.168.2.2
secret clave.key
comp-lzo
keepalive 10 60
ping-timer-rem
persist-tun
persist-key
log /var/log/vpn.log

Casi lo mismo que en el caso del servidor, sólo cambiamos la IP del servidor remoto y el orden de las IP’s privadas.

¡Ya está!

Sólo nos queda probarlo. Lanzamos el servicio en las dos máquinas y veremos si todo ha ido bien:

[osus@servidor1 ~]# service openvpn start
Starting openvpn:                                          [  OK  ]

[osus@servidor1 ~]# ifconfig tun0
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:192.168.2.1  P-t-P:192.168.2.2  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

Parece que sí. Para verificar que, en efecto nos hemos conectado satisfactoriamente revisaremos el log.

[osus@servidor1 ~]# tail -f /var/log/vpn.log
Mon Feb  2 20:36:11 2009 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Mon Feb  2 20:36:11 2009 LZO compression initialized
Mon Feb  2 20:36:11 2009 TUN/TAP device tun0 opened
Mon Feb  2 20:36:11 2009 /sbin/ip link set dev tun0 up mtu 1500
Mon Feb  2 20:36:11 2009 /sbin/ip addr add dev tun0 local 192.168.2.1 peer 192.168.2.2
Mon Feb  2 20:36:11 2009 UDPv4 link local (bound): [undef]:1194
Mon Feb  2 20:36:11 2009 UDPv4 link remote: servidor2.com:1194
Mon Feb  2 20:36:16 2009 Peer Connection Initiated with servidor2.com:1194
Mon Feb  2 20:36:16 2009 Initialization Sequence Completed

Parece que sí, que ha funcionado. ¿Veremos la segunda máquina desde la primera?

[osus@servidor1 ~]# ping 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
64 bytes from 192.168.2.2: icmp_seq=1 ttl=64 time=276 ms
64 bytes from 192.168.2.2: icmp_seq=2 ttl=64 time=197 ms

Eso es un sí ¿no? 😛 .

Consideraciones adicionales

Obviamente habrá que abrir el puerto que hemos configurado para el túnel en el router (si lo hubiese) y verificar que el firewall no está bloqueando el tráfico desde o hacia ese puerto, suele ser el principal problema a la hora de conectarse, si tu firewall está bien configurado bloqueará automáticamente todo el tráfico que no sea el permitido (http, smtp, dns…).

Si queremos que el túnel se inicie automáticamente al reiniciar las máquinas habrá que añadirlo a los servicios de arranque. En el caso de CentOS/RHEL sería:

chkconfig --level 2345 openvpn on

Finalmente nos queda un comentario. Supongamos que la primera máquina es nuestro servidor remoto y el segundo está en nuestra oficina y que queremos poder llegar desde el primero a otros servidores de la oficina. Supongamos también que la red de la oficina es del rango 192.168.0.0/24 (el túnel, como hemos visto, está en la red 192.168.2.0). En el servidor enrutamos el tráfico a las IP’s de la oficina a través del túnel añadiendo esta línea al archivo de configuración de la VPN, OpenVPN añadirá la ruta cada vez que se levante el túnel.

route-up "route add -net 192.168.0.0/24 tun0"

Ya sólo te queda añadir en el servidor cliente las reglas iptables adecuadas para que el tráfico se diriga hacia donde tú quieres que lo haga.

Éso es todo. Como os prometí, es un sistema muy sencillo a la vez que potente y ligero.

Copias de seguridad de bases de datos

A través de Pensamientos Ágiles llego a un artículo donde explican una interesante manera de hacer backups de bases de datos Mysql utilizando subversion. La idea es excelente, pero me plantea una serie de dudas si se trata de bases de datos de gran tamaño. Creo que es genial para mantener la estructura de la base de datos de un proyecto y tener controlados los cambios que se realizan sobre ella, pero mantener también los datos… no acabo de verlo.

En uno de mis proyectos tengo una base cuyo dump ocupa alrededor de 1Gb. Yo, sinceramente, no veo coherente mantener semejante archivo en svn. La idea es excelente y para bases de datos “pequeñas” creo que sí es útil, habría que decidir, en todo caso, qué entendemos por “pequeñas“. Pero insisto, no lo veo para archivos grandes, y 1Gb, tengamos el criterio de grande/pequeño que tengamos, es un archivo grande.

Normalmente lo que hago para hacer los backups es sacar un dump de cada base de datos, de manera que pueda recuperar cualquiera de ellas, y comprimir el resultado con bzip2. ¿Por qué bzip2 y no gzip? Simplemente por el nivel de compresión. En su día hicimos pruebas con ámbos sistemas y bzip2 salía mejor parado. Éstos son los resultados que obtendríamos hoy en día con la compresión estándar de ámbos sistemas:

-rw-r--r--   1 osus   users  1023246033 Feb  2 01:50 bbdd.sql
-rw-r--r--   1 osus   users   141481434 Feb  2 01:50 bbdd.sql.bz2
-rw-r--r--   1 osus   users   190733372 Feb  2 01:50 bbdd.sql.gz

Con bzip2 el backup pesa unos 50mb menos.

El script que utilizo para hacer backups de base de datos es más o menos lo que suele hacer la mayoría de la gente:

#!/bin/sh
dia=`date +%w`
for i in ` /usr/bin/mysql -N --execute='show databases;' `
do
echo "Haciendo backup de $i"
/usr/bin/mysqldump -C --opt $i > /backup/bbdd/$dia/$i.sql
bzip2 -fz /backup/bbdd/$dia/$i.sql
done

En otras palabras, mantengo las copias completas de los últimos siete días, semanalmente se se reemplazan por las del día en que nos encontramos. En la carpeta del día correspondiente genero los dumps comprimidos de todas las bases de datos.

El último paso es copiar los dumps del día a otra máquina. La tarea que se ejecuta diariamente lanza el proceso una vez termina de crear los backups. Esto lo hacemos a dos máquinas distintas. La primera es una máquina externa y se envían por ssh desatendido, utilizando claves públicas, de manera que la máquina remota no solicita la clave del usuario. La segunda es una máquina de nuestra oficina con la que tenemos una vpn que se levanta antes del backup, se copia con ssh destendido también y se cierra al terminar. En ésta máquina de la oficina hay otra tarea que diariamente restaura el backup de ciertas bases de datos en su Mysql local matando así dos pájaros de un tiro: por un lado tenemos la base de datos de desarrollo casi idéntica a la de producción, faltarían sólamente los últimos datos del día, y por otro lado nos aseguramosn de que los backups que se realizan son correctos ya que en caso contrario fallaría la restauración.

Hasta ahora nos ha funcionado bien el sistema. En alguna ocasión hemos tenido que recuperar alguna tabla de ciertas bases de datos y no hemos tenido excesivos problemas, todo ha funcionado a la perfección.

Vuelvo a repetir que esto no es una crítica al sistema del subversion, todo lo contrario, la idea me parece cojonuda, pero creo que habría que puntualizar o estudiar bien la viavilidad en ciertos escenarios.