Ir al contenido

Blog ·

Una sonda a la que no se puede convencer de nada

Monitorizar desde fuera de la red del cliente exige poner máquinas propias. Así impide la plataforma que una de esas máquinas haga cualquier otra cosa.

Por Provibr

Comprobar si la máquina de un cliente es alcanzable solo tiene sentido desde un sitio donde esa máquina no está. Un agente dentro de la red puede decir que el servicio está escuchando; no puede decir que la ruta hasta él está rota. Así que hay que poner máquinas propias en la internet pública y dejar que sean ellas las que establezcan la conexión.

Eso es una funcionalidad y un pasivo en la misma frase. Una sonda es hardware nuestro, en un centro de datos que se comparte con desconocidos, y mantiene abierta una conexión de control con nuestra plataforma. Si algo que llegue por esa conexión puede persuadirla de hacer algo que no sea medir, se convierte en el punto de apoyo más útil de todo el producto.

Un solo binario, y el token decide quién es cada uno

Una sonda no es un programa aparte. Es el mismo agente que ejecutan nuestros clientes, arrancado con probe enroll o probe run en lugar de run, sobre el mismo motor de comprobaciones y el mismo RPC de sesión. Una compilación recortada no podría ejecutar en absoluto un comando de hipervisor, lo que suena más seguro, pero implica una segunda implementación de los reintentos, la espera progresiva, la rotación de certificados y el motor de comprobaciones, y la segunda recibe menos atención cada mes. Conservamos un solo binario y trasladamos la frontera a un sitio donde se puede probar.

La forma evidente de que un solo binario se dé de alta como dos cosas distintas es entonces un campo en la petición de alta que diga cuál. Es la forma equivocada, y merece la pena decir sin rodeos por qué: un cliente que declara su propia clase declara sus propios privilegios, y toda comprobación posterior descansa sobre una cadena que aportó justamente aquello que se está comprobando.

Así que ese campo no existe. Hay dos tablas de tokens: enrollment_tokens, con una clave ajena no nula hacia agents, y monitoring_probe_enrollment_tokens, con otra hacia monitoring_probes. Un token existe exactamente en una de ellas, de modo que lo que se da de alta se deduce de algo que emitimos nosotros y no de algo que dijo quien llama. Los dos fallos devuelven una negativa idéntica, porque mensajes distintos le dirían a un atacante qué clase de token ha encontrado.

El certificado que vuelve lleva un nombre común de la forma probe:<id> y, como nombre alternativo del sujeto, un URI que no contiene ninguna organización, mientras que el URI del certificado de un agente sí nombra al cliente. Eso es para el operador que lee un certificado y explícitamente no es la frontera: un nombre común es una cadena que escribe una autoridad de certificación. La frontera es que una huella se resuelve en agents o en monitoring_probes, nunca en las dos, porque son dos tablas.

Un veredicto para cada mensaje, no una lista de los permitidos

El protocolo de sesión son dos mensajes envoltorio, cada uno con una elección entre alternativas en su interior. Una lista de permitidos que nombre solo lo que una sonda puede enviar se queda verde para siempre, incluido el día en que alguien añada un quinto tipo de mensaje, y ese día hay un tipo de mensaje que nada menciona y que, por tanto, nada rechaza. Así que las tablas de la pasarela nombran todos los campos de ambos envoltorios con un veredicto explícito: veintidós entradas hacia arriba, de las cuales se aceptan tres mensajes reales más un número de secuencia, y catorce hacia abajo, de las cuales se acepta una instrucción real, más tres confirmaciones de sesión. Una prueba lee el TypeScript generado y falla si un campo no tiene veredicto, y falla también en el otro sentido cuando un nombre de la tabla ya no existe en el protocolo, porque una regla que no restringe nada se lee exactamente igual que la cobertura. Añadir un mensaje de sesión cuesta ahora una línea, y esa línea es una decisión con el nombre de alguien en el commit.

