viernes, febrero 15, 2008

Alarma telefonica para servidores

El sueño de todo proveedor de servicios de internet es ofrecer la mayor cantidad de time-up's en sus servidores, ojalá el 100%. Pero es prácticamente imposible. Lo que es posible es contar con métodos de alerta temprana por fallas.
Lo más usual es tener habilitado un monitor de servicios que avise, ya sea vía mail o SMS, si algún imprevisto ocurre. Un problema se presenta si ese imprevisto ocurre a las 4 a.m. y estamos durmiendo (y no revisando el mail) y no escuchamos la alarma del SMS; o peor, el SMS se retrasa por problemas de congestión en la red de nuestro proveedor de servicio celular.
Para solucionar ésto, una alternativa es que nuestro monitor nos llame. ¡Sí!, nos llame al celular e insista hasta que contestemos :)

Mi propuesta es utilizar el servicio de VoIP de algun proveedor y un sintetizador de textos (Festival), para convertir a nuestro monitor de servicios en una alarma parlante:

Lo primero, instalar un cliente SIP en modo texto. Mi elección fue PJSIP. Para instalarlo en FreeBSD lo descargamos del link anterior y lo descomprimimos (en mi caso, en /usr/local/). Luego:

# cd /usr/local/pjproject-0.8.0
# ./configure

en este punto es necesario parchear el archivo pjproject-0.8.0/pjlib/include/pj/compat/socket.h:

Eliminar las siguientes lineas:

- #if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H != 0
- # include
- #endif

Y continuar:

# gmake dep
# gmake clean
# gmake

OJO GMAKE no make.

Una vez terminado el proceso es necesario crear un archivo de configuración (conf) con los datos de nuestra cuenta SIP:

########Ejemplo de archivo de configuracion "conf" ###################
# This is a comment in the config file.
--id sip:XXXX@sip.proveedor.dom
--registrar sip:sip.proveedor.dom
--realm *
--username XXXX
--password LA_PASSWD_AQUI
########################################################

Y esta listo para funcionar, a continuación comparto con uds el Script que se ejecuta cuando ocurre una alarma en uno de los servidores:

#!/usr/bin/perl

open(my $in, "<", "/usr/local/www/data/report.txt") or die "NOOOT!: $!"; open(my $out, ">", "/usr/local/pjsip/bin/mensaje.txt") or die "NOOOT!: $!";
open(my $in2, "<", "dnscache.txt") or die "NOOOOOT!!: $!"; while (<$in>) {
if (/NO activo en\s(\d+\.\d+\.\d+\.\d+)\s\w+\s(\d+)/) {
$ip = $1;
$puerto = $2;
while (<$in2>) {
if (/$ip\s+([\w\-\d]+)/) {
print $out "Problema en el servidor $1 puerto $puerto";
}
}
}}


close $in;
close $out;
close $in2;
system("sed -i -e 's/\\./ /g' /usr/local/pjsip/bin/mensaje.txt");

use IPC::Open3;
open(my $log, ">", "/usr/local/pjsip/bin/log.txt") or die "NOOOT!: $!";
my($a,$b,$c);
open3($a, $b, $c,'festival --tts /usr/local/pjsip/bin/mensaje.txt');
print $log "$b";

open($temp, "ps aux |grep pjsua-i386-unknown-freebsd6.0 |grep -v 'grep' |") || die "NOOOT!!!!: $!";
while (<$temp>) {
#print "$_";
if (/\w+\s([\d]+)/) {
#print "$1";
`kill -9 $1`;
}
}
close($temp);

my $ok=1;

sub llamada {
my($wtr, $rdr, $err);
$pid = open3($wtr, $rdr, $err,'pjsua-i386-unknown-freebsd6.0 --config-file /usr/local/pjsip/bin/conf --play-file /usr/local/pjsip/bin/mensaje.wav --auto-play --null-audio --duration=30 sip:xxxxxxxxxx\@sip.proveedor.dom');

while (<$rdr>) {
#print "$_";
if (/DISCONNECTED/) {
print $wtr "q\n";
}
if (/CONFIRMED/) {
$ok=0;
}
}
}
while ($ok) {
llamada();
}

Este script es llamado por el script vigia instalado en el cron, si no existe falla de sistema este script no es llamado.

Como pueden ver el script lo primero que hace es buscar en el reporte de alarma, que se actualiza cada 5 minutos por medio de un cron, la o las lineas que indiquen que un servidor "NO" esta activo busca la ip de dicho servidor en el archivo dnscache.txt y guarda en una variable el nombre de dicho servidor (cuando se prestan muchos servicios es normal que un servidor tenga mas de una ip por lo tanto es mas util el nombre que la misma), luego construye un mensaje con el nombre del servidor y el numero de puerto del servicio que fallo.

A continuacion el script invoca a Festival, para que nos lea el mensaje y lo deje en un archivo .wav single channel/mono, 16bit signed PCM, como? Casi olvido hablar de festival. Bueno para instalar como de costumbre vamos al port de nuestro FreeBSD consultamos en Freshports si es necesario actualizar los ports, y ejecutamos los siguiente pasos:

#cd /usr/ports/audio/festival/
#make install clean

#cd /usr/ports/audio/festvox-el11/
#make install clean

Este ultimo instalara la voz de hombre en español, para instalar una voz de mujer es necesario instalar festvox-hvs, como el echo de ser hombre o mujer la voz no era una de mis prioridades simplemente instale el11, ya que hvs depende de festival+OIG que no es compatible con festival por lo tanto era re-compilar etc...

Configuraciones Festival:

Editar el archivo /usr/local/share/festival/lib/siteinit.scm y agregar lo siguiente:

(set! voice_default 'voice_el_diphone)
(Parameter.set 'Audio_Method 'audio_Command)
(Parameter.set 'Audio_Command "/usr/local/bin/sox -t raw -r $SR -sw -c 1 $FILE /usr/local/pjsip/bin/mensaje.wav")

La primera linea es para definir el uso de la voz en español y las otras son para que genere el archivo .wav, para ello es necesario tener instalado sox (/usr/ports/audio/sox) previamente.

Una vez finalizada la configuracion de festival volvamos al script, si algo malo pasara en la ejecucion de festival podriamos leerlo en el archivo log.txt, a continuacion verifica si existe otra instancia de pjsip ejecutandose en el servidor de ser asi la mata y le da curso a esta alarma que problablemente sea mas importante, define una variable ok, y procede a definir la subrutina que utilizara a pjsip para efectuar la llamada de alerta, podemos ver que se trata de una llamada simple vasta con cambiar xxxxxxxxxx\@sip.proveedor.dom por el numero al que queramos avisar y el programa tratara de llamar hasta que le contesten gracias al bucle while del final del programa. Al responder la llamada escucharemos una agradeble voz media robotizada que nos dira que servidor tiene problemas y en que puerto, y no podremos decir que no lo escuchamos ya que se trata de un sistema bastante insistente.

Saludos

viernes, febrero 08, 2008

Limitar ancho de banda (server side)

La masividad de la banda ancha nos propone el siguiente problema: como repartir nuestro ancho de banda en la mayor cantidad de clientes posible, sin sacrificar la calidad de servicio.

La solución propuesta para este problema es un modulo llamado mod_bw para apache2, mod_bw es capaz de limitar el ancho de banda en cada virtualhost, directorio o servidor en general, para ello tiene varias opciones:

- Definir el ancho de banda máximo, para que este ancho de banda sea repartido en partes iguales entre las conexiones.

- Definir ancho de banda mínimo para cada conexión, una vez alcanzado el máximo de clientes (máximo de clientes = ancho de banda total / ancho de banda mínimo) rechaza futuras conexiones con un error personalizable.

- Definir ancho de banda máximo para ficheros grandes o de cierto tipo, ej: para archivos de audio no mas de X Kb/s o para archivos de cualquier tipo de mas de 500kb no mas de Y Kb/s.

- Definir Numero de conexiones máximas desde una ip/host/red.

1.- Implementación

Para implementar este modulo en FreeBSD basta con actualizar los ports y dirigirse a la carpeta /usr/ports/www/mod_bw/ y ejecutar el siguiente comando:

# make install clean

Luego abrir el archivo httpd.conf (usualmente en /usr/local/etc/apache2/) y descomentar la linea:

LoadModule bw_module libexec/apache2/mod_bw.so

y agregar la linea (opcional, por motivos de orden, la configuración se podría agregar directamente en httpd.conf pero recomiendo el uso de includes):

Include etc/apache2/mod_bw.conf

A continuacion crear el archivo /usr/local/etc/apache2/mod_bw.conf:

BandWidthModule On
ForceBandWidthModule On
BandWidth 192.168.0.0/255.255.0.0 0 Esta linea define ancho de banda ilimitado para la red 192.168.0.0
BandWidth all 10240 Esta linea define ancho de banda total para todos de 10 kb/s (ojo que las reglas se leen en orden)
MinBandWidth all 1024 Esta linea asegura un minimo de ancho de banda de 1 kb/s para cada cliente
#MinBandWidth all -1 Esta linea asegura un ancho de banda de 10 kb/s para cada conexion.
LargeFileLimit * 200 1024 Esta linea limita la descarga de cualquier tipo de fichero de mas de 200kb a 1kb/s

Y reiniciar el apache

#apachecrl graceful

2.- Reglas Posibles:

1 - BandWidthModule [On|Off]

Por defecto Off es necesario ponerla en On para que el modulo funcione

Example :
BandWidthModule On

2 - ForceBandWidthModule [On|Off]

Por defecto este modulo no va a atender todas las peticiones a menos que este parámetro este On.

Ejemplo :
(normal use)
AddOutputFilterByType MOD_BW text/html text/plain

(enabling Force)
ForceBandWidthModule On

3 - BandWidth [From] [bytes/s] ó BandWidth u:[User-Agent] [bytes/s]

Maximo ancho de banda o ancho de banda por cliente dependiendo del valor de MinBandWidth, acepta ip/host/red o incluro User-Agent.
Las reglas son evaluadas por orden.

Ejemplo :
BandWidth localhost 10240
BandWidth 192.168.218.5 0

BandWidth "u:^Mozilla/5(.*)" 10240
BandWidth "u:wget" 102400


4 - MinBandWidth [From] [bytes/s]

Determina la velocidad mínima por conexión, si es 0 corresponde al default 256 b/s, si es -1 corresponde al valor bandwidth para cada cliente.

Ejemplo :
BandWidth all 102400
MinBandWidth all 50000

En este caso tenemos un total de 10kb/s y un minimo de 50kb/s para cada cliente

BandWidth all 50000
MinBandWidth all -1

En este caso tenemos 50 kb/s para cada cliente

5 - LargeFileLimit [Type] [Minimum Size] [bytes/s]


Ejemplo :
LargeFileLimit .avi 500 10240

En este caso se limita la velocidad de descarga de archivos avi de mas de 500kb a 10kb/s

LargeFileLimit * 500 10240

En este caso se limita la velocidad de descarga de cualquier archivo de mas de 500kb a 10kb/s

6 - BandWidthError [Error]

Para personalizar el error en caso de alcanzar el maximo de conexiones, es posible usar cualquier numero entre 200 y 599 se recomienda usar el 503 y la directiva ErrorDocument de apache para dirigir este error a una pagina que explique el exceso de carga que justifica el problema de acceso.

Ejemplo :

ErrorDocument 510 /errors/maxconexceeded.html
BandWidthError 510

7 - MaxConnection [From] [Max] ó MaxConnection u:[User-Agent] [Max]

Sirve para limitar la cantidad de conexiones simultaneas desde una misma ip/host/red/user-agent, si este limite es sobrepasado se obtendra un error 503

Es NECESARIO definir un limite de BandWidth para el mismo ip/host/red/usr-agent aunque sea 0 ya que el programa usa el mismo espacio de memoria de bandwidthlimit para contar las conexiones, y asi ahorrarse uso de memoria, y si no se define esta variable MaxConnections no va a funcionar:

Ejemplo:
BandWidth all 0
MaxConnection all 20
ó
BandWidth all 0
BandWidth 192.168.0.0/24 10240
MaxConnection all 20
MaxConnection 192.168.0.0/24 5
MaxConnection "u:^Mozilla/5(.*)" 5
MaxConnection "u:wget" 5

por ultimo si se definen limites para un vhost y luego se define una directiva para un directorio dentro del vhost este ultimo tiene todo un nuevo contexto de reglas es decir no hereda las reglas del vhost por ende es necesario re definir todas las reglas pertinenetes.

ej:
<VirtualHost *>
BandWidthModule On
BandWidth all 16384
LargeFileLimit * 500 4096
<Directory />
LargeFileLimit * 100 1024
</Directory>

El directorio / no va a estar limitado a 16384

Saludos

viernes, febrero 01, 2008

AWStats

AWStats (http://awstats.sourceforge.net/) es un sistema de procesamiento de logs para web, ftp o mail, en este caso me toco implementarlo para estadisticas de web, lo primero fue actualizar los ports del FreeBSD ya que la version que tenia disponible era vulnerable a ataques de inyeccion, para esto es necesario instalar cvsup, en mi caso sin interfaz grafica, ya que realizo todo en consola por tratarse de servidores de produccion:

# cd /usr/ports/net/cvsup-without-gui
# make install clear
# cp /usr/share/examples/cvsup/ports-supfile /etc/port-supfile

En este ultimo fichero hay que elegir que ports queremos actualizar, por defecto los actualiza todos, de lo contrario debemos comentar la linea que dice "ports-all" y descomentar la linea que nos interese, en este caso "ports-www", luego:

# cvsup -h cvsup.freebsd.org /etc/ports-supfile ó cvsup2.freebsd.org, etc ver (http://www.freebsd.org/handbook/mirrors.html)

Esto actualizara los ports, una vez finalizado el proceso procedemos a instalar el awstats:

#cd /usr/ports/www/awstats
# make install clear

Es necesario al final de la instalacion crear un directorio wwwroot donde poner los archivos compartidos de la aplicacion (iconos, css, classes, etc) en mi caso cree un subdirectorio wwwroot en el directorio donde se instalo awstats (/usr/local/www/awstats) y agrege un include (por temas de orden) en el httpd.conf:

/usr/local/etc/apache2/httpd.conf:
Include /usr/local/etc/awstats/httpd.conf

/usr/local/etc/awstats/httpd.conf:
Alias /awstatsclasses "/usr/local/www/awstats/wwwroot/classes/"
Alias /awstatscss "/usr/local/www/awstats/wwwroot/css/"
Alias /awstatsicons "/usr/local/www/awstats/wwwroot/icons/"
ScriptAlias /awstats/ "/usr/local/www/awstats/wwwroot/cgi-bin/"Options None

<Directory "/usr/local/www/awstats/wwwroot/">
AllowOverride None
Order allow,deny
Allow from all
</Directory>

Es necesario crear un directorio de configuracion para awstats, en mi caso /usr/local/etc/awstats, dentro de este archivo ademas de poner el anexo de configuracion para apache, se deben almacenar los archivos de configuracion para procesar logs, uno por log que se desee procesar, es decir si nuestro servidor web aloja cuatro sitios diferentes es necesario crear cuatro archivos de configuracion uno por sitio (awstats.sitio.dom.conf) tambien es posible ejecutar un script dentro de la carpeta tools llamado awstats_configure.pl para generar un primer archivo de configuracion para el procesamiento de nuestros logs, este escript esta bastante auto documentado, por lo que no entrare en profundidad en ese tema, a continuacion dejo un ejemplo de archivo de configuracion:

awstats.sitio.dom.conf:

LogFile="/var/log/access.log"
LogType=W
LogFormat=1
LogSeparator=" "
SiteDomain="%%site%%"
HostAliases="%%aliases%%"
DNSLookup=2
DirData="/var/log/awstats/"
DirCgi="/awstats"
DirIcons="/awstatsicons"
AllowToUpdateStatsFromBrowser=0
AllowFullYearView=1
EnableLockForUpdate=1
DNSStaticCacheFile="dnscache.txt"
DNSLastUpdateCacheFile="dnscachelastupdate.txt"
SkipDNSLookupFor=""
AllowAccessFromWebToAuthenticatedUsersOnly=0
AllowAccessFromWebToFollowingAuthenticatedUsers=""
AllowAccessFromWebToFollowingIPAddresses=""
CreateDirDataIfNotExists=0
BuildHistoryFormat=text
BuildReportFormat=html
SaveDatabaseFilesWithPermissionsForEveryone=0
PurgeLogFile=0
ArchiveLogRecords=0
KeepBackupOfHistoricFiles=1
DefaultFile="index.html"
SkipHosts="127.0.0.1
SkipUserAgents=""
SkipFiles=""
SkipReferrersBlackList=""
OnlyHosts=""
OnlyUserAgents=""
OnlyFiles=""
NotPageList="css js class gif jpg jpeg png bmp ico swf"
ValidHTTPCodes="200 304"
ValidSMTPCodes="1 250"
AuthenticatedUsersNotCaseSensitive=0
URLNotCaseSensitive=0
URLWithAnchor=0
URLQuerySeparators="?;"
URLWithQuery=0
URLWithQueryWithOnlyFollowingParameters=""
URLWithQueryWithoutFollowingParameters=""
URLReferrerWithQuery=0
WarningMessages=1
ErrorMessages=""
DebugMessages=0
NbOfLinesForCorruptedLog=50
WrapperScript=""
DecodeUA=0
MiscTrackerUrl="/js/awstats_misc_tracker.js"
LevelForBrowsersDetection=2 # 0 disables Browsers detection.
# 2 reduces AWStats speed by 2%
# allphones reduces AWStats speed by 5%
LevelForOSDetection=2 # 0 disables OS detection.
# 2 reduces AWStats speed by 3%
LevelForRefererAnalyze=2 # 0 disables Origin detection.
# 2 reduces AWStats speed by 14%
LevelForRobotsDetection=2 # 0 disables Robots detection.
# 2 reduces AWStats speed by 2.5%
LevelForSearchEnginesDetection=2 # 0 disables Search engines detection.
# 2 reduces AWStats speed by 9%
LevelForKeywordsDetection=2 # 0 disables Keyphrases/Keywords detection.
# 2 reduces AWStats speed by 1%
LevelForFileTypesDetection=2 # 0 disables File types detection.
# 2 reduces AWStats speed by 1%
LevelForWormsDetection=0 # 0 disables Worms detection.
# 2 reduces AWStats speed by 15%
UseFramesWhenCGI=1
DetailedReportsOnNewWindows=1
Expires=3600
MaxRowsInHTMLOutput=1000
Lang="auto"
DirLang="./lang"
ShowMenu=1
ShowSummary=UVPHB
ShowMonthStats=UVPHB
ShowDaysOfMonthStats=VPHB
ShowDaysOfWeekStats=PHB
ShowHoursStats=PHB
ShowDomainsStats=PHB
ShowHostsStats=PHBL
ShowAuthenticatedUsers=0
ShowRobotsStats=HBL
ShowWormsStats=0
ShowEMailSenders=0
ShowEMailReceivers=0
ShowSessionsStats=1
ShowPagesStats=PBEX
ShowFileTypesStats=HB
ShowFileSizesStats=0
ShowOSStats=1
ShowBrowsersStats=1
ShowScreenSizeStats=0
ShowOriginStats=PH
ShowKeyphrasesStats=1
ShowKeywordsStats=1
ShowMiscStats=a
ShowHTTPErrorsStats=1
ShowSMTPErrorsStats=0
ShowClusterStats=0
AddDataArrayMonthStats=1
AddDataArrayShowDaysOfMonthStats=1
AddDataArrayShowDaysOfWeekStats=1
AddDataArrayShowHoursStats=1
IncludeInternalLinksInOriginSection=0
MaxNbOfDomain = 10
MinHitDomain = 1
MaxNbOfHostsShown = 10
MinHitHost = 1
MaxNbOfLoginShown = 10
MinHitLogin = 1
MaxNbOfRobotShown = 10
MinHitRobot = 1
MaxNbOfPageShown = 10
MinHitFile = 1
MaxNbOfOsShown = 10
MinHitOs = 1
MaxNbOfBrowsersShown = 10
MinHitBrowser = 1
MaxNbOfScreenSizesShown = 5
MinHitScreenSize = 1
MaxNbOfWindowSizesShown = 5
MinHitWindowSize = 1
MaxNbOfRefererShown = 10
MinHitRefer = 1
MaxNbOfKeyphrasesShown = 10
MinHitKeyphrase = 1
MaxNbOfKeywordsShown = 10
MinHitKeyword = 1
MaxNbOfEMailsShown = 20
MinHitEMail = 1
FirstDayOfWeek=0
ShowFlagLinks=""
ShowLinksOnUrl=1
UseHTTPSLinkForUrl=""
MaxLengthOfShownURL=64
HTMLHeadSection=""
HTMLEndSection=""
Logo="awstats_logo6.png"
LogoLink="http://awstats.sourceforge.net"
BarWidth = 260
BarHeight = 90
StyleSheet=""
color_Background="FFFFFF" # Background color for main page (Default = "FFFFFF")
color_TableBGTitle="CCCCDD" # Background color for table title (Default = "CCCCDD")
color_TableTitle="000000" # Table title font color (Default = "000000")
color_TableBG="CCCCDD" # Background color for table (Default ="CCCCDD")
color_TableRowTitle="FFFFFF" # Table row title font color (Default = "FFFFFF")
color_TableBGRowTitle="ECECEC" # Background color for row title (Default = "ECECEC")
color_TableBorder="ECECEC" # Table border color (Default = "ECECEC")
color_text="000000" # Color of text (Default = "000000")
color_textpercent="606060" # Color of text for percent values (Default = "606060")
color_titletext="000000" # Color of text title within colored Title Rows (Default = "000000")
color_weekend="EAEAEA" # Color for week-end days (Default = "EAEAEA")
color_link="0011BB" # Color of HTML links (Default = "0011BB")
color_hover="605040" # Color of HTML on-mouseover links (Default = "605040")
color_u="FFAA66" # Background color for number of unique visitors (Default = "FFAA66")
color_v="F4F090" # Background color for number of visites (Default = "F4F090")
color_p="4477DD" # Background color for number of pages (Default = "4477DD")
color_h="66DDEE" # Background color for number of hits (Default = "66DDEE")
color_k="2EA495" # Background color for number of bytes (Default = "2EA495")
color_s="8888DD" # Background color for number of search (Default = "8888DD")
color_e="CEC2E8" # Background color for number of entry pages (Default = "CEC2E8")
color_x="C1B2E2" # Background color for number of exit pages (Default = "C1B2E2")
ExtraTrackedRowsLimit=500


No es mi intencion documentar lo ya documentado por lo que no me referire a este archivo de configuracion, para detalles ver AWStats config.

Una vez realizada la configuracion de cada sitio, basta con ejecutar el siguiente comando en /usr/local/awstats/wwwroot/cgi-bin/

#perl awstats.pl -config=sitio.dom -update

Lo que buscara automaticamente en /usr/local/etc/awststas el archivo awstats.sitio.dom.conf y realizara un update de las estadisticas de sitio.dom, esta actualizacion se agrega a un archivo de estadisticas mensual que maneja awstats, y parte desde el ultimo registro analizado, es decir, no vuelve a revisar el access.log cada vez que se ejecuta el comando, para ver las estadisticas de cada sitio es necesario ir con el explorador a esta URL: http://sitio.dom/awstats/awstats.pl?config=sitio.dom

Tambien es posible generar paginas estaticas para cada dominio con el comando:

#perl awstats_buildstaticpages.pl -config=sitio.dom -update -lang=es -awstatsprog=/usr/local/www/awstats/cgi-bin/awstats.pl -dir=/directorio/donde_guardar/las_paginas_estaticas/

Para finalizar es necesario modificar el cron para que ejecute estas acciones de forma automatica cada cierto tiempo dependiendo de los requerimientos del usuario final, en este caso un compañero de trabajo programo un script en perl que revisa los directorios de logs para saber cuantos sitios se encuentran activos y genera automaticamente las configuraciones para ellos y luego ejecuta el update de estadisticas una vez al dia, no puedo revelar dicho script ya que no lo hice yo pero no es tan dificil de realizar, finalmente el cron quedo de esta forma.

33 01 * * * /usr/bin/perl /sites/conf/awstats_master.pl

Es decir cada dia a las 1:33am ejecuta el script en cuestion.

Saludos.

viernes, enero 25, 2008

FreeBSD + Tomcat 5.5 + Apache SOAP (2da parte)

Configuraciones particulares (web.xml):

Listado de directorios:

Para habilitar el listado en algun directorio de nuestras webapps, es necesario modificar el archivo $tomcat_dir/webapps/$nombre_webapp/WEB-INF/web.xml
<servlet>
<servletname>NOMBRE_CUALQUIERA</servletname>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
...
<servlet-mapping>
<servlet-name>MISMO_NOMBRE_QUE_ARRIBA</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Ojo que url-pattern es respecto a la url de nuestra webapp.

Proteccion de directorios con contraseña:

Ojo que .htaccess no va a funcionar en este caso, es necesario editar el archivo web.xml para realizar esta accion.
<resource-env-ref>
<description>Link a la base de datos de usuarios</description>
<resource-env-ref-name>UserDatabase</resource-env-ref-name>
<resource-env-ref-type>org.apache.catalina.UserDatabase</resource-env-ref-type>
</resource-env-ref>
Con esto definiremos que la informacion de user/pass la obtendremos de un archivo
llamado tomcat-users.xml ubicado en $tomcat_dir/conf/ (ver server.xml <resource name ="UserDatabase" ), podemos cambiar esta configuracion en server.xml para tomar los user/pass de una base de datos accedida por JDBC.
<security-constraint>
<web-resource-collection>
<web-resource-name>CUALQUIER_NOMBRE</web-resource-name>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>NOMBRE_DEL_ROL</role-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>NOMBRE_CUALQUIERA</realm-name>
</login-config>
<security-role>
<description>DESCRIPCION</description>
<role-name>NOMBRE_DEL_ROL</role-name>
</security-role>

Por ultimo el método de autentificación existen 4: BASIC el que todos conocemos cuando se abre una ventanita y te pregunta la clave, CLIENT-CERT que es con certificado X509, FORM para utilizar un formulario jsp y DIGEST que que es una forma de encriptación.
El nombre del rol lo debemos tener definido en tomcat-users.xml y el nuevamente el url-pattern es realtivo a la webapp de la cual estamos editando el web.xml.
Ejemplo de tomcat-users.xml:
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="role1" password="tomcat" roles="role1"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
</tomcat-users>

Instalar Driver Oracle:

Descargar Driver jdbc (ojdbc14.jar) de oracle ojo con la versión de la DB y la versión del jdk
http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html
Copiar ojdbc14.jar a $tomcat_dir/common/lib/

lunes, enero 21, 2008

FreeBSD + Tomcat 5.5 + Apache SOAP

Mi tercera tarea consiste en implementar/migrar un servidor apache/soap + tomcat, para ser utilizado en producción, para ello cuento con un equipo DELL PowerEdge 1950 con FreeBSD, el primer paso es revisar la configuración general del tomcat ubicada en /usr/local/tomcat5.5/conf/server.xml, eliminar de ella lo que no vamos a utilizar, lo ideal es hacer una copia de esta configuración por defecto ya que contiene bastante información comentada y complica la revisión de la configuración final.

HTTP connector: Por defecto vienen dos de estos conectores definidos uno con SSL y otro sin, tambien es posible utilizar compresion gzip en ellos.

<Connector port="8080"
maxThreads="700" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true" />
Connector sin SSL
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
Connector con SSL
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml"
Parámetros para habilitar compresión gzip

AJP Connector: Este connector se usa para comunicar tomcat con otro servidor web nativo, esta forma de conexion es mucho mas rapida y eficiente que un proxy http.

<Connector port="8009"
enableLookups="false" address="127.0.0.1" redirectPort="8443" protocol="AJP/1.3" />
Conexion AJP
<Connector port="8082"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" acceptCount="100" connectionTimeout="20000"
proxyPort="80" disableUploadTimeout="true" />
Conexion proxy http



LOGS: si estamos interesados en los logs de acceso y consultas al servidor debemos usar, en el caso de servidor de producción, org.apache.catalina.valves.FastCommonAccessLogValve

<Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
directory="logs" prefix="localhost_access_log." suffix=".txt"
pattern="common" resolveHosts="false"/>
Apache Portable Runtime (APR): Es una libreria altamente portable que Apache utiliza para mejorar su integración, escalabilidad y performance, nos permite acceso a funcionalidades propias del SO, y manejo de procesos (memoria compartida, Sockets, etc) entre otras cosas, para utilizar esta característica basta con compilar la libreria. Los sources están en $tomcar_dir/bin/tomcat-native.tar.gz, para poder realizar una compilación exitosa es necesario contar con APR 1.2+, OpenSSL 0.9.7+ (en el caso de utilizar connector HTTP-SSL), gcc y make en nuestro sistema.
En FreeBSD basta con instalar el port APR:

#cd /usr/ports/devel/apr/ && make install clean


luego de /usr/ports/distfiles copiar el archivo apr-x.x.x.tar.gz y descomprimirlo en algún lugar (ej /usr/local/tomcat5.5/apr) y luego descomprimir el archivo tomcat5.5/bin/tomcat-native.tar.gz. En su interior encontraremos la carpeta jni/native/ donde debemos ejecutar los comandos:

#export JAVA_HOME=/usr/local/diablo-jdk1.5.0/ <-
solo si no esta definido $JAVA_HOME previamente.
# sh buildconf --with-apr=(directorio donde previamente descomprimimos los sources de APR).
# ./configure --with-apr=/usr/bin/ --with-ssl=directorio de instalación de openssl (opcional)
# make

Con lo que se creara el archivo libtcnative-1.so en /usr/local/apr/lib, copiarlo a /usr/local/lib/ luego reiniciar Tomcat, tomcat detecta solo la presencia de la librería y comienza a usarla automáticamente, las configuraciones por defecto de esta librería suelen ser suficientes, pero de querer cambiar las opciones ver http://tomcat.apache.org/tomcat-5.5-doc/apr.html

Con esto el servidor debiera quedar en condiciones de producción, en el siguiente post me referire a algunas configuraciones particulares de las aplicaciones sobre tomcat (web.xml)

lunes, enero 14, 2008

M0n0wall, QoS (Quality Of Service) y VoIP

Mi segunda tarea fue mejorar la calidad de las comunicaciones a través de teléfonos IP al interior de la empresa para ello contamos con una red interna de 100mb y un enlace hacia inet de 2mb, todo esto manejado por un firewall m0n0wall (FreeBSD), la verdad es que esta tarea no es tan compleja desde el hecho que m0n0wall es un muy buen traffic-shaper.

Existen dos formas de manejo del ancho de banda uno estático y otro dinámico:

Estático: consiste en definir un ancho de banda exclusivo en base a ciertas reglas, por ejemplo designar 512 kbits/s para telefonía IP y 1,5 mbits/s para el resto de las conexiones, este sistema es el mas sencillo pero el menos óptimo ya que mientras no se establece ninguna comunicación VoIP esos 512kb/s no son utilizados por nadie aunque los necesite.
Dinámico: consiste en establecer pesos a cada tipo de conexión y asignar dinámicamente el uso del ancho de banda, esta fue la opción que decidimos implementar.

En m0n0wall existen 3 piezas clave para el manejo del ancho de banda, las reglas (rules), las colas (queue) y las cañerias (pipes):

Las cañerías se usan para establecer anchos de banda estáticos y las colas para racionar estos anchos de banda de manera dinámica, obviamente la reglas determinaran que paquetes acceden a que colas. Por lo tanto necesitamos definir estas tres piezas para tener un uso eficiente de nuestro ancho de banda (para informacion mas detallada ver guia "Traffic shaper 'manual' (alpha)" )

Las cañerías: En este caso (ancho de banda dinamico) definiremos dos cañerías levemente mas pequeñas que nuestro ancho de banda total una de entrada y otra de salida, se definen medianamente mas pequeñas, ya que m0n0wall dispondrá del total de esta cañería para realizar el traffic-shape y en el caso que este valor sea mayor al real (congestión o simplemente tu ISP no te da tus prometidos 2mb sino mas bien 1950kb/s) la distribución de ancho de banda ficticia puede afectar la calidad de la conexión que estas tratando de resguardar, y por ser esta, un factor critico en VoIP mejor prevenir que curar.

Colas: se deben definir dos colas (una de subida y otra de bajada) por cada peso que se quiera asignar, en este caso voy a definir dos colas para VoIP y dos para el resto del trafico Inet, he aquí una de las cosas mas importantes en este proceso los "pesos", para determinar el ancho de banda a utilizar por cada conexión no se utilizan prioridades sino que pesos, lo que quiere decir que aunque tenga mucho trafico VoIP nunca voy a dejar de permitir trafico del otro (web, telnet, ssh, ftp, etc) una prioridad mas alta me pondría en un lugar mas adelante de la cola mientras que un peso me dice cuantos paquetes de cada cola deben pasar a la cañería. Ej: si el peso VoIP es de 70 y el de la otra cola es 30, de cada 100 paquetes que entren a la pipa 70 serán VoIP y 30 de otro tipo, en el caso que existan, de no existir el 100% de la cañería sera usado por la cola que tenga paquetes para enviar.

Reglas: Las reglas determinaran que tipo de paquetes deben ir a que cola, no es muy complejo realizar este proceso gracias a la interfaz gráfica amigable que posee m0n0wall, lo importante es tener en cuenta que las reglas se ejecutan en orden y que en cuanto un paquete cumple una regla no se sigue evaluando, por lo tanto es recomendable siempre poner una regla al final con * para "agarrar" los paquetes que no cumplan ninguna regla de lo contrario estos paquetes se saltarían las colas y arruinarían el traffic-shaper. Para este caso particular la regla que establecí para el trafico VoIP fue en función del servidor de VoIP o PBX es decir todo trafico entrante o saliente de la PBX se marca como trafico VoIP y utiliza la cola de mas alto peso (determinar el peso ideal es un ejercicio de ensayo y error ;) )

Evaluación de la calidad VoIP

Para evaluar la calidad de la comunicación VoIP aparte de pedirle a los usuarios finales que realizaran llamadas y que estuvieran atentos a la calidad, instale un monitor (sniffer) especializado el AdventNet VQManager muy bueno y de fácil instalación tanto en Windows como en Linux, para el correcto funcionamiento de este sniffer, como el de cualquier otro, es necesario conectar la tarjeta de red en un puerto de nuestra topología que permita monitoreo, en el caso de un hub eso no es un problema pero en un switch no cualquier boca sirve, en mi caso utilice el puerto uplink del switch principal que esta fuera del firewall, pero con switch programables es posible poner cualquier otra boca en modo port-mirroring. Para saber mas al respecto es necesario referirse al manual de cada switch.

lunes, enero 07, 2008

Mantención servidor DNS

Mi primera tarea fue revisar que el servidor DNS de la empresa no tuviese entradas repetidas, ni zonas que ya no son clientes o algún otro tipo de problema de configuración.
Primero obtuve una lista de todos los dominios (zonas) definidas en el archivo de configuración del bind, normalmente /etc/namedb/named.conf, para ello escribí este script en perl:

#!/usr/bin/perl
open(my $in, "<", "named.conf") or die "No se puede abrir named.conf: $!"; open(my $out, ">", "dominios.txt") or die "no se puede escribir la salida: $!";

while (<$in>) {
if (/zone\s\"([\w\-\.]+)\"/) {
print $out "$1\n";
}
}

close $in;
close $out;
Mi experiencia en perl antes de esta tarea es igual a 0 (cero) por lo que pido comprensión a la hora de criticar el código :)

Luego con la lista de 1200 zonas que obtuve como resultado, la pase por un script que genera dos ficheros, uno con los dominios .cl y otro con los demás (ya que la respuesta al whois es diferente para diferentes raíces de dominio):

#!/usr/bin/perl
open(my $in, "<", "dominios.txt") or die "No se puede abrir named.conf: $!"; open(my $out, ">", "cl.txt") or die "no se puede escribir la salida:
$!";
open(my $otro, ">", "otros.txt") or die "no se puede escribir la
salida:
$!";

while (<$in>) {
if (/\w\.(cl)/) {
print $out "$_";
} else {
print $otro "$_";
}
}

close $in;
close $out;
El objetivo de esto es realizar una consulta whois por cada dominio de la lista, en mi caso particular la gran mayoría (unos 1000) eran .cl y el resto otros, así es que genere dos scripts mas para esto ultimo:

#!/usr/bin/perl
open(my $in, "<", "otros.txt") or die "No se puede abrir named.conf: $!"; open(my $out, ">", "reporte.otros.txt") or die "no se puede escribir la
salida: $!";

while (<$in>) {
print $out "\n\n$_";
# $dominio=$_;
$resultado = qx/whois -H $_/;
@lineas = split(/\n/,$resultado);
foreach (@lineas) {
if (/Name Server:\s*([\w\-\.]+)/) {
# print "$dominio\n";
print $out "$1\n";
}
}
}

close $in;
close $out;

#!/usr/bin/perl
open(my $in, "<", "cl.txt"); open(my $out, ">", "reporte.cl.txt");

while (<$in>) {
print $out "\n\n$_";
$resultado = qx/whois -H $_/;
@lineas = split(/\n/,$resultado);
foreach (@lineas) {
if (/([\w\.\-]+)\s\([\d\.]+\)/) {
print $out "$1\n";
}
}
}
close $in;
Close $out;
El primero guarda en reporte.otros.txt los nameserver registrados en donde corresponda para todos los dominios NO .cl y el segundo guarda en otro archivo (reporte.cl.txt) los nameservers registrados en nic.cl de los dominios .cl, luego con el gedit edite estos archivos y al buscar mi servidor DNS se destacaron todos los dóminos de los cuales actualmente somos servidor ya sea primario o secundario, y me permitió realizar la primera vuelta de eliminación de dóminos (aquellos de los cuales ya no somos nameserver, y aquellos que ya expiraron).

El siguiente problema era las zonas repetidas y/o mal definidas, para ello es útil referirse a los logs, en ellos encontré una linea que decía "cannot redefine zone 'dominio.dom' " lo que indica que existe un dominio definido mas de una vez, para ubicar todos estos errores en el log utilice el siguiente script perl:

#!/usr/bin/perl
open (IN,"named.log");
open (my $out,">", "repetidos.txt");

$i=0;

while() {
chomp;
if (/cannot redefine zone\s\'([\w\-\.]+)/) {
print $out "$1\n";
}
}
close IN;
close $out;
Otra información que encontré en los logs es " Err/TO getting serial# for "dominio.dom" " lo que significa que existen problemas para obtener información del DNS primario (por que nosotros somos servidor secundario para ese dominio) sobre ese dominio, lo que puede ser síntoma de que el dns primario que tenemos definido en nuestra configuración dejo de ser valido, a continuación script para encontrar esos dominios en el archivo de log.

#!/usr/bin/perl
open (IN,"named.log");
open (my $out,">", "serialerror.txt");

$i=0;

while() {
chomp;
if (/xfer-in: Err\/TO getting serial# for\s\"([\w\-\.]+)/) {
print $out "$1\n";
}
}
close IN;
close $out;
Otro error común que se puede detectar en los logs es "sysquery: findns error (NXDOMAIN) on " en mi caso correspondía a misspelling (meter mal los dedos al teclado) a la hora de definir zonas, sabrán que las zonas se definen en ficheros de texto independientes por lo tanto tratar de encontrar donde escribimos nas.dominio.dom en vez de ns.dominio.dom abriendo uno por uno los archivos de zona no es una opción para ellos tenemos find:

# find /ubicacion/deficheros/dezona -type f | xargs grep nas.domino
Bueno en base a esto pude generar un reporte con los dominios que debían ser reparados y aquellos que debían ser eliminados espero le sirva a alguien.

Saludos