martes, 31 de octubre de 2017

Solaris recovery detach device - Recuperar device detachado de zpool Solaris 10

Esta es una historia que jamas debió haber pasado pero que se sabe pasa en días que transcurren, acompañen me a visitar este carrusel de emociones en donde encontraremos miedo, odio, resignación y como en toda buena historia una luz de esperanza al fin del camino; pues bien, iniciemos.

Capitulo 1 - El desastre.

Un día cualquiera por cuestiones de la vida, en nuestro centro de datos ocurrió una variación  de energía y nuestro  almacenamiento EVA perdió discos en sus VG tanto que los volúmenes se volvieron inaccesibles, esta EVA presentaba LUNs a nuestra Sun Sparc M5000 las cuales dejaron de ver la información y se volvió inaccesible la información.

A continuación una evidencia de lo sucedido.

















Ante esta situación claramente no teníamos nada que hacer, revisamos nuestra EVA y los volúmenes estaban protejidos y sin respaldos actuales, lo único que teníamos era un espejo en algo viejo en otra EVA, espejo que habíamos detachado (detach) del zpool mirror en que estaba. Por lo que erá la única esperanza de recuperar los datos medianamente usables, esto después de buscar respaldos por todos lados y sin tener un punto de donde recuperarnos a través del sistema de respaldos.

Capitulo 2 - Buscando un punto de apoyo en el abismo.

Ante la situación comenzamos a tratar de acceder a los volumenes de la EVA la cual tenía los espejos a travez de "zpool import -F XXXDBPNINST" la mala noticia es que este comando no arrojaba nada, el volumen aparentemente no tenía información y comenzaron las preocupaciones, decidimos revisar si la base de datos de zfs contenía algo en el volumen, esto lo hicimos con:






















Al parecer nuestra información si estaba en el disco, solo que algo pasaba con la metadata, revisando la documentación de zpool nos encontramos con que detach reescribe todo el uberblock del volumen, justo aquí fuimos concientes en el problema enorme en el que estábamos, una pequeña búsqueda en internet nos dio a entender que no se podía recuperar la información por medios tradicionales, por lo cual no nos quedó mas que buscar ayuda en el soporte de Oracle, después de varios correos de control nos dan la fabulosa noticia que la proxima vez que queramos desacoplar un disco utilicemos split y no detach (ya sabíamos que era lo que hacía detach), Buscamos ayuda a uno de nuestros proveedores y la respuesta fue muy similar, ellos no podían hacer mucho ya que era un tema bastante complejo, lo mejor era buscar ayuda con Oracle.

Con el animo por el piso y con unas cuantas amenazas de lo que podía suceder si no recuperabamos la información decidimos buscar un poco mas en internet y con la calma y con los términos y documentación un poco mas afianzada y nos encontramos con este enlace (agrego una copia del correo con el código fuente)del 2008, en resumidas cuentas a alguien le había pasado lo mismo pero en solaris x86 y logro acceder al volumen, esto lo logró escribiendo un programa en C que básicamente lo que hacía era reescanear la base del disco para encontrar los puntos de inicio del zpool y de esta forma volver a reescribir el ublock, todo un hacker de zfs.

Capitulo 3 - Una luz al final del tunel

Pues bien ya se tenía la tarea para hacer, lo primero que se intento fue usar el compilado que vienen adjunto en la lista de correo, claramente no funciono, lo siguiente fue intentar compilarlo en solaris, para desgracia de nosotros el código fuente fue hecho para opensolaris y no para solaris en si mismo, así que tampoco nos funciono, eso y nuestros conocimientos de desarrollo no nos permitieron hacer las modificaciones necesarias.

Al no poder hacer mucho desde la maquina que si tenía acceso a la información decidimos extraer imagenes puras de la LUN para copiarlas a otro servidor y poder hacer las pruebas necesarias, así que decidimos hacer copias con dd en los solaris y pasarlas por scp al servidor donde originalmente habían ocurrido los daños.

Una vez las imagenes en el servidor donde si podiamos hacer las pruebas procedimos a crear LUNs con espacios exactamente iguales, label iguales y sectores de disco iguales, lo siguiente fue inyectar los datos con dd a las nuevas LUNs y verificar con zdb, las cuales eran iguales a lo que teníamos en el servidor espejo, bien, en este punto estabamos casi al inicio pero con nuestra información ya en la otra EVA, el problema seguía siendo como accederla, después de buscar mas y mas nos encontramos con esta entrada en donde solicitan que se ingrese una funcionalidad a zfsonlinux (si, el mismo port de opensolaris a linux) y donde  mencionan otro articulo donde se hace una modificación del código fuente del 2008, las esperanzas comenzaron a regenerarse, leyendo un poco los comentarios básicamente las pruebas habían sido exitosas con Ubuntu 14.04 y zfsonlinux 0.6.5.1.

