viernes, 17 de febrero de 2012

TCP Sockets: TIME_WAIT y los puertos efímeros son poco amigos

Hoy vamos a explicar un poco qué es TIME_WAIT y cómo puede darte por culo un rato largo.

[INTRODUCCIÓN]
Todos sabemos que las conexiones TCP ( capa 4 en modelo OSI, también llamada capa de transmisió de datos ) tienen estados. Umm... bueno, igual no todos lo sabemos. Refresquemos un poco qué son esto de los estados TCP antes de empezar.

TCP establece la conexión a tres vías[1]. Cuando se intenta establecer la conexión del host A al host B, el host A manda un paquete tipo SYN al host B. La conexión pasa a estar en un estado SYN_SENT. Cuando el host B le contesta con un paquete ACK-SYN, la conexión para a un estado llamado SYN_RECV. Cuando la conexión por fin se establece pasa a estado ESTABLISHED. También hay el estado LISTEN, que es el estado que tienen servicios tales como apache, memcached o sshd, esperando nuevas conexiones. Total, con todo esto nos tenemos que quedar con la copla de que una conexión TCP pasa por diferentes estados desde que se establece hasta que se cierra.

La lista completa de estados se puede ver fácilmente haciendo un man netstat, y buscando el apartado State.

Bien, entonces tenemos que los dos estados mas comunes son: ESTABLISHED conexiones establecidas y LISTEN, en el caso de que estemos en un servidor con varios servicios activos. Para ver una lista de las conexiones abiertas en nuestra máquina podemos hacer un:

$ sudo netstat -pan --tcp | less


[SUPOSICIÓN]
Si ejecuto un script muy simple que haga peticiones HTTP contra mi servidor web local SUPONGO que no habrá ningún problema (jeje, los cojones). Tengo un servidor Apache escuchando en el puero 80 en localhost. Problemos a ejecutar el siguiente comando en 5 consolas diferentes.

$ while true; do curl localhost; done

El comando "curl" abre una conexión TCP, hace una petición HTTP, se le devuelve un resultado (que no nos importa lo mas mínimo, ahora mismo) y CIERRA LA CONEXIÓN TCP. O esto es lo que nosotros esperamos, vamos. Entiendo que tener 5 instancias del script haría que normalmente tuviese 5 conexiones en estado ESTABLISHED, pero esto, nunca debería ocasionar un problema a nivel de sistema.

Hagamos la prueba. Ejecutamos cinco veces el script en consolas separadas, y en otra terminal ejecutamos:

$ sudo watch -n 0.5 'netstat -pan --tcp | grep ESTABLISHED | wc -l'


Pues efectivamente, nunca pasamos de 5 conexiones al mismo tiempo en estado ESTABLISHED pero... Umm.... pero al cabo de un minuto de estar ejecutando los scripts, el comando "curl" empieza a dar un error:

curl: (7) Failed to connect to 127.0.0.1: Cannot assign requested address


[PROBLEMA]
WTF. A ver, tenemos tan solo 5 conexiones abiertas. El error del curl parece decirnos que no puede obtener un puerto de origen para establecer la conexión. Veamos porqué:
Ejecutemos los scripts anteriores y pongamos un filtro diferente en el netstat:

$ sudo netstat -pan --tcp | grep TIME_WAIT | wc -l
5329
$ sudo netstat -pan --tcp | grep TIME_WAIT | wc -l
13440
$ sudo netstat -pan --tcp | grep TIME_WAIT | wc -l
19348
$ sudo netstat -pan --tcp | grep TIME_WAIT | wc -l
21526
$ sudo netstat -pan --tcp | grep TIME_WAIT | wc -l
28214


COJONES! Por algún motivo estan quedando chorrocientos sockets abiertos en estado TIME_WAIT!

[QUÉ ES TIME WAIT]
Ahora que nos hemos puesto un poco en matéria vamos a ver para que se usa el estado TIME_WAIT, y como puede llegar a ser un problema. De la definición del man de nestat sacamos que:

TIME_WAIT: The socket is waiting after close to handle packets still in the network.

Umm... según esto parece que la conexión no se cierra hasta que pase un tiempo para poder capturar los paquetes que se habían "perdido" por la red debido a posibles problemas de enrutamiento. Jummm... explicación algo escueta. Sobre todo porque no dice cuanto vamos a tener que esperar. Vamos a investigar un poquito más sobre el tema...

El RFC 1122 en el apartado "Transport layer, TCP" [2] dice algo así como:
When a connection is closed actively, it MUST linger in TIME-WAIT state for a time 2xMSL (Maximum Segment Lifetime).

