1 puntos por GN⁺ 5 시간 전 | 1 comentarios | Compartir por WhatsApp
  • ssh-init-vm inyecta una clave privada temporal de host SSH con cloud-init para evitar ataques de intermediario en la primera conexión SSH a una VM nueva, y hace que solo se confíe en ella mientras se genera o importa la clave de host de largo plazo
  • Funciona incluso en VPS o nubes sin funciones dedicadas de protección de acceso, como Hetzner Cloud, y solo requiere cloud-init, que tiene amplio soporte
  • En el esquema habitual de Trust On First Use, si se responde yes a la pregunta de ssh “The authenticity of host [...] can't be established”, un atacante puede actuar como proxy del tráfico o presentar una máquina que parezca ser la VM del usuario
  • Si se coloca directamente la clave privada de host SSH de largo plazo en el userdata de cloud-init, eso ayuda a autenticar la primera conexión, pero el material de clave sensible puede quedar expuesto a través del servicio de metadatos, SSRF, los sistemas del proveedor de nube o la workstation del administrador
  • ssh-init-vm guarda la clave temporal en un directorio temporal y no la agrega a ~/.ssh/known_hosts; además, no almacena la salida de la VM tal cual, sino que depende de la rotación de claves de host de OpenSSH para registrar la clave de largo plazo

Problema de exposición del userdata de cloud-init

  • Si se inyecta una clave privada de host SSH de largo plazo con cloud-init, se puede poner la clave pública en ~/.ssh/known_hosts para autenticar la primera conexión, pero la clave privada puede filtrarse por varias vías
  • Un proceso arbitrario dentro de la VM normalmente puede obtener el userdata desde el servicio de metadatos, que suele ser legible; en una VM de Hetzner, el contenido de cloud-init puede verse en http://169.254.169.254/hetzner/v1/userdata
  • Un atacante puede hacer que un proceso filtre metadatos mediante SSRF, y ese tipo de bloqueo no siempre se aplica incluso en entornos con funciones de protección dedicadas
  • El userdata también puede quedar expuesto en otros sistemas del proveedor de nube; Hetzner incluso indica en la documentación de su API de creación de servidores que no se debe usar para guardar “passwords or other sensitive information”
  • La workstation del administrador también puede ser un lugar donde el userdata de cloud-init quede almacenado o por donde pase, así que poner ahí una clave privada de largo plazo crea un riesgo de exposición mientras la clave siga siendo válida

Análisis de seguridad y modelo de amenazas

  • La premisa es confiar en el protocolo OpenSSH y su implementación, sin depender de la capacidad del administrador para detectar un ataque
  • Protección frente a atacantes de red

    • Lo que se protege es la integridad de la workstation del administrador y de la VM
    • El atacante es un intermediario con control total de la red, y puede llegar a conocer el userdata de cloud-init en algún momento después de que el script termine con éxito o falle
    • La protección funciona porque el atacante no conoce el material de claves cuando todavía tiene valor
    • El script guarda la clave temporal de host SSH en un directorio temporal para evitar su uso accidental, y no agrega la clave temporal de host SSH a ~/.ssh/known_hosts
  • Si la workstation del administrador está comprometida

    • Lo que se protege se limita a la VM y a su clave privada de host SSH de largo plazo
    • Se asume que el atacante controla por completo la red y la workstation del administrador, pero no accede a la VM real
    • La clave privada de host SSH de largo plazo nunca estuvo en la workstation del administrador, y como el atacante no accede a la VM real, no obtiene la clave de host de largo plazo de la VM
    • Si el atacante sí accede a la VM real, es muy probable que pueda conocer las claves de host SSH con algo como ssh root@<VM> cat /etc/ssh/ssh_host_*
  • Si la VM o el proveedor están comprometidos

    • Lo que se protege se limita a la integridad de la workstation del administrador
    • El atacante puede controlar por completo la red y también la VM o al proveedor
    • Incluso en ese caso, la integridad de la workstation del administrador se protege gracias a la premisa de que OpenSSH es seguro
    • Como defensa adicional, el script no escribe la salida de la VM directamente en ~/.ssh/known_hosts, sino que usa la rotación de claves de OpenSSH para registrar la clave de host SSH de largo plazo
    • Este enfoque evita que un host comprometido alimente con datos maliciosos al parser de known_hosts, y hace que solo se escriban en ~/.ssh/known_hosts las claves que la VM realmente controla
    • También permite manejar correctamente opciones de OpenSSH como HashKnownHosts y otras opciones relacionadas en el futuro

Condiciones para que un ataque de intermediario funcione en la práctica

  • Que un ataque MITM tenga éxito depende de si el usuario detecta en la práctica que todas las conexiones desde el principio van a la máquina equivocada, de si se niega a introducir una contraseña y de si configura reenvío del agent de ssh o de X11
  • Bajo las condiciones simplificadas de ssh-mitm, el ataque tiene muchas probabilidades de funcionar si el atacante puede engañar al usuario dándole acceso a una máquina bajo control del atacante en lugar del host real
  • También tiene éxito si el atacante engaña al usuario para obtener información con la que pueda iniciar sesión en el host real
    • Si el usuario inicia sesión en la máquina del atacante con contraseña, el atacante puede tener éxito
    • Si el usuario inicia sesión con cualquier método de autenticación y luego escribe una contraseña en el prompt, el atacante puede tener éxito
    • Si el usuario inicia sesión con cualquier método de autenticación y reenvía la conexión de ssh-agent, el atacante puede tener éxito
  • Si no se cumple ninguna de esas condiciones, el atacante necesita acceso al host real para engañar al usuario, pero es muy probable que fracase porque no puede iniciar sesión en el host real solo con la entrada del usuario
  • Si el usuario reenvía una conexión X11, el atacante puede incluso lograr atacar la workstation del administrador independientemente del método de autenticación

1 comentarios

 
GN⁺ 5 시간 전
Comentarios de Lobste.rs
  • Está genial que se pueda automatizar. Yo venía verificando la huella SSH del servidor por un canal aparte desde la consola del proveedor de nube
    No administro tantas instancias en la nube, así que no me molesta que haya algunos pasos manuales en el aprovisionamiento

    • Ese método también funciona. Solo que el proveedor que estaba usando no tenía eso conectado, y al mismo tiempo yo estaba armando un script de automatización de la configuración
  • Si ya tienes automatizada la zona DNS, también hay otro enfoque: crear un token de un solo uso con alcance muy limitado, por ejemplo permitiendo únicamente crear un registro en my-server-hostname.example.net
    Ese token se le pasa al servidor con cloud-init, y el servidor sube su clave pública SSH como un registro SSHFP en el DNS. Después, el cliente SSH puede validar automáticamente el registro SSHFP, y la zona DNS debe estar firmada con DNSSEC
    Este flujo permite que el servidor conserve su clave privada de host SSH y aun así evita la rotación de claves. La mayoría de los proveedores DNS no soportan tokens de acceso de un solo uso tan granulares, pero se podría tener un servicio web interno sencillo que valide el token y luego haga la llamada al API usando un token permanente y sin restricción de alcance. El servidor SSH no puede acceder a ese token permanente

    • Es una idea creativa, y con tokens de un solo uso para servicios personalizados parece lo bastante flexible como para funcionar bien
      Aun así, probablemente preferiría generar certificados SSH en vez de escribir en un dominio con DNSSEC, y a partir de ahí ya depende de qué solución se adapte mejor a cada entorno
      Me da curiosidad saber si conoces algún software o proveedor que permita crear tokens tan flexibles, o si hace falta desarrollar una parte por cuenta propia
  • Bastante limpio. Aunque creo que en la fecha del artículo están invertidos el mes y el día

    • Siempre da gusto trabajar con OpenSSH, y ya corregí el formato de mes/día
  • Hace tiempo hice un servicio experimental para explorar el mismo problema del huevo y la gallina con SSH
    Crea registros DNS SSHFP bajo demanda; si te interesa, puedes ver https://github.com/tedb/sshfp

  • Me alegra que se mencione el servicio de metadatos 169.254.169.254, bastante consistente entre proveedores de VM. Se puede comprobar viendo varias entradas cloudinit/source/DataSource*.py en el código fuente de cloud-init
    En lo personal, cada vez me fatiga más cloud-init por su diseño y sus limitaciones. Me interesa unificar la configuración del sistema entre máquinas virtuales locales con QEMU, máquinas remotas, contenedores y hardware físico
    El proyecto arch-boxes muestra cómo se construye la imagen de cloud-init de ArchLinux, y es un conjunto de scripts de shell muy simple. Si este método se adapta con guestfish o µvm, se pueden usar exactamente los mismos scripts para imágenes compatibles con OCI, imágenes de disco para QEMU o proveedores cloud, y para aprovisionar máquinas físicas nuevas
    Con algunas banderas de QEMU, se puede reproducir el mismo enfoque sin depender de cloud-init. Hasta donde sé, systemd.system-credentials no permite pasar claves de host temporales, solo credenciales para ~/.ssh/authorized_keys como ssh.authorized_keys.root
    En su lugar, se puede crear un archivo unit que se ejecute en la etapa de initrd o junto con systemd-firstboot.service. Ese unit puede incluirse previamente en la imagen o inyectarse temporalmente con credenciales systemd.extra-unit.*, y activarse con la opción de línea de comandos del kernel systemd.wants=…. En QEMU, se puede simular la presencia de un servicio de metadatos con -netdev user,id=metadata,net=169.254.0.0/16,dhcpstart=169.254.0.15,guestfwd=tcp:169.254.169.254:80-cmd:…. Aun así, es muy probable que haya que activar la interfaz generada, y eso también podría resolverse mejor con un unit temporal
    Así se obtiene bastante flexibilidad con una complejidad relativamente baja al aplicar una configuración del sistema consistente en varios tipos de “máquinas”. De hecho, si nos enfocamos solo en esta tarea, lo mejor parece ser que la herramienta de creación de imágenes genere imágenes de máquina con claves de host fijas y que instale como servicio de SystemD un script personalizado de rotación de claves de host, ejecutado en el primer reinicio o al apagar

    • En ArchLinux, si activas el HOOK de systemd en /etc/mkinitcpio.conf, puedes y en realidad debes escribir archivos unit de SystemD para las tareas que se ejecutan en initrd
      En la práctica, usarlos solo es un poco más engorroso que escribir {/etc,/usr/lib}/initcpio/hooks
      Pero como es bastante fácil habilitar systemd-networking y systemd-resolved en initrd, se pueden programar tareas antes de que initrd le ceda el control al sistema de archivos raíz
      Claro, en hardware físico como laptops puede no encajar tan bien, porque para la conexión Wi‑Fi hace falta algo como NetworkManager, pero para VM de QEMU y VM alojadas funciona bien, y muchas tareas de arranque del sistema encajan naturalmente en ese espacio
      La meta es no depender de cloud-init, no quedar atado a un proveedor cloud específico, obtener consistencia entre máquinas físicas, contenedores, VM locales y VM alojadas, y reducir las dependencias prácticamente a SystemD
    • Quizá también podría servir algo como https://github.com/the-maldridge/shinit/, una herramienta muy pequeña a la que solo le agregas las funciones que necesitas