El objetivo de esta tarea es el de configurar un sistema de instalación con PXE/TFTP que realice una instalación completa de un sistema Debian Buster de forma totalmente desatendida, pasándole todos los parámetros necesarios por “preseeding”.
La idea es configurar un entorno PXE (Preboot eXecution Environment) que nos permita arrancar e instalar el sistema operativo en máquinas a través de una red, pero nos hacen falta dos elementos esenciales, el primero de ellos, un protocolo que nos permita servir los ficheros necesarios para la instalación a la máquina cliente. Ahí es donde entra el juego el protocolo TFTP (Trivial File Transfer Protocol), que es un protocolo de transferencia muy simple semejante a una versión básica de FTP.
Además del protocolo TFTP, necesitaremos hacer uso de un servidor DHCP que permita asignar direcciones IP dentro de la red a los clientes, ya que es un requisito básico para que los clientes se puedan comunicar con el servidor. Aquí surge el primer problema, ya que en mi red doméstica tengo un servidor DHCP (el router de mi proveedor) que sirve direcciones IP a los dispositivos que existen en mi casa, por lo que configurar otro servidor DHCP dentro de la misma red puede llegar a ocasionar conflictos. Por ello, voy a hacer uso de Vagrant para crear un escenario aislado de mi red doméstica, de manera que la práctica se pueda llevar a cabo de forma totalmente segura. El fichero de configuración Vagrantfile es el siguiente:
Como se puede apreciar, el escenario de Vagrant es bastante simple, con una única máquina que hace la función de servidor, ya que la máquina cliente la crearemos manualmente para realizar la instalación por red. Tras ello, ya podremos levantar el escenario, haciendo uso de la instrucción:
A continuación comenzará a descargar el box (en caso de que no lo tuviésemos previamente) y a generar la máquina virtual con los parámetros que le hemos establecido en el Vagrantfile. Una vez que haya finalizado el proceso, nos podremos conectar a la misma ejecutando la siguiente instrucción:
Ya nos encontramos dentro de la máquina. Para verificar que las interfaces de red se han generado correctamente, ejecutaremos el comando:
Efectivamente, contamos con dos interfaces:
- eth0: Generada automáticamente por VirtualBox y conectada a un router virtual NAT, con dirección IP 10.0.2.15. A través de ella saldremos al exterior cuando sea necesario.
- eth1: Creada por nosotros y conectada a una red interna lan1, con dirección IP estática 192.168.100.5.
Tras realizar dicha comprobación, todo está listo para configurar el protocolo TFTP y el protocolo DHCP, pero la buena noticia es que existe un paquete que nos facilita ambos servicios. Se trata de dnsmasq, así que vamos a proceder a su instalación, no sin antes upgradear los paquetes instalados, ya que el box con el tiempo se va quedando desactualizado. Para ello, ejecutaremos el comando (con permisos de administrador, ejecutando el comando sudo su -
):
Una vez que hayamos instalado el paquete, tendremos que configurarlo para que realice las funciones deseadas. Para ello, vamos a modificar el fichero /etc/dnsmasq.conf, que es una plantilla de configuración totalmente comentada, ejecutando el comando:
De todas las opciones de configuración existentes, las únicas que nos interesan son las siguientes:
Ya hemos finalizado de configurar dnsmasq, por lo que acto seguido sería conveniente reiniciar el servicio para que cargue la nueva configuración, pero en este caso, nos falta un paso por realizar. El directorio que hemos especificado para el protocolo TFTP no se encuentra creado, así primero tendremos que crearlo, pues de lo contrario, dará error a la hora de cargar la nueva configuración. Para ello, ejecutamos el comando:
Tras ello, ya podemos reiniciar el servicio para que así cargue la nueva configuración. Para ello, ejecutamos el comando:
Para verificar que el servicio ha cargado correctamente la nueva configuración y se encuentra actualmente activo sin ningún tipo de error, ejecutaremos el comando:
Efectivamente, el servicio se ha reiniciado correctamente y se encuentra activo. Además, si nos fijamos en los mensajes que ha devuelto, podemos apreciar que ha configurado correctamente el rango DHCP especificado con su correspondiente tiempo de concesión y el protocolo TFTP haciendo uso del directorio /srv/tftp.
Una vez que el servicio dnsmasq está funcionando correctamente, es hora de descargar los ficheros necesarios en dicho directorio (/srv/tftp). Tras una detallada búsqueda, conseguí encontrar el comprimido que contiene los ficheros necesarios para la instalación por red. En este caso, son los ficheros para Debian Buster, aunque si quisiésemos cualquier otra distribución, bastaría con cambiar buster en la URL por la distribución deseada.
Dado que los ficheros los queremos descargar dentro de /srv/tftp, primero tendremos que movernos a dicho directorio. Para ello, ejecutaremos el comando:
Una vez dentro del mismo, ya podremos hacer uso de wget
para descargar el fichero en cuestión:
Al parecer, el fichero ya ha terminado de descargarse, así que para verificarlo, vamos a hacer uso del comando ls
:
Efectivamente, el fichero está descargado en dicho directorio, pero los ficheros no son accesibles todavía, pues se encuentran comprimidos. Para descomprimir el .tar.gz y posteriormente eliminar el fichero comprimido (ya que no nos hará falta), ejecutamos el comando:
- -z: Utiliza gzip para descomprimir el fichero.
- -x: Indica a tar que desempaquete el fichero.
- -f: Indica a tar que el siguiente argumento es el nombre del fichero .tar.gz.
Para verificar que se ha descomprimido correctamente y que el fichero comprimido se ha eliminado de dicho directorio, haremos uso de ls -l
:
Como se puede apreciar, el fichero se ha descomprimido correctamente y tras ello, ha sido eliminado. Además, ya existe el fichero pxelinux.0 que los clientes van a usar para arrancar a través de la red.
Llegados a este punto, ya tenemos una configuración totalmente funcional que permitiría a la máquina arrancar a través de la red, pero todavía nos falta llevar a cabo la configuración para automatizar dicha instalación. De igual forma, vamos a proceder a crear una máquina virtual que inicie a través de la red para asegurarnos que hasta ahora, no hemos cometido ningún error.
En este caso, he creado una máquina en VirtualBox sin ningún tipo de imagen ISO anexada, especificando el siguiente orden de arranque:
Gracias al mismo, primero tratará de arrancar usando el disco duro, pero al no tener nada dentro, pasará a la siguiente opción, la red. Si el orden fuese al revés (primero red y luego disco duro), tras terminar la instalación tendríamos que volver a invertir el orden de arranque ya que volverá a intentar iniciar desde la red.
La otra parte de la configuración muy importante es la configuración de red. Tendremos que asegurarnos que el adaptador de red de la máquina virtual pertenezca a la red interna creada con anterioridad, “lan1”, ya que de lo contrario, no podrá arrancar desde la red gracias al servidor previamente configurado.
Una vez realizadas ambas configuraciones, podremos aplicar los cambios y tratar de iniciar la máquina para ver si inicia correctamente.
Como se puede apreciar en el mensaje mostrado, la interfaz de red se ha levantado correctamente y está esperando a recibir una dirección por DHCP.
Tras esperar un par de segundos, la máquina virtual ha sido capaz de cargar el instalador por red de Debian, por lo que podemos concluir que hasta ahora, todo funciona correctamente, por lo que ya hemos realizado la mitad del recorrido.
Tras ello, nos queda configurar el preseeding para automatizar dicha instalación. Al fin y al cabo, lo que necesitamos el almacenar las respuestas a las preguntas que nos hace el instalador de Debian para posteriormente usarlo en las máquinas clientes. En este caso, he decidido usar un servidor web (apache2) para almacenar un fichero con dichas respuestas, aunque también sería posible introducir dichas respuestas en la propia imagen de arranque a través del initrd.gz, pero me parecía más interesante hacerlo con un servidor HTTP. Para instalar apache2 ejecutaremos el comando:
Una vez instalado, es importante mencionar que el DocumentRoot del VirtualHost que trae apache2 configurado por defecto se encuentra en /var/www/html, en castellano, esto significa que el directorio en el que debemos introducir los ficheros que deseamos servir es /var/www/html, por lo que dentro del mismo, debemos generar dicho fichero de configuración. En mi caso, el nombre que he decidido usar es preseed.txt. Para ello, ejecutamos el comando:
El fichero vacío ya se encuentra generado, así que ahora debemos introducir la configuración deseada en el mismo. Para ello, Debian nos ofrece una plantilla que podremos adaptar a nuestro gusto. Esta configuración es bastante personal, por lo que cada uno debe adaptarla a sus necesidades. En mi caso, copié la plantilla dentro de dicho fichero para posteriormente modificarlo, haciendo uso del comando:
En mi caso, he dejado la plantilla tal y como viene por defecto, exceptuando las siguientes modificaciones:
La configuración de preseeding ya está completada, pero todavía queda un factor muy importante a tener en cuenta. La máquina cliente tiene que ser capaz de salir al exterior para descargar la paquetería durante la instalación, ya que nosotros lo único que tenemos en el servidor es el propio instalador y el fichero de preseeding, no los paquetes necesarios. Dado que tenemos que mantener en todo momento la conectividad con el servidor (pues tendrá que hacer uso del fichero de preseeding alojado en el mismo), no podemos utilizar una interfaz de red secundaria con conectividad al exterior, ya que no tendría conectividad con el servidor. Para ello, mi solución ha sido configurar NAT en el servidor, de manera que la máquina cliente (cuya puerta de enlace predeterminada es la dirección del servidor, dado que así lo ha configurado dnsmasq) sea capaz de salir al exterior a través del servidor.
Lo primero que tendremos que hacer será activar el bit de forward, permitiendo así que los paquetes puedan pasar de una interfaz a otra en el servidor, ya que por defecto, en Debian viene deshabilitado por razones de seguridad. Para ello, ejecutamos el comando:
Esta modificación no sería persistente, ya que se encuentra en memoria, por lo que si quisiésemos hacer que perdure en el tiempo, tendríamos que modificar el fichero /etc/sysctl.conf y asignar el valor 1
a la instrucción net.ipv4.ip_forward
.
Tras ello, tendremos que instalar el paquete que nos va a permitir hacer NAT. En este caso se trata de nftables, un cortafuegos que consta con dicha funcionalidad. Para ello, ejecutamos el comando:
Cuando el paquete haya terminado de instalarse, tendremos que arrancar y habilitar el demonio, de manera que se inicie cada vez que se arranque la máquina y lea la configuración almacenada en el fichero (lo veremos a continuación). Para ello, ejecutamos los comandos:
Una vez habilitado y arrancado el demonio, tendremos que crear una nueva tabla en nftables de nombre nat. Para ello, ejecutamos el comando:
Para verificar que la creación de dicha tabla se ha realizado correctamente, ejecutamos el comando:
Efectivamente, así ha sido. Dentro de dicha tabla tendremos que crear la cadena POSTROUTING, es decir, aquella que permite modificar paquetes justo antes de que salgan del equipo, permitiendo por tanto hacer Source NAT (SNAT), para posteriormente alojar dentro de la misma, la regla que necesitamos. Para crear dicha cadena ejecutaremos el comando:
En este caso, hemos especificado que esta cadena sea poco prioritaria (a mayor sea el número, menor es la prioridad), de manera que si en un futuro quisiéramos añadir una cadena PREROUTING, es decir, aquella que permite modificar paquetes entrantes antes de que se tome una decisión de enrutamiento, permitiendo por tanto hacer Destination NAT (DNAT), bastaría con indicarle a esta última una prioridad más alta, de manera que las reglas albergadas en el interior de la cadena PREROUTING se ejecuten antes que las de la cadena POSTROUTING.
De nuevo, vamos a verificar que dicha cadena se ha generado correctamente, ejecutando para ello el comando:
Nos queda un último paso, añadir la regla necesaria a la cadena POSTROUTING, de manera que nos permita hacer SNAT dinámico (masquerade), pues la dirección IP “pública” del servidor no siempre va a ser la misma, al haber sido asignada por DHCP a través del router virtual NAT de VirtualBox. Para ello, ejecutaremos el comando:
En este caso, hemos especificado que se trata de una regla para la cadena postrouting (pues debe aplicarse justo antes de salir de la máquina), además, la interfaz (oifname) que debemos introducir será aquella por la que van a salir los paquetes, es decir, la que está conectada a Internet (aunque sea haciendo doble NAT, no es algo relevante en estas circunstancias), en este caso, eth0, además de indicar que esta regla la vamos a aplicar a todos aquellos paquetes que provengan de la red (ip saddr) 192.168.100.0/24. Además, para aquellos paquetes que cumplan dicha regla, vamos a contarlos (counter) y a enmascararlos, pues la IP “pública” del router es dinámica (masquerade).
Por último, vamos a volver a verificar que dicha regla se ha añadido correctamente, ejecutando para ello el comando:
Efectivamente, así ha sido. Al igual que pasaba con el bit de forward, esta configuración se encuentra cargada en memoria, por lo que para conseguir que perdure en el tiempo, vamos a ejecutar el comando:
Gracias a ello, habremos guardado la configuración en un fichero en /etc/ de nombre nftables.conf que se importará de forma automática cuando reiniciemos la máquina gracias al daemon que se encuentra habilitado. En caso de que no cargase de nuevo la configuración, podríamos hacerlo manualmente con el comando nft -f /etc/nftables.conf
.
Una vez que se ha configurado NAT en el servidor, ya está todo listo para realizar la instalación. Para ello, volveremos a arrancar la máquina cliente, pero tenemos que realizar un paso muy importante, leer el fichero de preseeding, ya que en caso de simplemente pulsar en “Install”, va a realizar una instalación común de forma interactiva. Para ello, cuando cargue el instalador, pulsaremos en ESC para que nos abra una prompt. Tras ello, tendremos que escribir una instrucción de la siguiente forma:
En este caso, mi instrucción queda de la forma:
Tras cargar la instrucción, la instalación comenzará a realizarse de forma totalmente automatizada sin preguntar nada, ya que todas las instrucciones que necesita las va a leer de dicho fichero.
Después de algunos minutos, la instalación finalizará y por tanto, la máquina virtual se reiniciará. Dado que en el momento de la creación de la máquina virtual configuramos correctamente el orden de arranque, ahora arrancará desde el disco duro, de manera que nos aparecerá el login al sistema y podremos acceder con las credenciales anteriormente especificadas:
Como se puede apreciar, estamos usando la ventana que nos proporciona VirtualBox para ver la máquina virtual, así que vamos a ir un paso más allá y nos vamos a conectar por SSH a la VM, pero para ello, necesitamos saber la dirección IP que ha obtenido por DHCP, ejecutando para ello el comando ip a
:
Como se puede apreciar, la interfaz de red que está conectada a la red interna es la de nombre “enp0s3”, con dirección IP 192.168.100.92, así que volveremos a la shell de la máquina virtual que actúa como servidor (muy importante realizar la conexión SSH desde el servidor, ya que desde la máquina anfitriona no es posible, al estar usando un doble NAT en el que los puertos para hacer DNAT no se encuentran “abiertos”). Para ello, ejecutaremos el comando:
Ya nos encontramos dentro de la máquina cliente con un Debian instalado y totalmente funcional. Para finalizar la tarea, he decidido llevar a cabo algunas pruebas para demostrar que todo lo que hemos configurado en el fichero de preseeding realmente ha funcionado, así que lo primero que me gustaría demostrar son las contraseñas. Para ello, he creado un pequeño programa en Python haciendo uso del módulo crypt que nos servirá para encriptar una contraseña y compararla con la existente en el fichero /etc/shadow, que como todos sabemos, contiene un campo con la contraseña cifrada del usuario.
Lo primero que haremos será ver el contenido de dicho fichero, para obtener la contraseña cifrada para su posterior comparación. Para ello, haremos uso del comando:
Como se puede apreciar, aquí tenemos (entre otras cosas), las contraseñas cifradas de alvaro y root. El programa que he creado es el siguiente:
No voy a entrar a explicar su funcionamiento de forma detallada ya que no es el objetivo de este post, pero en pocas palabras, lo que hace, tal y como he mencionado antes, es encriptar una contraseña que le pasemos por teclado y comparar que el resultado de dicha encriptación es igual al almacenado en dicho fichero. Si coinciden, significa que la contraseña es la misma, si no lo hace, significa que las contraseñas son diferentes.
Primero lo comprobaré con el usuario alvaro, introduciendole al programa la línea completa del /etc/shadow referente (el propio programa se encargará de separar la contraseña del resto de campos) y la contraseña en claro, que en este caso es prueba123123.
Como se puede apreciar, nos ha devuelto un mensaje indicando que la contraseña es correcta. Para verificar que el programa realmente funciona, voy a volver a hacer la misma prueba pero esta vez, introduciendo una contraseña incorrecta a propósito.
Como era de esperar, ahora ha devuelto un mensaje informando que no es correcta. Volvemos a repetir las mismas pruebas con el usuario root, cuya contraseña es r00tme.
Como se puede apreciar, nos ha devuelto un mensaje indicando que la contraseña es correcta. Una vez más, vamos a comprobar que no funciona si introducimos una contraseña errónea.
Genial, ya se ha demostrado que el funcionamiento de las contraseñas es el correcto, así que vamos a pasar a lo siguiente, el almacenamiento, para ello, haremos uso de los comandos lsblk
y lsblk -f
:
Como se puede apreciar en el esquema de particionado, tenemos un disco duro virtual de nombre sda con una capacidad de 20G, que fue la asignada en el momento de la creación, que cuenta con 3 particiones a su vez, siendo la primera de ellas (sda1) una ext2 con una capacidad de 243M pensada para alojar el /boot, por lo que el GRUB se ha instalado en dicha ubicación gracias a la instrucción que especificamos en el fichero de preseeding. Por último, la tercera partición (sda3) es un dispositivo de bloques con una capacidad de 19.8G perteneciente a un grupo de volúmenes LVM.
A su vez, dicho grupo de volúmenes se ha usado para crear 3 volúmenes lógicos, uno para el sistema de ficheros raíz con una capacidad de 6.9G, otro para el área de intercambio, con una capacidad de alrededor de 1G y otro para el /home, tal y como especificamos en el fichero de preseeding, con una capacidad de 11.9G.
Estos valores son generados automáticamente por el particionamiento guiado de LVM que incorpora Debian, aunque en caso de haberlo necesitado, se podría haber especificado con detalle el esquema de particiones deseado en el fichero de preseeding, aunque se sale del objetivo de esta tarea.
Lo siguiente que me gustaría mostrar es el sources.list para verificar que realmente ha utilizado el mirror especificado (deb.debian.org) durante la instalación y además,ha llevado a cabo las modificaciones oportunas en dicho fichero para hacerlo persistente. Para ello, ejecutaremos el comando:
Efectivamente, así ha sido, ha configurado además los repositorios deb-src para ficheros fuente, aunque no sería necesario en este caso, por lo que se podrían comentar.
Por último, me gustaría comprobar que ha instalado los dos paquetes que se especificaron (tree y dnsutils), así que para ello, haremos uso de apt policy
:
Como se puede apreciar, ambos paquetes han sido correctamente instalados, por lo que podemos concluir que la instalación se ha llevado a cabo siguiendo estrictamente los pasos indicados en el fichero de preseeding.