PXE on CentOS / RHEL 7 without Xinetd


There are tons of articles on the internet about configuring a PXE server, most of them unnecessary long and complex. Moreover, they all focus on using Xinetd. As you know, Xinetd is a daemon thatLET’S JUST LEAVE WIKIPEDIA EXPLAIN IT:

In computer networking, xinetd (extended Internet daemon) is an open-source super-server daemon which runs on many Unix-like systems and manages Internet-based connectivity.

Source: Wikipedia

Well, at least the English version of this article includes a brief explanation of how Xinetd allows for on-demand services. That is, avoid running services while they are not receiving connections. So all cool with Xinetd, but truth is that because of its limitations, it’s being used less and less these days.

In modern operating systems, like CentOS/RHEL7, and since Fedora 15 (released almost 4 years ago!), Systemd can not only replace Xinetd’s functionality, but also add some features of its own, like centralization of logs and status monitoring. Systemd deserves a whole article on its own (or several), but before I write about that, I need to do proper research (?), so I’ll leave that for another time.

Back to PXE, I was surprised to see that even Red Hat, on their official RHEL7 documentation (here), uses Xinetd to run tftp and provide PXE service, something completely unnecessary, as I’m about to display, ladies and gentlemen (?).

 

Configuring PXE in just 5 steps

  1. Install the necessary packages:
    # yum install syslinux tftp-server

    Note that even though Xinetd will be installed as a dependency, both the Xinetd “super-service” and the tftp Xinetd “sub-service” will be disabled:

    # systemctl disable xinetd.service
    rm '/etc/systemd/system/multi-user.target.wants/xinetd.service'
    #
    # grep disable /etc/xinetd.d/tftp
    disable = yes
    #
  2. On the other hand, the tftp socket on Systemd will be enabled and listening. Since firewalld is active, I’ll add the tftp port on the appropriate zone:
    # 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. When PXE booting, the server needs to send the client some basic files, which are provided by the syslinux package that I installed on the first step. Then needs to send the kernel and the initial ramdisk matching the target operating system, (just CentOS7 in this case), which I can get from the OS iso, or manually getting the files from the public repository:
    # 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. I will now create a menu for my PXE service, that will have entries for two different deployment methods: an automated one using a kickstart, and a manual one using a public repository (assuming of course that the client has access to internet). Then I’ll add a third entry for booting from hard drive:
    # 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
    #

    When a client boots from PXE, it will display the menu like this:
    pxe

  5. And finally, for a server to be able to use the PXE service, the DHCP server (which could be on the same PXE server or on a different one) needs to know which is the “next-server”, and what file to provide to get the environment started. Something like “go to this guy and get this”. You do that by adding these two lines in red on dhcpd.conf:
    # 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";
    }
    #

 

That’s all in regards to configuring PXE. When I complete the steps above, the file structure will look like this:

# 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

 

Additionally, since I’m not using Xinetd now, if I wanted to modify the service startup parameters, I no longer modify /etc/xinetd.d/tftp, but the corresponding Systemd file instead. I recommend making a copy of the original file to /etc/systemd/system (takes precedence over /usr/lib/systemd/system) so that future updates don’t overwrite the customized file.

For example, to increase verbosity for this service, I add “-vvv” on the line that indicates the executable command (ExecStart):

# 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
#

Then, watching journalctl while a client boots on PXE, it shows detailed information of the process:

# 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

 

That’s all, and the service runs without running Xinetd, which may be removed from future OS versions, so this configuration won’t be affected.

Leave a comment

Your email address will not be published. Required fields are marked *