Hacia arriba, una sonda puede enviar tres cosas: el saludo, el latido y el resultado de una comprobación. La denegación que merece explicación es la de los resultados de comando: el despachador empareja un resultado con un comando por identificador y no por remitente, así que una sonda que pudiera enviar uno podría cerrar un comando perteneciente a cualquier agente de la flota. Hacia abajo se rechazan diez entradas, y cuatro de ellas son la razón de ser de todo el diseño:

MensajeQué llevaríaPor qué una sonda no puede verlo nunca
Entrega de credencialesLos accesos al panel y al hipervisor de un clienteUna sonda no tiene caja fuerte ni integración; no hay estado en el que esto signifique algo
Olvido de credencialesLa instrucción de borrar uno de esos accesosLa misma razón, al revés
Directiva de arranque por redQué servir a una máquina que arranca desde la redUna sonda no aprovisiona nada
Apertura de automatizaciónUn entorno aislado que ejecuta scripts del clienteUna sonda no ejecuta código de ningún cliente

El registro en el que una sonda no está

La tabla de arriba es la mitad demostrable de la promesa. La mitad que sostiene el peso es que no hay ruta. El despacho de comandos encuentra su destino por identificador de agente; las sondas viven en su propio registro, indexado por identificador de sonda, y no tienen identificador de agente en absoluto. Ninguna búsqueda puede devolver una sesión de sonda al código que envía una entrega de credenciales, ni siquiera si alguien añadiera una clase nueva de instrucción y se olvidara por completo de la tabla.

La única función que escribe hacia una sonda comprueba el veredicto en el origen y no en el receptor, porque un mensaje que se colara llegaría a una máquina nuestra que ejecuta el mismo binario que el agente de un cliente, y esa máquina podría sencillamente cumplirlo. Hace además una comprobación que la tabla no puede expresar: command se acepta, pero una petición de comando es a su vez una elección entre veinticuatro alternativas, y todas menos tres son familias de instrucciones de proveedor, así que sin ella «una sonda puede recibir comandos» significaría en silencio «una sonda puede recibir un comando de hipervisor». La carga útil tiene que ser exactamente un campo, y ese campo tiene que ser la comprobación de monitorización. La prueba recorre todos los campos de carga útil del código generado y afirma que todos menos uno se rechazan.

Elegir una sonda, y conmutar a la siguiente

Cada ubicación puede alojar más de una sonda, así que algo tiene que decidir. Un hash módulo el número de sondas es incorrecto el día en que esa cifra cambia: casi todas las reglas se mueven a la vez, y por todo el producto hay gráficas de latencia que dan un salto el mismo día sin motivo visible. El hashing de rendezvous mueve solo lo que estaba adscrito a la sonda que se fue. La clave es la revisión de la regla y la ubicación. La revisión es inmutable, de modo que una regla que el cliente edita puede aterrizar legítimamente en otro sitio, mientras que la elección dentro de una misma revisión no se desplaza nunca. El slot queda fuera de la clave a propósito: con el número de tick dentro, mediciones consecutivas caerían en sondas distintas, y una diferencia entre dos puntos podría ser igual de bien una diferencia entre dos máquinas. La comparabilidad a lo largo del tiempo vale más que repartir la carga.

Una sonda puede fallar de dos maneras, que se advierten en momentos distintos: puede no estar cuando se intenta enviar, o puede aceptar el comando y no responder nunca, cosa que solo aflora cuando ha pasado el tiempo de espera de la propia comprobación. Las dos producen un intento más dentro del mismo tick, y nunca un segundo tick. Una conmutación por error no es una segunda medición, y si lo fuera, una ubicación con mala suerte duplicaría en silencio el número de muestras. El techo son dos intentos físicos, porque entre los dos tienen que caber dentro del plazo del tick.

Cuando una ubicación no tiene ninguna sonda sana, el slot se reclama igualmente y la fuente se registra como no disponible. Nunca como caída: que falte una ubicación nuestra no dice nada sobre la máquina del cliente, y llamarlo interrupción sería una falsa alarma generada por nuestra propia infraestructura.

Tres sitios donde se rechaza una dirección privada

