Hilarious (?)

PXE en CentOS / RHEL 7 sin Xinetd

Hay toneladas de artículos en internet acerca de como configurar un server PXE, en su mayoría innecesariamente largos y complejos. Además, todos están enfocados en usar tftp como un servicio de Xinetd. Como ustedes saben, Xinetd es un demonio que sirve paYA FUE, QUE LO EXPLIQUE WIKIPEDIA:

xinetd (eXtended InterNET Daemon), Demonio EXtendido de Internet, es un servicio o demonio que usan gran parte de los sistemas Unix dedicado a administrar la conectividad basada en Internet.

Fuente: Wikipedia

Bueno, la explicación de Wikipedia es bastante chota. No dice por ejemplo que Xinetd permite conexiones bajo demanda. Es decir, evita tener que correr demonios cuando no están recibiendo conexiones. Todo bien entonces con Xinetd, pero la realidad es que debido a sus limitaciones, se usa cada vez menos.

En sistemas operativos actuales como CentOS/RHEL7, y desde Fedora 15 (salido hace casi 4 años!), Systemd puede no solo reemplazar la funcionalidad de Xinetd, sino también proporcionar algunas ventajas propias, como centralización de logs y monitoreo de estado. Systemd merece un artículo aparte (o varios) pero antes de escribir sobre eso debo estudiar el tema en profundidad (?), y por ahora me da paja no tengo suficiente tiempo.

Volviendo a PXE, me sorprendió ver que hasta Red Hat, en su documentación oficial de RHEL7 (acá) usa Xinetd para correr tftp y proveer servicio PXE, algo completamente innecesario, como voy a demostrar a continuación, damas y caballeros (?).

 

Configurando PXE en solo 5 pasos

  1. Instalo los paquetes necesarios:
    # yum install syslinux tftp-server

    Notese que a pesar de que Xinetd es instalado como dependencia, tanto el “super-servicio” Xinetd, como su “sub-servicio” tftp van a estar deshabilitados:

    # systemctl disable xinetd.service
    rm '/etc/systemd/system/multi-user.target.wants/xinetd.service'
    #
    # grep disable /etc/xinetd.d/tftp
    disable = yes
    #
  2. Habilito el socket de tftp en Systemd. Opcionalmente, si firewalld está activo, agrego el servicio tftp en la zona apropiada:
    # systemctl start tftp.socket
    # systemctl enable tftp.socket
    ln -s '/usr/lib/systemd/system/tftp.socket' '/etc/systemd/system/sockets.target.wants/tftp.socket'
    # firewall-cmd --add-service=tftp --zone=internal --permanent
    success
    # firewall-cmd --reload
    success
  3. Al bootear por PXE, el server debe enviar al cliente algunos archivos básicos, provistos por el paquete syslinux que instalé en el primer paso. Luego deberá enviar, además, el kernel y el initial ramdisk correspondiente al sistema operativo que esté instalando (en este caso, solo CentOS7), los cuales puedo obtener de su imagen ISO, o descargando los archivos desde la url del repositorio:
    # cd /var/lib/tftpboot
    # cp /usr/share/syslinux/{pxelinux.0,vesamenu.c32} .
    #
    # wget -q http://mirror.centos.org/centos/7.1.1503/os/x86_64/isolinux/{vmlinuz,initrd.img,splash.png}
    #
  4. Creo un menú para mi servicio PXE que tenga distintas entradas para los tipos de instalación que yo quiera hacer. En este caso voy a hacer 2: Una para hacer instalaciónes mediante un Kickstart en mi red, otra para instalaciones manuales usando un repositorio público (asumiendo que el cliente tiene salida a internet). Luego una tercera entrada para iniciar con el disco rígido, sin instalar:
    # mkdir /var/lib/tftpboot/pxelinux.cfg
    # cd /var/lib/tftpboot/pxelinux.cfg/
    # vi default
    # cat default
    default vesamenu.c32
    timeout 200
    menu background splash.png
    ontimeout local
    label ks
      menu label ^Install CentOS7 using Kickstart
      menu default
      kernel vmlinuz
      append initrd=initrd.img ip=dhcp ksdevice=link ks=http://server.example.com/pub/centos7/lab.ks
    label repo
      menu label ^Install CentOS7 without Kickstart
      kernel vmlinuz
      append initrd=initrd.img ip=dhcp ksdevice=link repo=http://mirror.centos.org/centos/7.1.1503/os/x86_64/
    label local
      menu label Boot from Hard Drive
      localboot 0xffff
    #

    Al iniciar un cliente por PXE, este menú se verá así:
    pxe

  5. Por último, para que un cliente pueda usar este server PXE, hace falta que el servidor DHCP de mi red (que puede ser el mismo server PXE o no) indíque a sus clientes cual es el “next-server”, y que archivo solicitar. Eso lo hago agregando estás dos líneas al final de la zona a la que quiero servir:
    # cat /etc/dhcp/dhcpd.conf
    allow unknown-clients;
    option domain-name-servers 10.11.1.254;
    option domain-name "example.com";
    authoritative;
    
    subnet 10.11.1.0 netmask 255.255.255.0 {
          range 10.11.1.100 10.11.1.109;
          option routers 10.11.1.254;
          option broadcast-address 10.11.1.255;
          next-server 10.11.1.254;
          filename "pxelinux.0";
    }
    #

 