Vaaaale. Ahora ya tenemos un poco mas de información. Resulta que tenemos que esperarnos 2xMSL antes de que una conexión pase de TIME_WAIT a CLOSED ( y se libere la conexión ). Genial. Y ahora... ¿Dónde coño puedo consultar el tamaño del Maximum Segment Lifetime? Pues ejecutando este comando:

$ cat /proc/sys/net/ipv4/tcp_fin_timeout
30

Que en mi máquina Debian me da un resultado de 30 segundos. En máquinas con Ubuntu el valor acostumbra a ser 60, en máquinas Solaris acostumbra a ser 120. Entonces parece ser que, en mi máquina, tardaré 2*MSL, o sea 2*30=60 segundos en liberar una conexión. El problema que hemos experimentado antes es que hay muchos sockets (conexiones) abiertas en estado TIME_WAIT. Cuando llegamos al límite de conexiones efímeras del sistema, el kernel nos dá un error diciendo "umm... me sabe mal, pero no me quedan puertos efímeros". Esto se traduce en el error que nos saca el "curl". O sea, no es problema del comando "curl" que vaya dejando conexiones abiertas, ya que si no tendríamos muchas conexiones en estado ESTABLISHED, y no es el caso. Tampoco es problema del kernel, ya que éste implementa el RFC 1122 como dios manda.

[PUERTOS EFÍMEROS]
"Ei. Ei, espera. Cuando has empezado a hablar de puertos efímeros y toda esta mierda me he perdido. A ver si te explicas un poquito más, macho."
Veamos. Cuando establecemos una conexión TCP con nuestro navegador a un servidor Web, necesitamos un puerto de origen [3]. Estos puertos, se conocen como puertos efímeros. El protocolo TCP necesita definir un puerto origen y un puerto destino. En nuestro ejemplo, el puerto destino será el 80 (servidor web), y el puerto origen será un puerto efímero. El Kernel nos asigna un puerto efímero automáticamente cuando establecemos una conexión nueva. Para ver el rango de puertos efímeros que tenemos definido podemos ejecutar:

$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000

Si hacemos 61000-32768 nos dá 28232. Si os fijais, es un número muy parecido al número de sockets en estado TIME_WAIT que teníamos cuando ha empezado a petar todo. Esto es debido a que un socket en estado TIME_WAIT todavía es un socket activo. Es lo mismo como si estuviese en estado ESTABLISHED, por lo que está ocupando un puerto efímero. Si acumulamos muchos sockets en estado TIME_WAIT, vamos agotando los puertos efímeros. Cuando llegamos al límite y tenemos reservados *todos* los puertos efímeros, al kernel no le queda otra que devolver error cada vez que se quiere crear un socket nuevo. Bonito, verdad?

[POSIBLES SOLUCIONES]
Bueno, una de ellas pasa por aumentar el número de puertos efímeros del sistema. Esto se puede conseguir simplemente ejecutando:
$ sudo su - root
$ echo "1025 61000" > /proc/sys/net/ipv4/ip_local_port_range

Hecho esto, ahora disponemos de 60K puertos efímeros y no solo 30K. En realidad esto no es una solución de nada. Simplemente tendremos que esperar 2 minutos y no 1 para que los procesos "curl" agoten el número de sockets. Estamos haciendo que el sistema aguante un poquito mas, pero ya está.


Otra solución parece que puede venir modificando el parámetro 2*MSL. Si disminuimos este valor del MSL, los sockets en estado TIME_WAIT deberían estar en este estado durante menos tiempo. El parámetro es ajustable mediante el comando comentado anteriormente:
$ sudo su -c 'echo 5 > /proc/sys/net/ipv4/tcp_fin_timeout'

Si probais de aplicar este cambio vereis que en realidad las conexiones en estado TIME_WAIT no se cierran a los 10 segundos como sería lo esperado (2*MSL; 2*5=10), sino que se siguien cerrando a los 60 segundos. Por qué ocurre esto? Simple. El valor "60 segundos" está hardcoded en el kernel de linux. Así de bonito. Si miramos el fichero /include/net/tcp.h línea 105 del kernel de línux veremos algo tal que así:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT state, about 60 seconds */

A partir de línea 202 del fichero /net/ipv4/tcp_minisocks.c vereis que hace uso intensivo de la variable TCP_TIMEWAIT_LEN, justo en la sección donde define el estado TCP_TIME_WAIT al socket TCP. Por lo que modificar a través de las sysctls el parámetro "tcp_fin_timeout" no va a servir de un carajo. Ni 2*MSL ni pollas. Harcoded en el código. (Esto vulnera el RFC 1122 y la regla 2MSL? Si alguien lo sabe que por favor comente! Gracias )


La última solución, que dista mucho de ser algo perfecto pasa por activar el tcp_tw_recycle y tcp_tw_reuse. Ambas directivas son bastante peligrosas, y vulneran explícitamente el RFC 1122. No deberían usarse en entornos de producción o sin saber muy bien qué se está haciendo. La documentación que he encontrado al respecto es [4] y [5].

TCP_TW_RECYCLE: It enables fast recycling of TIME_WAIT sockets. The default value is 0 (disabled). The sysctl documentation incorrectly states the default as enabled. It can be changed to 1 (enabled) in many cases. Known to cause some issues with hoststated (load balancing and fail over) if enabled, should be used with caution.
$ echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle

TCP_TW_REUSE: This allows reusing sockets in TIME_WAIT state for new connections when it is safe from protocol viewpoint. Default value is 0 (disabled). It is generally a safer alternative to tcp_tw_recycle
Note: The tcp_tw_reuse setting is particularly useful in environments where numerous short connections are open and left in TIME_WAIT state, such as web servers. Reusing the sockets can be very effective in reducing server load.
$ echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

O sea, usadlos, pero con mucho cuidado.


La solución definitiva viene en forma de pregunta: ¿Qué coño haceis creando 30K conexiones TCP en menos de un minuto, criaturas del Señor?
Si habeis llegado aquí porque os habeis encontrado con el problema del TIME_WAIT en un servidor de producción cuando recibía bastante tráfico os tengo que decir algo que no os va gustar: algo estáis haciendo mal. En este caso no es suficiente con cerrar las conexiones a la BBDD, caches, o lo que sea cada, vez que se hace una consulta. La solución pasa por hacer pooling de conexiones y demás. REAPROVECHAR conexiones. Pero bueno, esto es otra guerra que se sale del alcance de este post. Aún así para dudas, ruegos y preguntas teneis los comentarios. Intentaré ayudar hasta donde pueda/sepa.


[ÚLTIMAS NOTAS]
Nótese que este problema es provocado por la conjunción de crear muchas conexiones por parte de un CLIENTE (importante este punto) durante un periodo muy corto de tiempo. Esto hace que se acumulen muchos sockets abiertos (bueno, o no-cerrados, para ser mas exactos) en estado TIME_WAIT. Cuando se llega al punto que la suma de todos los sockets en dicho estado es igual al número de puertos efímeros de la máquina, el kernel no es capaz de crear sockets nuevos hasta que no se liberan recursos, devolviendo un error a cualquier proceso que intente crear una conexión nueva (nótese que las conexiones ya establecidas seguirán funcionando normalmente).

Espero que les haya gustado el post. La zona de comentarios está disponible para hacer preguntas, o proponer soluciones alternativas al problema.

Un saludo, Jan.

[1] http://danred.wordpress.com/2009/09/28/establecimiento-de-conexion-tcp-de-3-vias/
[2] http://www.rfc-editor.org/rfc/rfc1122.txt página 87
[3] Recordemos que un socket está formado por un IP Origen - Puerto Origen - IP Destino - Puerto Destino.
[4] http://www.speedguide.net/articles/linux-tweaking-121
[5] http://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt

domingo, 12 de febrero de 2012

Wikipedia, querida. ¿Por qué no te preocupas por mi privacidad?

Lo veo y no me lo creo. Por Diox![1]...

Página de login de la www.wikipedia.org. Se accede des del link que aparece en la parte superior derecha que reza algo así como Log in / create account. Si nos fijamos en el tooltip de dicho botón pone: You are encouraged to log in; however, it is not mandatory. [alt-shift-o]

Oh. Resulta que incluso tenemos un shortcut para ir a la página de login: alt-shit-o. Curioso.

Pongo el nombre de usuario, pongo la contraseña y.... oh! Wait. Me fijo antes de darle al botón Log in que no estoy en una página segura. O sea, que no estoy con HTTPS. ¿Será posible? Miro los opciones de la página dándole al logo de la wikipedia, justo a la izquierda de la URL en firefox y me dice: "Your connection to this website is not encrypted". Mecagonlaputa.

Le doy a "More information" y me encuentro con esto:



Jumm.... parece que está claro. La página no está cifrada. Bueno, como todavía tengo la esperanza de que la propiedad "action" del form vaya a una URL segura (esto es posible? tendré que probarlo[2]). Hago una prueba simple:

Pongo enmarcha el "wireshark" capturando todo el tráfico. Le doy a "login" en la página de la Wikipedia y.... e voilá. Esto es lo que ha capturado el wireshark:


POST /w/index.php?title=Special:UserLogin&action=submitlogin&type=login&returnto=Main+Page&campaign=ACP1 HTTP/1.1
Host: en.wikipedia.org
[...] # Borradas las demás cabezeras por no tener releváncia en este caso

wpName=jcarreras&wpPassword=ilovemiami&wpLoginAttempt=Log+in&wpLoginToken=c7b2dc6fc071c5fe3b6f96efb8ef6122