Lo siguiente fue montar nuestro servidor Ubuntu, presentar las LUNs a este servidor y seguir las instrucciones siguientes:

Originally written by Jeff Bonwick (http://www.mail-archive.com/zfs-discuss@opensolaris.org/msg15748.html), and updated by James Lee to work with modern ZFS libs (https://www.mail-archive.com/zfs-discuss@opensolaris.org/msg47316.html), I was able to compile this life-saving utility in Ubuntu 14.04 and have verified that it works. (I'm using ZFSonLinux.)
Download the ZFSonLinux tarball and replace the cmd/zhack/zhack.c file with "labelfix.c". Note that zhack is just a simple utility that we're replacing so that we don't have to setup the build environment. (It's hard, so we'll reuse the good work of the ZFSonLinux people.)
Run "./configure; make" and if all goes well then the zfs tools will be built, except for zhack, which we replaced. Run that with the device path to recover your data.

El codigo fuente modificado para zfsonlinux es el siguiente:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

/*
 * Write a label block with a ZBT checksum.
 */
static void
label_write(int fd, uint64_t offset, uint64_t size, void *buf)
{
        zio_eck_t *zbt, zbt_orig;
        zio_cksum_t zc;

        zbt = (zio_eck_t *)((char *)buf + size) - 1;
        zbt_orig = *zbt;

        ZIO_SET_CHECKSUM(&zbt->zec_cksum, offset, 0, 0, 0);

        zio_checksum_SHA256(buf, size, &zc);
        zbt->zec_cksum = zc;

        VERIFY(pwrite64(fd, buf, size, offset) == size);

        *zbt = zbt_orig;
}

int
main(int argc, char **argv)
{
        int fd;
        vdev_label_t vl;
        nvlist_t *config;
        uberblock_t *ub = (uberblock_t *)vl.vl_uberblock;
        uint64_t txg;
        char *buf;
        size_t buflen;

        VERIFY(argc == 2);
        VERIFY((fd = open(argv[1], O_RDWR)) != -1);
        VERIFY(pread64(fd, &vl, sizeof (vdev_label_t), 0) ==
            sizeof (vdev_label_t));
        VERIFY(nvlist_unpack(vl.vl_vdev_phys.vp_nvlist,
            sizeof (vl.vl_vdev_phys.vp_nvlist), &config, 0) == 0);
        VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0);
        VERIFY(txg == 0);
        VERIFY(ub->ub_txg == 0);
        VERIFY(ub->ub_rootbp.blk_birth != 0);

        txg = ub->ub_rootbp.blk_birth;
        ub->ub_txg = txg;

        VERIFY(nvlist_remove_all(config, ZPOOL_CONFIG_POOL_TXG) == 0);
        VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG, txg) == 0);
        buf = vl.vl_vdev_phys.vp_nvlist;
        buflen = sizeof (vl.vl_vdev_phys.vp_nvlist);
        VERIFY(nvlist_pack(config, &buf, &buflen, NV_ENCODE_XDR, 0) == 0);

        label_write(fd, offsetof(vdev_label_t, vl_uberblock),
            1ULL << UBERBLOCK_SHIFT, ub);

        label_write(fd, offsetof(vdev_label_t, vl_vdev_phys),
            VDEV_PHYS_SIZE, &vl.vl_vdev_phys);

        fsync(fd);

        return (0);
}

Con animos de gastar todas las opciones disponibles, compilamos el código fuente como lo dice la sección anterior, el cual arrojo un error pero a la hora de revisar los binarios del paquete efectivamente se encontraba el binario zhack.

Capitulo 4 - Lecciones aprendidas

Con el momento de la verdad delante de nosotros ejecutamos el comando ./zhack /dev/dsk/c5t600508B40009BD73000050000F3F0000d0s2 el comando no arrojo ningún error, pero al momento de hacer el zimport nuevamente nos mostró la información en el ubuntu, en ese momento pudimos descansar, lo siguiente fue pasar por rsync la información al solaris ya que el hack no permitia ver el volumen en el solaris, así que con nuevos volumenes en el solaris nos faltaba hacer rsync a todo el volumen abierto y realizar las configuraciones faltantes.

Lo que aprendimos de toda esta odisea fue que siempre, siempre debemos tener respaldos, el tener un soporte no te garantiza que te hagan el trabajo sucio y por ultimo que internet es una fuente inagotable de información.

Espero que esta recopilación de información sea útil para algún sysadmin y pueda acceder a toda la historia del hack sin tener que deambular días por la web.