Blog ·
Un agente real, una pasarela de pruebas
Cinco rondas terminaron con la misma reserva: sin demostrar sobre el cable. Cerrarla exigió un agente real, una contraseña plantada y dos medidas vacías.
Por Provibr
Cinco rondas seguidas cerraron su informe con una versión de la misma frase: el cambio de protocolo es aditivo, las pruebas unitarias están en verde, y nada de ello se ha ejercitado contra un agente real. Adaptadores de monitorización, el sobre sellado, las actualizaciones automáticas del agente, un paso de automatización que espera a un comando. Los cinco eran correctos hasta donde nadie podía ver, y los cinco llevaban una reserva que heredaba la ronda siguiente.
Esa reserva es barata de escribir y se acumula. Cada ronda añade una, nadie se equivoca, y al cabo de cinco hay una pila de comportamiento que solo ha existido en un banco de pruebas. Esta ronda se dedicó a retirar la pila en lugar de aumentarla: sin función nueva, sin cambio de protocolo, sin publicación. Ochenta y dos medidas contra un agente real.
El montaje, en una frase
Un binario de agente real, compilado para el mismo destino que se distribuye, firmado con una clave de desarrollo, corriendo dentro de un contenedor desechable en una máquina que no es el portátil de nadie, hablando con una pasarela de pruebas por un túnel inverso, con siete servicios desechables en el bucle local para medir y una segunda copia del mismo binario inscrita como sonda alojada.
Todo en esa frase carga peso. El binario es el que se distribuye y no una compilación de prueba, porque los fallos interesantes viven en la serialización y en TLS, no en la lógica. La pasarela es a propósito una instancia de desarrollo en puertos distintos, porque la de producción escucha en los evidentes, y un agente que llame al lado equivocado sería una tarde muy mala. Los servicios son contenedores desechables con el software real en versiones reales y no imitaciones, porque el sentido entero del ejercicio es que las imitaciones son justo lo que ya había.
Los servicios de base de datos recibieron una contraseña canario, y esa elección hizo posible el apartado siguiente.
| Grupo | Medidas | Qué resuelve |
|---|---|---|
| Adaptadores de monitorización | 44 | Seis protocolos de base de datos y directorio, credenciales correctas y erróneas, y el estado de regla resultante |
| Actualizaciones automáticas | 19 | Programar, aplicar, reiniciar y rechazar una compilación manipulada |
| Paso de automatización | 11 | Un comando que termina dentro de la espera y otro que no |
| Sonda alojada | 4 | El mismo binario inscrito como sonda de plataforma en lugar de agente de cliente |
| Fuente caída | 4 | Un agente desconectado produce su propio estado, nunca una avería falsa |
Una búsqueda con un valor plantado
La afirmación que más interesaba comprobar es una negación: un secreto de monitorización viaja sellado y nunca se escribe en la fila del comando, en el registro de eventos, en el bus de mensajes ni en ninguno de los dos registros de proceso.
Las negaciones son las afirmaciones que fallan en silencio. Las contraseñas no fueron por tanto cadenas aleatorias cuya ausencia no prueba nada; fueron valores plantados, elegidos para que una sola aparición en cualquier sitio resultara inconfundible. Después se registraron siete capas: las cargas de comando guardadas, la tabla de eventos, la configuración de comprobación guardada, los resultados guardados, el bus de mensajes en texto y en su forma codificada, el registro de la pasarela y el del agente. En conjunto, cerca de medio millón de caracteres de material.
Cero coincidencias, que es la respuesta deseada y también la que da una búsqueda rota.
Por eso la misma búsqueda se lanzó contra un archivo que sin duda contenía la palabra, y la encontró. Ese control positivo es la diferencia entre «buscamos y no había nada» y «no buscamos». Cuesta una línea y es la línea más valiosa del guion.
Otras dos exigencias cubren el mismo fallo por el otro lado: la ejecución debe observar al menos una docena de comandos de comprobación reales y varias decenas de mensajes del bus antes de poder concluir nada. Ambas estuvieron en rojo en algún momento de la ronda, en pasadas que de otro modo habrían informado de un barrido limpio sobre nada en absoluto. Un barrido que no halla infracciones en un conjunto vacío no es prueba, y se parece exactamente al éxito.
Dos formas de que una medida esté en verde sin medir
Las dos ocurrieron en esta ronda, en guiones escritos por alguien que conoce este modo de fallo y escribe sobre él con regularidad.
La primera: una consulta seleccionó una columna y el código leyó después otra propiedad de las filas del resultado. En un lenguaje con tipos eso es un error; aquí es undefined, para siempre, en silencio, y toda comparación con ese valor falla de un modo que parece una aserción que nunca salta en vez de una que siempre se cumple. La comprobación informó de éxito porque no tenía nada que comparar.
La segunda: una prueba de pertenencia contra una lista vacía. Es una afirmación sobre nada, se satisface trivialmente, y es el estado final natural de cualquier guion cuya etapa anterior produjo cero filas en silencio. Ambas son baratas de cubrir: comprobar la forma de lo obtenido y comprobar que el conjunto por examinar no está vacío.
Cinco nombres de columna en esos guiones estaban adivinados en lugar de consultados. Aquellos fallaron a gritos, que es la variante barata del mismo error, pero es el mismo error: un comando dice cómo se llama una columna, y la versión en la que una suposición acaba en una lista que nadie ejecuta es la versión que sobrevive a la revisión.
Un agente que rechaza inscribirse dos veces
A mitad de las pruebas de actualización el agente dejó de volver. Desde fuera parecía un bucle de reinicio, y la primera hora se fue en la ruta de actualización.
El agente hacía exactamente lo correcto. La inscripción escribe un archivo de estado, y el guion de instalación generado comprueba ese archivo y rechaza inscribirse de nuevo si está presente. Reinscribir un anfitrión ya inscrito reemplaza una identidad, y hacerlo por accidente es como una máquina pierde su certificado y su almacén en un solo paso. Nuestro bucle de supervisión, escrito para la ocasión, no tenía esa puerta, y cada intento de reinicio chocaba con el rechazo propio y correcto del agente.
La lección no trata de la puerta. Trata de que un rechazo correcto y un defecto tienen la misma forma vistos de lejos, y de que lo que los separa es leer lo que dijo realmente la otra parte en lugar de lo que sugería el síntoma.
De esos mismos días salieron tres sorpresas más, las tres decisiones de diseño chocando con la realidad:
Las horas de actualización programadas viven en una rejilla de cinco minutos, impuesta por una restricción de base de datos. «Dentro de un minuto», que es lo que quiere quien escribe una prueba, no existe. Lo que lo hace practicable es una ventana de recuperación: una hora perdida aún se ejecuta durante un cuarto de hora después. Por eso mismo una medida repetida dentro de ese mismo cuarto de hora debe elegir una hora de rejilla libre, ya que cada hora se reclama de forma única por agente y una segunda pasada contra la misma se rechaza con razón.
Una comprobación TCP viene con TLS activado. Apuntada a un puerto sin cifrar falla con un error de TLS, lo cual es correcto y parece una comprobación rota hasta que uno mira qué es un valor por omisión.
Y la fila de auditoría de un paso de automatización cuya espera venció se congela en el estado que vio el flujo. El comando pasó a tiempo agotado sesenta milisegundos después y la fila siguió diciendo lo que decía. Eso no es un fallo en la fila, es un fallo en el comentario que la acompañaba, que prometía leer allí el desenlace final. Esa promesa se retiró y se respondió como corresponde en otro sitio.
Un aviso que agrupa y un recuento que dice cero
El más afilado de los hallazgos pequeños. Tras una actualización correcta la plataforma escribe un aviso. La medida contó los avisos creados desde el inicio de la ejecución y obtuvo cero.
Los avisos agrupan. Una segunda aparición de lo mismo incrementa un contador en la fila existente y deja intacta su marca de creación, que es justo lo deseable en un panel: «este agente se cayó» con una cifra al lado en lugar de cuarenta filas. Es también justo lo que hace que un recuento sobre la marca de creación no informe de nada mientras el aviso está allí a la vista.
La medida estaba equivocada, el producto acertaba, y la única razón por la que se sabe en qué sentido era es que alguien abrió el panel en vez de creer a la consulta. Toda agregación tiene una forma, y una prueba que no la conoce comprueba su propia suposición.
Lo que anotaríamos
- «Sin demostrar sobre el cable» es una deuda, y se acumula. Una ronda puede cargarla. Cinco rondas son una pila de comportamiento que nunca existió fuera del banco de pruebas.
- Demostrar una negación con un valor plantado y un control positivo. Sin él, cero coincidencias y una búsqueda rota son indistinguibles.
- Dar a cada barrido un suelo. Comprobar que la entrada no está vacía antes de concluir nada sobre ella.
- Un rechazo correcto parece un defecto de lejos. Leer lo que dijo la otra parte antes de reescribir la propia.
- No adivinar ni un nombre de columna, ni un valor por omisión, ni una rejilla. Los tres están a un comando de distancia, y la suposición que llega a una lista es la que nunca salta.
- Conocer la forma de lo que se cuenta. Las filas agrupadas no llevan la marca de creación por la que se filtra.
Para que quede claro: en esta ronda no se publicó nada. Ningún cambio de producto, ningún cambio de protocolo, ninguna versión del agente. Lo que salió es que cinco reservas desaparecen y un puñado de defectos pequeños quedan corregidos, y la limpieza se contó en lugar de suponerse: sin filas sobrantes, sin contenedores sobrantes, sin archivos sobrantes.
Algunas cosas quedaron sin demostrar. La ruta de inyección de la automatización se ejercitó por su núcleo y no por el panel, los adaptadores no corrieron desde una sonda alojada, y sigue sin haber medida contra un hipervisor real.
Del trabajo de plataforma en Provibr. Por qué el secreto de monitorización se sella en cada intento está en un secreto que viaja una sola vez, y cómo una actualización firmada del agente rechaza una compilación manipulada está en cómo una actualización firmada del agente se rechaza a sí misma.