lunes, 9 de julio de 2012

Boys say GO!!!


Boys say Go!


Ya desde hace aproximadamente unos cinco años que la gigante empresa Google comenzó a trabajar en un nuevo lenguaje de programación. Desde 2007, al menos, tenemos noticias del desarrollo del lenguaje Go. Y desde fines del 2011 sabemos que está suficientemente estable como para darle una oportunidad en nuestras máquinas. En este artículo vamos a meternos un poco en las generalidades del lenguaje Go 1, y quién nos dice, quizá en los próximos podamos jugar un poco más con él.

Generalidades

En un anuncio de fines del año 2009, Google anunció la existencia de las primeras versiones beta del lenguaje de programación Go, autoproclamándose como rápido, productivo, y divertido para los desarrolladores. El concepto de “diversión de desarrolladores” me dejó algo preocupado. Los otros dos tampoco pasaron desapercibidos por mi cabeza.

En ese momento Goggle describió al lenguaje Go como experimental, y un intento de combinar la velocidad de trabajo de lenguajes dinámicos como lo es Python, con los niveles de performance que se pueden encontrar en C/C++. Cuando leemos las opiniones de algunos miembros del “Go team”, encontramos opiniones que lo ponen como “compilable en pocos segundos” y “veloz casi como el C/C++”.

Los que hemos tenido el gusto de trabajar con Java sabemos lo que eso significa, y cómo nuestros ojos se pueden llenar de lágrimas con el sólo hecho de pensar que la promesa de perfomance se cumpla de una vez por todas. Y si aparte hemos trabajado con Perl, Python ó Ruby, nos corre un frío por la espalda de pensar en términos de simplicidad de programación.

Según la literatura nativa del gigante de las nubes, el mundo ha cambiado radicalmente en términos de computación en la última década, pero no han emergido nuevos lenguajes de programación que los acompañen. Por ejemplo, la capacidad de procesamiento de las máquinas se ha multiplicado de forma exponencial, hecho no acompañado por un decremento en los tiempos necesarios para programar dichos sistemas.

Es cierto que la gente de sistemas lentamente (o no tanto) comenzó a dejar los leguajes de programación fuertes como lo son C/C++ y Java en favor de los interpretados como son Python y Javascript.

Por lo tanto, Go nace como respuesta a la creciente frustración generada en torno de los lenguajes y entornos de programación existentes, dado que la programación se ha vuelto en extremo complicada, así como la elección del lenguaje de programación adecuado para cada tarea. Dicha elección se debía basar en simplicidad en la programación versus rapidez en la compilación versus ejecución eficiente, no existiendo una combinación de las tres en un mismo lenguaje.

Go intenta cubrir el gap existente entre la simplicidad de programación existente en un lenguaje interpretado y dinámico con la eficiencia y velocidad de uno estático, del estilo de los compilados.

Algo de historia

Robert Griesemer, Rob Pike y Ken Thompson comenzaron a pensar en las bondades de este nuevo lenguaje de programación allá por el 21 de setiembre del 2007, cuando acá en Argentina estábamos festejando la primavera, y allá en USA estaban sintiendo los primeros fríos de otoño, y oían el ruido de empresas como Lehman Brothers que comenzaban a decir que tenían algunos problemitas económicos.

En unos pocos días los objetivos planteados se transformaron en un plan de desarrollo de una solución, y mejor aún, ya se sabía aproximadamente cómo debía ser esa solución. Claro está, en ese momento aún todo era una promesa, por lo que se trabajaba part-time en ese proyecto, mientras que se seguía con las tareas habituales.

En enero del 2008 Ken trabajó en un compilador sobre el cual volcar y explorar las ideas que se habían plasmado un tiempo atrás. Ese compilador ya generaba código C en su salida. Pero las cosas comenzaron a ponerse más jugosas para mediados del 2008.

En ese momento las acciones de Lehman Brothers habían bajado un 75%, AIG anunciaba que no podía seguir brindando seguridad para los fondos de inversión en USA, se veía en el horizonte una nueva crisis económica de dimensiones bíblicas, y en Google el proyecto se había vuelto de tiempo completo. Así es, todo el team comenzó a trabajar en forma intensiva en la generación del lenguaje de programación Go.

En mayo del 2008, IanTaylos de forma independiente comenzó a trabajar en un front end de GCC para Go usando el borrador de las especificaciones existentes. Para finales de ese mismo año, se unía al proyecto Russ Cox para ayudar a mover el proyecto del estado de prototipo al de realidad.

Así, Go se volvió público el 10 de noviembre del 2009, momento desde el cual mucha gente de diferentes comunidades contribuyeron con ideas, discusiones, y por supuesto, código.

De todo este proceso, obtuvimos un lenguaje nuevo, cuya sintáxis puede tener algo de parecido a C, pero con una intensa lavada de rostro, y alguna que otra cosa que nos hará la vida miserable a la hora de la depuración, como lo es la declaración del tipo de una variable luego de su nombre. Si antes pensábamos en “int x”, ahora debemos imaginar un “x int”.

Ah, también obtuvimos una mascota que es, desde mi humilde punto de vista, espantosa. La idea de ese bicho inmundo, dibujado por un niño que nunca la ha visto, con lápices gastados, y bajo los claros efectos de drogas pesadas, me deja pensando sobre la verdadera capacidad de la gente de marketing de Google. No entiendo cómo algunas cosas les salen tan bien y otras tan mal. Pero bueh, esta vez la gente de IT se llevó todas las rosas. Como debe ser, qué tanto.



Ni chicha ni limonada

Go es y no es un lenguaje orientado a objetos. Si bien tiene tipos y métodos y permite un estilo de programación orientada a objetos, no posee una jerarquía de tipos. El concepto de “interface” de Go provee un aspecto bastante diferente del que posee Java, por ejemplo, ya que se consideró desde el momento del diseño que como ahora está sería mucho más fácil de usar, y de una u otra forma, mucho más general.

Así y todo tenemos formas de embeber tipos dentro de otros tipos, logrando un comportamiento similar (nótese que digo similar, y no igual) al que tendrían las subclases. Luego, los métodos en Go son más sencillos que en C++ o Java, ya que se pueden definir para cualquier tipo de datos, aún para los incorporados en el mismo lenguaje.

En sí, en elementos como estos, o como los referidos al manejo de interfaces en general, funciones y métodos, o dispatch automático de método, vemos el intento de Google por generar un lenguaje de programación que simplifique muchas de las cosas que hoy en día nos molestan en otros lenguajes.



Empecemos

Sé que ya les debe estar picando el cuerpo por las ganas de probar este lenguaje de programación. Si no es así, hay médicos y otros dealers que pueden ayudarlos. Entonces vamos a ver cómo instalamos el lenguaje Go en nuestras máquinas, y comenzamos a hacer de las nuestras.

Lo primero será entender que tenemos dos tipos de compilador Go: uno llamado “gc”, independiente, y otro llamado “gccgo”, que apunta a ser parte del proyecto GCC. Duerman tranquilos, porque si ejecutan “yum search gccgo”, o “apt-cache search gccgo”, no van a encontrar nada.

Según dice Google, gc es más maduro y está mejor probado que gccgo. Por lo tanto, iremos por la primer opción.

