Migración de Zimbra

Este fin de semana me puse como objetivo migrar el mail server de la empresa que ya estaba haciendo agua. Corría sobre un server con un disco que daba errores de lectura, no había RAID y la última semana hubo un 90% de uso de CPU en I/O :).

Arranqué el sábado al medio día y fue un día perdido. Tuve la loca idea de respetar la arquitectura del nodo para crear la VM y montar Zimbra en 64bits. Resulta que migrar el mail server a un nuevo servidor requiere de mucho matching entre el origen y el destino, por lo que fue prácticamente imposible hacerlo. Recapacité por la noche y el domingo fui por un aproach más conservador :).

Lo primero a tener en cuenta para migrar este productor (que es all-in-one-fucking-package) es aceptar que hay que usar una de las distros soportadas, y no tratar de hacer magia (al menos para no sufrir y que salga andando).

El proceso básico está en este post y no parece complicado, pero hay varios obstáculos que pasar. Lo primero, es que hay que hacer una instalación dummy de zimbra con la misma versión que teníamos antes. Mi problema era que tenía una versión muy vieja de Zimbra y solo había paquetes para Debian y Ubuntu 6.06, pero yo quería al menos Ubuntu 8.04.

El truco que usé fue editar /etc/debian_version y ponerle lo que espera y pasó sin problemas (calculo que por mero azar :D).

Una vez eso el resto fue copiar con rsync de un server a otro (25Gb tardan MUCHO, pero MUCHO!) y ejecutar el installer de la nueva versión, que tardó también como 6 hs en actualizar las tablas de MySQL. Esto último creo que fue causa de que el logger en el server viejo no estaba limpiando después de procesar, por lo que había muchos logs y eso hizo que demore tanto.

La migración fue perfecta, sin errores, pero teniendo problemas con el LDAP. Confieso que  odio ldap e iba a decir “hasta acá llegué, lo dejo” pero apareció rápidamente en el wiki la solución : regenerar los certificados. Anduvo en el primer intento.

Cosas a tener muy en cuenta por si les toca :

  • El nuevo server debe tener el mismo Domain Name y MX record a puntados (la ip puede cambiar, pero el DNS debe estar actualizado)
  • Bloquear el puerto 25 mientras se migra, asi en el nuevo server no empiezan a entrar mails hasta que no estemos seguros de que anda
  • Hacer backup antes de borrar algo 🙂
  • Usar solo las distros/versiones soportadas, ahorra miles de dolores de cabeza
  • Planificar con tiempo la copia de archivos, puede tardar más de lo que uno cree 🙂
Anuncios

Do it right! – Manejo de dependencias

En estos días he tenido varias discusiones sobre algunas prácticas muy acostumbradas en varios proyectos, que a mi parecer están muy lejos de lo útil. Es por eso que decidí escribir un par de artículos denominados “Do it right!” (hazlo bien!, si mi inglés no es tan malo como pienso :D). En esta primer entrega voy a tratar el tema de manejo de dependencias.

Escenario

Muchas veces uno se encuentra con un software que necesita y le es útil. Vamos a suponer de entrada que no tiene paquete para su distribución y/o sistema operativo favorito. En al leer el README vemos que depende de muchas cosas : alguna biblioteca de procesamiento de imágenes, algún captcha system o lo que sea.

Mirando mejor nos encontramos con una realidad muy fea : todas las dependencias están incluidas en el archivo que nos bajamos originalmente, y alguna veces se cargan por métodos poco ortodoxos.

Cualquiera que haya bajado cosas en PHP o Java seguramente se ha encontrado con esto : Clases bajadas de phpclasses.org o miles de archivos JAR en un directorio lib de la aplicación Java que se cargan en el classpath. También lo he visto en varios proyectos Rails últimamente.

La excusa que siempre escucho es “Because it makes the app more self contained“, si, si, BWTF. Pero la pregunta es : ¿es la forma correcta?. Si respondes que si, estás muy lejos de la realidad y seguramente tu sysadmin te odia mucho, pero mucho :).

El Problema

Entonces, ¿cuál es el problema con esto?. En principio parece una maravilla, nos facilita todo, descomprimimos y ya está!, el sysadmin (nosotros) agradecido, pero si el sysdamin es otra persona y se respeta los va a putear mucho, pero muuuuuucho.

