Presentaciones de la segunda parte de la asignatura “Servidores Web”

Aquí dejo las presentaciones que he usado en las dos última sesiones de la asignatura de Servidores Web del Máster Universitario en Desarrollo de Aplicaciones y Servicios Web.

En estas sesiones hemos tratado:

  • Nginx:

  • Nodejs (características y usos principales):

  • Pruebas en servicios web:

  • Cloud: AWS y Azure

¡Espero que os sea útil!


Transparencias de la asignatura “Servidores Web”

Aquí dejo las presentaciones que he usado en las dos primeras sesiones de la asignatura de Servidores Web del Máster Universitario en Desarrollo de Aplicaciones y Servicios Web.

La primera sesión fue sobre el protocolo HTTP:

La segunda sobre Apache:

¡Espero que os sean útiles!


Creando cuadro de mandos con Grafana. Segunda parte

Con las instalaciones y configuraciones vistas en la entrada anterior, tenemos la infraestructura1 de medición y análisis que nos permitirá disponer de un potente framework  para realizar análisis en tiempo real de series históricas. Lo que nos falta es crear el cuadro de mando que necesitemos para monitorizar y analizar nuestro servicio. En la URL http://IP_de_Grafana:3000 tendremos la aplicación web que, fácilmente, nos lo permitirá. El proceso de configurar Grafana con Influxdb es muy sencillo y está muy bien documentado en el sitio web de Grafana.

Leer más


Infraestructura de monitorización basada en Grafana (más Influxdb y Telegraf). Primera parte

Como prueba de concepto comentaré los pasos que he seguido para disponer de un panel que muestre la analítica de un servidor web con Apache. Junto a Grafana, tendremos influxdb y Telegraf, los 3 elementos de nuestra infraestructura de medición y análisis que nos permitirá disponer de un potente framework  para realizar análisis en tiempo real de series históricas.

Leer más


Thepiratebay.se, bloqueo y técnicas para evadirlo

Creo que casi todos conocemos ya que un juez ha ordenado bloquear el acceso a The piratebay desde España. Esta orden afecta a los proveedores de servicio españoles que suelen cumplirlo mediante:

  • Las resoluciones DNS
  • Inspección de paquetes

(El que no usen un filtro contra la IP es porque esa IP puede estar asignada para más de un sitio web y estos no deberían “pagar” por el hecho denunciado si no son los responsables)

Si se aplica el bloqueo en el DNS, con indicarle a nuestros sistemas que usen otros que no se hayan visto afectados por la orden sobra para que podamos seguir accediendo a dicho sitio. Por ejemplo, podemos usar openDNS o los de Google (8.8.8.8).

Si se realiza inspección de paquetes, con conectarnos mediante HTTPS sobra para acceder al sitio. En la inspección de paquetes lo que se busca es información relevante para realizar la tarea encomendada (normalmente, se busca en el nivel de aplicación). Para este caso, lo normal es que se busque la cabecera host del protocolo HTTP ya que se encarga de identificar el sitio al que nos queremos conectar. Si usamos HTTPS esta información va cifrada y “no la encontrará el analizador”.

Si se usaran combinadas las 2 técnicas, tendríamos que combinar las 2 “soluciones”. No es necesario ni VPNs ni TOR como se menciona en este artículo sobre cómo saltarse el bloqueo, aunque, evidentemente, son buenas opciones que, además, nos dan otras ventajas añadidas a simplemente conectarnos a ThepirateBay.se

¡Espero que os sea útil!

PD: No soy usuario de este sitio, pero he probado desde mi PC, con Vodafone como ISP, y  funciona perfectamente  la conexión con HTTPS y no con HTTP.

PD2: Conseguir el efecto que pretende la orden del juez es muy complicado tal y como funciona y se organiza Internet

 

 


ownCloud y Raspberry PI

Tras un aviso de Dropbox para que reduzca los muchísimos GB que me sobran al haber “caducado la oferta” por pertenecer a la UA, he decidio crearme mi “ownCloud” con un disco duro de 1TB que tenía y una Raspberry PI.

Sobre como instalarlo hay muchas entradas en la red (I, II, III, IV,  …) así que poco más que añadir o comentar, salvo:

Leer más


Cómo controlar la recepción de peticiones abusivas desde una misma IP

Hace unos meses escribí esta entrada sobre un guión en Python para la detección de peticiones abusivas hacia un servidor web Apache. Me faltó indicar cómo controlarlas y es muy sencillo:

iptables -I INPUT --dport 80  -p tcp --syn -m recent --name "CONTROL-WEB" --set
iptables -I INPUT -p tcp --dport 80 --syn -m recent --update --seconds 30 --hitcount 20 -j DROP

Estas líneas de iptables, que podemos añadir al principio de la lista de órdenes de nuestro filtro de acceso al servidor, se encargan de contar el número de peticiones de establecimiento de conexión por cada dirección IP en los últimos 30 segundos, descartando los que puedan llegar cuando la cuenta ha superado el valor de 20 e impidiendo, por tanto, la conexión desde esa IP. El guión de la entrada mencionada nos puede servir para hacernos una idea de qué es lo habitual en nuestro sistema y ajustar los segundos y número de conexiones en esos segundos que queremos admitir.