Recordemos que para poder instalar gc nuestro sistema deberá poseer FreeBSD 7 o superior (al día de la fecha Debian GNU/kFreeBSD no está soportado), Linux 2.6.23 o superior con glibc (CentOS, RHEL, o sus derivados en versión 5 no están soportados, así como las distribuciones para ARM), MacOS X 10.6/7 (utilizando el gcc que obtenemos con Xcode), o Windows 200 o superior (usando mingw gcc, obviamente).

Bajaremos entonces el paquete correspondiente a nuestra distribución desde el URL http://code.google.com/p/go/downloads/list, lo descomprimiremos y destarearemos en /usr/local, y agregaremos los valores de este nuevo path a nuestras variables de entorno:

# cd /usr/local
# tar zxvf /home/hecsa/Downloads/go1.0.2.linux-amd64.tar.gz
...
$ vi .bash_profile
(agregar estas líneas)
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
(salvar el archivo)
$ . ./.bash_profile
$ which go
/usr/local/go/bin/go

Listo, el prolongado proceso de instalación del lenguaje Go 1 ha llegado a su fín. Sencillo, ¿no?



La hora de la verdad

Como siempre, un programador no puede jactarse de serlo si no ha escrito su primer programa, es decir, el tan afamado “Hola, mundo!”. No podemos ser menos en este caso, así que aquí vamos:

$ vi hola.go
(agregar este contenido)
package main
import "fmt"
func main() {
fmt.Printf("Hola, mundo!\n")
}
(salvar el archivo)

Y como es de esperar, lo ejecutamos con el comando “go run”:

$ go run hola.go
Hola, mundo

Ahora bien, lo primero que me vino a la mente cuando ví este tan elaborado programa en base al cual se erigen cursos enteros de varios meses de duración fue analizar hasta dónde lo comentado por Google era o no real, y para eso, decidí armar dos programas más, también una nueva demostración de increíbles niveles de desarrollo por mi parte humildemente.

El primero es en C nativo:

$ vi hola.c
(agregar estas líneas al archivo)
#include
main()
{
printf("Hola, mundo!\n");
}
(salvar el archivo)

Y lo compilo con el GCC, pero utilizando el comando “time” antes, veremos para qué en breve:

$ time gcc -o hola hola.c
real 0m0.775s
user 0m0.085s
sys 0m0.079s

Como uno de los puntos de comparación es el lenguaje de programación Java, no quiero dejar de comparar el nivel de performance frente a un programa altamente elaborado como el que estamos escribiendo aquí.

Entonces, escribo su equivalente en Java:

$ vi HolaMundo.java
(agrego estas líneas al archivo)
class HolaMundo
{
public static void main(String args[])
{
System.out.println("Hola, mundo!");
}
}
(salvo el archivo)

Y lo compilo con el JDK 1.7.0_05 de Oracle:

$ time javac HolaMundo.java
real 0m2.020s
user 0m2.826s
sys 0m0.143s

Como el código de Go puede también compararse con un código interpretado (en palabras de los mismos desarrolladores, se lo compara en simplicidad con Python), he decidido armar el mismo código avanzadísimo, el “Hola, mundo!”, pero en Python:


$ vi Hola (nótese la mayúscula para diferenciarlo del programa en C)
(agregar estas líneas)
#!/usr/bin/python
print "Hola, mundo!"
(salvar el archivo)
$ chmod +x Hola

Ahora ha llegado la hora de la verdad. Ejecutaremos cada uno de los tres “Hola, mundo!”, y veremos el tiempo que significa para un mismo sistema:

$ time go run hola.go
Hola, mundo!
real 0m0.521s
user 0m0.433s
sys 0m0.074s

$ time java HolaMundo
Hola, mundo!

real 0m0.234s
user 0m0.138s
sys 0m0.040s

$ time ./hola
Hola, mundo!

real 0m0.003s
user 0m0.000s
sys 0m0.003s

$ time ./Hola
Hola, mundo!

real 0m0.077s
user 0m0.055s
sys 0m0.018s

Como podemos ver, el programa en C es por lejos una mejor opción para este tan elaborado ejemplo. Pero no olvidemos una pequeña trampa: nosotros nunca hemos compilado el programa en Go. Por lo tanto, si queremos ser justos, al menos en un primer momento, y con un razonamiento casi irracional, tendríamos que sumar los tiempos. Entonces:

Go:

real 0m0.521s
user 0m0.433s
sys 0m0.074s

C:

real 0m0.003s + 0m0.775s = 0m0.778s
user 0m0.000s + 0m0.085s = 0m0.085s
sys 0m0.003s + 0m0.079s = 0m0.082s

Java:

real 0m0.234s + 0m2.020s = 0m2.254s
user 0m0.138s + 0m2.826s = 0m2.964s
sys 0m0.040s + 0m0.143s = 0m0.183s

Python:

real 0m0.077s
user 0m0.055s
sys 0m0.018s

¿Es posible extraer una conclusión de esto? Sí, pero muy vaga: C es por lejos más rápido que Go, y ni que hablar que Java. La relación en tiempos de ejecución es de 1 a 180, aproximadamente. A Java directamente lo dejamos fuera de este partido, juega en otra liga, mucho más lenta, por supuesto. PERO, si consideramos que Go contempla también dentro de su entorno el interpretar el código, vemos que Python aún es más rápido que Go en una relación de 1 a 7.

Obviamente, no es esto lo que hay que evaluar en un lenguaje de programación, ya que estamos de acuerdo que escribir, por ejemplo, una página web utilizando C va a llevarnos notáblemente más tiempo que 0.778 segundos, así como dudo que podamos armar un driver en html.

Pero bueno, son las primeras pruebas que ejecuto, y son reales.

Conclusión

En este artículo hemos entregado sólo una breve reseña de lo que es el lenguaje de programación Go 1, y hemos hecho una despiadada demostración de genialidad de desarrollo al adentrarnos en complicadísimos algoritmos dignos de la más avanzada bibliografía (no se lo crean, por favor). En futuros números podremos ponernos a sacarle un poco más de jugo a este nuevo lenguaje, viendo para qué es bueno, y qué podemos hacer con él. ¡Hasta pronto, amigos!
Y recuerden: “Don't say no, boys say Go!”

Hernán “HeCSa” Saltiel
AOSUG leader
CaFeLUG Member
Boca happy fan
Club Amigos de Pumper Nic


PD: El título de este artículo y su frase final se refieren a un tema musical antiquísimo de Depeche Mode que se puede escuchar aquí, en su excelente versión en vivo en Londres, año 1986: http://www.youtube.com/watch?v=qVAUOxveuvc

viernes, 27 de abril de 2012

Más conexiones en tu PostgreSQL

Bueno, este artículo es un clásico, pero no podía faltar aquí.
El tema es así, resulta que tenemos un máximo de conexiones en PostgreSQL de 100, ni una más, ni una menos.
Y como siempre pasa, llegamos al límite.
Entonces, intentamos incrementarlo de una forma muy sencilla, cambiando el parámetro "max_connections" en el archivo postgresql.conf.
Pero es entonces, cuando ejecutamos el comando "service postgresql-9.1 restart" que vemos que un mensaje del estilo de "Failed" aparece.
Verificamos nuestro archivo de startup de postgres ("pgstartup.log"), y nos encontramos con esto: 

FATAL:  could not create shared memory segment: Invalid argument
DETAIL:  Failed system call was shmget(key=5432001, size=39223296, 03600).
HINT:  This error usually means that PostgreSQL's request for a shared memory
segment exceeded your kernel's SHMMAX parameter.  You can either reduce the re
quest size or reconfigure the kernel with larger SHMMAX.  To reduce the request
size (currently 39223296 bytes), reduce PostgreSQL's shared memory usage,
perhaps by reducing shared_buffers or max_connections.
        If the request size is already small, it's possible that it is less
than your kernel's SHMMIN parameter, in which case raising the request size or
reconfiguring SHMMIN is called for.
        The PostgreSQL documentation contains more information about shared
memory configuration.


Eso quiere decir que, antes de cambiar el valor de "max_connections", tenemos que aumentar el valor del parámetro de kernel "shmmax".
Para ver este valor podemos ejecutar el siguiente comando:

[root@server ~]# ipcs -lm

------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1


El valor que tendremos que incrementar es el identificado con "max seg size (kbytes)". Está en 32 MB, y lo subiremos para que soporte cuatro veces lo que ahora tenemos.



Para saber el valor en bytes tenemos dos opciones: usar la calculadora, o ejecutar el siguiente comando:

[root@server ~]# cat /proc/sys/kernel/shmmax
33554432

Entonces, sencillamente multiplicamos este valor por cuatro (134217728), y agregamos una línea como la siguiente en el archivo /etc/sysctl.conf:

kernel.shmmax = 134217728

Ahora si ya teníamos el valor de "max_connections" en 400, podremos rebootear nuestro sistema, y ver cómo la base de datos funciona sin problemas, mostrándonos su capacidad de soportar hasta 400 conexiones.

Listo, todo funcionando! Hasta la próxima!



martes, 24 de enero de 2012

SPICE - Una introducción para entenderlo