Uno de los problemas con empaquetar todas las dependencias es la facilidad de solucionar problemas a futuro. El mejor ejemplo que he vivido es el típico cambio de time zone en nuestro país. PHP, por ejemplo, tiene su propia DB de timezones, y si actualizamos el tzdata de nuestro servidor, maravillosamente PHP ni se entera. Está bien que en este caso como programadores no podemos hacer mucho, es el lenguaje el que está mal, pero el ejemplo ilustra mi punto.

Un software con el que tuve que trabajar, vBulletin, va aún más allá. Tiene su propia DB de timezones por arriba de la que tiene PHP!, hardcodeada en un array!. ¿La excusa? Que la de PHP no anda bien en Windows y es mejor de esta manera.

Otro ejemplo que puedo dar es el otro día cuando migré unos sitios en el trabajo. Uno de los admines se quejó que dejó de andar porque usaba la función “dl” de PHP para cargar extensiones en tiempo de ejecución! (es decir, cualquier script PHP puede cargar un .so que extiende PHP de cualquier manera, incluso para saltearse otras medidas de seguridad), lo que no solo es una chanchada, sino que es un potencial problema de seguridad.

Tener el manejo de dependencias con la aplicación además nos prohíbe actualizar fácilmente un bug de seguridad, usabilidad o lo que sea de esa dependencia. No podemos en muchos casos hacer un upgrade de ese paquete que está embebido dentro de un todo llamado “aplicación”.

En rails he visto ya muchos que acostumbran a usar “rake gems:unpack” para meter las gemas de las que dependen en el directorio vendors/gems, así no las tienen que instalar aparte. Rubygems disgusta en muchos ambientes, no voy a entrar en eso hoy :). Si uno depende  de una versión específica la forma correcta sería especificar en el environment.rb la versión, y luego que el que hace el deploy haga un “rake gems:install” para asegurar que se cumplan las dependencias. Con Capistrano es una papa hacer esta tarea automáticamente ;).

Entonces, ¿nunca más lo hago?

No me gustan los extremos y creo que estas malas prácticas que se ven hoy tiene usos muy específicos.

Supongamos que el proyecto X usa la lib Y, pero tal cual la provee upstreem no me sirve. Lo lógico es hacer un patch para upstream solucionando el problema o agregando la característica.

Mientras upstream acepta el patch y realiza un nuevo release, ahí si tiene sentido tal vez, y de forma temporal tener esa dependencia embebida.

Un ejemplo donde yo lo estoy haciendo es con el uso de la gema contacts, que depende de la gema json. En una aplicación con Rails 1.2.6 no hay ningún problema, pero en otra que usa Rails 2.1 si, porque este último integra un parser de JSON y conflictua con la gema del mismo nombre. La opción lógica sería migrar la aplicación 1.2.6 a una nueva versión de Rails, cosa que no siempre es fácil y se necesita el tiempo. Como no se pudo, es más fácil meter en uno de los proyectos la gema de manera que no moleste al resto de los proyectos en el transcurso de la actualzación.

Seguro hay algún otro caso, ahora no se me ocurre alguno que realmente lo justifique.

Puppet y not trusted hosts

Hoy estaba terminando de configurar Puppet y me empezó a saltar el siguiente error en los clientes :

Certificates were not trusted: hostname was not match with the server certificate

Buscando un poco llegué a esta página donde explican el motivo y es que al no tener un FQDN para mi puppetmaster la biblioteca de Ruby lanza una excepción que hace que falle.

Por default el puppetmaster pone el nombre del certificado como “puppetmaster ” (Debian Etch) y claro, el cliente tiene configurado server=192.168.x.y por lo que no coincide.

La solución que hizo que todo ande fue editar el /etc/puppet/puppet.conf en el servidor maestro y dentro de la sección [puppetmasterd] agregar certname=192.168.x.y de amanera que ahora sí ambos hablan de lo mismo :).

No se que tan lindo/feo es tener una IP como nombre de certificado, pero anda.

Sobreviviendo ataques

