Saltar al contenido principal

Seguridad

Modelo​

El código del job se trata como hostil. La barrera principal es la VM; la segunda es el confinamiento del proceso que ejecuta la VM.

La documentación de la libkrun es explícita: el guest y el VMM están en el mismo contexto de seguridad. Un escape de la VM cae dentro del proceso rc vmm, con sus privilegios. Por eso el aislamiento es la VM más el confinamiento de ese proceso, y ese confinamiento hoy es distinto según la plataforma.

Capas de un job (en ambas plataformas)​

CapaCómo
VM por jobcada job se ejecuta en una VM nueva, destruida al final; nada pasa de un job al siguiente
root de solo lecturael root es un ext4 montado de solo lectura (/dev/vda); solo se escribe en el tmpfs de /tmp (256 MiB)
estado efímeroel tmpfs desaparece con la VM
sin redkrun_disable_implicit_vsock + krun_add_vsock(ctx, 0): sin TSI, sin interfaz de red
sin virtiofsel root es un disco, no un directorio compartido del host; virtiofs no impediría que el guest alcanzara otros directorios del host
límites por lenguajevCPU y RAM limitados en el catálogo; timeout por defecto 10 s, máximo 300 s, con SIGKILL al grupo del proceso
límites de entradastdin ≤ 1.000.000 bytes, hasta 32 archivos, código ≤ 4 MiB, nombres sin /, sin .., sin empezar con .
si el padre muere, la VM muereninguna VM queda huérfana si el host se cae

Por plataforma​

ElementomacOSLinux
hipervisorHypervisor.frameworkKVM
permiso para crear una VMentitlement com.apple.security.hypervisor en el binarioacceso a /dev/kvm
la VM muere con el padrepipe de vigilancia en el fd 3Pdeathsig
timeoutSIGKILL al grupo del procesoSIGKILL al grupo del proceso
uid/gid sin privilegios en el proceso de la VM—soportado por la spec (krun_setuid/krun_setgid)
Landlockno existe en macOSaplicado al proceso vmm de cada VM de job
seccompno existe en macOSdenylist aplicada a todos los hilos (TSYNC)

En macOS el confinamiento del proceso vmm se reduce al entitlement y al grupo de procesos.

Confinamiento en Linux​

Antes de entregar el proceso a la libkrun, el vmm de una VM de job (root en disco, sin red) aplica:

MecanismoLo que queda permitidoLo que queda denegado
Landlock (archivos)lectura del disco root y de las bibliotecas ya cargadas, /proc, /sys; escritura en la consola y en el directorio propio de la VM (run/<id>/); /dev/kvm con ioctltodo el resto del sistema de archivos
Landlock (red, ABI ≥ 4)nadatodo bind y connect TCP (y UDP en la ABI 10)
Landlock (alcance, ABI ≥ 6)—señales y sockets abstractos fuera del propio proceso
Landlock (socket Unix, ABI ≥ 9)el socket de la propia VMconnect al socket de otra VM
seccompel restoexecve, ptrace, mount, unshare/setns, módulos del kernel, kexec, bpf, perf_event_open, llavero, reloj, swap, reboot, userfaultfd y similares (EPERM)

La libkrunfw se carga antes de Landlock, porque la libkrun solo la abre dentro de krun_start_enter y ese open fallaría después del confinamiento. El seccomp es una denylist, y no una allowlist, a propósito: la allowlist exigiría medir cada syscall de la libkrun en un Linux con KVM, y una que faltara tumbaría todas las VMs.

Medido en Ubuntu 26.04 (kernel 7.0, aarch64, Landlock ABI 8), con el propio proceso confinado: lectura de archivos fuera de la política, escritura junto al disco, TCP, execve y personality denegados; lectura del disco y creación del propio socket permitidas. Sin el confinamiento, la misma sonda lo consigue todo (prueba de fallo).

No medido: una microVM completa bajo el confinamiento en un Linux con KVM (la VM de prueba no tiene /dev/kvm).

Brecha conocida: con Landlock ABI < 9 el kernel no controla connect a un socket Unix por ruta; el proceso de una VM todavía alcanza el socket de otra. rc doctor lo advierte. RUNNER_SANDBOX=off desactiva el confinamiento para diagnóstico, con un aviso en stderr.

Máquinas persistentes​

Una máquina renuncia a parte del aislamiento de un job (guarda estado y puede tener red), y cada apertura está declarada y tiene un mínimo:

CapaCómo
red desactivada por defectosin --net (o net, o -p solo), la máquina no tiene tarjeta de red; con -p sin --net, el host alcanza el puerto, pero el guest no sale
política de salidagateway en espacio de usuario en el propio proceso vmm, sin TSI: allow-host por nombre, CIDR, --block-private; un nombre fuera de la lista recibe NXDOMAIN; una IP solo vale si vino de un nombre permitido. Consulte Red y volúmenes
mínimo que ninguna regla abremetadata de la nube y todo el link-local; el loopback del host solo con --allow-cidr 127.0.0.1, nunca por una IP aprendida en DNS (DNS rebinding)
rechazos registradostoda salida denegada va a egress-denials.log en el directorio de la máquina
credencial sin lecturael guest ve un placeholder; el gateway lo cambia por el valor real solo en el TLS del host del binding, con una CA propia de la máquina (name constraints). Un placeholder en la URL se rechaza con 403. Consulte Credenciales
secretos--secret-env/--secret-file se leen en el start y se envían al guest solo en memoria; no quedan en el registro ni en el disco de la máquina
agente SSHel guest usa las claves del host mediante un socket reenviado; la clave privada no entra en la VM
volumen :rola protección está en el dispositivo, no solo en la opción de montaje: mount -o remount,rw por el root del guest no abre el volumen (probado)
virtualización anidada--nested se rechaza donde el host no admite anidamiento; por la API, solo con rc serve --allow-nested-virt
pantallael visor escucha solo en 127.0.0.1 del host, sin contraseña

Lo que una máquina no protege: el root del guest lee el entorno de sus propios procesos, así que un secreto entregado a la máquina está al alcance del código que se ejecuta en ella. La sustitución de credenciales tampoco impide que el servidor devuelva el valor en el cuerpo de la respuesta.

Lo que demuestra la prueba de extremo a extremo​

PropiedadPrueba
sin redla conexión a 1.1.1.1 falla dentro de la VM; con la red activada a propósito, la prueba falla
root de solo lecturaescribir en /etc falla
sin estado entre jobsel archivo escrito en /tmp por un job no existe en el job siguiente
límite de memoriaasignar 2 GiB en una VM de 512 MiB falla, sin afectar al host
timeoutel job en bucle infinito se mata y ninguna VM sobrevive

La API​

La API HTTP no tiene autenticación. El valor por defecto es escuchar solo en 127.0.0.1. No la exponga fuera de loopback sin un proxy autenticado delante.

Build de imágenes​

El pull de la imagen ocurre en el host. El resto del build se ejecuta en una VM propia, con red, porque la instalación de e2fsprogs y GNU tar y el warmup (apk add, por ejemplo) la necesitan. La red de la VM de build no llega a la VM del job. El tarball base de Alpine usado por la VM de build tiene el sha256 verificado antes de usarse. Las imágenes de lenguajes vienen de las imágenes OCI oficiales.

Pendientes​

  • validar una microVM completa bajo Landlock + seccomp en un Linux con KVM;
  • autenticación en la API (hoy delegada a un proxy);
  • aislamiento de sockets Unix entre VMs en kernels con Landlock ABI < 9.