(Artículo publicado en la revista Tuxinfo: http://www.tuxinfo.com.ar )



    Ya hemos hablado, en varias oportunidades, sobre formas de virtualizar servidores. También hemos abordado los temas relativos a la virtualización de escritorios. Alguna vez hablamos de computación en la nube, y nos maravillamos por la forma en la que este concepto evoluciona y se aplica, cada día, a más elementos que hasta ahora acostumbrábamos usar en forma local. Y es en este tópico en particular donde nace una nueva forma de exportar las interfaces gráficas de los escritorios para alimentar clientes delgados, o PCs que ya no tienen la potencia necesaria para funcionar por sí solas. SPICE, un acrónimo de “Simple Protocol for Independent Computing Environment”, será el invitado de honor de este artículo.

    Hoy nos pondremos a analizar SPICE, que está emergiendo como una tecnología alternativa, pero altamente mejorada, a los sistemas de escritorios exportados por la red, y que se perfila para ser, en un futuro cercano, el protocolo elegido cuando se deseen las mejores prestaciones en lo que a integración, velocidad y gestión de audio y video refiere.


Dos se vuelven uno


    SPICE fue originalmente desarrollado por la empresa Qumranet, quien en su época de independencia corporativa se dedicaba a desarrollar entornos de virtualización de escritorios basados en el uso de KVM (Kernel Virtual Machine). Suena bastante lógico que esta empresa haya trabajado sobre este protocolo si tenemos en cuenta que fueron ellos mismos los que crearon y mantuvieron el mismísimo hipervisor KVM, que hoy en día podemos ver en sistemas operativos GNU/Linux y hasta en OpenIndiana gracias a los aportes y el desarrollo de la empresa Joyent.

    Este producto resultó como un subconjunto de su idea original, Solid ICE, que en su momento parecía estar posicionándose como el producto de virtualización de escritorios por excelencia, dado que entregaba sus interfaces mediante el uso de una interfaz web, o un pequeño cliente delgado. Como todos nos podemos imaginar, ICE es parte del nombre SPICE, justamente por ser el acrónimo de “Independent Computing Environment”.



    Y como muchas veces pasa en el mundo corporativo, esta pequeña empresa israelí que armaba soluciones de código cerrado para GNU/Linux fue comprada en el año 2008 por Red Hat, quien un tiempo después integró su cartera de productos con los que Qumranet poseía. Por supuesto, abrió su código a las comunidades, permitiéndoles alimentarlo y acrecentarlo para llegar a ser lo que es ahora.


Lo que pensamos que es

    Así es que SPICE es una solución de computación remota que provee a sus clientes acceso a sus entornos gráficos y dispositivos (teclado, mouse, micrófono, parlantes, etc.). Logra una experiencia similar a la que se tiene cuando se interactúa con una máquina local gracias al uso de un sistema de análisis de potencia que dependiendo de lo que se posea del lado del cliente puede bien enviar comandos para generar (render) los gráficos del lado del cliente, si éste tiene suficiente potencia, o puede procesar y sólo enviar los elementos necesarios para dibujar pantallas del lado del cliente, si el cliente tiene poco poder de procesamiento.

    A diferencia de los protocolos de presentación remota de escritorios, tales como RDP, VNC o ICA, SPICE se presenta como una arquitectura basada en varias capas que interactúan para mejorar la experiencia del usuario. Estas capas son:

SPICE Driver: Es un componente que se encuentra en cada escritorio virtual. Un escritorio virtual es una de las máquinas virtuales que se podrá generar partiendo de un hipervisor como lo es, efectivamente, KVM.
SPICE Device: Es éste el componente existente en el entorno de virtualización en sí mismo, como ser un hipervisor.
SPICE Client: Este componente reside del otro lado de esta relación, es decir, en el cliente delgado, PC a punto de ser tirada a la basura por vieja, o el navegador. A través de esta capa se accede a cada escritorio virtual.

    Desde el punto de vista de la arquitectura, SPICE está compuesto por el protocolo SPICE, el servidor SPICE, y el cliente SPICE. Sus capas de “Driver” y “Device” serán entregadas por un dispositivo QLX y un driver QLX, respectivamente. El dispositivo QLX se encuentra en el paquete de virtulización “qemu”, y el driver QLX en el mismo sistema operativo que exporta su interfaz gráfica.



    Cuando un cliente se conecta a un servidor SPICE, lo hace a través de canales. Cada tipo de canal está dedicado a un tipo de datos en particular. Usa su propio socket, y puede estar encriptado utilizando SSL. Del lado del cliente cada canal será manejado por un hilo de procesamiento separado (thread), lo que nos permitirá definir valores que gestionen el nivel de rendimiento de nuestros escritorios. Si quisiéramos, podríamos aplicar QoS (Quality of Service) a cada thread, y entonces modificar su grado de precedencia dentro del sistema operativo.

    El canal principal se llama “RedClient” y es el responsable de controlar todos los demás. Parte de este control es la creación de canales, su conexión y desconexión, entre otros.

    Entre los demás canales encontramos:

Main: Este canal está implementado por RedClient.
DisplayChannel: Es el responsable de gestionar los comandos gráficos, así como las imágenes y los flujos de video.
InputsChannel: Es quien gestiona la entrada por teclado y mouse.
CursorChannel: Es el que maneja la posición y visibilidad del cursor.
PlaybackChannel: Para nuestra grata alegría, este canal maneja la forma en la cual el servidor envía audio y video al cliente de forma tal de permitir ver, por ejemplo, videos en forma remota con prácticamente ningún tipo de retardo.
RecordChannel: Es el encargado de capturar audio y video del lado del cliente, y enviarlo al servidor.

    A diferencia de otros protocolos que envían al cliente actualizaciones de frame-buffer, SPICE envía comandos de gráficos 2D, y hasta 3D, si bien este último aún no está listo. Media hora de horno, y lo podremos disfrutar.


Algo un poco divertido

    Veamos, entonces, la forma en la que se establece una conexión entre un cliente y un servidor de SPICE.

    El proceso de conexión de canales es iniciado por el cliente. Éste envía un mensaje (RedLinkMess) al servidor. Entonces, el servidor responde con otro mensaje (RedLinkReply). Cuando el cliente recibe el RedLinkReply examina su código de error, y actúa en consecuencia. En el caso de no haber un código de error (porque no han habido errores) encripta su contraseña utilizando la clave pública que le envió el servidor dentro del mensaje RedLinkreply y se lo envía al servidor. Entonces el servidor recibe la clave, y envía un mensaje más con el detalle del enlace que el cliente debe utilizar. El cliente examina el link, y si es adecuado (se verifica que su código de error coincida con el que el cliente posee) se establece una conexión válida. No es tan terrible, y ocurre en una fracción de segundo.

    Si alguna vez hemos viajado en un autobús (colectivo para mis paisanos) sabemos que de tanto en tanto hemos prestado nuestro boleto a un inspector que nos lo solicitaba para saber si habíamos pagado o no. Si habíamos pagado, todos felices. Si no habíamos pagado, bueno, nos bajaban del autobús, generalmente acompañados de algún vocablo o frase mundana que evocaba a un pariente cercano nuestro.
   
    SPICE también tiene una forma de controlar boletos (tickets) a través de su sistema de ticketing. Este sistema es la forma que tiene SPICE para asegurarse que el cliente que está sólicitando la conexión es una fuente confiable. El servidor logra esto generando un ticket que contiene una clave y un tiempo de expiración. Cuando el tiempo de la conexión supera este valor, el ticket completo expira.

    Este ticket se encontrará encriptado generando una clave RSA de 1024 bits cuya porción pública es enviada al cliente mediante el mensaje RedLinkInfo. El cliente utiliza esta clave para encriptar la contraseña y enviarla de vuelta al servidor a través del mensaje recién comentado, RedLinkMess. Entonces el servidor desencripta la contraseña, la compara con el ticket, y se asegura que se ha recibido dentro del período de tiempo establecido.

    Para hacer corta una historia larga, SPICE implementa una forma de comunicación muy parecida a la que tiene el mismísimo protocolo TCP/IP, con formatos propios de ping, por ejemplo.


Moverse

    Hasta ahora nos hemos focalizado en el caso de tener un servidor y un cliente. Pero ¿qué pasaría si un servidor se debe sacar de línea, o si sencillamente se cae, producto de un mal funcionamiento? Todo está pensado en el protocolo SPICE, y para casos como estos, tenemos a nuestra disposición un conjunto de mensajes que nos permitirán migrar la sesión desde un servidor a otro.

    Principalmente, se comenzará enviando mensajes para que se migran los canales de mensajes que el cliente se encuentra utilizando, y que por lo tanto están abiertos. El canal principal será el que utilizará para iniciar el proceso de migración de canales cuando el servidor envía un mensaje al cliente. Entonces éste examina sus componentes, y envía una respuesta al servidor.

    Entonces es que el cliente comienza a utilizar los canales de comunicación con el servidor de destino, desafectando de esta responsabilidad al servidor de origen. Así de sencillo es que una sesión pase de un servidor a otro.

    Si pensamos en un esquema de computación en la nube, donde no hay uno, sino cientos de servidores funcionando como si fueran uno solo, veremos que este mecanismo es extremadamente útil, ya que nos permite balancear la carga de las conexiones entre los diferentes servidores sabiendo que existe la posibilidad de mover sesiones desde un servidor a otro sin generar inconvenientes en el cliente, entregándole una sensación de continuidad muy buena.

    Y por supuesto, dado que uno de los usos del sistema de escritorios virtualizados es el de alimentar soluciones de recuperación ante desastres, encontramos en SPICE una excelente opción para entregar escritorios remotos aún cuando un centro de cómputos entero se destruya, pasando a otro centro de cómputos alternativo sin que el cliente note la diferencia.



Si te querés divertir

    Para finalizar este artículo sobre las bondades del sistema SPICE, abordaremos un tema que ha dejado con la boca abierta a muchas personas. Es la capacidad de manejar video y audio en forma remota prácticamente sin retardo entre el servidor y el cliente, inclusive sobre redes WAN como lo es internet.

    SPICE ofrece muchos mecanismos diferentes de compresión de imágenes que se pueden elegir en el momento de inicializar el servidor, y dinámicamente mientras éste está funcionando. Un método propietario de SPICE es el denominado Quic, basado en el algoritmo SFALIC.

    Como siempre, SFALIC es un acrónimo que significa “Simple Fast and Adaptive Lossless Image Compression”. Resulta que este algoritmo ha sido diseñado, desde su base, para entregar compresión a velocidades elevadísimas. Se basa en predicción lineal, y un método de modelado predictivo de control de errores. Por lo tanto, Quic tendrá un manejo predictivo para el envío de imágenes, permitiendo entonces entregar video por medio de redes WAN con un nivel increible de velocidad y exactitud.

    Otra opción de compresión es LZSS, o su pariente, el Global LZ (Lempel Ziv), o GLZ, que gestiona cambios en las imágenes para enviar comandos de renderización al cliente. Sin meternos en demasiados detalles, GLZ es un algoritmo que analiza las repeticiones de ocurrencias dentro de una cadena de audio, por ejemplo, y las parametriza para evitar el envío redundante de información.

    Imaginemos este último como si escucháramos una canción de discoteca, donde por media hora se escucha el mismo ritmo, las mismas meoldías y el mismo estribillo por períodos de un minuto y medio. No tendría sentido enviar por red media hora de sonidos, si se puede enviar sólo un minuto y medio, y luego un parámetro que especifique la cantidad de veces que se debe repetir. Lo mismo para el video, y así tenemos como resultado un sistema de audio y video bidireccional de alta velocidad.


Conclusión

    Hemos metido nuestra nariz en el protocolo SPICE, y nos hemos enterado que el mundo no termina donde VNC, RDP o ICA nos han dejado. Encontramos que este protocolo, que está en plena producción para entornos virtualizados de escritorios, está siendo mejorado para que se comporte y se maneje de la misma forma en la que hoy usamos VNC, por sólo citar un ejemplo de notable simplicidad.

    Esperemos ver en breve estas implementaciones funcionando en nuestros servidores. Como hacemos en estos casos, no podemos sino sacarnos el sombrero ante tan buen producto ;-) . ¡Nos vemos el mes que viene!


Sobre la originalidad y la reacción

