Blog ·
«Sin privilegios» se mide, no se configura
Restaurar la copia de un contenedor empieza por leer la configuración del archivo, porque el valor seguro de un indicador no se puede enviar a ciegas.
Por Provibr
Restaurar un contenedor en Proxmox es la misma llamada de API que crear uno. Se hace un post al endpoint de contenedores de un nodo, se le entrega el archivo y se añade el indicador que convierte la creación en una restauración. El camino de la máquina virtual ni siquiera tiene ese indicador: ahí el archivo en sí es la restauración. Esa simetría es cómoda y esconde una trampa, porque uno de los campos de esa llamada describe una propiedad de seguridad que ya tiene un valor dentro del archivo.
Enviar el valor seguro puede romper el contenedor. Omitir el campo puede meter un contenedor privilegiado en una flota que se supone que no tiene ninguno. La única jugada correcta es leer primero el archivo y actuar después según lo que diga.
Dos endpoints que se parecen
El camino de la máquina virtual y el del contenedor se diferencian en más sitios de los que sugiere el nombre:
| Máquina virtual | Contenedor | |
|---|---|---|
| Endpoint | POST /nodes/{node}/qemu | POST /nodes/{node}/lxc |
| El archivo | archive=<volid> | ostemplate=<volid> más restore=1 |
| El nombre | name= | hostname= |
| El almacenamiento de destino debe aceptar | images | rootdir |
| Tipo de tarea en el identificador de tarea | qmrestore | vzrestore |
Dos de esas filas se equivocan con facilidad de una manera que produce un error confuso. Proxmox llama plantilla de sistema operativo al archivo de un contenedor, porque el mismo parámetro acepta las dos cosas, y en una máquina virtual la presencia de archive es por sí misma la restauración, de modo que no existe ningún indicador aparte. Y images y rootdir son dos tipos de contenido distintos: un almacenamiento puede ofrecer uno sin el otro, así que exigir images en todas partes rechaza almacenamiento de contenedores perfectamente válido, mientras que no comprobar nada le entrega al cliente una frase sobre directorios de contenedor que no significa nada para él.
Cuál de las dos clases es esto se decide una sola vez. Se lee del nombre de fichero en el momento en que el archivo se lista, se conserva junto al punto de restauración y viaja con la instrucción; el agente elige su endpoint a partir de esa palabra y no tiene ningún camino de reserva. Tiene que ser en ese orden, porque un 404 de Proxmox no distingue un invitado de la otra clase de un invitado que ya no existe. Probar el otro endpoint tras un 404 permitiría dirigirse a una máquina virtual borrada como si fuera un contenedor y, en un clúster donde ese número se haya vuelto a emitir desde entonces, se toca al invitado equivocado.
Hay un indicador que no se envía nunca, y se llama force. Esta es la restauración aditiva: aparece una máquina junto a la original y la original sigue funcionando. force es el indicador con el que Proxmox sobrescribe un invitado existente, existe en los dos endpoints y pertenece a la otra clase de restauración, que tiene su propio mensaje, su propio permiso y una confirmación que hay que teclear. Dejarlo permanentemente fuera de este camino de código sale más barato que acordarse de no ponerlo.
Hay un indicador que se envía siempre, y se llama unique. Sin él, el invitado restaurado hereda las direcciones de hardware de las tarjetas de red originales, y dos máquinas que responden a la misma dirección son una caída en la máquina que se estaba teniendo cuidado de no tocar.
Tres negativas antes de que se escriba nada
Cada comprobación previa existe porque la alternativa es una cita de Proxmox sobre la que un cliente no puede actuar. El archivo tiene que seguir estando ahí, porque la plataforma lo elige de un índice que por construcción es una observación del pasado. El número de destino tiene que estar libre, y libre tiene que significar libre en el hipervisor y no libre en nuestra propia administración: la plataforma reparte dentro del rango que gestiona, así que un invitado que alguien creó a mano es del todo real y del todo ausente de cualquier registro nuestro. Y el almacenamiento de destino tiene que aceptar el tipo de contenido correcto.
Para un contenedor hay una cuarta, y es de la que trata este artículo.
Tres cosas que se pueden hacer con un archivo privilegiado, y dos están mal en silencio
Los contenedores sin privilegios son el valor por defecto seguro, y la plataforma no tiene manera de expresar ninguna otra cosa. Así que cuando resulta que un archivo contiene un contenedor privilegiado, el agente tiene exactamente tres opciones.
La primera es enviar de todos modos el indicador de sin privilegios. Proxmox aplica entonces una reasignación de identidades sobre el sistema de archivos restaurado; su propia descripción del parámetro dice que el campo no debería modificarse a mano. La restauración sale bien, la tarea informa de que todo va correctamente, y el contenedor no funciona.
La segunda es omitir el campo. Proxmox toma entonces el valor del archivo, y un contenedor privilegiado entra en la flota: uno que comparte el kernel del host con privilegios de root, que es justo lo que la política existe para evitar.
La tercera es negarse, con una frase que dice qué hay en el archivo. Eso es lo que ocurre, y ocurre antes de que se escriba nada. No se envía ninguna petición de crear el contenedor.
Leer la configuración que hay dentro del archivo
La medición es una sola llamada al endpoint de extracción del archivo. Merece la pena conocer tres detalles.
La respuesta es texto y no JSON. Es el fichero de configuración del contenedor, una clave y un valor por línea, exactamente como está en el disco, así que necesita su propio analizador pequeño junto al que se ocupa del JSON de la API.
Y la ausencia de la clave significa lo contrario de lo que suele significar una ausencia. Proxmox escribe la línea de sin privilegios solo cuando es verdadera. Una configuración sin ella describe un contenedor privilegiado. Cualquier lector que trate «no encontrado» como «desconocido, probablemente bien» deja pasar justamente el caso para el que se construyó la comprobación, así que la comparación es generosa con lo que acepta como verdadero y rechaza todo lo demás.
Si la configuración no se puede leer en absoluto, la restauración también se rechaza, y la cita de Proxmox viaja de vuelta con ella. Eso suele ser un mensaje de permisos, porque el endpoint de extracción quiere derechos de copia de seguridad sobre el invitado y derechos de asignación sobre el almacenamiento de copias. Fallar en cerrado aquí tiene un coste real: un token de API al que le falta un permiso no puede restaurar un contenedor, y el motivo es una comprobación y no una limitación. Sigue siendo el lado correcto hacia el que fallar, porque la pregunta abierta que hay detrás tiene dos respuestas equivocadas en silencio.
La misma lectura responde a una segunda pregunta
La configuración que hay dentro del archivo dice también qué puntos de montaje formaron parte de él, y las reglas de Proxmox ahí no son las que la gente espera:
| Qué dice la configuración | ¿En el archivo? | Por qué |
|---|---|---|
| El sistema de archivos raíz | Siempre | No tiene opción de copia de seguridad en absoluto; el campo no está en su esquema |
| Un volumen gestionado con la opción de copia activada | Sí | Habilitado explícitamente |
| Un volumen gestionado sin ella | No | La opción tiene que estar activada para que un punto de montaje se incluya |
| Un directorio del host | No | El contenido de los bind mounts no se copia |
| Un dispositivo de bloques del host | No | Lo mismo, para los puntos de montaje de dispositivo |
La tercera fila es la peligrosa. En el disco de una máquina virtual, excluir es la excepción e incluir es lo predeterminado; en un punto de montaje de contenedor es al revés. Invertirlo produce una tabla de cobertura que dice «está en la copia de seguridad» sobre la configuración más común que existe, lo cual es peor que no tener tabla, porque es una promesa.
Las secciones de instantánea de la configuración se saltan. Si no, un contenedor con cinco instantáneas aportaría cinco versiones del mismo sistema de archivos raíz, cada una de un momento arbitrario.
Una sola llamada sirve para las dos preguntas en lugar de dos, porque dos llamadas podrían describir dos archivos distintos. Y el veredicto se calcula en la plataforma, no en el agente: el agente informa de las líneas tal como las encontró, y no hay ningún campo en el que pudiera afirmar cobertura completa aunque quisiera. Demostrablemente fuera de la copia de seguridad gana a no medido, así que un contenedor con un bind mount y una línea ilegible es parcial y no desconocido, y la línea ilegible conserva su propia palabra en la tabla.
Hay un indicador que dice si la configuración llegó a leerse, y no es decoración. Un agente que no la pudo leer devuelve una lista vacía, y una lista vacía no se distingue de un contenedor sin puntos de montaje, salvo porque lo segundo no existe.
Cuando nadie puede decir qué ha pasado
Una restauración que empieza y luego pierde su respuesta, porque el agente se reinició o se cayó la conexión, no se puede adivinar. Hay tres fuentes de evidencia: el índice de archivos, la máquina que se suponía que iba a aparecer y el estado de la tarea de Proxmox. Solo la tercera puede establecer un fallo; las otras dos solo pueden confirmar un éxito, porque un archivo que falta y una máquina que falta encajan igual de bien con una restauración que sigue en marcha.
Ese razonamiento ocurre en la plataforma. La consulta de la tarea que hace el agente se limita a traer de vuelta la cita de Proxmox y no saca ninguna conclusión propia, porque decidir qué suman las tres fuentes exige tener la ejecución y la intención de la que partió una al lado de la otra, y solo la plataforma tiene las dos.
No se reintenta nada eternamente. Hay un contador con un techo de ocho intentos y, con el intervalo por defecto de seis horas, eso sale a unos dos días. Los intentos en los que la tarea seguía demostrablemente en marcha no cuentan contra él, porque a una copia de seguridad grande por una red lenta se le permite tardar una noche. Cuando el contador se agota, la ejecución pasa a desconocida y nunca a fallida. Fallida sería una afirmación que nadie verificó, y completada afirmaría un archivo que nadie ha visto.
Lo que dejaríamos por escrito
- No enviar un valor para una propiedad que ya existe en la entrada. Leerla y después confirmarla o negarse.
- Averiguar qué significa una clave ausente antes de escribir el analizador. Aquí significa el valor inseguro, que es lo contrario de la convención habitual.
- Fallar en cerrado cuando la medición no está disponible, y decir en voz alta lo que eso cuesta, porque alguien se va a topar con ello.
- Hacer que una sola lectura responda a todas las preguntas que pueda. Dos llamadas pueden describir dos estados distintos.
- Poner el juicio donde está el contexto. Un observador que no puede expresar un veredicto no puede equivocarse en él.
- Dar a un reintento agotado su propia palabra terminal. Desconocido es honesto; fallido y completado son las dos afirmaciones que nadie hizo.
Para dejar claro qué es esto: las copias de seguridad de cliente están detrás de un interruptor de despliegue apagado por defecto, así que nada de esto se puede comprar hoy. Y el límite importante de la evidencia: no se ha ejecutado ninguna copia de seguridad ni ninguna restauración reales contra un hipervisor en marcha. El comportamiento de los parámetros descrito arriba se leyó en la propia fuente y la documentación de Proxmox, y el código se ejercitó contra un servidor sustituto que responde a los mismos endpoints. La llamada de extracción tampoco se ha probado contra un archivo en un servidor de copias dedicado, solo contra almacenamiento basado en ficheros.
Del trabajo de copias de seguridad en Provibr. Por qué un archivo que nadie ha restaurado todavía no es una copia de seguridad está en sin restauración no hay copia de seguridad, qué garantizan de verdad los distintos modos de copia está en los modos vzdump de Proxmox, y por qué reinstalar un contenedor es destruirlo y reconstruirlo está en reinstalar un contenedor es reemplazarlo.