La primera orden crea una lista dinámica de nombre CONTROL-WEB en la que insertará la IP de origen del paquete. La segunda, comprueba si esa IP se ha añadido a la lista en los últimos 30″ y, en el caso de que se hayan contabilizado más de 20 peticiones, deniega la petición.

Podemos ser más específicos indicando que solo queremos contabilizar los inicio de conexión:

iptables -I INPUT -p tcp --dport 80 -m state --state NEW -m recent -name "CONTROL-WEB" --set
iptables -I INPUT -p tcp --dport 80 -m state --state NEW -m recent --update --seconds 30 --hitcount 50 -j DROP

En el directorio /proc/net/xt_recent encontraremos un fichero de nombre CONTROL-WEB y cuyo contenido es la lista de IPs añadidas por las reglas anteriores.

Referencia

  1. man iptables

Virtualhost con nombre en Apache y Nagios

RECORDATORIO: Si estás comprobando el funcionamiento de tu sitio web con Nagios y dispones de varios sitios definidos con VirtualHost basados en el nombre debes asegurarte que en la petición se mande el nombre del sitio deseado.

Si lo hiciéramos “a mano”, la conversación desde el cliente nagios al servidor Apache sería así:

telnet nombre_servidor 80
GET / HTTP/1.0
HOST:nombre_servidor
línea en blanco
línea en blanco

Para comprobar la disponibilidad, Nagios utiliza la orden check_http internamente y, para asegurarnos que indica el nombre del servidor, la invocación debe incluir el parámetro “-u <nombre_servidor>”:

check_http -I -p 80 -u <nombre_servidor> -R "string a buscar"

Como saber las IPs que han realizado más de N peticiones en S segundos a nuestro servicio Apache

Tras una pregunta de un alumno en clase sobre control y detección de peticiones abusivas, recordé el guión que desarrollé, cómo no con Python ;), para detectar “ráfagas” de conexiones. Con Python es muy fácil analizar los logs del servidor Apache y consultar qué IPs nos han realizado más de X peticiones (entendiendo por X un parámetro que introducimos como argumento) en un número de segundos determinado (también introducido como argumento del guión)

Dependiendo de qué información queramos extraer, ajustaremos el número de peticiones y segundos. Por ejemplo, ante subidas en la carga del servidor, podemos comprobar qué IPs cumplen un patrón y, si consideramos que se están realizando con mala fe, bloquear el tráfico proveniente de estas IPs.

Aunque en EPSAlicante rotamos los logs, por si no se hace o por si la información que queremos analizar está en un periodo de tiempo determinado, esto también puede indicarsele al guión (D días, H horas y M minutos antes).

El desarrollo de este guión apenas me llevó 2 horas (y con interrupciones) y, sin embargo creo que es muy útil. Podemos hasta ejecutarlo periódicamente para ver qué obtenemos. ¡Nunca se sabe las sorpresas que nos podemos llevar!

Creo que incluso sería bueno (posible mejora) que la información la proporcionara en XML para integrarlo con otros sistemas de administración y/o seguridad.

Justo debajo de este párrafo está el guión. La función más importante es readHistoryIP que devuelve un diccionario con clave IP y valor otro diccionario cuya clave es la fecha de registro del evento y el valor el número de veces que para esa IP aparece un evento +/- segundos con respecto a la fecha del evento. En el cuerpo principal se muestra la información de las IPs que aparecen más veces que del valor indicado por parámetro para el intervalo de tiempo establecido.

#!/usr/bin/python
#encoding:utf-8
try:
 import sys,optparse, datetime,re
except:
 print("Error running 'import sys,optparse,datetime,re'. Maybe you have to install some python library")

def isValid(ip):
 if (re.match("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",ip)):
 return True
 elif re.match(r'^[a-zA-Z0-9_]{,63}(.[a-zA-Z0-9_]{,63}){3}$',ip): #("^W+(.W)$", ip):
 return True
 else:
 #print "Name/IP host wrong. You have to write a FQDN name or a IP direction"
 return False
 return False