Que bonito! Si señor! Un POST de toda la vida que contiene dos variables llamadas wpName y wpPassword. Todo viajando en claro a través de la red. Con dos cojones.

Fijándome un poquito mas veo que justo debajo del login hay un apartado que pone: "Secure you account". El primer punto pone: "Consider logging in on the secure server." y tiene un enlace que te lleva a la misma página pero usando HTTPS. Mi pregunta es: ¡¿por que carajo éste no es el modo de hacer login por defecto?! Un login que mande una contraseña en claro SIEMPRE debe ir a través de un canal cifrado/seguro. Siempre.

Después continua tocando otros puntos sobre seguridad como:
  • If your password only contains letters or only numbers [...] consider changing it. Para qué? Si el password viaja en cleartext?! No tiene importancia la longitud de la misma si la contraseña viaja en texto plano.
  • To avoid becoming a victim of phishing, always verify that you are viewing Wikipedia's login page [...] Ummm... exactamente PARA ESTO sierve HTTPS y los certificados SSL. Aparte de cifrar todo el canal de información, verifican que el servidor és quién dice ser.
  • Do not give out your password to anyone. Jejeje, les falta decir: "nosotros lo haremos por tí, majo".

Pues vaya, esto simplemente haciendo un MITM con arpspoof+tcpdump o con Ettercap directamente y, ale, regalando tu contraseña a toda la gente que se encuentra en tu red local! Yuhuuu! Felicidad!

Un saludo, Jan.

[1] http://www.frikipedia.es/friki/Diox
[2] Me refiero poner en el action de un form, una url que sea algo así como https://foo.bar/login

domingo, 5 de febrero de 2012

Fin de una época... AKA adiós trabajo, adiós

A finales de este mes también acabo en mi trabajo =).

"Pobre Jan. ¿Te sacan? Puta crisis, afecta a todo el mundo..."
Ummm... Pues no. No me sacan. Ni mucho menos. Os cuento.

Resulta que a mi me encanta estudiar y aprender cosas nuevas. Acabé el Grado Superior de Administración de Sistemas Informáticos, hice las prácticas en eConcept y les molé. Me hicieron un contrato fijo, cobraba un suelo que no estaba mal y *aprendía un montón*. Esto para mí es lo más importante. Casi casi es lo que valoro más: aprender cosas nuevas. Esto es así porque: disfruto aprendiendo, me da un valor añadido, puedo ponerme nuevas metas, me mantengo al día y en definitiva todavía me considero un estudiante.

En la empresa somos muy poquitos. 6 técnicos en total: 4 programadores y 2 de sistemas. Todos son unos cracks: tenemos a un ex-empleado de Microsoft, otro compañero a estado trabajando en el Centro de Supercomputación de Barcelona, un sysadmin que aparte de su trabajo habitual también da clases de CISCO CCNA, y más de uno en el equipo a recibido ofertas por parte de Google para que trabajen para ellos. Trabajar con gente así es una pasada, ya que aprendes muchísimo, se te contagia el interés por las cosas más complicadas o supuestamente difíciles y.... buffff, es genial. Y aunque sean unos técnicos increibles son todavía mejores personas. Ha sido una pasada estar con ellos. Esto es lo que voy a echar de menos.

He estado en proyectos muy interesantes: telefonía de VozIP (con centralita propia y demás) haciendo todo el tema de tarificación y tarifas planas, así como prácticamente todo el backoffice para gestionar los clientes, extensiones, geográficos, etc... También me dieron un proyecto para mi solito sobre gestión de contratos de telefonía (VozIP, fija, móvil y ADSL) para que los comerciales puedan generar de manera electrónica los contratos en casa de los mismos clientes y mandarles el contrato rellenado y listo para firmar al email del cliente para que lo revisen cuando quieran. También he estado ayudando en el proyecto de www.nauticholidays.com haciendo un pequeño CMS, habilitar temas de SEO y me he estado encargando del mantenimiento del proyecto entero durante un par de meses. Todo muy chulo, la verdad.

Bueno, en realidad no todo ha sido *tan* chulo. He tenido que hacer cosas menos agradables como encargarme de mantener una aplicación de facturación (Microsoft Navision, una versión del año de la Pepa) que fallaba (falla) mas que una escopeta de feria... pero bueno. Mejor nos quedamos con las cosas buenas. =)

Básicamente me quedé en la empresa porque el tipo de trabajo era muy interesante, los compañeros eran geniales y sobretodo porque iba a aprender un montón. La otra opción era empezar la carrera de informática en la UPC de Barcelona. Lo planteé del siguiente modo: ahora trabajo, tendré experiència en el Mundo Real(tm), aprenderé un monton y ahorraré todo lo que cobre para poder ir a estudiar la carrera. Y esto he hecho.

