¿El contenedor LXD no puede abrir /dev/net/tun? Revise la entrada unix-char
LXD debe pasar el dispositivo TUN del host al contenedor antes de que un cliente VPN pueda usarlo. Vea qué añade unix-char, qué no, y cómo comprobarlo.
LXD expone un dispositivo de caracteres del host a un contenedor añadiendo un nodo de dispositivo, no enseñando al invitado a crear uno. Si el contenedor no tiene /dev/net/tun, el host puede tener el dispositivo y el invitado aun así no poder abrirlo. La entrada unix-char es la mitad de ese camino del lado del gestor, y es deliberadamente más estrecha que un contenedor privilegiado. Por eso el nodo del host y el nodo del contenedor merecen comprobaciones separadas.
El mecanismo es lo bastante corto para tenerlo a la vista: LXD resuelve una ruta del host, crea un nodo correspondiente dentro de la instancia y añade el dispositivo al cgroup de dispositivos del contenedor. Después, el proceso del invitado necesita su propio permiso y su propia capacidad para usar el nodo en algo.
Qué cambia realmente la entrada unix-char
Un dispositivo unix-char indica a LXD que exponga un dispositivo de caracteres del host en una ruta elegida dentro del contenedor. La entrada tiene tres partes que importan: la etiqueta del dispositivo que usa el gestor, la ruta source en el host y el path que ve el invitado. Solo las dos últimas describen el dispositivo en sí. Una errata en cualquiera de ellas produce un nodo en un lugar que el cliente nunca abre.
En el host, el dispositivo suele ser /dev/net/tun. Dentro del contenedor puede mantener la misma ruta o elegir otra, pero la ruta debe coincidir con lo que espera el cliente VPN. Mantener /dev/net/tun evita un segundo problema de configuración, porque la mayoría de los clientes abren esa ruta sin preguntar.
El gestor aplica la entrada cuando la instancia arranca o cuando el dispositivo se conecta en caliente. No copia un controlador dentro del invitado ni crea una interfaz de túnel. Hace visible el nodo y permite el acceso a través del grupo de control de dispositivos del contenedor.
Declare el dispositivo en la instancia, no dentro del invitado
Añada la entrada desde el contexto de administración de LXD, indicando la instancia y una etiqueta que pueda reconocer después. Para un contenedor llamado vpnbox, el comando es:
lxc config device add vpnbox tun unix-char source=/dev/net/tun path=/dev/net/tun
lxc config device show vpnbox
La etiqueta tun es local a la configuración de la instancia. No es un nombre de interfaz y no tiene que coincidir con nada del invitado. Si un perfil ya proporciona el mismo dispositivo, añadir una segunda entrada crea un conflicto en lugar de una redundancia, así que revise primero la configuración efectiva.
Editar archivos dentro del contenedor es la capa equivocada. Un mknod en un script de arranque puede parecer que funciona en un contenedor privilegiado, y luego desaparecer al recrearlo o fallar en uno menos privilegiado. El gestor es dueño del conjunto de dispositivos de la instancia; deje que también lo sea del nodo.
Ajuste la propiedad y el modo al proceso del invitado
La entrada unix-char puede definir uid, gid y mode para el nodo dentro del contenedor. Úselos para coincidir con la cuenta que ejecuta el cliente VPN, en lugar de abrir el dispositivo a todos los usuarios o pasar el contenedor a modo privilegiado.
Visibilidad no es lo mismo que permiso. Un nodo propiedad de root con modo 600 es visible para el servicio que se ejecuta como usuario sin privilegios y aun así no puede abrirlo. Compruebe primero la cuenta del invitado, luego asigne la propiedad a esa cuenta y mantenga el modo no más amplio de lo que el cliente necesita.
El proceso también necesita la capacidad de configurar una interfaz de túnel, normalmente CAP_NET_ADMIN en el espacio de nombres de red del contenedor. Pasar el dispositivo no otorga esa capacidad, y otorgar la capacidad no proporciona el dispositivo; son requisitos independientes.
Sepa qué no otorga la entrada
Un nodo visible no es una VPN que funcione. El paquete del cliente todavía hay que instalarlo, el perfil todavía tiene que ser válido y el servicio todavía tiene que arrancar. La autenticación, las rutas y el DNS siguen siendo trabajo del cliente, y un fallo ahí no es prueba de que la entrada del dispositivo esté mal.
systemd puede añadir otra restricción. Una unit con DeviceAllow o una política de dispositivos endurecida puede denegar el acceso a un nodo que el gestor de contenedores haya proporcionado. Revise la política de dispositivos de la unit cuando el nodo existe pero el servicio informa de un error de permiso, y cambie esa política en lugar de ampliar el contenedor.
La conexión en caliente es opcional y explícita. Una entrada con required=false necesita una ruta de origen y puede aparecer o desaparecer a medida que cambia el dispositivo del host. Ese ciclo de vida encaja con hardware que va y viene; para un dispositivo TUN que la VPN necesita al arrancar, una entrada opcional puede dejar que el servicio arranque antes de que exista su requisito previo.
Verifique visibilidad, apertura e interfaz en ese orden
Primero confirme el nodo dentro del contenedor previsto. La ruta, los números mayor y menor y la propiedad deben coincidir con el dispositivo del host y con la entrada que añadió. Un nodo ausente significa que el gestor no aplicó la entrada, o la aplicó a otra instancia.
lxc exec vpnbox -- ls -l /dev/net/tun
lxc exec vpnbox -- sh -c 'test -c /dev/net/tun && echo char-device-ok'
Después confirme que la cuenta del servicio puede abrirlo. Una prueba corta con esa cuenta separa un problema de propiedad de un problema de servicio. Por último, arranque el cliente y confirme que aparece una interfaz de túnel; solo ese tercer paso demuestra todo el camino, porque los dos primeros pueden pasar mientras el perfil o la capacidad del cliente siguen mal.
Anote la instancia, la etiqueta del dispositivo, el origen en el host, la ruta en el invitado y la cuenta propietaria cuando se apruebe el cambio. Esos cinco valores son los que una recreación, migración o cambio de perfil posterior tiene que conservar, y son mucho más fáciles de comparar que un error de permiso sin contexto.