18.7.07

How To: Filtar el tráfico P2P mediante un firewall Linux con iptables

Puede que no sea una cuestión que nos quite el sueño el filtrar tráfico P2P, pero cuando en una red empresarial, el personal se anima a descargarse la película del momento, el último disco de Shakira y algún que otro vídeo para añadir a la pornoteca, te dices que si, que ha llegado el momento.
Ya hemos visto en anteriores posts que filtrar las conexiones a sistemas de chat como el IRC o el MSN es realmente fácil utilizando sencillas reglas de iptables, pero con el P2P no pinta tan fácil, por la forma en que P2P funciona:

Para evitar que un equipo de la red interna pueda ser accesible desde el exterior utilizamos un firewall que nos cierre los puertos y que, al mismo tiempo, permita a los equipos de la LAN acceder libremente a Internet.
Pero el P2P es realmente puñetero ya ha sido programado para saltarse esta limitación. Al abrir un cliente P2P en la LAN se establece una conexión con el exterior que el firewall considera confiable y a partir de este momento los paquetes que vengan de vuelta están marcados como ESTABLISHED,RELATED, con lo que pasan directamente al cliente LAN, saltándose todo tipo de filtro. Otros clientes más avanzados envían los datos directamente en UDP, cuyos datagramas no almacenan tanta información como TCP y marcan los paquetes como RELATED en origen, con lo que el firewall tampoco se entera y deja pasar los datos como perico por su casa.

Se me ocurren varias soluciones:

1) Marcar los paquetes que considero P2P y eliminarlos, pero esto tendrá un coste de CPU que no se si estoy dispuesto a asumir.
2) Instalar un proxy y hacer pasar las peticiones web,ftp y correo a través del mismo pero... ¿y si alguien utiliza VoIP por ejemplo?
3) Utilizar la librería ipp2p para iptables que provee de métodos de detección de paquetes según protocolos P2P, con lo que podemos hacer filtros más exactos.

Así que, optamos por utilizar ipp2p. Los pasos para instalarlo son los siguientes:

1) Instalar los paquetes necesarios (obviamos la instalación de los compiladores):

coruscant:~# apt-get install iptables-dev kernel-headers-`uname-r`

2) Descargarse los fuentes de la versión de iptables que tenemos y descomprimirlos:

coruscant:~# iptables --version
iptables v1.3.6
coruscant:/usr/src# wget http://www.netfilter.org/projects/iptables/files/iptables-1.3.6.tar.bz2
coruscant:/usr/src# bunzip iptables-1.3.6.tar.bz2; tar xf iptables-1.3.6.tar


3) Descargarse la última versión de ipp2p de la web http://www.ipp2p.org/downloads_en.html a día de hoy y descomprimirlos:

coruscant:/usr/src# wget http://www.ipp2p.org/downloads/ipp2p-0.8.1_rc1.tar.gz ; tar zxvf ipp2p-0.8.1_rc1.tar.gz

4) Entrar en el directorio del ipp2p y editar el Makefile para que apunte a los fuentes del iptables que nos hemos descargado:

coruscant:/usr/src/ipp2p-0.8.1_rc1# vi Makefile

y cambiamos la variable a nuestro valor:

IPTABLES_SRC = /usr/src/iptables-1.3.6

5) Compilamos la librería

coruscant:/usr/src/ipp2p-0.8.1_rc1# make

6) Copiamos los módulos compilados y los cargamos

coruscant:/usr/src/ipp2p-0.8.1_rc1# cp ibipt_ipp2p.so /lib/modules/iptables
coruscant:/usr/src/ipp2p-0.8.1_rc1# cp ipt_ipp2p.ko /lib/modules/`uname -r`/kernel/net/ipv4
coruscant:/usr/src/ipp2p-0.8.1_rc1# depmod -ae
coruscant:/usr/src/ipp2p-0.8.1_rc1# modprobe ipt_ipp2p


7) Añadir a nuestro script iptables las líneas necesarias para que comience a filtrar, por ejemplo:

# Adiós edonkey, emule y demás amigos
iptables -I FORWARD -p tcp -m ipp2p --edk -j DROP
# Adiós bittorrent y amigos
iptables -I FORWARD -p tcp -m ipp2p --bit -j DROP
# Adiños kazza & CO
iptables -I FORWARD -p tcp -m ipp2p --kazaa -j DROP
...
...


Por último recomiendaría hacerse con un buen palo para cuando los usuarios se pongan tontines por haberles cortado las descargas.

16.7.07

Espabilando un pelín a Apache2

Nadie discute que Apache2 es ideal para instalar en grandes servidores que atienden miles de peticiones al día, ya que está perfectamente preparado para ello gracias a "elementos" como los threads. Pero puede ser que necesitemos instalar Apache como pequeño servidor de pruebas o corparativo e incluso como servidor público en una máquina con el rendimiento justito. En este caso es recomendable ajustar al detalle los valores del servidor para hacerlo rendir sin que tengamos que pagar un alto precio en cuando a consumo de recursos se refiere. Algunos de los apartados de la configuración que podemos tocar para mejorar la eficiencia son los siguientes:

MaxClients y Max/MinSpareServers