El caso es que en la empresa durante el primer año aprendía un montón de cosas cada semana. Era exagerado. Python, SQLAlchemy, CSS, JavaScript, el framework de desarollo (pylons), cosas de sistemas, el modelo MVC, bases de datos no relacionales (MongoDB), colas asíncronas (beanstalk), repositorios (primero bazar y después mercurial), desnormalización de datos, caches, procesos de deploy super-rápidos, Scrum.... La 'putada' de aprender tanto es que al final del primer año alcanzé lo que llamo la "zona de confort".

Esta zona es peligrosa. Llegas a ella cuando empiezas a sentirte cómodo con todas las cosas nuevas que has ido aprendiendo. Llega un momento que ya sabes todo lo que necesitas para hacer tu trabajo de manera eficinente y solo de vez en cuando necesitas revisar la documentación o aprender algo nuevo. Esta zona no me gusta porque es aburrida. Aprender cosas nuevas mola. Aprendes cosas para poder trabajar. Cuando dejas de aprender lo único que haces es trabajar. Y yo todavía me considero un estudiante, no un trabajador. Tendré 40 años ( o mas ¬¬ ) para trabajar. Ahora que me gusta aprender: coño, tengo que aprovechar. O sea, esto es mi punto de vista. Para la empresa la "zona de confort" significa que tienen un trabajador que ya ha alcanzado el nivel de conocimientos que necesitan para que ocupe su puesto de trabajo sin necesidad de formarse o de ayuda externa, por lo que es ideal.

Durante el segundo año he estado trabajando. Sin mas. Aprendiendo muy poquito, pero asentando los conocimientos adquiridos. Y ahorrando, ahorrando mucho. No ha sido tan divertido y emocionante como el primero, pero también ha estado bien. El caso es que ahora he conseguido tener un colchón de dinerito suficiente para estudiar mas o menos tres añitos en Barcelona, por lo que me he preguntado: ¿Por qué seguir trabajando?

Y el tema ha estado claro. El simple hecho de tener esta duda hace que respuesta no pueda ser otra: me las piro. Entre esto y el hecho de que han habido un poco de lios familiares, un servidor se va a vivir a BCN dentro de un mesecito. Tendré 6 meses todos para mi, antes de que empiece la carrera de informática. Tengo ya un par de proyectitos personales pensados que me gustaría hacer y pienso estar con los amigos y familiares. Disfrutar la vida en general, así, por que si =)

A quien ésto no le de envidia, es que no tiene alma, jeje ;)

CISCO CCNA

El año pasado empecé el curso de CISCO CCNA, o sea Cisco Certified Networking Associate. Muy interesante, la verdad. Para el que no lo sepa la empresa CISCO es una multinacional norteamericana líder en el mercado de las redes (networking). Venden des de routers para hogares familiares ( los Lynksys, puede que tengas uno ), hasta el hardware que se utiliza para comunicar los diferentes continientes como Europa y América. O sea, el backbone de Internet funciona sobre hardware de CISCO. Tienen una gama de productos increíbles, y no sólo ofrecen productos, si no que también imparten formación. Hay lo que ellos llaman "la carrera de CISCO" que se basa en tres niveles con un curso principal en cada nivel, y otros 4 módulos de ampliación. Para verlo mas claramente tenemos el primer nivel que es el CCNA:

CCNA: Curso principal, normalmente de unas 260 horas, es el que yo estoy haciendo. Incluye:
  • Bloque 1- Aspectos básicos del networking: OSI, TCP/IP, direccionamiento, capa de enlace de datos/física, Ethernet, planificació de cableado, etc...
  • Bloque 2 - Conceptos y protocolos de enrutamiento: Enrutamiento estático, protocolos de enrutamiento dinámico (RIP, VLSM, CIDR, RIPv2, EIGRP, OSPF)
  • Bloque 3 - Conmutación y WLAN: Diseño de redes LAN, configuración de switches, VLAN, VTP, STP, enrutamiento entre VLAN, configuración de WLAN.
  • Bloque 4 - Acceso a redes WAN: PPP, FrameRelay, Seguridad en la red, ACL, VPN's, DHCP, DNS y resolución de problemas en la red.
CCNA Security. Unas 80 horas de curso. Es un módulo de ampliación.
CCNA QoS: 80 horas, va sobre Quality of Service (Calidad de servicio). Para poder distinguir el tráfico prioritario (VozIP, HTTP) y hacer que siempre llegue con una calidad mínima.
CCNA WiFi: 80 horas.
CCNA Service Provider Operation: ni puta idea =). 80 horas seguramente por ser un módulo expansión.