def readHistoryIP(logfile,sec, deltaday,deltahour,deltaminutes):
 months = {"Ene":1, "Feb":2, "Mar":3, "Abr":4, "May":5, "Jun":6, "Jul":7, "Ago":8, "Sept":9, "Oct":10, "Nov":11, "Dic":12}
 today = datetime.datetime.now()
 refDay= datetime.timedelta(days=int(deltaday), hours=int(deltahour), minutes=int(deltaminutes))

 ipHitListing = { }
 try:
 contents = open(logfile, "r")
 except IOError, e:
 print 'Error openning file '+ logfile +": "+e.strerror
 raise
 except:
 print 'Error openning file '+ logfile
 raise
 # go through each line of the logfile
 for line in contents:
 # split the string to isolate the IP address and date
 ip = line.split(" ", 1)[0]
 dateRecord=line.split(" ",5)[4][1:]
 date = dateRecord.split(":",1)[0]
 day=date.split("/",1)[0]
 month=date.split("/",2)[1]
 year =date.split("/",3)[2]
 hour = dateRecord.split(":",2)[1]
 minute = dateRecord.split(":",3)[2]
 seconds = dateRecord.split(":",4)[3]
 eventdate=datetime.datetime(int(year),int(months[month]),int(day),int(hour),int(minute),int(seconds))
 #Check if ip is right
 #print "Analizing IP: '" + ip + "'"
 if isValid(ip):
 if eventdate > today - refDay:
 intervalDelta=datetime.timedelta(seconds=int(sec))
 count=False
 if (ip in ipHitListing): 
 for ipData in ipHitListing[ip].items():
 if eventdate < ipData[0] + intervalDelta:
 ipHitListing[ip]= {ipData[0]:int(ipData[1])+1}
 count=True
 break
 if count == False:
 ipHitListing[ip]= {eventdate:1}
 else:
 ipHitListing[ip]= {eventdate:1}
 return ipHitListing
def main():
 parser = optparse.OptionParser("usage%prog " + "[-f <file>] -n Number of events -s seconds to find events -d number of days to analice -H number of hours to analice -m number of minutes to analice")
 parser.add_option('-f', dest = 'file', type = 'string', help = 'Please, specify the Apache access file', default="/var/log/apache2/access.log")
 parser.add_option('-n', dest = 'number', type = 'string', help = 'Please, specify the number of connections to detect. By default 10', default="10")
 parser.add_option('-d', dest = 'deltaday', type = 'string', help = 'Please, specify the number of days to check. By default 10', default="10")
 parser.add_option('-H', dest = 'deltahour', type = 'string', help = 'Please, specify the number of hours to check. By default 10', default="10")
 parser.add_option('-m', dest = 'deltaminute', type = 'string', help = 'Please, specify the number of minutes to check. By default 10', default="10")
 parser.add_option('-s', dest = 'seconds', type = 'string', help = 'Please, specify the seconds. By default: 1"', default="1")
 (options, args) = parser.parse_args()
 HitsDictionary = readHistoryIP(options.file,options.seconds, options.deltaday, options.deltahour, options.deltaminute)
 for ip in HitsDictionary.keys():
 if HitsDictionary[ip].values()[0] > int(options.number):
 print "Attention---> IP: " + ip + ", " + str(HitsDictionary[ip].values()[0]) + " access in " + options.seconds + " seconds"
if __name__ == "__main__":
 main()

Referencias:

  1. Python para todos, de Raúl González Duque
  2. Python cookbook, de Alex Martelli, Anna Martelli y David Ascher, O’Reilly

¿Cómo se distribuyen los estados de las peticiones que recibe nuestro servidor Apache2?

Si queremos saber cuál es el porcentaje, sobre el total de respuestas, de un estado HTTP determinado (o todos los posibles) dentro de las peticiones de servicio que recibe nuestro servidor Web Apache, con el guión escrito en Python al final de esta entrada, lo podemos obtener fácilmente.

¿Para qué nos puede servir esta información? En principio, para saber cómo está trabajando nuestro servidor y cómo se está relacionando con los clientes:

  • Los estados de error, bien del cliente o del servidor, nos permitirán conocer los porcentajes de error y detectar problemas
  • Estados específicos, como el 304 que nos permitirá saber el porcentaje de páginas solicitadas a pesar de que el cliente tiene una copia en cache vigente (y analizar rendimientos)
  • Porcentajes de accesos no autorizados, solicitudes incorrectas,…
#!/usr/bin/python
#encoding:utf-8
try:
 import sys,optparse
except:
 print("Error running 'import sys,optparse'. Maybe you have to install some python library")
def clientStatePercentage(logfile,state):
 try:
 contents = open(logfile, "r")
 except IOError, e:
 print 'Error openning file '+ logfile +": "+e.strerror
 raise
 except:
 print 'Error openning file '+ logfile
 raise
 totalRequests = 0
 requests = 0
 for line in contents:
 totalRequests += 1
 if line.split(" ")[8] == state:
 requests += 1
 return int(0.5+float(100*requests)/totalRequests)
parser = optparse.OptionParser("usage%prog " + "[-f <file>] [-e HTTP STATE[,HTTP_STATE2,[...]]]")
parser.add_option('-f', dest = 'file', type = 'string', help = 'Please, specify the Apache access file', default="/var/log/apache2/access.log")
parser.add_option('-e', dest = 'state', type = 'string', help = 'Please, specify the HTTP State', default="304")
(options, args) = parser.parse_args()
log=options.file
states=options.state.split(',')
for state in states:
 print 'Percentage of requests with ' + state + ' state: ' + str(clientStatePercentage(log,state)) + '%'

Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más información.