Author Archives: alfredo

Nexus: Molan, hasta que se convierten en ladrillos al actualizarlos

Era un día normal para un venerable y fiable Nexus 3000, concretamente un 3064 que corría NX-OS 6.0(2)U6(6) tras una pequeña actualización sin novedad y sin hacer más que conmutar paquetes a nivel 2.

Había llegado para él el día de actualizar. Se había decidido que iba a empezar a terminar vLANes, es decir, a nivel 3; lo cual requiere ponerle direcciones IP públicas, y las versiones 6 tienen algunos feos fallos de seguridad. Por supuesto que a base de ACLs y/o cortafuegos es posible aislar lo bastante las IPs públicas de un gateway. Pero ya hace muchos años que Rommel popularizó la defensa en profundidad, y en redes es más que aplicable lo que tanto sufrimiento costó entender a los militares.

El concienzudo proceso de actualización de los Nexus requiere, en este momento, pasar por varias versiones intermedias partiendo de la 6 hasta llegar a la deseada, la 9.3.8 en el día de actos. Así que, armados con la paciencia nacida de la experiencia, mandamos la 7.0.3.I7.9. Runtime checks, boot variables, configuration copy y todo lo habitual hasta llegar a

Finishing the upgrade, switch will reboot in 10 seconds.

Fue lo último que dijo.

Mosqueados tras un ratito, consola en ristre, desenchufado de la corriente y vuelto a enchufar, nos informa por la consola:

(c) Copyright 2011, Cisco Systems.
N3000 BIOS v.4.5.0, Thu 11/09/2017, 05:38 PM

Y nada más. Ni boot loader, ni pulse Del… Nada.

Colgado.

Vaya, qué inconveniente.

Así que vamos a ver qué se hace en estos casos. Buscando por Internet no encontré nada de nada, así que escribí en los foros de Cisco. Las respuestas no pudieron ser más descorazonadoras: Una vez se aseguraron de que lo habitual no iba a funcionar, solo quedó como posibilidad abrir un caso con el TAC de Cisco y probablemente cambiarlo.

Pero estos switches ya otoñales no suelen tener un SmartNet activo, y Cisco ya no hace caso de nadie sin ello. Así que, mientras uno buscaba (con poco éxito) si aún se venden SmartNets para este modelo, el otro fue a desmontar el equipo, convencidos de que la cosa iba para largo. Tal vez, razonamos, haya alguna forma de fulminar la memoria flash y obligarle a irse al boot loader; o de hacerle arrancar de alguna manera.

Foto del módulo eUSB
Módulo eUSB fabricado por Viking Technology

Pues sí. Y más fácil que lo que nos temíamos.

El almacenamiento en estos equipos es una memoria flash USB. Es una versión un poco especial de USB, pensada para entornos industriales y para montarse de placa a placa, pero es USB al fin y al cabo. Está sujeta con un tornillo a la placa base y lleva un conector estilo DB9 miniaturizado (tal vez un Hirose DF9).

Bueno, pensó alguien. ¿Y si arranco el equipo sin la flash?

Dicho y hecho:

Press ctrl L to go to loader prompt in 2 secs

loader>

¡Tenemos boot loader! Desde aquí, ya todo es un paseo: Con el boot loader, se le puede arrancar por tftp o por un pincho USB. Esto último, con el equipo sin red fuera de la mesa, parece lo más inmediato…

loader> boot usb1:nxos.7.0.3.I7.9.bin

Pero, parafraseando al androide chungo de Blade Runner, el dios de la mecánica no se había terminado de ciscar en nosotros aún, y al equipo en cuestión no le daba la gana de arrancar desde el puerto USB.

Vaya.

Lo siguiente.

¿Y si, dado que se puede desmontar este módulo, probamos con el de otro equipo parecido?

Dicho y hecho, en menos de lo que se tarda en leerlo estábamos quitando los aproximadamente 150.000 tornillos Phillips del 1 de una aleación similar a la plastilina que sujetan la tapa del segundo equipo y cambiando su módulo al enfermito.

Y arrancó. Sin más. Bueno, es lo que esperábamos, ¿no?

Lo que no esperábamos es que el módulo del switch enfermo… Hiciera arrancar al «donante». Pero había que rendirse a la evidencia, evidencia presentada en nuestro terminal con un lacónico

%ASCII-CFG-2-CONF_CONTROL: System ready

Pofale.

Desde aquí, actualizar con mucho cuidadito nuevamente y a correr.

Mola la informática. Aunque a veces… Felix qui potuit rerum cognoscere causas. Aunque, en el caso que nos ocupa, nada más actualizar y en el siguiente arranque, pasó casi desapercibido un mensaje que decía algo así como: «Vaya, el firmware eUSB necesita actualizarse. Voy para allá… Hecho, sigamos con lo nuestro».

