TrueNAS + Proxmox: arreglamos el CHAP iSCSI roto en SCALE 25.10
Llevamos el plugin de almacenamiento TrueNAS para Proxmox VE a producción: NVMe/TCP y iSCSI, snapshots ZFS, LXC y multipath. Por el camino encontramos dos bugs upstream que hacían imposible autenticar iSCSI con CHAP contra TrueNAS SCALE 25.10 — uno era código muerto. Los arreglamos y el PR está abierto.
TL;DR
Estuvimos poniendo en producción el plugin de almacenamiento de TrueNAS para Proxmox VE — el que deja que Proxmox cree, agrande y snapshotee zvols en una cabina TrueNAS por API, en vez de que alguien lo haga a mano. Funciona bien, y sobre él montamos tres series de parches propias.
Lo que más nos costó, y lo que probablemente te interese si estás en la misma: la autenticación CHAP de iSCSI no podía funcionar contra TrueNAS SCALE 25.10. No era un problema de configuración. Eran dos bugs distintos, uno de ellos código que llevaba tiempo sin ejecutarse nunca. Los arreglamos, los verificamos en vivo y el pull request está abierto en el repositorio oficial: truenas/truenas-proxmox-plugin#95. El fork completo, con todo lo demás que le hicimos, está publicado bajo GPLv3 en github.com/alfonsokuen/truenas-proxmox-plugin.
Si tu cabina tiene CHAP activado y el plugin te dice
initiator failed authorization, no estás haciendo nada mal. Sigue leyendo.
El contexto: por qué este plugin importa
Proxmox VE habla iSCSI de forma nativa, pero de la manera aburrida: tú creas el LUN en la cabina, tú lo expones, tú lo mapeas. Cuando quieres un snapshot, Proxmox no puede pedírselo a ZFS — o metes LVM por encima (y pierdes las capacidades del sistema de archivos que ya pagaste) o te vas a NFS con qcow2 (y pierdes rendimiento de bloque).
El plugin de TrueNAS cierra ese hueco: Proxmox habla con la API de la cabina y el zvol se crea, se mapea, se agranda y se snapshotea solo. Los snapshots son de ZFS: instantáneos y sin coste de espacio hasta que divergen. Y desde TrueNAS SCALE 25.10 puedes usar NVMe/TCP en vez de iSCSI, con menos latencia y menos CPU por operación.
Es software libre (GPLv3) del propio TrueNAS. Nosotros trabajamos sobre él, no en su lugar.
Los dos bugs de CHAP
CHAP es la autenticación de iSCSI: sin credenciales correctas, el target no te deja ni mirar. Es lo mínimo que quieres si el tráfico de almacenamiento comparte cableado con cualquier otra cosa.
Al activarlo, el plugin dejaba de encontrar el target. El mensaje era el clásico
initiator failed authorization — que normalmente significa “escribiste mal el secreto”. No era eso.
Bug 1 — el CHAP de discovery ahora es implícito
TrueNAS SCALE 25.10 cambió el modelo: en cuanto existe cualquier grupo de autenticación iSCSI en
la cabina, el descubrimiento de targets exige CHAP. Y el interruptor que antes permitía separar ambas
cosas (discovery_authmethod en el portal) ya no existe en la API — pedirlo devuelve un error de
campo desconocido.
El plugin configuraba las credenciales de la sesión, pero no las del descubrimiento. Resultado: la sesión nunca llegaba a intentarse, porque el paso previo moría.
Detalle que nos costó una sesión entera de diagnóstico: un grupo de autenticación huérfano — uno
que quedó de una prueba vieja y al que ningún target apunta — es suficiente para activar este
comportamiento en todo el portal. Ves el portal abierto, el target sin autenticación, la lista de
iniciadores permitida, y aun así te rechaza. Si te está pasando: busca registros iscsi/auth que ya
no use nadie.
Bug 2 — el bucle de login llevaba tiempo sin ejecutarse
Este es el que da miedo. El plugin listaba los nodos iSCSI descubiertos para iniciar sesión en cada
uno, con iscsiadm -m node -T <iqn>. Ese comando no imprime una lista: imprime el registro
completo de configuración de ese nodo. El parser esperaba líneas del tipo portal,tpgt iqn, no
encontraba ninguna, y la lista quedaba siempre vacía.
Es decir: el bucle principal de login era código muerto. Toda sesión entraba por la ruta de respaldo, que inicia sesión sin autenticación — y de paso reinicia la base de datos de descubrimiento.
Sin CHAP, esto colaba: la sesión sin autenticar funcionaba y nadie notaba nada. Con CHAP no había sesión posible. Un camino de respaldo que tapa que el camino principal jamás se ejecuta es el tipo de fallo que sobrevive años: todo se ve verde porque el error nunca se nota.
Nuestro arreglo lista todos los nodos y filtra, tolera el sufijo ,tpgt del formato real y hace que
el respaldo también sepa de CHAP. Ambos bugs existen igual en el código oficial, con las mismas
líneas exactas — por eso el PR va contra master y no contra nuestra copia.
Cómo lo verificamos: sesión CHAP completa desde un cliente en limpio (sin restos de sesiones previas) en dos servidores distintos, VM real con entrada/salida sobre el LUN, y el caso negativo obligatorio — intentar sesión sin credenciales y ver que el target la rechaza. Un login que funciona no prueba que la autenticación esté activa; solo lo prueba ver el rechazo.
La otra serie: qué pasa cuando la cabina se cae
El segundo frente no era un bug, sino un comportamiento incómodo bajo fallo.
Proxmox sondea cada almacenamiento periódicamente para pintarte el estado. Cuando la cabina no responde, cada sondeo se queda esperando a que expire el tiempo de espera de la API. Con varios almacenamientos configurados, el demonio de estadísticas se atasca y el panel entero se pone lento, aunque las máquinas virtuales sigan corriendo tan tranquilas.
Lo interesante es dónde estaba el coste. Hay un cortacircuitos que protege la consulta de estado, pero Proxmox llama a “activar el almacenamiento” antes que a “consultar estado” en cada barrido — y esa activación hacía su propia consulta a la API, sin protección. Medido: 15 de 15 sondeos tardando unos 10 segundos cada uno, con el cortacircuitos marcando “todo bien”.
La solución no fue saltarse ese paso — es el único que vuelve a publicar los portales cuando la cabina revive, así que saltarlo convierte un corte temporal en uno permanente. Lo que hicimos fue acotarlo: mientras hay un fallo reciente, ese trabajo tiene 2 segundos de presupuesto en vez del tiempo de espera completo. Se sigue intentando; simplemente deja de secuestrar el sondeo.
Dos lecciones que nos llevamos, y que valen fuera de este plugin:
- Un mecanismo de supresión sin traza visible se diagnostica como averiado. Nuestro cortacircuitos funcionaba, pero su única señal estaba en un nivel de registro que por defecto se descarta. Parecía roto. Casi lo “arreglamos” a peor.
- No clasifiques errores buscando palabras dentro de un mensaje ajeno. Un error de integridad de datos puede arrastrar la palabra “timeout” en su traza y hacerse pasar por un corte de red — y entonces archivas en silencio justo la única pista que tenías. Ahora clasificamos por las frases que genera el propio motor de reintentos, ancladas al inicio del mensaje.
Verificado con cortes de verdad: apagón controlado de la API con carga viva, primer fallo ruidoso, luego silencio acotado, y recuperación limpia al volver — sin tormenta de reintentos ni de alertas.
Qué probamos, una por una
El README del plugin promete dieciséis capacidades. Las recorrimos las dieciséis con evidencia, porque una matriz de funciones sin pruebas es una lista de deseos. Estas son las que más se notan:
| Capacidad | Cómo quedó |
|---|---|
| Doble transporte (iSCSI y NVMe/TCP) | Ambos activos y probados por separado |
| Snapshots ZFS | Instantáneos, con rollback verificado por hash |
| Snapshot en vivo con RAM | Disco + estado de memoria; la VM reanuda tras el rollback |
| Almacenamiento para contenedores LXC | Raíz en el plugin, respaldo en caliente y restauración idéntica |
| Clúster | Migración en vivo entre tres nodos, 7-8 s por salto, hash idéntico |
| Multipath | Apagón de un camino con escritura viva: continúa por el otro y se reengancha en 5 s |
| Redimensionar | Solo crecer; encoger se rechaza con un mensaje que dice cuánto falta |
| Aprovisionamiento fino y compresión | 8 GB reservados que ocupan 0 en disco hasta que se escriben |
| CHAP | Funcional tras nuestros dos parches |
| Validación de configuración | Rechaza configuraciones inválidas sin dejar el archivo a medias |
| Recuperación de errores | La serie de resiliencia de arriba |
| Tamaño de bloque | Valor explícito respetado en ambos transportes |
También montamos NTFS sobre un volumen NVMe/TCP, con snapshot y rollback, para confirmar que al plugin le da igual lo que el sistema operativo invitado escriba dentro: 201 archivos, rollback exacto, copia y restauración byte a byte.
Suite de pruebas: 332 casos automáticos, más pruebas de mutación (introducir errores a propósito en el código para comprobar que alguna prueba lo caza). En tres rondas de revisión, los mutantes que sobrevivieron se convirtieron en pruebas nuevas.
Rendimiento: el número honesto
Lo que medimos en el laboratorio: 26.600 IOPS de lectura aleatoria 4K, 12.300 de escritura aleatoria, y unos 107 MB/s secuenciales.
Ese último número delata la medición: son los ~107 MB/s de un enlace de 1 Gb. No medimos el plugin, medimos el cable. Y está bien que así sea, porque explica de dónde viene el rendimiento real: una vez el volumen está mapeado, los datos no pasan por el plugin. Van por el driver NVMe/TCP del kernel, exactamente igual que un LUN configurado a mano. El plugin trabaja en el plano de control — crear, mapear, snapshotear, autenticar — no en el de datos.
Por eso no hicimos una comparativa contra nuestra cabina productiva: no habría medido nada nuevo, y habría metido carga sintética en un sistema con clientes encima.
En qué estado está
El plugin corre en un clúster Proxmox de tres nodos contra el almacenamiento productivo, en modo cerrado: lista blanca de iniciadores y autenticación DH-CHAP sobre NVMe/TCP. La cabina de laboratorio donde hicimos las pruebas destructivas ya se desmanteló — cumplió su función y no tiene sentido dejar credenciales vivas de algo que no vuelve a usarse.
La primera ola de máquinas ya está migrada. Cuatro cargas de bajo riesgo, elegidas a propósito para estrenar el camino. La más interesante se movió sin apagarla: 80 GB de disco copiados en 34 segundos mientras el servidor seguía atendiendo, con tres semanas de tiempo activo ininterrumpido antes y después. Los discos de origen siguen ahí, marcados como no usados, hasta que cierre el periodo de observación — liberar el espacio antiguo es lo último que se hace, no lo primero.
Las olas siguientes llevan una puerta de control antes de cada una, y los volúmenes grandes van al final. Mover el disco de una máquina que está sirviendo a clientes no es una operación que merezca prisa, aunque ya sepamos que funciona.
Los dos parches de CHAP están propuestos al proyecto oficial en el PR #95. Si el mantenedor los acepta, cualquiera que use TrueNAS SCALE 25.10 con Proxmox y CHAP se ahorra el diagnóstico completo. Nos parece el mejor destino posible para ese trabajo.
Y el fork entero está publicado, con la misma licencia que el original (GPLv3):
github.com/alfonsokuen/truenas-proxmox-plugin.
Ahí están las tres series de parches, la batería de pruebas, las herramientas de diagnóstico y
provocación de fallos que usamos, y un DIVERGENCE-IDK.md que explica qué cambia respecto al
oficial y por qué. Si solo te interesa el arreglo de CHAP, está aislado en el PR; si quieres lo
demás, está entero ahí — incluido lo que no acabamos de cerrar, anotado como tal.
Actualización — 19 de septiembre de 2026
Casi un mes después, lo que ha pasado con esto, para bien y para mal.
Sigue en producción, y ha crecido. Los tres nodos llevan desde entonces sirviendo sus máquinas
desde la cabina por NVMe/TCP, y el fork va por su decimonovena build. Lo que se le ha añadido desde
que escribimos lo de arriba: reconocimiento de portales por nombre DNS en PVE 9, reintentos acotados
para que pvestatd no se quede colgado cuando la cabina no contesta, aceptación de los nombres de
campo antiguos para que una configuración vieja no deje de funcionar al actualizar, y un rollback de
snapshot que ya no es recursivo ni edita la configuración de la VM a mano.
Lo más pedido: ver en Proxmox los snapshots hechos desde TrueNAS. Si una tarea periódica de la
cabina fotografía el zvol de una VM, Proxmox no se entera: el snapshot no sale en la pestaña
correspondiente y, si es más reciente que el último que hiciste desde Proxmox, qm rollback se
niega con «not most recent snapshot» sin darte forma de ver ni borrar el que estorba. Ahora hay un
comando que los importa a la configuración del invitado —con la fecha real de creación en ZFS como
marca de tiempo—, para máquinas virtuales y para contenedores:
truenas-proxmox-manage import-snapshots <vmid> --dry-run
Está enviado al proyecto oficial en el PR #110. Si lo aceptan, dejará de hacer falta ningún fork para esto, que es exactamente lo que queremos.
Un hallazgo que no es del plugin y que conviene que sepas. Persiguiendo otra cosa nos topamos
con que mover o migrar un disco a cualquier almacenamiento de bloques —no solo este— ejecuta
qemu-img convert con la caché por defecto. Si un fallo de escritura llega después de que el dato
ya esté en la caché de página, el error se descarta: la copia termina, qemu-img sale con código 0,
la tarea dice OK y al destino le faltan datos. Lo reprodujimos con dm-flakey sobre un dispositivo
de bucle: con la caché por defecto, 1,9 de 2 GiB no llegaron nunca al disco y nada falló; la misma
copia con -t none aborta con error y no pierde nada. Está reportado con la receta completa en el
bug 8054 de Proxmox. No decimos que la
solución sea trivial, porque -t none es más lento y el compromiso es real; decimos que hoy un
almacenamiento que descarta escrituras informa de éxito, y eso conviene saberlo antes de fiarse de
una migración que dijo OK.
Y lo que no salió bien. El install.sh que enlaza el README de la rama principal del proyecto
oficial pertenece a una generación anterior a las betas actuales: pide la clave de API con los
nombres de campo viejos y el asistente falla con broker: scfg missing api_host/api_key aunque la
red y la clave sean correctas. Lo reportamos como
issue #107 y resulta que no éramos
los únicos: otros dos forks del proyecto habían parcheado lo mismo por su cuenta.
Instalar nuestra build ya no requiere clonar ni compilar nada:
curl -sSL https://github.com/alfonsokuen/truenas-proxmox-plugin/releases/latest/download/install-idk.sh | bash
Descarga el .deb de la release, comprueba su sha256 antes de instalar y admite --dry-run para
ver qué haría sin tocar el sistema. Los paquetes, con sus sumas y las notas de cada versión, están
en las releases del repositorio.
Si estás montando esto
Tres cosas que nos habría gustado saber antes:
- Un grupo de autenticación iSCSI huérfano cambia el comportamiento de todo el portal. Antes de perseguir credenciales, revisa si quedó alguno de una prueba anterior.
- Prueba siempre el caso negativo. Que la sesión conecte no prueba que CHAP esté activo; solo lo prueba ver un intento sin credenciales siendo rechazado.
- La resiliencia se prueba apagando cosas. Un cortacircuitos que nunca viste dispararse no es un cortacircuitos, es una suposición.
¿Tienes Proxmox y una cabina TrueNAS, o estás evaluando salir de un almacenamiento compartido que se quedó corto? Esto es exactamente lo que hacemos: diseño, migración en caliente y operación de infraestructura virtualizada. Escríbenos y lo vemos con tu caso concreto.
¿Vas a instalarlo? El procedimiento nodo por nodo, con los tres errores que dejan el plugin a medias, está en la guía de instalación del plugin en un clúster.
Si lo que necesitas es almacenamiento centralizado en tu oficina sin operar nada de esto tú mismo: NAS on-site. Y si la infraestructura completa te queda grande, cloud privado y DevOps.