Una regla de monitorización es un campo donde alguien nombra un destino y una máquina nuestra se conecta a él cada minuto. Sin límites, eso es un motor de falsificación de peticiones con una dirección de facturación encima. El objetivo es una asignación de dirección perteneciente al propio servidor del cliente y no texto libre, lo que elimina la mayor parte del problema; el resto es una lista de rangos que una sonda alojada no puede tocar, por encima de los que están cerrados en todas partes:

RangoPor qué está cerrado
10/8, 172.16/12, 192.168/16Espacio privado. En una sonda alojada eso es el centro de datos, no el cliente
100.64/10NAT de nivel de operador, donde los proveedores cuelgan balanceadores y rutas de gestión
192.0.0/24, 192.0.2/24, 198.18/15, 198.51.100/24, 203.0.113/24, 192.88.99/24Asignaciones de protocolo, evaluación comparativa, documentación, un anycast de retransmisión obsoleto. Ninguno de esos rangos puede ser una máquina de cliente, así que lo que responda está más cerca que el objetivo
fc00::/7, 2001:db8::/32La primera y la tercera fila de arriba, en IPv6: espacio local único y espacio de documentación

Las formas IPv4 mapeadas se desenvuelven primero; de lo contrario, cada regla de arriba tiene una grafía que la sortea.

La comprobación se ejecuta en tres sitios y hacen falta los tres. La plataforma rechaza antes de enviar, de modo que un objetivo malo no llega nunca a una máquina que pudiera cumplirlo. La sonda rechaza en el momento de ejecutar, porque solo ahí se sabe a qué se resolvió realmente un nombre, y eso puede variar de una ejecución a otra. Y la plataforma vuelve a leer la dirección informada cuando llega el resultado, con lo que una sonda que midió en otro sitio se convierte en una respuesta inválida y no en un dato. Un destino privado es un error de configuración y nunca un fallo: no se midió nada, así que no hay nada que decir sobre el objetivo.

Lo que dejaríamos por escrito

  1. Nunca dejar que el cliente nombre su propia clase. Derivarla de algo emitido por uno mismo, y mantener idénticos los dos rechazos para que el error no sea un oráculo.
  2. Enumerar el protocolo entero, no la parte permitida. Dejar que una prueba lea el código generado y exija un veredicto por campo, en ambos sentidos, para que un tipo de mensaje nuevo sea una prueba en rojo y no un hueco.
  3. Poner la frontera real donde no se pueda olvidar. Un registro aparte sin identificador de agente significa que no hay ruta, diga lo que diga la tabla.
  4. Comprobar la carga útil además del envoltorio. «Se permiten comandos» no es la misma afirmación que «se permite este comando».
  5. Mantener el reloj fuera de la clave de asignación, y hacer que una conmutación por error sea un intento más y no una muestra más, para que un cambio en los datos no pueda confundirse nunca con un cambio en el instrumento.

Para dejarlo claro: la monitorización alojada está detrás de un interruptor de despliegue apagado por defecto, no hay debajo ninguna capa de derechos de uso, y nada de esto se puede comprar hoy. Lo que no está demostrado también merece nombrarse. El subcomando de sonda no se ha llegado a ejecutar nunca de extremo a extremo, porque uno de sus componentes de red no compila en la máquina donde se escribió esto; las políticas de ubicación solo se han ejercitado en su forma más simple; IPv6 no se ha medido en ninguna parte. La conexión en sí se recorrió de verdad, desde el alta y a través de una sesión hasta una comprobación que volvió, y un mensaje prohibido por esa conexión fue rechazado.

Del trabajo de monitorización en Provibr. Por qué hay que demostrar un nombre de host antes de que pueda ser un objetivo está en demostrar la propiedad de un nombre de host, y por qué un incidente no se cierra solo porque hayan dejado de llegar las mediciones está en un incidente que nunca cierra por silencio.

Empiece con un host y un agente

Cree una cuenta, instale el agente con un solo comando y conecte su primer host de Proxmox. El periodo de prueba dura 14 días y no pide ningún dato de pago.