Eso es todo lo necesario en cuanto a PXE. Una vez completados los pasos anteriores, la estructura de archivos de mi servidor PXE es la siguiente:

# find /var/lib/tftpboot/
/var/lib/tftpboot/
/var/lib/tftpboot/pxelinux.0
/var/lib/tftpboot/vesamenu.c32
/var/lib/tftpboot/vmlinuz
/var/lib/tftpboot/initrd.img
/var/lib/tftpboot/splash.png
/var/lib/tftpboot/pxelinux.cfg
/var/lib/tftpboot/pxelinux.cfg/default

 

Adicionalmente, dado que no estoy usando Xinetd, si quisiera modificar los parámetros con que corre el servicio, ya no debo hacerlo en /etc/xinetd.d/tftp, sino en el archivo correspondiente de systemd. Recomiendo copiar el original a /etc/systemd/system (tiene precedencia), para que futuras actualizaciones del paquete no sobreescriban el archivo modificado.

Por ejemplo, para aumentar la cantidad de información registrada por el journal de systemd, agrego “-vvv” en la línea que indica cual es el ejecutable:

# cp /usr/lib/systemd/system/tftp.service /etc/systemd/system
# vi /etc/systemd/system/tftp.service 
# cat /etc/systemd/system/tftp.service
[Unit]
Description=Tftp Server

[Service]
ExecStart=/usr/sbin/in.tftpd -vvv -s /var/lib/tftpboot
StandardInput=socket
#
# systemctl daemon-reload
#

Luego, consultando en el journal de systemd mientras un cliente inicia por PXE, puedo ver la información bien detallada de lo que sucede durante el proceso:

# journalctl -f -n0
 -- Logs begin at Fri 2015-05-01 12:08:18 ART. --
 May 01 15:52:02 server.example.com dhcpd[1635]: DHCPACK on 10.11.1.100 to 52:54:00:7f:d6:fc (labo101) via eth1
 May 01 15:52:31 server.example.com dhcpd[1635]: DHCPDISCOVER from 52:54:00:7f:d6:fc via eth1
 May 01 15:52:32 server.example.com dhcpd[1635]: DHCPOFFER on 10.11.1.101 to 52:54:00:7f:d6:fc via eth1
 May 01 15:52:34 server.example.com dhcpd[1635]: DHCPREQUEST for 10.11.1.101 (10.11.1.254) from 52:54:00:7f:d6:fc via eth1
 May 01 15:52:34 server.example.com dhcpd[1635]: DHCPACK on 10.11.1.101 to 52:54:00:7f:d6:fc via eth1
 May 01 15:52:34 server.example.com in.tftpd[23119]: RRQ from ::ffff:10.11.1.101 filename pxelinux.0
 May 01 15:52:34 server.example.com in.tftpd[23120]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/de975950-911c-244c-9a77-5fd6631dda4b
 May 01 15:52:34 server.example.com in.tftpd[23120]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23121]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/01-52-54-00-7f-d6-fc
 May 01 15:52:34 server.example.com in.tftpd[23121]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23122]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A0B0165
 May 01 15:52:34 server.example.com in.tftpd[23122]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23123]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A0B016
 May 01 15:52:34 server.example.com in.tftpd[23123]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23124]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A0B01
 May 01 15:52:34 server.example.com in.tftpd[23124]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23125]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A0B0
 May 01 15:52:34 server.example.com in.tftpd[23125]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23126]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A0B
 May 01 15:52:34 server.example.com in.tftpd[23126]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23127]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A0
 May 01 15:52:34 server.example.com in.tftpd[23127]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23128]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0A
 May 01 15:52:34 server.example.com in.tftpd[23128]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23129]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/0
 May 01 15:52:34 server.example.com in.tftpd[23129]: sending NAK (1, File not found) to ::ffff:10.11.1.101
 May 01 15:52:34 server.example.com in.tftpd[23130]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/default
 May 01 15:52:34 server.example.com in.tftpd[23131]: RRQ from ::ffff:10.11.1.101 filename vesamenu.c32
 May 01 15:52:35 server.example.com in.tftpd[23132]: RRQ from ::ffff:10.11.1.101 filename pxelinux.cfg/default
 May 01 15:52:35 server.example.com in.tftpd[23133]: RRQ from ::ffff:10.11.1.101 filename splash.png

 

Eso es todo, y queda el servicio funcionando sin necesidad de usar Xinetd. Quizá este hasta sea quitado por completo en futuras versiones de CentOS/RHEL, así que esta configuración va a seguir siendo útil, ya que no se verá afectada.

Leave a comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *