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
- 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 #
- 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
- 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} #
- 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:
- 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.
One thought on “PXE on CentOS / RHEL 7 without Xinetd”
Outstanding, thanks very much for this hiqh-quality and concise article. I never cared much for installing xinetd just to get tftp-server running.