Todavía no he acabado el curso, me quedan dos o tres capítulos del bloque 4 para poderme presentar al último examen parcial, y después tendré que hacer acopio de toda mi voluntad y fuerzas y ponerme a estudiar muy fuerte para verme lo suficientemente preparado como para hacer el exámen final. Tienes que sacar más de un 80% de puntuación para poder aprovar, por lo que no es moco de pavo. Cada intento son unos 200€.

Bueno, este es mi primer motivo por el cual no he estado escribiendo mucho por aquí. Otra cosa que ha influído ha sido el trabajo. Después de hacer 8 horas de curro al día, llegas a casa con pogas ganas de investigar cosas nuevas (aunque algo se hace), pero ya no te quedan fuerzas para escribir nada interesante.

Pero bueno, no desesperemos! El curso de CCNA prácticamente ya lo he acabado y dentro de poco tendré mucho tiempo libre. Os puedo anticipar que he empezado a jugar con el lenguaje de programación de Erlang, que me está encantando. Es un lenguaje funcional donde tenemos "variables invariables", que son variables pero que sólo pueden ser asignadas una vez (o sea, nada de "i++"). No existen las estructuras "while" o "for", ya que se usa recursividad como estructura iterativa, y las listas son el tipo de dato más común en el lenguaje. Una pasada! Ya os contaré lo que vaya aprendiendo y dónde quiero llegar con todo esto. Tengo un proyectito chulo chulo. Si todo sale bien será mi primer proyecto publicado como OpenSource!!

Dentro de un mesecito mas o menos tengo pensado volver a retomar el blog, y intentaré escribir 2 o 3 entradas semanales como en "mis tiempos mozos", por lo que estad preparados para lo que se avecina! =)

Un saludo, Jan.

sábado, 17 de diciembre de 2011

Plugins Firefox II

Hace tiempo escribí una entrada sobre los plugins que venía usando en Firefox. Bueno, parece ser que he tirado hacia el minimalismo últimamente y resulta que ya no uso ninguno de los plugins mencionados. Ahora uso tan solo dos:
Adblock Plus: Te quita toooooda la publicidad de Internet. Dejas de verla. Incluso la propaganda que pone youtube.com antes de visualizar un video? Esta también dejas de verla. Es genial. Recomiendo a todo el mundo que se lo instale ni que sea para probar. Además, se va actualizando solo cada tanto, para mantener la base de datos de contenidos publicitaros que tu navegador no tiene que cargar. Super chulo.
ShareMeNot: Sirve para que todos aquellos botones "sociales" como por ejemplo el "Me gusta" de facebook, o el "+1" de Google, dejen de mandar información a Facebook/Google/Twitter sobre las páginas que visitas. Muy interesante para mantener la privacidad.

El ShareMeNot puede que requiera de una explicación adicional. Imaginemos que www.marca.com decide poner el botón "Me gusta" en todas sus páginas. Pues si estás logueado en facebook en el navegador y al mismo momento entras en www.marca.com, Facebook sabe que has entrado en esta página. Esto es debido a que automáticamente asocia el "Me gusta" con tu perfil. El problema es que Facebook sabe las páginas que vas visitando y lo va registrando. Y esto no mola. Por ejemplo, si la página web www.MeGustanLasGordasPelirrojas.com tiene un botón de "Me gusta" y estás logueado en facebook al mismo tiempo, Facebook a partir de ahora ya sabe "un poquito más" acerca de tí. Puede que a partir de ahora te haga publicidad de ciertas chicas con cierto problema de obesidad. Por esto está bien tener un plugin de firefox que te prevenga que se vaya mandando información sobre qué visito a todos los sitios web 2.0 y mantener así la privacidad de nuestros datos.

Y esto es todo por hoy. Un saludo!

domingo, 20 de noviembre de 2011

Clase de buceo

En el post anterior[1] comentaba cómo configurar las PAM para desactivar el tiempo de espera en caso de haberte equivocado escribiendo un password. Este post explica el proceso que seguí para saber que no es posible establecer un tiempo de espera personalizado. Siempre te tienes que esperar dos segundos en caso de haberte equivocado de password, o puedes desactivar el tiempo de espera. Lo que no puedes hacer es decir, "espérate 5 segundos en caso de que me haya equivocado de password antes de volver a preguntarme".. También se puede ver lo bonito que es el OpenSource, y la de que el código fuente sea público.

[Preámbulo]

Resulta que en el fichero /etc/pam.d/login hay una opción que pone: "auth optional pam_faildelay.so delay=3000000". Un comentario al lado explica claramente lo que hace esto:

"Enforce a minimal delay in case of failure (in microseconds)". O lo que es lo mismo: Fuerza una pequeña espera en caso de fallo en la autenticación. 3 segundos, para ser mas exactos.