Vaya.

megacli: Mola, relativamente

Megacli sirve para todo, y no hay nada en un chip RAID de LSI/Avago/Comesellamenhoy que no pueda configurar. Esas son las buenas noticias.

Las regulares… Pues son chips complicadillos y por tanto la línea de comandos de megacli, tiene su complejidad. Incluso antes de que a alguien se le ocurrieran esas graciosas abreviaturas.

El ejercicio de hoy era sacar unos discos de una venerable 2108 de un Supermicro otoñal, y meterlos en un Dell r620 con su H310.

Al meterlos, hacen blip-blip y todo parece ir bien, pero… No aparecen por ninguna parte en /dev/pordondesemire.

Megacli sí los ve, o mejor dicho, su primo megaclisas-status: Unconfigured(good). No parece mala señal.

Bueno, pues solo hay que cambiarlos a JBOD para poder dárselos a Ceph, ZFS o a cualquier otra cosa con o sin Z, ¿no?.

No…

megacli -PDMakeJBOD -PhysDrv[32:6] -aALL
Adapter: 0: Failed to change PD state at EnclId-32 SlotId-6.

Vaya. Que no se puede. Aguanta y tira.

¿Y por qué no se puede? Je. Porque vienen con la meadit… Quiero decir, con la firma del RAID precedente. Podrían tener el detalle de decirlo, pero vaya, no, no lo tienen. Bueno.

¿Y tiene solución? Claro. La artesanal, que es meter el disco en alguna parte que no sepa nada de RAIDs y ciscarse la firma, por ejemplo con un dd; o la megacli, que ya que estamos…

root@lab.tecnocratica ~ # megacli -CfgForeign -Scan -a0

There are 1 foreign configuration(s) on controller 0.

Pues al carajo con ella, ¿no?

root@lab.tecnocratica ~ # megacli -CfgForeign -Clear -a0

Foreign configuration 0 is cleared on controller 0.

Pues a la carga de nuevo:

root@lab.tecnocratica ~ # megacli -PDMakeJBOD -PhysDrv[32:6] -a0

Adapter: 0: EnclId-32 SlotId-6 state changed to JBOD.

Pues como siempre, tras un rato de rompimiento de cabeza, eso era todo. La verdad es que estaría bien que si alguien busca por «controladora perc o raid de dell o de supermicro o de quien carajo sea no ve discos que vienen de otra controladora» encontrase esta receta, que le haría ahorrar algo de tiempo. Se intentará…

ix0: Unsupported SFP+ module detected!

Ya el año pasado, me tocó lidiar con la estúpida decisión de Intel de que sus chips de 10G no funcionan con los SFP que no entran en la corta lista de los que tienen probados.

La historia se repite, y esta vez ha sido un venerable FreeNAS 11.1 en un nuevo armario y con nuevos y flamantes cables AOC el que lleva el controlador de Intel que ha decidido, vaya hombre, que los AOC no son lo bastante flamantes; que si eso, que compre otros.

Uno no tiene tiempo ni ganas de dejarse llevar por las veleidades de una empresa que tiene gente trabajando en puestos que llevan a la vez la palabra technical y la palabra marketing (pobrecicos).

Usando la clásica técnica de poner en un buscador el mensaje de error que sale en dmesg y que da título a este artículo, en seguida se llega a algún ticket del redmine de IXsystems / FreeNAS; no en vano es uno de los mayores distribuidores de FreeBSD del momento.

El caso es que, como suele suceder, las cosas no pueden ser tan fáciles.

¿Es cierto que añadir a /boot/loader.conf las palabras mágicas:

hw.ix.unsupported_sfp=1

resuelve el problema? Si.

Pero no es menos cierto que en la versión 11.1, no. Vamos, lo ignora completamente en la versión que va en el kernel de la 11.1.

Así que, usando la clásica y probada por la experiencia técnica de la huida hacia delante, me bajé la ISO de la última estable de FreeNAS y le hice una actualización in situ. Las actualizaciones de FreeNAS son una gozada, si bien hacerlas sobre uno de esos KVM viejos de Supermicro no son la mejor experiencia del mundo. Pero en una máquina virtual Windows bien añeja (de Windows 7 para atrás) funcionan, así que no me voy a quejar demasiado. Total, hace años que por estas cosillas dejé de comprar Supermicro y pasé a comprar Dell.

Una vez hecha la actualización, arrancado el sistema nuevo, ya todo…