Está terminando un día largo, de esos que uno espera que no le toquen, pero que tarde o temprano llegan. Ayer a la noche en 3DG fuimos víctimas de un pequeño ataque. Por suerte los atacantes super buena onda. Luego de que apagamos el primer incendio estuvimos chateando con ellos y nos dieron la data de por donde entraron, cómo, qué cosas modificaron y lo mejor de todo, donde nos habían dejado los backups que habían hecho :).

El ataque consistió en hacer que nuestros sitios (el target era el foro que es el que tiene más tráfico, pero afectó a otros sitios también) sean redirigidos a una página muy graciosa que no pienso linkear porque no es ATP. Para lograrlo (ya que el foro está aislado y no pudieron entrar por ahí) nos crackearon el sistema de publicidad e insertaron un banner javascript que hacía el redirect. Simple y efectivo.

Para lograr el acceso al server de ads fue más fácil, simplemente explotaron un SQL Injection que por la fecha de última modificación del script, estaba desde el 2001 :). Con eso consiguieron el password de admin para poder poner su publicidad. Nuestro segundo problema fue obvio, el usuario que usa ese script para acceder a la DB tenía demasiados privilegios y pudo leer y modificar una otra base de datos.

Más allá del trabajo que tuvimos que hacer para recuperar de nuestros backups cosas por las dudas (aunque nos hicieron backups tampoco confiar a ciegas :P) tuvimos que empezar a auditar cosas que teníamos pendiente hace rato. Para empezar necesitamos bajar los servicios completamente y la forma más linda que encontré fue usando un rewrite rule de apache, como sigue :

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/imagenes
RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
RewriteCond %{REQUEST_URI} !/maintenance.html$

RewriteRule $ /maintenance.html [R=302,L] 

De esta manera cuando terminamos de hacer el mantenimiento simplemente borramos el maintenance.html y el sitio vuelve solo a la vida. De paso ya lo dejamos para cuando hagamos futuros updates.

Lo otro que nos entró en duda cuando arreglamos el problema fue : ¿lo arreglamos realmente? ¿tendremos otro agujero en algún lado?. Para poder revisar esto comencé a buscar herramientas para auditar SQL Injections y me encontré con un post donde habían varias soluciones. La que usamos finalmente fue sqlmap porque fue la que mejor nos pareció que andaba.

Fue una tarde divertida viendo que podíamos romper de nuestro viejo sitio. Para escanearlo simplemente corríamos :

#$ sqlmap -dbs -u "http://127.0.0.1/scriptbuggeado.php?Id=1"

El programa primero trata de ver si el parámetro Id es vulnerable a diferentes formas de hacer injection y si descubre alguna trata de obtener la lista de DBs. Es lindo ver cuando aparecen todas tus DBs en la consola :). Si le agregamos “-v 2” es más gracioso aún, porque los nombres van apareciendo letra a letra (parece que las va adivinando o algo, no me fijé en el código para ver como lo hace).

A esta hora cerramos ya el agujero del ataque y dos más que detectamos.

El último problema que encontramos fue que habían subido un shell. Para esto crackearon el password de programa para enviar newsletters y subieron un archivo php que tiene un shell re lindo que tiene funciones para escanear vulnerabilidades localmente. Acá el problema fue que el sysadmin anterior dejó permiso para ejecutar script en el directorio donde el programa guarda attachments que después se usan en el email (como imágenes en esos emails molestos HTML que nos llegan todos los días). Sacando los permisos para ejecutar cualquier tipo de script el shell ya no funciona.

Como sysadmin “temporal” fue una experiencia divertida, sobre todo porque no hubo pérdida de datos y la buena onda de los atacantes 🙂 (¿tendré el Síndrome de Estocolmo?).

Phusion Passenger (a.k.a. mod_rails)

Finalmente hoy leo que ya está disponible la primer versión estable de mod_rails, y si todo anda como dicen, no más problemas con fcgi o mod_balancer + mongrels. Simplemente deploy a la PHP. Será posible?

Ventaja inmensa cuando se tiene más de un sitio en el mismo host. Hoy en día (apacha+mongrel) debo recordar y documentar que rango de puertos usa cada aplicación (son 3 por ahora) y al hacer los updates tener que restartear N procesos diferentes y cosas así.

Voy a ponerlo en los servers de testing del trabajo a ver como se porta y si todo sale bien pasarlo a producción :).