.. _ipxe-install:

Install |CL-ATTR| over the network with iPXE
############################################

This guide describes how to install |CL-ATTR| using :abbr:`PXE (Pre-boot
Execution Environment)`.

PXE is an industry standard that describes client-server interaction with
network-boot software and uses the DHCP and TFTP protocols. This guide shows one
method of using the PXE environment to install |CL|.

The PXE extension called `iPXE`_ adds support for additional protocols such as
HTTP, :abbr:`iSCSI (Internet Small Computer Systems Interface)`, :abbr:`AoE
(ATA over Ethernet\*)`, and :abbr:`FCoE (Fiber Channel over Ethernet\*)`. iPXE
enables network booting on computers with no built-in PXE support.

To install |CL| through iPXE, you must create a PXE client. Figure 1 depicts
the flow of information between a PXE server and a PXE client.

.. figure:: ./figures/network-boot-flow.png
   :alt: PXE information flow

   Figure 1: PXE information flow.

.. caution::

   The |CL| image that boots through the PXE process automatically erases all
   data and partitions on the PXE client system and creates 3 new partitions
   to install onto.

Prerequisites
*************

Before booting with iPXE, make the following preparations.

Connect the PXE server and PXE clients to a switch on a private network, as
shown in Figure 2.

.. figure:: ./figures/network-boot-setup.png
   :alt: Network topology

   Figure 2: Network topology.

Your PXE client must have a boot order where the network boot option is
prioritized before the disk boot option.

Your PXE server must have:

* Ethernet/LAN boot option.
* At least two network adapters.
* Connection to a public network.
* Secure boot option disabled.

.. note::

   You must disable the secure boot option in the BIOS because the UEFI
   binaries used to boot |CL| are not signed.


Configuration
*************

To set up |CL| using iPXE automatically, use the :file:`configure-ipxe.sh`
script included with :abbr:`ICIS (Ister Cloud Init Service)`. For additional
instructions on the script, refer to the guide on the `ICIS GitHub repository`_.

To set up |CL| manually, perform the steps below.

#. Define the variables used for iPXE boot configuration.

   .. code-block:: console

      ipxe_app_name=ipxe
      ipxe_port=50000
      web_root=/var/www
      ipxe_root=$web_root/$ipxe_app_name
      tftp_root=/srv/tftp
      external_iface=eno1
      internal_iface=eno2
      pxe_subnet=192.168.1
      pxe_internal_ip=$pxe_subnet.1
      pxe_subnet_mask_ip=255.255.255.0
      pxe_subnet_bitmask=16

#. Log in and get root privilege.

   .. code-block:: bash

      sudo -s

#. Add the `pxe-server` bundle to your |CL| system. The bundle contains all
   files needed to run a PXE server.

   .. code-block:: bash

      sudo swupd bundle-add pxe-server

#. Download the latest network-bootable release of |CL| and extract the
   files.

   .. code-block:: bash

      sudo mkdir -p $ipxe_root
      sudo curl -o /tmp/clear-pxe.tar.xz \
        https://download.clearlinux.org/current/clear-$(curl \
        https://download.clearlinux.org/latest)-pxe.tar.xz
      sudo tar -xJf /tmp/clear-pxe.tar.xz -C $ipxe_root
      sudo ln -sf $(ls $ipxe_root | grep 'org.clearlinux.*') $ipxe_root/linux

   .. note::

      Ensure that the initial ramdisk file is named :file:`initrd` and
      the kernel file is named :file:`linux`, which is a symbolic link to the
      actual kernel file.

#. Create an iPXE boot script with the following contents. During an iPXE
   boot, the iPXE boot script directs the PXE client to download the files to
   boot and install |CL|. Use the names previously given to the initial
   ramdisk and kernel files.

   .. code-block:: console

      sudo cat > $ipxe_root/ipxe_boot_script.ipxe << EOF
      #!ipxe
      kernel linux quiet init=/usr/lib/systemd/systemd-bootchart \
      initcall_debug tsc=reliable no_timer_check noreplace-smp rw \
      initrd=initrd
      initrd initrd
      boot
      EOF

#. The `pxe-server` bundle contains a lightweight web-server known as
   `nginx`. Create a configuration file for `nginx` to serve |CL| to PXE
   clients with the following contents:

   .. code-block:: console

      sudo mkdir -p /etc/nginx/conf.d
      sudo cat > /etc/nginx/conf.d/$ipxe_app_name.conf << EOF
      server {
        listen $ipxe_port;
        server_name localhost;
        location /$ipxe_app_name/ {
          root $web_root;
          autoindex on;
        }
      }
      EOF

      sudo cp /usr/share/nginx/conf/nginx.conf.example /etc/nginx/nginx.conf

   .. note::

      Create a separate `nginx` configuration file to serve network-bootable
      images on a non-standard port number. This action saves existing `nginx`
      configurations.

#. Start `nginx` and enable the startup on boot option.

   .. code-block:: bash

      sudo systemctl start nginx
      sudo systemctl enable nginx

#. The `pxe-server` bundle contains a lightweight DNS server which
   conflicts with the DNS stub listener provided in `systemd-resolved`.
   Disable the DNS stub listener and temporarily stop `systemd-resolved`.

   .. code-block:: console

      sudo mkdir -p /etc/systemd
      sudo cat > /etc/systemd/resolved.conf << EOF
      [Resolve]
      DNSStubListener=no
      EOF

      sudo systemctl stop systemd-resolved