Hace ya un buen tiempo tuve este debate con alguien. Ya ni recuerdo qué lo disparó, pero recuerdo haber hablado de esto. No sé si fue alguna medida internacional de esas que nos dejan boquiabiertos, o alguna otra cosa.
La cosa es que, con todo el tema de SOPA y PIPA, otra vez hablamos de estos temas.
Cuando menos nos lo esperábamos, nuevamente apareció la amenaza de tener un Internet censurado. Y no fue sólo una amenaza, ya que algunos sitios, como es el caso de Megaupload, fueron cerrados por el gobierno de un país diferente del mío. No quiero decir con esto que las decisiones de mi gobierno siempre representan mi pensaniento...
Como nota de color, cito un diario de mi tierra que decidió hablar de Anonymous y Sony, aún luego de haber declarado, el mismo Anonymous, que en ESE caso sí no tenía nada que ver. No hay nada que hacer. El que nació para clarinete no llega a saxofón. No tiene nada que ver, pero leyendo sobre SOPA y PIPA en ese diario me encontré con eso. Me hizo reir un rato.
La cuestión es que generalmente nos encontramos ante actitudes desmesuradas por parte de algunas entidades no representativas, o al menos no para nosotros, que apuntan a defender a quien le da de comer. Ante eso, tenemos dos posibilidades. Una es demostrar que podemos también darles de comer (no, no me gusta esa opción), y la otra es reaccionar en consecuencia.
Claro está, todo tipo de reacción es eso: una respuesta a la acción original de alguien o algo.
Entonces, mi idea es bien sencilla, y se basa justamente en la originalidad. Es decir basta a todo este juego de reacciones, y comenzar a tener acciones originales, que apunten al beneficio de la comunidad informática en general, y a la libertad de que gozamos.
Entonces, eso implicaría una nueva organización que esté completamente de espaldas al esquema de control por parte de entidades como aquellas de que hemos tenido tristes noticias últimamente, manejada por personas que sí se interesen en lo que esta tecnología (léase: ideología) tiene para ofrecer.
Hay un millón de cabos sueltos en este esquema de originalidad, así que agradeceré que los que lean esto le pongan y agreguen los pensamientos que se les crucen, ya que mi cabeza está explotando de repentinas ideas viejas, pero muy renovadas.
Salutte!

sábado, 7 de enero de 2012

Otra vez VirtualBox

Como lo hago siempre luego de instalar un nuevo sistema operativo en mi máquina, me decidí a agregar el repositorio para que me funcione el YUM de mi Fedora 16 con VirtualBox, y así poder revivir mis máquinas virtuales.
Luego de la instalación del repo y de VirtualBox, en lo que no voy a ahondar porque no tendría sentido considerando que está más que claro en el mismo sitio virtualbox.org, me dispuse a instalar el tan querido Extension Pack de VirtualBox para tener RDP, USB, y algún que otro beneficio en las máquinas virtuales.
Pero la sorpresa no fue muy agradable cuando me encontré con el mensaje "Failed to install the Extension Pack The installer failed with exit code 127: The value for the SHELL variable was not found the /etc/shells file".
Más abajo menciona algo sobre que el incidente ya ha sido reportado.
Así que hurgando por la red me encontré con que en un sistema recientemente implementado se debe instalar, primero, todo el kit de compilación de kernel, y el dkms.
Allá vamos, ejecuté, como root:


# yum install dkms binutils gcc make patch libgomp glibc-headers glibc-devel kernel-headers kernel-devel

No fue tan terrible, sólo 28 MB de binarios debieron bajar a mi máquina. Entonces nuevamente fui a la carga haciendo doble click en el ícono correspondiente al Extension Pack. Pero nada, el mismo error de antes.
Mirando un poco el comando VBoxManage me encontré con un hermoso parámetro, justamente el "extpack". Efectivamente, era lo que estaba buscando.
No tuve más que ejecutar:

$ VBoxManage extpack install --replace /home/hecsa/Downloads/Oracle_VM_VirtualBox_Extension_Pack-4.1.8-75467.vbox-extpack 
WARNING: The vboxdrv kernel module is not loaded. Either there is no module
         available for the current kernel (3.1.6-1.fc16.x86_64) or it failed to
         load. Please recompile the kernel module and install it by

           sudo /etc/init.d/vboxdrv setup

         You will not be able to start VMs until this problem is fixed.
0%...
Progress state: NS_ERROR_FAILURE
VBoxManage: error: Failed to install "/home/hecsa/Downloads/Oracle_VM_VirtualBox_Extension_Pack-4.1.8-75467.vbox-extpack": The installer failed with exit code 127: The value for the SHELL variable was not found the /etc/shells file

VBoxManage: error: This incident has been reported.

Bueno, otra vez lo mismo, pero esta vez en modo texto.
Lo hice sencillo, confié en la lógica de los mensajes, y por ende de los programadores de este excelente producto, y me decidí a rebootear el sistema para poder tener el módulo compilado. Pero antes, me dije a mí mismo "no será que algún programador, quizá, no es tan brillante como mi cerebro opina, y se olvidó de verificar si quien lo ejecuta es root?", e hice lo mismo como ese usuario, y no como el mío, hecsa.
El resultado fue interesante:

# VBoxManage extpack install --replace /home/hecsa/Downloads/Oracle_VM_VirtualBox_Extension_Pack-4.1.8-75467.vbox-extpack
WARNING: The vboxdrv kernel module is not loaded. Either there is no module
         available for the current kernel (3.1.6-1.fc16.x86_64) or it failed to
         load. Please recompile the kernel module and install it by

           sudo /etc/init.d/vboxdrv setup

         You will not be able to start VMs until this problem is fixed.
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Successfully installed "Oracle VM VirtualBox Extension Pack".

Si bien me dispuse a rebootear mi maquinilla, me puse a pensar que los programadores se equivocaron en varias cosas:
- Se olvidaron de verificar si el usuario que ejecuta esto tiene permisos de root o no.
- Dicen que no se podrá levantar ninguna VM, pero no es así, dado que sí levantan, pero sin el Extension Pack, al menos hasta que rebootee. Pero luego del reboot, la gran sorpresa: las máquinas virtuales aún no levantaban el USB.
Bien, seguí las instrucciones de ejecutar:

# /etc/init.d/vboxdrv setup

...y luego volví a probar suerte...pero sin suerte. Los USB aún no se ven, y probaré volver a bootear para ver si ahora sí los levanta.
Luego del reboot, ejecuté de nuevo:


# VBoxManage extpack install --replace /home/hecsa/Downloads/Oracle_VM_VirtualBox_Extension_Pack-4.1.8-75467.vbox-extpack
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Successfully installed "Oracle VM VirtualBox Extension Pack".

Wow! Sin erorres! Pero nada es tan bello, el sistema sigue sin reconocer el subsistema USB.
La frustración no tiene límites...qué quieren que les diga...esto con Sun no pasaba...

[ACTUALIZACIÓN - GRACIAS SALVA!!!]
No olviden agregar Vuestro usuario al grupo vboxusers, de esa forma al reloguearse todo vuelve a estar en su lugar, y el error desaparece.

lunes, 8 de agosto de 2011

Punk Fluid, the Shorewall


 Para los que ya se cansaron de saber que sus máquinas están abiertas al mundo y no se animaron a jugar con las herramientas que Netfilter posee, en este artículo veremos algunos conceptos iniciales sobre la teoría de filtrado de paquetes en GNU/Linux, apuntando a una implementación del producto Shorewall para una instalación muy típica.



La problemática

El escenario planteado comienza a tomar forma cuando vemos el estado inicial de nuestra instalación, y el punto al que queremos llegar. De esa forma veremos cómo encarar un proyecto en el cual tengamos que implementar un sistema del tipo firewall, utilizando la mayor cantidad de filtros, redireccionamientos, y demás juguetes posibles. Y todo esto lo haremos implementando el producto Shorewall para simplificar nuestra dura vida.

Originalmente nuestra red sólo tiene una salida a internet, con dirección IP pública variable o fija (no modifica mucho el contenido de este artículo que esto varíe, ya que en el caso de no tener una dirección IP fija, de seguro se podrá utilizar algún servicio de DNS dinámico para los casos de IP variable, como ser dyndns, o no-ip). A través de un pequeño equipo basado en GNU/Linux con dos tarjetas de red, una que se conecta con esta salida pública, y otra apuntando a la red privada, es que todos los integrantes de nuestra empresa están obteniendo salida a Internet utilizando un proxy (léase Squid, o lo que se prefiera. En nuestro caso pensamos en Squid) configurado en el puerto 8008.

Veamos este diagrama esquemático para tener una idea más acabada de la instalación existente:



El desafío planteado incluye varios elementos a tener en cuenta, como ser:

  1. Todo cliente que quiera salir a Internet directamente, y sin configurar su proxy en su sistema, deberá ser redirigido automáticamente al puerto 8008.
  2. Todo protocolo de red diferente de la navegación por Internet deberá salir al mundo utilizando enmascaramiento de direcciones IP.
  3. Sólo habrá un cliente que tendrá permitido salir a Internet sin pasar por el proxy, y tendrá la dirección IP 10.100.100.10.
  4. La red interna continuará utilizando el rango de direcciones IP 10.100.100.0/24.
  5. Se implementará un servidor Web interno, que no deberá ser accedido desde afuera de la red. Su dirección IP será 10.100.100.50.
  6. Se deberá implementar un servidor de aplicaciones que utilice los datos de la base que se encuentra en un servidor con dirección IP 10.100.100.100. El servidor es PostgreSQL, por lo que se accede a sus datos a través del puerto 5432. El software del servidor de aplicaciones es un Tomcat, que usa el puerto TCP 8080 para funcionar.
  7. La puerta de enlace predeterminada de todos los puestos clientes será 10.100.100.1, es decir, será el servidor que ahora es un proxy.

De acuerdo a estas premisas, vemos que el esquema de red original al menos a nivel físico no cambiaría demasiado. Sólo hay un punto que es fundamental y que se modificará, que es el correspondiente a dónde se pondrá el servidor de aplicaciones.

Como se pretende que dicho servidor sea visible desde el mundo exterior, y que se conecte con un servidor de bases de datos, tenemos que pensar en que si lo ubicamos dentro de la red interna, quien ingrese a él desde Internet, también estará en posición de violar las demás máquinas de la red interna. Para ello, tendremos que crear una nueva red que permitirá sólo el ingreso desde Internet a través del puerto 80, y sólo el contacto con el servidor de bases de datos a través del puerto 5432. Así es como aparece el concepto de zona desmilitariada, o DMZ.

La DMZ es una red que permitirá esta comunicación, de forma que si alguien pudiera ingresar y romper nuestro servidor de aplicacciones, no podría acceder a más que el puerto 5432 de nuestro servidor de bases de datos. Desde afuera de nuestra red, los clientes deberán apuntar a un URL normal, sin invocar el puerto 8080, por lo que de alguna forma tendré que redirigir el puerto 80 al 8080 del servidor de aplicaciones.

Por otro lado, si estamos hablando de una nueva red, tendremos que pensar, también, en un nuevo rango de direcciones IP. Necesitamos que estas direcciones sean privadas, por lo que hemos definido que las mismas se encuentren en el rango 10.100.150/0. La pata del servidor firewall tendrá la dirección 10.100.150.1, y el servidor Tomcat tendrá la dirección 10.100.150.10.

Con estos puntos en mente, entonces, el esquema de nuestra red queda modificado para tomar esta forma:



Si se quiere pasar a meter mano, no hace falta leer las secciones con la teoría de lo que implementaremos. Aún así, recomiendo dejar de lado el pragmatismo por un instante, y leerlas para comprender qué es lo que estaremos haciendo, y así poder modificar lo que se exponga en este artículo de forma acorde a nuestra instalación.

Conceptos

Netfilter es el nombre que recibe el conjunto de porciones de código que están dentro del kernel de GNU/Linux y que le permiten registrar el comportamiento de las funciones de red utilizadas por cada paquete cuando pasa por un sistema y hace uso de ellas.
Por ejemplo, si un usuario ejecuta el comando “ping” para verificar la existencia de un sistema en la red, la máquina de destino de ese comando debe tener un programa que escuche el protocolo que usa el “ping” (ICMP), y por lo tanto algunas funciones en el kernel que registren el uso de los recursos de red para esta tarea.
A través de Netfilter podré modificar el comportamiento de cada paquete de red que llegue o salga de una máquina.
Lo más común del mundo es conocer Netfilter no por su mismo nombre, sino por la implementación de programas como “iptables” en los kernels 2.4 y 2.6, por “ipchains” en los kernels 2.2, y por “ipfwadm” en los kernels 2.0.

¿Cómo es que funciona, entonces, algo como iptables, y cómo nos ayudará a realizar nuestra tarea? Sencillo, iptables conservará una serie de reglas de coincidencia que permitirán la ejecución de determinadas acciones cada vez que un paquete de red cumpla con alguna de ellas. Entonces, de seguro nos encontraremos con dos partes bien definidas en cada regla:

  • Una regla de coincidencia, donde podré especificar protocolo, puerto, dirección IP de origen y de destino, y conjunto de reglas a la que quiero agregarla.
  • Una acción o serie de acciones a ejecutar sobre los paquetes de red que coincidan con dicha regla.

Por ejemplo, para el caso que queremos implementar, algunas reglas serían:

  • Si un paquete de red quiere ingresar vía Internet, y apunta al puerto 80, redirigirlo al puerto 8080 del servidor 10.100.150.10 que se encuentra en la DMZ.
  • Si un paquete de red tiene origen en la red interna, tiene dirección IP diferente de 10.100.100.10, y quiere acceder a Internet, redirigirlo al puerto 8008.
  • Impedir el ingreso de cualquier paquete de red, no importa su origen, al firewall o cualquier otra red interna, a menos que no ingrese explícitamente por el puerto 80.

La lista de reglas en “pseudocódigo” siguen para representar la realidad de nuestras premisas. He escrito sólo algunas aquí, que repasaremos en el momento de realizar la implementación.

Ahora bien, iptables tiene una sintáxis que no es exáctamente amigable a la hora de generar reglas. Puede ser engorroso, algo confuso, y muchas veces un error nos lleva a tener un problema de seguridad grave. Aquí es donde el programa Shorewall hace su aparición, demostrándonos que todo se puede simplificar considerablemente cuando se quiere.



Shorewall

Shorewall, una abreviatura de “Shoreline Firewall”, como comentamos más arriba, viene a ser la herramienta que simplificará la configuración de iptables al punto de volverse casi documentada por sí misma. Lo que antes es una serie de reglas larguísima, ahora es sólo unas cuantas líneas en algunos archivos. Cuando Shorewall se ejecuta lee estas líneas de configuración, y con la ayuda de iptables genera todas las reglas necesarias para hacer lo que hemos definido. Y iptables, como vimos más arriba, hace uso de Netfilter para ello. Ahora las piezas del rompecabezas comienzan a encastrar, ¿no?.