…Una mierda. Resulta que la actualización instala su propio /boot/loader.conf. Bueno, no pasa nada: Otra vez KVM basado en Java y volver a añadir hw.ix.unsupported_sfp=1 al final del ficherito.

Y esta vez sí, por fin, quejándose como un abuelo hablando de política en el bar del pueblo, por fin levantó los puertos de 10G. Y el resto, como se suele decir… Es historia.

Los switches HP 7900 y sus rutas inactivas

No molan mucho, la verdad.

Los HP 7900 y sus compañeros de gama pelo arriba, pelo abajo, corren Comware. Comware es un sistema operativo que HP compró en el paquete de 3Com en 2010. Había sido diseñado por H3C, en la que 3Com participaba junto con Huawei y de la que compró a su socio su parte en 2007. Esto es el motivo por el que los switches HP y los Huawei hablan el mismo idioma.

Recientemente, nos encontramos un caso en que se había instalado un par de switches HP7900 en cluster para un proveedor de servicios de alojamiento, perdón, de cloud. En seguida se hizo evidente que una de las primeras necesidades, desde un punto de vista de diseño de red, era una frontera nueva: Los HP solamente tienen memoria para 32.768 rutas aprendidas por BGP, mas alguna (unas pocas) «extra». En el momento de escribir este artículo, la Tabla va por encima de las 800.000, y subiendo. Evidentemente, no podían hacer ni un atisbo de balanceo de tráfico, y olvidémonos de puntos neutros o cualquier otro refinamiento.

Lo interesante que observé en Comware es que, cuando se le acaba la memoria para rutas, no tira la sesión de BGP que le ha llenado la memoria, como hacen los Cisco (y con ello agravando el problema); simplemente, está llena, pues está llena: Ya no se instalan más rutas.

Pero no se instalan, no se instalan: Si con la memoria llena se añade una IP a un interface, ese interface no funciona a nivel 3 porque no tiene memoria ni para la ruta conectada.

¿Solución que le daban localmente a este problema? Reiniciar el bicho. ¿Por qué si se reinicia ya funciona el nuevo interface? Fácil: Las conectadas llegan antes a la tabla que las aprendidas por BGP, que han de esperar a que la sesión se establezca. Pero claro: Toda la red sin conectividad ninguna mientras dura el rearranque. Ole.

En fin. Una pareja de Mikrotiks resolvió muy lindamente una frontera nueva. A día de hoy, la propuesta de Mikrotik no es una panacea: Seguimos esperando una implantación de BGP mejor, y sería deseable que la estabilidad mejorase un pelo. Pero funcionan en general bastante bien, conmutan tráfico como campeones, tienen IPFix (algo casi imprescindible en nuestros días), y el precio es imbatible.

Recuperando datos a lo bruto

Recuperar datos es una de esas actividades que consisten en un largo periodo de marrón, coronado (a veces) por un momento de euforia.

Vamos a correr un tupido velo sobre la historia que llevó al punto de partida de nuestro marrón de hoy. Describamos, simplemente, dicho punto de partida: Hay un contenedor OpenVZ corriendo en un servidor, en el cual «se» (¿no son utilísimos los reflexivos en las empresas?) ha hecho un DROP DATABASE seguido del nombre de una base de datos. Base de datos de la que no solamente no hay copia de los datos; sino que, puestos a giñarla, tampoco la hay de la estructura.

Así a primera vista, después de los preceptivos frontomanotazos y pelotiramientos, quedaron dos cosas claras:

  1. Para recuperar una tabla MyISAM (las InnoDB son otra historia), se necesitan al menos su .frm (estructura) y .MYD (datos propiamente dichos). Si solo se tienen los datos, ahí están, pero usarlos es un trabajo más bien de chinos.
  2. Cuanto más tiempo pase y más se escriba en el volumen afectado, mucho peor.

El primer intento, que tenía posibilidades de no haber pasado ya unas horas en un hipervisor un tanto activo, fue con ext4magic. Tiene su cosa, el asunto. Si el servidor se puede parar, es más fácil. Si no, hay que trabajar haciendo una copia del journal del sistema de ficheros, en este caso un ext4:

debugfs -R "dump <8> /root/copiadeljournal" /dev/mapper/pve-data

En este caso, almacenamos la copia del journal en /root, y el sistema de ficheros conteniendo lo que deseamos recuperar está en /dev/mapper/pve-data. Un Proxmox, como habrá adivinado ya el lector avezado.

Luego, burguesamente, podemos pedir a ext4magic que nos recupere todo lo del directorio en cuestión:

./ext4magic /dev/mapper/pve-data -j /root/copiadeljournal -f private/666/var/lib/mysql/pordiosmibasededatos -a $(date -d "-5day" +%s) -b $(date -d +%s) -R

En este caso, «666» es el identificador del contenedor. No es coincidencia; es que uno es así de paranoico filtrando lo que publica.

Pero claro… Si ha pasado tiempo, dependiendo de exactamente lo que se le pida, dice que el inodo ya está siendo reutilizado, o que simplemente aquello ya no ta.

Otro intento fue con testdisk. Testdisk es un gran invento, pero en este caso hubo el mismo problema: Había pasado demasiado tiempo. Mejor dicho: Demasiadas escrituras.

¿Qué hacer, qué hacer? El datario amenazaba alternativamente con suicidarse y con homicidar al causante del desaguisado; se seguían sucediendo el tiempo y las escrituras, y no estábamos más cerca de una solución que al principio.

Llamé a mi escriptillo, un tanto gráficamente, aladesesperada.sh. Era este:

#!/bin/bash
Bloque=0
 while [ $Bloque -le 157313 ]
 do
      echo " $Bloque "
      dd if=/dev/mapper/pve-data of=/root/tmp-$Bloque bs=10M skip=$Bloque count=1 status=noxfer > /dev/null
      grep -q 'cadenapeculiarquetepasas' /root/tmp-$Bloque
      if [ $? -eq 0 ]; then
     echo Encontrado algo
      else
     rm /root/tmp-$Bloque
      fi
      Bloque=$[$Bloque +1]
 done

No me darán un premio a la elegancia, pero esto es como funciona. El numerete es simplemente el tamaño de la partición donde estamos recuperando los datos expresada en cachos de 10 megas. Luego, simplemente vamos cogiendo cachitos de 10 megas en 10 megas y, si algún cachito contiene una cadena que sabemos que está en la base de datos, la conservamos. Si no, la borramos.

Luego hice una pequeña reforma que multiplicó por varias veces la velocidad de ejecución, usando un tmpfs:

mount -t tmpfs -o size=20m tmpfs /root/tmp

Y correspondientemente:

#!/bin/bash
Bloque=0
 while [ $Bloque -le 157313 ]
 do
      echo " $Bloque "
      dd if=/dev/mapper/pve-data of=/root/tmp/tmp-$Bloque bs=10M skip=$Bloque count=1 status=noxfer > /dev/null
      grep -q 'cadenapeculiarquetepasas' /root/tmp/tmp-$Bloque
      if [ $? -eq 0 ]; then
      mv /root/tmp/tmp-$Bloque /root/encontrado-$Bloque
      else
     rm /root/tmp/tmp-$Bloque
      fi
      Bloque=$[$Bloque +1]
 done

Con esto, vino uno de esos escasos y preciados momentos de suspiro: Aparecieron los datos. Al menos, la mayoría de ellos. Hubo que hacer varios intentos, concatenar varios cachos y recortarlos a base de dd, pero al final aparecieron.

Pero hay un problema, ¿no? ¿Qué hemos dicho sobre las tablas MyISAM?

al menos su .frm (estructura) y .MYD (datos propiamente dichos)

Total. Que necesitamos recuperar del disco unos .frm, que como cadena para buscar solamente tienen unos nombres de campo… Nada peculiares. Y encima, tienen un formato tirando a complejo y ni siquiera del todo bien documentado.

Así que, para esto, echamos mano del amigo de testdisc: Photorec.

Photorec es una pequeña maravilla de recuperador de datos. No poca gente debe algo parecido al perdón divino de su falta de copias de seguridad a esta utilidad escrita y mantenida desde hace muchos años por Christophe GRENIER. Photorec busca a lo bruto y recupera un montón de formatos de ficheros; entre ellos, los frm de MyISAM.

Dicho y hecho, una ejecución rápida de Photorec en el volumen pronto encontró varios archivos de MySQL. Varios. Unos pocos… Miles. Mêrde. Y ahora, ¿qué?

Pues se coge ./aladesesperada.sh buscando por los nombres de los campos (lo cual genera «solamente» unos cientos de ficheros de a diez megas), y seguidamente… Corremos photorec sobre la concatenación de todos esos ficheros.

Eso y un poco de recortes más con dd, algo de ensayo y error, y ¡por fin!. Poner los ficheros finales en /var/lib/mysql, usuarios, permisos, arrancar el servidor (no harás todo esto con el servidor MySQL andando, ¿verdad?), y no sin quejarse un poco…

mysql> REPAIR TABLE tablon USE_FRM;

+----------------+--------+----------+----------------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+----------------+--------+----------+----------------------------------------------------------------+
| basedepatos.tablon | repair | info | Found link that points at xxxxx (outside data file) at yyyyy |

[…]

| basedepatos.tablon | repair | warning | Number of rows changed from 0 to 4711 |
| basedepatos.tablon | repair | status | OK |