Pues parece obvio pensar que si cambio 3000000 a 1000000, el tiempo de espera cuado haga login fallido se reducirá a 1 segundo.

Probemos antes de hacer el cambio:
$ sudo echo 1 # Comando chorra, pero lo importante es que nos pida el password
[sudo] password for inedit: badpassword
--- pasan unos dos segundos, aproximadamente ---

Hacemos el cambio en el fichero, poniendo el parámetro "delay=1000000". Probamos de nuevo:
$ sudo echo 1
[sudo] password for inedit: badpassword
--- pasan unos dos segundos, aproximadamente. Lo mismo que antes... ---


mmmm... mierda, no funciona. Probemos lo propuesto en el post anterior, o sea, poner el parámetro nodelay en el fichero /etc/pam.d/common-auth:
auth [success=2 default=ignore] pam_unix.so nullok_secure nodelay

Probamos de nuevo:

$ sudo echo 1
[sudo] password for inedit: badpassword
--- inmediatamente despues nos dice que la contraseña es incorrecta y que lo intentemos de nuevo. Tiempo de espera: cero segunos ---

Genial. Parece que el parámetro "nodelay" funciona. Pero ¿Por qué no funciona el "delay=3000000"?


[Siguiendo pasos lógicos]
Vale, resulta que el parámetro "nodelay" se lo ponemos como parámetro a un fichero llamado "pam_unix.so". Ni idea de qué coño hace este fichero, pero busquemos a ver si existe ene el sistema:

$ locate pam_unix.so
/lib/x86_64-linux-gnu/security/pam_unix.so

Touché, existe. mmmm..... vale. ¿Ahora qué hacemos con el? Ni idea de que hace realmente el fichero. Lo he abierto y es un binario....mmmm.... Vale, ya lo tengo. Descubramos qué paquete ha creado este fichero:

$ dpkg-query -S /lib/x86_64-linux-gnu/security/pam_unix.so
libpam-modules: /lib/x86_64-linux-gnu/security/pam_unix.so


Con que libpam-modules, eh? Vaaaale, pues mira, estamos siguiendo la pista y no nos va mal. Ahora ¿cuál puede ser el siguiente paso? Pues descargar el código fuente y que hace realmente el código:
$ mkdir ~/killme
$ cd ~/killme
$ apt-get source libpam-modules

Esto nos habrá creado un par de ficheros en el directorio "killme" [2].

[Buceando en el código]
Vale, ahora tenemos el código de la "libpam". ¿Qué hacemos? Lo obvio, aquí sería buscar alguna referéncia a "pam_unix.so". Pero como los ficheros ".so" son compilados y nosotros nos hemos descargado el código fuente, vamos a probar lo siguiente:
$ find -name "pam_unix*"

Oh! Genial, tenemos resultados! Parece que existe una carpeta interesante: "./pam-1.1.3/modules/pam_unix"

Vale, ahora deberíamos buscar algo de utilidad dentro de esta carpeta. Juguemos con "grep", a ver si hay suerte:
$ cd ./pam-1.1.3/modules/pam_unix2
$ grep nodelay -i # -i es para hace "ignore-case". O sea, que no distinga entre mayúsculas y minúsculas.
pam_unix.8:\fBnodelay\fR
pam_unix.8.xml:
README:nodelay
support.c: if (off(UNIX_NODELAY, ctrl)) {
support.h:#define UNIX_NODELAY 16 /* admin does not want a fail-delay */
support.h:/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0100000},



Tenemos resultados! Parecen interesantes los ficheros "support.c" y "support.h". Revisado el fichero "support.h" parece que solo hay la declaración del parámetro UNIX_NODELAY.
Veamos el fichero "support.c", a ver que contiene:

[...]
#ifdef HAVE_PAM_FAIL_DELAY
if (off(UNIX_NODELAY, ctrl)) {
D(("setting delay"));
(void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
}
#endif
[...]


Eureka! Lo hemos encontrado! Vale, yo no tengo ni idea de C. Nunca he programado nada serio en C, ni mucho menos una librería de sistema para Linux. Pero si hemos llegado hasta este fichero buscando cosas lógicas, ahora no nos va a detener un poco de código en C. Parece que es el código fuente de "pam_unix.so".

Vamos a intentar leer lo que pone este pedacito de código. La condición "if" parece que comprueba lo siguiente:
SI (el parámetretro UNIX_NODELAY és igual a falso) ENTONCES {
Espérate 2 segundos
}


Pues ya lo tenemos, señores. Resulta que no se puede definir un tiempo de espera, ya que el tiempo de espera está puesto de modo estático en el código. En ningún momento el código va a leer la variable "delay=3000000", por lo que si ponemos "delay=5000000" es normal que lo ignore.

[conclusión]
¿Cómo hemos podido sacar todo esto? Pues la respuesta es muy simple: porque es Software Libre y se puede conseguir el código fuente muy facilmente a través de Internet. Inspeccionando el código vemos claramente lo que hace ( y mejor todavía, lo que no hace ). En este punto, y si tuviese los conocimientos de C necesarios, podría reescribir el código fuente para que aceptara un parámetro "delay" y que pudiese ser definido des de la pam.d/login. Una vez hechos los cambios, podría mandarlos al mantenedor del paquete y en caso de gustarle dichos cambios, los incorporaría en el código de la libpam. Y el mundo sería un lugar mejor y lloverían gominolas del cielo :)


[1] Recomiendo que te lo leas, sinó este pot no tiene mucho sentido.
[2] Me gusta bastante crear directorios con el nombre "kill" o "killme" para usos temporales, ya que es un modo de saber si puedo borrar el directorio sin ni tan solo preocuparme de ver que contiene. En cambio, un directorio que llamado "temp" o "temporal".... bueno, si, son datos temporales. Pero hasta cuando? Servirá lo que hay dentro? Es algo lioso. Usando "killme" el tema está claro, puedes borrar la carpeta cuando quieras sin ningun problema, ya que el contenido de la misma no es importante ;)

jueves, 8 de septiembre de 2011

Saber a qué paquete pertenece un fichero

Algunas veces hay ficheros con nombres curiosos, y uno se pregunta qué paquete debe haber instalado este fichero. Por ejemplo el fichero:

/usr/bin/pico

Yo me pregunto, ¿quién lo habrá puesto ahí?

Pues fácil:
$ sudo aptitude install dlocate -y
$ dlocate /usr/bin/pico
> perl: /usr/bin/piconv

Pues mira, ha sido el paquete "perl".

Esto también sirve para ficheros de configuración, así como carpetas del sistema. Todo lo que esté "trackeado" por dpkg, es suceptible de ser encontrado por dlocate.

Un saludo, Jan.

UPDATE: El comando "dpkg-query -S /usr/bin/see" también hace lo mismo.

"sudo su" sin espera ( nodelay )

Pues como hoy me he tirado 30 minutos buscando esta opción y considerando que hay poca información sobre ella ( y menos en castellano ), así va:

¡NODELAY en pam_unix.so!

[¿De qué va el tema?]
Pues se trata de optimización de tiempo y comodiad en la consola. Cuando ejecutamos un comando con "sudo", normalmente nos pide la contraseña ( a menos que lo hayamos desactivado ). Esto es la política de seguridad bastante recomendada, ya que si no cualquier persona te podria juankear el ordenador si lo dejas solo un momento sin bloquear.

[Total, dónde está el problema, entonces?]
Pues que si al ejectuar cualquier comando con sudo, te equivocas al picar tu password, te tienes que esperar 3 segundos para volverlo a picar. Y esto es un coñazo. Ejemplo:

inedit@tpad:~$ sudo echo 1
[sudo] password for inedit: foo
- espera de tres segundos -
Invalid password, please try again:
[sudo] password for inedit: bar
- espera otra vez de tres segundos -
....etc...

[HOWTO]
Total, que esta opción se puede desactivar a través de las PAM de linux del siguiente modo:

sudo vim /etc/pam.d/common-auth

Y modificar la lína donde pone:
auth [success=2 default=ignore] pam_unix.so nullok_secure>

Y cambiarla por:
auth [success=2 default=ignore] pam_unix.so nullok_secure nodelay

De este modo se desactiva totalmente el tiempo de espera entre logins. Cuando te equivoques al escribir tu password, automáticamente te volverá a preguntar la clave.

[Precaución]
Y uno se preguntará, ¿por qué puñetas no estaba desactivado esto ya? Pues por motivos de seguridad: previene que se lanzen ataques de fuerza bruta contra el PC, ya que el hecho de que te tengas que esperar 3 segundos tras cada intento fallido, limita mucho los ataques ( los hace inviables por la cantidad de tiempo que se necesita probar un número acceptable de claves ). Aplicando la regla anterior estamos quiando esta restricción. O sea que cuidadín en ejecutar algo así en una máquina de producción donde tenga SSH expuesto en Internet y demás. Quien avisa no es traidor.

[Nota final]
Como siempre, en un extremo tenemos seguridad, y en el otro tenemos comodidad. No podemos elegir ambos. Por lo que lo mejor es quedarse siempre en un punto medio.

Un saludo, Jan.

Posdata: Si alguien consigue sacar algo en claro sobre el fichero /etc/pam.d/login y su parámetro "pam_faildelayso delay=3000000", que por favor deje un comentario y así completamos la información. Yo no he sido capaz de ajustarlo a un segundo. Gracias de antemano.