Un punto importante a tener en cuenta es que Shorewall no es un proceso demonio que estará en ejecución todo el tiempo, sino que se ejecuta para realizar sus configuraciones, y luego finaliza, por lo que carece de sentido verificar si está o no en ejecución, y sí el verificar si las reglas que hemos configurado están activas. Para esto último, una mirada rápida al archivo /var/log/messages o /var/log/kern.log serán suficientes, así como la ejecución de “iptables -L”.

Shorewall se instala muy fácilmente. Se utilizará “apt-get” o “rpm” de esta forma:

# apt-get install shorewall
(para los sistemas operativos basados en Debian, como ser el mismo Debian o Ubuntu)

Los paquetes para Red Hat, CentOS, Fedora, Suse y demás distros basadas en .rpm se pueden bajar directamente de la página de Shorewall (http://shorewall.net), e instalar con el comando “rpm -Uvh PAQUETE.rpm”.

Una vez instalados los paquetes, encontraremos ciertos archivos y directorios nuevos, a saber:

  • Directorio /etc/shorewall: en él encontraremos todos los archivos de configuración de este paquete a nivel de reglas.
  • Archivo /etc/default/shorewall: ciertas configuraciones de caracter más general se encontrarán en este archivo, como ser la orden de ejecutar o no Shorewall cuando se invoque. Sirve para generar la configuración sabiendo que nadie va a ejecutarlo y, por error, dejarnos por ejemplo sin acceso al servidor firewall.
  • Archivo /etc/init.d/shorewall: este archivo estará relacionado por medio de links simbólicos con los correspondientes en los directorios /etc/rcX.d.
  • Archivo /var/log/kern.log: en este archivo, típicamente, se dejarán los mensajes correspondientes a las acciones que se han ejecutado a nivel de kernel cuando un paquete de red coincidió con alguna regla.

Con todo esto instalado, comenzaremos a transformar nuestras premisas en reglas reales.


Reglas y escuadras

Shorewall utiliza para su proceso de configuración una serie de alias que permiten asociar zonas de red a tarjetas o conexiones de red. Una zona de red será aquella que tenga determinada condición común, como ser “Red Interna”, “Red Externa”, “Zona Desmilitarizada”, etc.

En nuestro ejemplo, consideraremos tres zonas de red diferentes; la red interna, que será la que corresponde a todo lo que está dentro de nuestra propia red y que queremos proteger del mundo exterior; la red externa, que comprende ni más ni menos que a todo Internet; y finalmente la zona desmilitariada, que será aquella que contendrá sólo los servidores que queremos exponer parcialmente hacia la red externa, y parcialmente hacia la red interna, sin disminuir por ello el nivel de seguridad.

Sus nombres de zona serán, entonces, “lan” para la red interna, “wan” para la red externa, y “dmz” para la zona desmilitarizada.

Suponemos que nuestro equipo firewall dispone de tres tarjetas de red, denominadas:

  • eth0: corresponde a la red interna, o zona “lan”.
  • eth1: corresponde a la red externa, o zona “wan”.
  • eth2: corresponde a la red intermedia, o zona “dmz”.
  • fw: si bien no es una tarjeta de red, se podrán realizar conexiones hacia y desde él.

Debemos, entonces, asociar cada tarjeta o conexión de red a una zona dentro de los archivos de configuración de Shorewall. Para ello, editaremos o crearemos el archivo /etc/shorewall/interfaces con el siguiente contenido:

lan eth0
wan eth1
dmz eth2

Sencillo, ¿no? Ahora bien, como Shorewall soporta tanto zonas configuradas con Ipv4, como aquellas con Ipv6, procederemos a editar o crear el archivo /etc/shorewall/zones, donde definiremos cuál de las dos usaremos, o si una zona se trata del mismo firewall. En nuestro caso, será Ipv4:

fw firewall
lan ipv4
wan ipv4
dmz ipv4

El motivo por el cual se debe dar de alta el firewall dentro de las zonas es que se podrían generar reglas que se apliquen sólo cuando determinado tráfico de red es hacia o desde el firewall, y no a través de él.

Teniendo las asociaciones listas, veremos cuál es el formato que tienen dos archivos importantes: /etc/shorewall/policy y /etc/shorewall/rules.

En general, el formato será basado en columnas que definirán:

  • Resultado esperado: es el resultado que pretendemos cuando un paquete de red coincide con una determinada regla. Por ejemplo, podríamos especificar:
  • ACCEPT: el paquete de red es aceptado.
  • DROP: el paquete de red es descartado.
  • REJECT: el paquete de red es denegado. Difiere del anterior en que quien lo envía toma conocimiento de esta acción.
  • DNAT: Se ejerce “NAT” sobre la dirección de destino. En nuestro caso, todo lo que llegue al puerto 80 del servidor firewall, será redirigido al puerto 8080 del servidor de aplicaciones, y las respuestas de dicho servidor saldrán como si nunca hubiera habido un redireccionamiento. Por eso se llama “Destination NAT”.
  • SNAT: si un cliente de la red interna debe realizar un pedido a un servidor en la red externa, su dirección será reescrita de forma tal que cuando el servidor responda, dicha respuesta llegue nuevamente al cliente en cuestión. Por eso se llama “Source NAT”.
  • Cliente: es el punto donde se genera la comunicación de red. Por ejemplo, en el caso de una conexión desde Internet hacia nuestro equipo, la máquina que hace el llamado por medio de su navegador será el cliente.
  • Servidor: es el punto al cual llegarán los paquetes de red. En el caso del servidor del proxy, por ejemplo, el cliente será cualquiera de los puestos de trabajo, y el servidor estará en algún lugar de internet.
  • Familia de protocolos: en este caso, la familia de protocolos podrá ser “tcp”, “udp”, “icmp”, etc.
  • Puerto: en este caso, se registrará el puerto al cual se invoca desde el lado del cliente. En el ejemplo del servidor de aplicaciones, el puerto sería 80.
  • Comentarios: en este campo se colocarán comentarios que nos guíen sobre qué afecta esa regla. Por ejemplo, documentar nuestros archivos con algo del estilo “Sólo pasan desde Internet hacia 10.100.150.10” puede ser muy útil si queremos en algún momento modificar las reglas.

Existe un archivo importante que es /etc/shorewall/masq. En ese archivo configuraremos las diferentes redes que enmascararán sus direcciones IP cuando deban acceder a otras. En nuestro caso, lo haremos con los puestos que salgan a Internet y que utilicen un puerto diferente del 80, redirigido al proxy que tenemos en el puerto 8008.

El formato de este archivo también se basa en el uso de columnas, o campos, y sus usos son los siguientes:

  • Interface: se especifica en este caso cuál será el destino del cual se espera recibir respuestas cuando se enmascare una dirección o rango de direcciones IP. Por ejemplo, si pensamos en todo Internet, tendremos que colocar 0.0.0.0/0.
  • Dirección o rango de origen: aquí configuraremos la dirección o rango de direcciones IP que serán enmascaradas cuando deban acceder alguna dirección especificada en el campo anterior. Por ejemplo, para los puestos cliente, tendremos que colocar 10.100.100.0/0.

Por último, veremos un archivo más, que también se debiera editar o crear, y que es el /etc/shorewall/policy. Este archivo contendrá políticas generales, fuera de lo que son las reglas, que se aplicarán al firewall en general. Su formato en sí es muy parecido al /etc/shorewall/rules, también basado en columnas:

  • Cliente: como vimos antes, desde donde se generan las conexiones.
  • Servidor: ídem, hacia donde van las conexiones.
  • Política: qué se hará en forma predeterminada. Las acciones también podrán ser “ACCEPT”, “DROP”, etc.
  • Nivel de log: en este campo definiremos si queremos que se genere un registro en el log cada vez que se produzca un error (“err”), sólo por cuestiones informativas (“info”), u otros casos. Tengamos en cuenta que puede ser bastante grande un archivo de log cuando el tráfico es fuerte, por lo que esto se debe regular bien.

Ahora, definiremos qué es lo que se hará en cada zona, dependiendo de las premisas de que partimos. Para ello, editaremos o crearemos el archivo /etc/shorewall/rules. Veamos cada una de estas premisas.

Premisas 1 y 3: Todo cliente que quiera salir a Internet directamente, y sin configurar su proxy en su sistema, deberá ser redirigido automáticamente al puerto 8008. Sólo habrá un cliente que tendrá permitido salir a Internet sin pasar por el proxy, y tendrá la dirección IP 10.100.100.10.

Para lograr esto, tendremos que pensar que el cliente será cualquier máquina de la red 10.100.100.0/24 que quiera salir a internet. Por lo tanto, el cliente será “lan”, el servidor será el puerto 8008, el protocolo será de la familia “tcp”, el puerto será el 80, y la acción a realizar será “REDIRECT” con todos ellos, a excepción del cliente 10.100.100.10, que podrá salir a Internet sin pasar por este proxy. La regla resultante será, entonces:

REDIRECT lan:!10.100.100.10 8008 tcp www

Notemos que en la sección del cliente, estamos agregando todo lo que provenga de “lan”, y exceptuando la dirección 10.100.100.10 colocándole un “!” antes. Si tuviéramos más máquinas que exceptuar, las agregaríamos separadas por comas, sin espacios. Si por ejemplo, tuviéramos que exceptuar las direcciones 10.100.100.10 y 10.100.100.9, esa expresión se vería reemplazada por “!10.100.100.10,10.100.100.9”.

Premisa 2: Todo protocolo de red diferente de la navegación por Internet deberá salir al mundo utilizando enmascaramiento de direcciones IP.

Para lograrlo, veamos cómo configurar el archivo /etc/shorewall/masq siguendo los lineamientos que vimos antes:

0.0.0.0/0 10.100.100.0/0

Así de sencillo es configurar el enmascaramiento de direcciones IP.

Premisa 5: Se implementará un servidor Web interno, que no deberá ser accedido desde afuera de la red. Su dirección IP será 10.100.100.50.

En este caso, tendremos que realizar, como acción un NAT en el destino, por lo que la acción se llamará “DNAT”. El cliente será Internet en general, el servidor estará en la zona dmz, específicamente en la dirección 10.100.150.10, el puerto invocado por el cliente será el 80, pero el sistema tendrá que enviar los pedidos desde este puerto al 8080. La regla entonces quedará así:

DNAT wan dmz:10.100.105.10:8080 tcp 80

Premisa 6: Se deberá implementar un servidor de aplicaciones que utilice los datos de la base que se encuentra en un servidor con dirección IP 10.100.100.100. El servidor es PostgreSQL, por lo que se accede a sus datos a través del puerto 5432. El software del servidor de aplicaciones es un Tomcat, que usa el puerto TCP 8080 para funcionar.

En este caso el tema se pone un poco, y solo un poco más complicado. El cliente, como vemos, es el servidor de aplicaciones que tiene la dirección 10.100.150.10 y está ubicado en la zona “dmz”, y el servidor se encuentra en el puerto 5432 de la dirección IP 10.100.100.100 de la zona “lan”. Lo que se hará a nivel de acción será aceptar estos pedidos. La regla entonces quedará así:

ACCEPT dmz:10.100.150.10 lan:10.100.100.100 tcp 5432

Bueno, tal parece que ya tenemos todas las reglas en su lugar. Sólo nos queda una sección de la configuración que tocar a nivel de reglas, y es el archivo /etc/shorewall/policy.

Una de las cosas que deberé especificar es que todo lo que no esté explícitamente aceptado, cuando se trate de una conexión desde Internet hacia la red interna, o hacia el mismo firewall, estará denegado. Eso se hace sólo con declarar lo siguiente:

wan all DROP err

Al agregar como servidor “all” le especificamos a Shorewall que no importa a qué zona un paquete de red se esté dirigiendo, deberá ser eliminado.

Si lo que deseo es un nivel de logueo exhaustivo, en este archivo declararé las reglas:

lan all ACCEPT info
fw all ACCEPT err

Con estas reglas, ya no queda mucho más por configurar para lograr las premisas que fueron planteadas.

Ahora, podremos modificar el archivo /etc/default/shorewall cambiando la entrada:

startup=0

...por:

startup=1
IMPORTANTE: Veamos que no hemos habilitado el SSH hacia nuestro firewall desde ninguna zona. Si lo queremos dejar habilitado para conectarnos desde nuestra red interna, deberemos agregar una entrada como la siguiente en /etc/shorewall/rules:

ACCEPT lan fw tcp 22

...y si queremos acceder a nuestro firewall desde Internet, la regla cambiará para tomar esta forma:

ACCEPT wan fw tcp 22

Con esto configurado, sólo debemos ejecutar Shorewall con el comando:

# /etc/init.d/shorewall start

Interfases gráficas

Si bien vemos que la configuración de un firewall profesional, gracias a Shorewall es notablemente sencilla, podría ocurrir que un fanático de los navegadores se encuentre en medio de nuestra tropa de sysadmins, por lo que tendremos que implementar alguna ventana que lo haga felíz.

Una de las aplicaciones difundidas, y de mucho uso, es el denominado “Webmin”, que posee un módulo específico para la administración de firewalls basados en Shorewall.
Recordemos que Webmin usa generalmente el puerto 10000 para funcionar, por lo que antes de activar el firewall debemos agregar una regla que nos permita conexiones desde una determinada red hacia él.

Esto lo lograremos agregando a /etc/shorewall/rules una línea como la siguiente si queremos que se acceda Webmin desde la red interna:

ACCEPT lan fw tcp 10000

...o como la siguiente si queremos que se acceda desde Internet:

ACCEPT wan fw tcp 10000

Ahora instalamos el paquete webmin, su módulo de control de Shorewall, relanzamos los procesos de shorewall con:

# /etc/init.d/shorewall restart

Y con esto terminamos nuestra tarea, dejando a nuestro sysadmin amigo con una sonrisa en su cara, y una ventanita como ésta en nuestro navegador:



Conclusión

Con estos pocos comandos, como vimos más arriba, hemos configurado un firewall de características profesionales. Con él tenemos protegida nuestra red interna, redirigida nuestra red externa, y hasta contamos con una zona desmilitarizada donde podremos colocar nuestro servidor de aplicaciones conectado a una base de datos sólo por un puerto.

Un aspecto también importante de este tipo de configuraciones es que salvo un consumo demasiado elevado a nivel de tráfico de red, máquinas muy chicas, y que sólo posean la capacidad de tener varias tarjetas de red genéricas pueden servir para armar un verdadero sistema de seguridad perimetral. Espero que hayan disfrutado de los conceptos aquí volcados, y de la configuración de ejemplo que hemos realizado.

Los espero el mes que viene, con más artículos técnicos para depilarnos el flequillo. ¡Nos vemos!