Como dicen, el resto es historia: Intentar recoger los cachitos que faltan, y hacer un mysqldump final y largarlo al dueño, que ya a estas alturas estaba pensando en un duelo a pistola a tres pasos.

MacOS: Mola, salvo cuando se le va la pinza creando Preboots

Lo malo de solo tener un culo es que hay que aceptarlo como es, y el mío es inquieto. Vamos, que a media lectura de la página de Apple sobre las bondades de Catalina, ya mi culo estaba firmando que recaerá sobre mí y mi descendencia hasta la octava generación el karma por correr betas, y que bla bla bla aceptaryaleche.

Pero las cosas a veces van y otras veces se arrastran, y en mi caso el arrastre por todos los motores de búsqueda conocidos por el hombre empezó con un misterioso mensaje de error: Could not create a preboot volume for APFS install.

Por la Internet alante se encuentra todo tipo de recetas para salir de esa. Para variar, ninguna me funcionó. Pero salí de esa. ¿Cómo?

Observé que mi directorio /Volumes contenía una cantidad enfermiza de directorios cuyo objetivo en la vida, aparentemente, es montar el volumen Preboot:

alfredo@Alfredobot-274 /Volumes> ls
InstallESD    Preboot 18  Preboot 29  Preboot 4   Preboot 50  Preboot 61  Preboot 72  Preboot 83  Preboot 94
Macintosh HD    Preboot 19  Preboot 3   Preboot 40  Preboot 51  Preboot 62  Preboot 73  Preboot 84  Preboot 95
Preboot        Preboot 2   Preboot 30  Preboot 41  Preboot 52  Preboot 63  Preboot 74  Preboot 85  Preboot 96
Preboot 1    Preboot 20  Preboot 31  Preboot 42  Preboot 53  Preboot 64  Preboot 75  Preboot 86  Preboot 97
Preboot 10    Preboot 21  Preboot 32  Preboot 43  Preboot 54  Preboot 65  Preboot 76  Preboot 87  Preboot 98
Preboot 11    Preboot 22  Preboot 33  Preboot 44  Preboot 55  Preboot 66  Preboot 77  Preboot 88  Preboot 99
Preboot 12    Preboot 23  Preboot 34  Preboot 45  Preboot 56  Preboot 67  Preboot 78  Preboot 89
Preboot 13    Preboot 24  Preboot 35  Preboot 46  Preboot 57  Preboot 68  Preboot 79  Preboot 9
Preboot 14    Preboot 25  Preboot 36  Preboot 47  Preboot 58  Preboot 69  Preboot 8   Preboot 90
Preboot 15    Preboot 26  Preboot 37  Preboot 48  Preboot 59  Preboot 7   Preboot 80  Preboot 91
Preboot 16    Preboot 27  Preboot 38  Preboot 49  Preboot 6   Preboot 70  Preboot 81  Preboot 92
Preboot 17    Preboot 28  Preboot 39  Preboot 5   Preboot 60  Preboot 71  Preboot 82  Preboot 93

Y observé, en particular, que había hasta el Preboot 99. Ni uno más, ni uno menos. Así que, cabezazo a cabezazo contra la mesa, acabé por formular la hipótesis de que lo que el programa de instalación quería no era otra cosa que crear un punto de montaje nuevo para el Preboot. Aparentemente, si ya existe /Volumes/Preboot, intenta /Volumes/Preboot 1, y si no /Volumes/Preboot 2, etc… Hasta /Volumes/Preboot 99, y ahí lo deja por imposible. Pero, ¿acaso dice «Unable to create /Volumes/Preboot directory» o algo así clarificador? Noooo, esto lo ha hecho uno que viene de Microsoft, y lo que dice, que al traducirlo despista más todavía, es lo que se ve en el título.

Total. Que me los cargué todos, excepto el primero y el último, por si acaso. Innecesaria precaución: Tras instalar Catalina, no quedó ni uno. Y Catalina funciona, al menos lo bastante como para contarlo.

Ansible y autenticación mysql

Trabajando con una Debian o Ubuntu por defecto, la autenticación para el usuario root de MySQL/MariaDB está tan lejos como anteponer sudo a lo que sea que queramos hacer desde el shell.

Esto es, obviamente, enormemente práctico. ¿Queremos crear una base de datos? sudo mysql. ¿Examinar una base de datos? sudo mysqldump basedepatos. Etcétera. Sin necesidad de recordar ninguna clave o mantener una insegura.

Al pasar alguna de estas actividades a Ansible, sin embargo, tenemos todas las papeletas de encontrarnos de morros con esto:

fatal: [mizervio]: FAILED! => {"changed": false, "msg": "unable to connect to database, check login_user and login_password are correct or /root/.my.cnf has the credentials. Exception message: (1698, \"Access denied for user 'root'@'localhost'\")"}

¿Esto qué es? Bueno, pues que la magia de la que hablaba al principio es posible simplemente porque usamos el socket de autenticación… Y eso lo podemos hacer porque somos root. Este es el socket de autenticación de una Debian 10:

srwxrwxrwx 1 mysql mysql    0 sep  9 18:39 /run/mysqld/mysqld.sock

Pero Ansible no usa el socket de autenticación. A no ser que se lo digamos explícitamente. Por qué han elegido este comportamiento, en lugar de ser consistentes con la línea de comandos, probablemente tiene que ver con pensar en ello demasiado o demasiado poco, pero no seré yo quien arrample contra unos tipos a los que, la verdad, estoy más que agradecido por haber creado esta máquina de ahorrar tiempo y dolores de cabeza.

Además, la solución es fácil. Se coge la receta uno, y se añade en la tarea en que se llame a mysql_db:

login_unix_socket: /run/mysqld/mysqld.sock

Listo, resuelto todo. De nada.

ixgbe failed to load because an unsupported SFP+ or QSFP module type was detected

En la más pura y molesta tradición de Cisco, ahora el controlador que lleva el kernel (para valores de «ahora» cercanos a 4.19) para los chips de red de Intel (disculpad la palabrota), solamente quiere usar SFPs que hayan probado anteriormente.

Si tienes un SFP de «su» marca, todo va bien. Si no es de «su» marca, ya puede ser el mejor del mercado, que el controlador decide que a la mierda:

ixgbe 0000:01:00.0: failed to load because an unsupported SFP+ or QSFP module type was detected.

Señor, dame paciencia que para mala leche ya tenemos a Linus.

Total. Que, si nos pasa eso, tenemos dos posibles soluciones:

  • La buena, que es decirle al controlador que se verifique al que lo programó:

echo "options ixgbe allow_unsupported_sfp=1" > /etc/modprobe.d/ixgbe.conf

Si nuestro kernel lleva el controlador de marras compilado dentro, tendremos que pasarlo por la línea de comandos del kernel. Prosaicamente, añadir donde toque (/etc/default/grub, /etc/grub.d/ o lo que tenga nuestra distro) ixgbe.allow_unsupported_sfp=1.

Y con eso, y si acaso el juicioso uso de rmmod ixgbe ; insmod ixgbe, estaremos de nuevo en marcha, como si no se les hubiera ocurrido… Bueno, no sigo, que me enciendo.

ModSecurity con Nginx en Buster

Tras pasarlo regular para poner a marchar un Nginx como proxy inverso con ModSecurity, he pensado que sería buena idea volcar en alguna parte mis notas para ahorrar quebraderos de cabeza a otros sufridos informáticos.

Buster lleva la librería ModSecurity 3 ya empaquetada. No hay más que instalarla con apt.

Lo que da un poco de problema es compilar el módulo de nginx. Esto, por ahora, hay que hacerlo a mano.

Primero, instalar nginx con apt.

Luego, bajar el fuente de la versión exacta. En el momento de escribir esto:

dpkg -l nginx
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-============-============-==========================================
ii nginx 1.14.2-2 all small, powerful, scalable web/proxy server

Por tanto:

wget -O - http://nginx.org/download/nginx-1.14.2.tar.gz | tar xzf -

También necesitamos el conector de ModSecurity con nginx:

git clone https://github.com/SpiderLabs/ModSecurity-nginx.git

Ahora, ojo a esto. Aparte de build-essential, hacen falta otras cosas. La más enrevesada es libpcre3-dev. Sin esto, no compila el módulo de nginx. Pero es que además hay que configurar el conector para que la use. Es decir, ya en el directorio del fuente de nginx (nginx-1.14.2 en este caso):

./configure --with-compat --add-dynamic-module=../ModSecurity-nginx --without-http_rewrite_module --without-http_gzip_module --with-pcre

Hecho esto, ya no hay más que hacer make.

Otra cosa que despista un poco es dónde pone Debian las cosas. El módulo compilado lo podemos encontrar en objs/ngx_http_modsecurity_module.so y casi cualquier guía que encontremos por ahí dice de ponerlo en /etc/nginx/modules. Pero nop, eso no es el estilo Debian. Para hacerlo al estilo Debian, hay que soltarlo en /usr/lib/nginx/modules/ y seguidamente crear la configuración que lo carga. Verbigracia:

echo "load_module modules/ngx_http_modsecurity_module.so;" > /tmp/mod-security.conf && sudo mv /tmp/mod-security.conf /etc/nginx/modules-available/

cd /etc/nginx/modules-enabled/

sudo ln -s ../modules-available/mod-security.conf 50-mod-security.conf

sudo service nginx restart

Y nuestros esfuerzos se verán recompensados con un:

nginx: [emerg] module "/usr/share/nginx/modules/ngx_http_modsecurity_module.so" is not binary compatible in /etc/nginx/modules-enabled/50-mod-security.conf:1

Mierda.

Total. Que vamos a ver cómo está construído el nginx del paquete Debian:

nginx -V

Esto suelta el chorizo que en su día le pasaron al nginx al compilarlo para empaquetarlo. Y nos dice que:

configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-sWHVb6/nginx-1.14.2=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-sWHVb6/nginx-1.14.2/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-sWHVb6/nginx-1.14.2/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-sWHVb6/nginx-1.14.2/debian/modules/http-echo --add-dynamic-module=/build/nginx-sWHVb6/nginx-1.14.2/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-sWHVb6/nginx-1.14.2/debian/modules/http-subs-filter

Vale

Pues nada, paciencia y vamos a ver.

make clean

./configure --add-dynamic-module=../ModSecurity-nginx --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-sWHVb6/nginx-1.14.2=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-mail=dynamic --with-mail_ssl_module

make

sudo cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/ngx_http_modsecurity_module.so

sudo service nginx start

Los otros –add-dynamic-module se pueden omitir. Y ya estamos listos.

Configurar un proxy inverso con ModSecurity es otra historia y debe ser contada en otra ocasión.

Debian live a medida

En Github se pueden encontrar excelentes instrucciones para instalar una Debian Stretch con ZFS en la raíz. ZFS no está disponible directa y cómodamente en el instalador de Debian porque las licencias de Linux y de ZFS no son compatibles, por lo que es necesario <suspiro>hacerlo a mano</suspiro>.

En fin. La forma más normal, como se puede leer, es arrancar con una Debian «live» y seguidamente hacer un montón de cosas a mano. Esto es muy instructivo, pero cuando a uno le esperan una pila de hipervisores, que pesan más que uno y hasta algunos opinan que valen más, en todos los cuales hay que instalar una ZFS en raíz… Es de recibo buscar un método algo más automático.

El primer paso es automatizar un poquejo las acciones con un script en bash. Esto es bastante agradecido ya; no hay más que copiar y pegar, y la instalación se hace sola (más o menos).

Pero arrancar con una Debian Live tiene sus cosillas, como que hay que instalar una tonelada de software todas y cada una de las veces. Entre otras cosas, el zfs va en dkms y depende de un montón de cosas.

Así que, redondeando, decidí hacerme una Debian Live a medida. Esto no es tan difícil, especialmente si alguien lo ha hecho antes y ha vivido para documentarlo, como por ejemplo ha hecho Will Haley con este excelente artículo. Partiendo del mismo, he hecho unas pocas adaptaciones y esto es lo que me funciona, para hacer una ISO de Debian Stretch (live) especialmente para instalar Debian con ZFS en raíz, utilizando una Debian Stretch.

En el artículo de Will Haley también explica cómo generar un pincho USB. Pero como la ISO me soluciona la papeleta (porque uso iDRAC, IPMIs, iLOs y otras cosas que comienzan con i), aquí me he quedado.

La cosa empieza instalando en la Debian en que vayamos a trabajar lo necesario:

sudo apt install debootstrap squashfs-tools xorriso grub-pc-bin grub-efi-amd64-bin mtools

Nos creamos un directorio (voy a usar el mismo nombre que Will Haley para no liarnos) y algunos subdirectorios que usaremos más adelante:

mkdir -p LIVE_BOOT/{scratch,image/live}

Nos montamos una Debian básica en ese directorio:

sudo debootstrap --arch=amd64 --variant=minbase stretch LIVE_BOOT/chroot http://deb.debian.org/debian/

En lugar de http://deb.debian.org/debian, podemos usar nuestro mirror favorito si tenemos uno. Igualmente, la arquitectura puede ser i386 o lo que necesitemos.

Nos vamos al chroot:

sudo chroot LIVE_BOOT/chroot

Elegimos un nombre para nuestra Debian «viva» y lo ponemos:

echo "viva" > /etc/hostname
echo "127.0.1.1 viva" >> /etc/hosts

Le instalamos un kernel y lo necesario para el arranque:

apt install --no-install-recommends linux-image-amd64 live-boot systemd-sysv

Añadimos a la masa los paquetes que queramos tener disponibles. Varios de los siguientes son prácticamente una necesidad para poder tener red y esas cosillas:

sudo apt install curl openssh-client openssh-server iproute2 iputils-ping less sudo emacs-nox

Y si queremos usar esta imagen para instalar un ZFS en raíz, podemos ir metiendo algunos paquetes que necesitaremos. Desafortunadamente, zfs-dkms no se puede compilar en un chroot (ni idea de por qué), pero al menos se puede dejar instalado:

sudo apt install debootstrap gdisk dpkg-dev linux-headers-amd64
sudo apt install zfs-dkms

Limpiamos:

sudo apt clean

Creamos un usuario si nos apetece y le damos sudo. O bien simplemente ponemos una clave a root:

sudo useradd -m -s /bin/bash debian
sudo passwd debian
sudo passwd root
sudo visudo

Y nos salimos del chroot

exit

Ya tenemos nuestro entorno listo; ahora hacemos con él un sistema de ficheros:

sudo mksquashfs LIVE_BOOT/chroot LIVE_BOOT/image/live/filesystem.squashfs -e boot

Copiamos el kernel y el sistema de ficheros al directorio donde guardamos lo que irá en la imagen:

sudo cp LIVE_BOOT/chroot/boot/vmlinuz-* LIVE_BOOT/image/vmlinuz
sudo cp LIVE_BOOT/chroot/boot/initrd.img-* LIVE_BOOT/image/initrd

Ahora vamos a crear la configuración de GrUB. El comando search lo que hace es buscar un sistema de ficheros que contenga el fichero /DEBIAN_CUSTOM que vamos a crear seguidamente. Lo podríamos haber llamado /Susana o cualquier otra cosa. El caso es que el sistema de ficheros que pesque con ese fichero creado es el que va a llamar raíz:

cat <<'EOF' > LIVE_BOOT/scratch/grub.cfg
search --set=root --file /DEBIAN_CUSTOM
insmod all_video
set default="0"
set timeout=5
menuentry "Debian Live" {
linux /vmlinuz boot=live quiet nomodeset
initrd /initrd
}
EOF

Creamos el fichero prometido en su sitio:

touch LIVE_BOOT/image/DEBIAN_CUSTOM

Creamos la imagen UEFI con GrUB:

sudo grub-mkstandalone --format=x86_64-efi --output=LIVE_BOOT/scratch/bootx64.efi --locales="" --fonts="" "boot/grub/grub.cfg=LIVE_BOOT/scratch/grub.cfg"

Creamos la imagen de arranque UEFI, de 16 bits de toda la vida (al menos de la vida del PC):

cd LIVE_BOOT/scratch
dd if=/dev/zero of=efiboot.img bs=1M count=10
mkfs.vfat efiboot.img
mmd -i efiboot.img efi efi/boot
mcopy -i efiboot.img ./bootx64.efi ::efi/boot/
cd ../..

Y una imagen de arranque para BIOS:

sudo grub-mkstandalone --format=i386-pc --output=LIVE_BOOT/scratch/core.img --install-modules="linux normal iso9660 biosdisk memdisk search tar ls" --modules="linux normal iso9660 biosdisk search" --locales="" --fonts=""  "boot/grub/grub.cfg=LIVE_BOOT/scratch/grub.cfg"

Concatenamos la imagen de arranque de CD con el sistema de ficheros que hemos creado:

cat /usr/lib/grub/i386-pc/cdboot.img LIVE_BOOT/scratch/core.img > /tmp/bios.img
sudo mv /tmp/bios.img LIVE_BOOT/scratch/bios.img

Y por fin, crear la ISO:

sudo xorriso -as mkisofs -iso-level 3 -full-iso9660-filenames -volid "DEBIAN_CUSTOM" -eltorito-boot boot/grub/bios.img -no-emul-boot         -boot-load-size 4 -boot-info-table --eltorito-catalog boot/grub/boot.cat --grub2-boot-info --grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img -eltorito-alt-boot -e EFI/efiboot.img -no-emul-boot -append_partition 2 0xef ${HOME}/LIVE_BOOT/scratch/efiboot.img     -output "${HOME}/LIVE_BOOT/debian-custom.iso" -graft-points         "${HOME}/LIVE_BOOT/image"  /boot/grub/bios.img=$HOME/LIVE_BOOT/scratch/bios.img  /EFI/efiboot.img=$HOME/LIVE_BOOT/scratch/efiboot.img

Listo para usar. A pasarlo pipa.

Algunas notas actualizadas a finales de 2020:

Todo esto funciona perfectamente con buster, sin más que cambiar «stretch» por «buster» en la llamada a debootstrap.

Estando en el chroot, algunos paquetes más que encuentro útil instalar, para tenerlos disponibles luego:

isc-dhcp-client sudo