En este apartado podremos ajustar el número de procesos padres/hijos a unos valores más adecuados al uso que deseamos darle al servidor. Es muy común la tendencia a reducir los "MaxClients" hasta valores ínfimos, pensando en la regla de tres de que menos procesos, menor consumo de memoria. Y si, hasta aquí, esta afirmación es cierta, pero en cuando realizamos peticiones al servidor web, si este valor es bajo el servidor puede hasta bloquearse por momentos, denegar conexiones e incluso permanecer totalmente inoperante. Por la contra, un alto número de "MaxClients" se traduce en un rendimiento positivo del servidor pero en una carga de memoria dependiendo del número de conexiones establecidas. Un número importante de "MaxClients" se va a traducir por tanto en un rendimiento positivo pero debemos ser cuidadosos con este valor cuando el servidor es de acceso externo.
En cuanto a los procesos hijos "Max/MinSpareServers" la situación es similar. No por poner un valor bajo en estas etiquetas vamos a tener un rendimiento mejor, ya que la situación es igual que en el caso anterior. Un valor bajo de estos valores obligará al servidor a abrir más procesos padres para satisfacer la demanda de peticiones. Al igual que en el caso anterior, es una pescadilla que se muerde la cola, ya que reduciendo el número de hijos el consumo de memoria bajará pero la carga del sistema será mayor hemos calculado mal estos números, volviendo al servidor inoperante.
Hay desmitificar por tanto, que el reducir el número de padres/hijos a valores ínfimos no es sinónimo de rendimiento y, al contrario, podemos pagar un alto precio en consumo de recursos.

HostnameLookup

Con esta directiva Apache intenta resolver los nombres de host en lugar de utilizar las direcciones IP. Esto puede ser útil para log o para aplicaciones CGI pero requiere un esfuerzo que nos podemos ahorrar ya que, por lo general, nos dará igual tratar con la IP que con el nombre. Desactivando esta directiva optimizaremos, por tanto, nuestro servicio.

AllowOverride

Esta función es muy útil y muy conocida. Mediante ficheros .htaccess guardados en cada directorio, podemos controlar el acceso a misma mediante juegos de usuario/pwd. Esto es muy útil pero, ¿vamos a usarlo en nuestro servidor? Porque si no es así, podemos desactivarlo y ahorrarle al servicio que por cada directorio que lea, está buscando el fichero.

FastCGI

Si vamos a hacer un uso intensivo de CGI, FastCGI es una interesante alternativa al módulo que viene por defecto en Apache2. El rendimiento de este módulo alternativo es sensiblemente mejor que los mod_X que vienen en las distribuciones de Apache2 (mod_cgi,mod_php...). Más información sobre FastCGI puede encontrarse en la web del proyecto http://www.fastcgi.com/, así como el API para diferentes lenguajes.

6.7.07

Python: Información meteorológica fácil con pymetar

Necesitaba escribir un script que me capturase la información meteorológica para mostrar en pantalla los resultados. Dándole unas vueltas a Google, he visto que con pymetar es una tontería, ya que nos da todo hecho para que podamos atacar a una estación meteorológica de las que hay instaladas en los aereopuertos. Para conseguir el código correspondiente a la estación que nos interesa, debemos mirar en http://www.nws.noaa.gov/tg/siteloc.shtml y cambiar el valor de la variable stationId.

En este ejemplo simple, utilizo dos diccionarios, uno para traducir a español el resultado y otro para cargar un gráfico según el estado del cielo. El módulo pymetar nos prevee de más métodos, que pueden consultarse en la documentación del módulo.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import pymetar
import sys

# Global vars
stationId = 'LEPA' # Codigo de la estacion meteorologica
equivalencias = {'N':'Norte','S':'Sur','E':'Este', \
'W':'Oeste','NW':'Noroeste','NE':'Noreste','SSW':'Sur/Suroeste',
'SSE':'Sur/Sureste','NNE':'Norte/Noreste','NNW':'Norte/Noroeste','SE':'Sureste', \
'SW':'Suroeste','ENE':'Este/Noreste','WNW':'Oeste/Noroeste', \
'ESE':'Este/Sureste','WSW':'Oeste/Suroeste'}

iconos = {'sun':'sol.png','suncloud':'solnube.png','cloud':'nubes.png','rain':'lluvia.png', \
'snow':'nieve.png','storm':'tormenta.png','fog':'niebla.png'}

rf=pymetar.ReportFetcher(stationId)
rep=rf.FetchReport()
rp=pymetar.ReportParser()
wData=rp.ParseReport(rep)

if wData == None:
print 'Icono: --'
else:
print 'Icono: ' + iconos[wData.getPixmap()]
#print wData.getPixmap()
print 'Temperatura: ' + str(int(wData.getTemperatureCelsius())) + 'ºC'
print 'Humedad: ' + str(int(wData.getHumidity())) + '%'
print 'Visibilidad: ' + str(int(wData.getVisibilityKilometers())) + ' Km'
print 'Punto de condensacion: ' + str(int(wData.getDewPointCelsius())) + 'ºC'
print 'Viento: ' + equivalencias[str(wData.getWindCompass())] + ' a ' + str(int(wData.getWindSpeed())) + ' Km/h'
print 'Presion: ' + str(int(wData.getPressure())) + ' ba'