#. Assign a static IP address to the network adapter for the private network
   and restart `systemd-networkd` with the following commands:

   .. code-block:: console

      sudo mkdir -p /etc/systemd/network
      sudo cat > /etc/systemd/network/70-internal-static.network << EOF
      [Match]
      Name=$internal_iface
      [Network]
      DHCP=no
      Address=$pxe_internal_ip/$pxe_subnet_bitmask
      EOF

      sudo systemctl restart systemd-networkd

#. Configure :abbr:`NAT (Network Address Translation)` to route traffic from
   the private network to the public network. This action makes the PXE
   server act as a router. To make these changes persistent during reboots, save the
   changes to the firewall with the following commands:

   .. code-block:: bash

      sudo iptables -t nat -F POSTROUTING
      sudo iptables -t nat -A POSTROUTING -o $external_iface -j MASQUERADE
      sudo systemctl enable iptables-save.service
      sudo systemctl restart iptables-save.service
      sudo systemctl enable iptables-restore.service
      sudo systemctl restart iptables-restore.service

   .. note::

      The firewall masks packets to make them appear as coming from the PXE
      server and hides PXE clients from the public network.

#. Configure the kernel to forward network packets to different
   interfaces. Otherwise, NAT will not work.

   .. code-block:: bash

      sudo mkdir -p /etc/sysctl.d
      sudo echo net.ipv4.ip_forward=1 > /etc/sysctl.d/80-nat-forwarding.conf
      sudo echo 1 > /proc/sys/net/ipv4/ip_forward

#. The `pxe-server` bundle contains iPXE firmware images that allow computers
   without an iPXE implementation to perform an iPXE boot. Create a TFTP
   hosting directory and populate the directory with the iPXE firmware images
   with the following commands:

   .. code-block:: bash

      sudo mkdir -p $tftp_root
      sudo ln -sf /usr/share/ipxe/undionly.kpxe $tftp_root/undionly.kpxe

#. The `pxe-server` bundle contains a lightweight TFTP, DNS, and DHCP
   server known as `dnsmasq`. Create a configuration file for `dnsmasq`
   to listen on a dedicated IP address for those functions. PXE clients on
   the private network will use this IP address.

   .. code-block:: console

      sudo cat > /etc/dnsmasq.conf << EOF
      listen-address=$pxe_internal_ip
      EOF

#. Add the options to serve iPXE firmware images to PXE clients over TFTP to
   the `dnsmasq` configuration file.

   .. code-block:: console

      sudo cat >> /etc/dnsmasq.conf << EOF
      enable-tftp
      tftp-root=$tftp_root
      EOF

#. Add the options to host a DHCP server for PXE clients to the `dnsmasq`
   configuration file.

   .. code-block:: console

      sudo cat >> /etc/dnsmasq.conf << EOF
      dhcp-leasefile=/var/db/dnsmasq.leases

      dhcp-authoritative
      dhcp-option=option:router,$pxe_internal_ip
      dhcp-option=option:dns-server,$pxe_internal_ip

      dhcp-match=set:pxeclient,60,PXEClient*
      dhcp-range=tag:pxeclient,$pxe_subnet.2,$pxe_subnet.253,$pxe_subnet_mask_ip,15m
      dhcp-range=tag:!pxeclient,$pxe_subnet.2,$pxe_subnet.253,$pxe_subnet_mask_ip,6h

      dhcp-match=set:ipxeboot,175
      dhcp-boot=tag:ipxeboot,http://$pxe_internal_ip:$ipxe_port/$ipxe_app_name/ipxe_boot_script.ipxe
      dhcp-boot=tag:!ipxeboot,undionly.kpxe,$pxe_internal_ip
      EOF


   The configuration provides the following important functions:

   * Directs PXE clients without an iPXE implementation to the TFTP server
     to acquire architecture-specific iPXE firmware images that allow them
     to perform an iPXE boot.
   * Activates only on the network adapter that has an IP address on the
     defined subnet.
   * Directs PXE clients to the DNS server.
   * Directs PXE clients to the PXE server for routing via NAT.
   * Divides the private network into two pools of IP addresses. One pool
     is for network boot and one pool is used after boot. Each pool has
     their own lease times.

#. Create a file for `dnsmasq` to record the IP addresses it provides
   to PXE clients.

   .. code-block:: bash

      sudo mkdir -p /var/db
      sudo touch /var/db/dnsmasq.leases

#. Start `dnsmasq` and enable startup on boot.

   .. code-block:: bash

      sudo systemctl enable dnsmasq
      sudo systemctl restart dnsmasq

#. Start `systemd-resolved`.

   .. code-block:: bash

      sudo systemctl start systemd-resolved

   .. note::

      `systemd-resolved` dynamically updates the list of DNS servers for the
      private network if you use the `dnsmasq` DNS server. The setup creates a
      pass-through DNS server that relies on the DNS servers listed in
      :file:`/etc/resolv.conf`.

#. Power on the PXE client and watch the client boot and install |CL|.

   After booting, |CL| automatically partitions the hard drive,
   installs itself, updates to the latest version, and reboots.


**Congratulations!** You have successfully installed and configured a PXE
server that enables PXE clients to boot and install |CL| over the network.


.. _iPXE:
   http://ipxe.org/

.. _ICIS GitHub repository:
   https://github.com/clearlinux/ister-cloud-init-svc
