../kea-netboot

Using kea DHCP server for netbooting

Table of contents

Recently, I’ve been playing around with PXE booting using iPXE and various DHCP servers: dnsmasq, isc-dhcp, pfsense, opnsense (yes the last 2 are not only DHCP servers).

Since isc-dhcp has reached EOL, ISC recommended switching to Kea dhcp server. So that’s what we’re going to setup today.

Prerequisites

I’m using 2 VMs for this: 1 VM running Ubuntu Server 24.04, and 1 blank VM for netbooting.

The Ubuntu 24.04 VM has 2 network interface, connect to 2 different networks. Make sure 1 of those network has Internet access. Here’s what it looks like in VMWare:

VM network adapter config

The setup looks like this:

Basic diagram

Installed softwares on the Ubuntu 24.04 server:

Install them with this command:

$ sudo apt install kea postgresql tftpd-hpa -y

For other OSes and platforms, check out Kea installation docs.

If the installer asks to setup control agent, just say no. We won’t go into using control agent to configure kea here, but you can check the docs on how to use the agent.

Setup networking

In my Ubuntu VM, the network adapter connected to NAT is named ens33, and the other adapter is named ens37.

We’re going to use netplan to configure the ens37 network. Use your distro tool if you’re using other OSes.

Open /etc/netplan/20-netboot.yaml and paste this in:

network:
    ethernets:
        ens37:
            addresses:
                - 10.10.10.1/24 # You can use any network here
    version: 2

Then apply the configuration and check the result:

$ sudo netplan apply
$ ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:5b:95:0b brd ff:ff:ff:ff:ff:ff
    altname enp2s1
    inet 172.20.20.109/24 metric 100 brd 172.20.20.255 scope global dynamic ens33
       valid_lft 1703sec preferred_lft 1703sec
    inet6 fe80::20c:29ff:fe5b:950b/64 scope link
       valid_lft forever preferred_lft forever
3: ens37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:5b:95:15 brd ff:ff:ff:ff:ff:ff
    altname enp2s5
    inet 10.10.10.1/24 brd 10.10.10.255 scope global ens37
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe5b:9515/64 scope link
       valid_lft forever preferred_lft forever

Next, we’re going to enable IPv4 forwarding and NAT on the VM. You can use iptables or nftables. I’m using nftables here.

Edit /etc/nftables.conf and add the NAT configuration:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        ...
        chain nat {
                type nat hook postrouting priority srcnat;
                oifname ens33 masquerade
        }
}

Save the file, then restart nftables.service:

$ sudo systemctl restart nftables.service

Finally, enable IPv4 forwarding:

$ sudo sysctl net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

Setup the database

Kea supports using a database to store its leases and host reservations. This is what we’re going to setup first.

First, change user to postgres and create a user for Kea:

$ sudo -u postgres bash
# We're running bash under user postgres now
$ createuser --pwprompt kea
Enter password for new role:
Enter it again:

Then, create 2 seperate databases for Kea: kea-leases and kea-hosts:

# Still running under user postgres
$ createdb -O kea kea-leases
$ createdb -O kea kea-hosts
# Exit out of postgres user
$ exit

Now, initialize both databases. There are 2 ways you can do this:

$ psql -d kea-leases -U kea -f /usr/share/kea/scripts/pgsql/dhcpdb_create.pgsql
Password for user kea:
CREATE TABLE
CREATE INDEX
CREATE INDEX
CREATE TABLE
CREATE INDEX
CREATE TABLE
START TRANSACTION
INSERT 0 1
INSERT 0 1
INSERT 0 1
COMMIT
CREATE TABLE
START TRANSACTION
INSERT 0 1
COMMIT
$ psql -d kea-hosts -U kea -f /usr/share/kea/scripts/pgsql/dhcpdb_create.pgsql
Password for user kea:
CREATE TABLE
CREATE INDEX
CREATE INDEX
CREATE TABLE
CREATE INDEX
CREATE TABLE
START TRANSACTION
INSERT 0 1
INSERT 0 1
INSERT 0 1
COMMIT
CREATE TABLE
START TRANSACTION
INSERT 0 1
COMMIT
$ kea-admin db-init pgsql -u kea -p <your kea user password> -n kea-leases
$ kea-admin db-init pgsql -u kea -p <your kea user password> -n kea-hosts

We’ve finished setting up the database. For more information, check out the database administration docs.

Configuring Kea

We’re only configuring DHCPv4 in this post. DHCPv6 should be quite similar.

Kea is using JSON for its configuration. Our Kea installation on Ubuntu has 4 files in /etc/kea: kea-ctrl-agent.conf, kea-dhcp4.conf, kea-dhcp6.conf, kea-dhcp-ddns.conf. Since we’re only using DHCPv4, the only file we need to edit is kea-dhcp4.conf

If you check the kea-dhcp4.conf file, you might be whelmed with the amount of options you can tweak. Let’s make a backup first:

$ sudo cp /etc/kea/kea-dhcp4.conf /etc/kea/kea-dhcp4.conf.bak

Now use your favourite editor and open /etc/kea/kea-dhcp4.conf. Delete everything in it, we’re starting from scratch.

All configuration for DHCPv4 must be under Dhcpv4 key. Paste in the configuration below, we’re going through each of them:

{
    // Shell, C, or C++ style comments are all permitted in the JSON configuration file if the file is used locally
    "Dhcp4": {
        "interfaces-config": {
            "interfaces": [
                "ens37"
            ]
        },
        "lease-database": {
            "type": "postgresql",
            "name": "kea-leases",
            "host": "localhost",
            "user": "kea",
            "password": "kea"
        },
        "hosts-database": {
            "type": "postgresql",
            "name": "kea-hosts",
            "user": "kea",
            "password": "kea",
            "host": "localhost"
        },
        "expired-leases-processing": {
            "reclaim-timer-wait-time": 10,
            "flush-reclaimed-timer-wait-time": 25,
            "hold-reclaimed-time": 3600,
            "max-reclaim-leases": 100,
            "max-reclaim-time": 250,
            "unwarned-reclaim-cycles": 5
        },
        "renew-timer": 900,
        "rebind-timer": 1800,
        "valid-lifetime": 3600,
        "option-data": [
            {
                "name": "domain-name-servers",
                "data": "1.1.1.1, 1.0.0.1"
            },
            {
                "name": "domain-name",
                "data": "demo.null"
            },
            {
                "name": "domain-search",
                "data": "demo.null"
            }
        ],
        "client-classes": [
            {
                "name": "iPXE",
                "test": "option[175].exists",
                "option-data": [
                    {
                        "name": "tftp-server-name",
                        "data": "10.10.10.1"
                    },
                    {
                        "name": "boot-file-name",
                        "data": "http://boot.netboot.xyz"
                    }
                ]
            },
            {
                "name": "UEFI clients",
                "test": "option[93].hex == 0x0007 and not option[175].exists",
                "option-data": [
                    {
                        "name": "tftp-server-name",
                        "data": "10.10.10.1"
                    },
                    {
                        "name": "boot-file-name",
                        "data": "ipxe.efi"
                    }
                ]
            },
            {
                "name": "BIOS clients",
                "test": "option[93].hex == 0x0000 and not option[175].exists",
                "option-data": [
                    {
                        "name": "tftp-server-name",
                        "data": "10.10.10.1"
                    },
                    {
                        "name": "boot-file-name",
                        "data": "undionly.kpxe"
                    }
                ]
            }
        ],
        "subnet4": [
            {
                "id": 1,
                "subnet": "10.10.10.0/24",
                "pools": [
                    {
                        "pool": "10.10.10.10 - 10.10.10.100"
                    }
                ],
                "option-data": [
                    {
                        "name": "routers",
                        "data": "10.10.10.1"
                    }
                ]
            }
        ],
        "loggers": [
            {
                "name": "kea-dhcp4",
                "output_options": [
                    {
                        "output": "stdout",
                        "pattern": "%-5p %m\n"
                    }
                ],
                "severity": "DEBUG",
                "debuglevel": 60
            }
        ]
    }
}

We’ve finished configuring Kea! Now restart kea-dhcp4-server.service and check its log output:

$ sudo systemctl restart kea-dhcp4-server.service
$ sudo systemctl status kea-dhcp4-server.service
 kea-dhcp4-server.service - Kea IPv4 DHCP daemon
     Loaded: loaded (/usr/lib/systemd/system/kea-dhcp4-server.service; enabled; preset: enabled)
     Active: active (running) since Wed 2024-07-17 15:12:24 UTC; 1s ago
       Docs: man:kea-dhcp4(8)
   Main PID: 2366 (kea-dhcp4)
      Tasks: 6 (limit: 2218)
     Memory: 3.4M (peak: 3.7M)
        CPU: 32ms
     CGroup: /system.slice/kea-dhcp4-server.service
             └─2366 /usr/sbin/kea-dhcp4 -c /etc/kea/kea-dhcp4.conf

Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: DEBUG DHCPSRV_PGSQL_GET_VERSION obtaining schema version information
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: INFO  DHCPSRV_PGSQL_HOST_DB opening PostgreSQL hosts database: host=localhost max-reconnect-tries=5 name=kea-hosts password=***** reconnect-wait-time=500 type=postgresql universe=4 user=kea
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: DEBUG DHCPSRV_PGSQL_HOST_DB_GET_VERSION obtaining schema version information for the PostgreSQL hosts database
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: DEBUG DHCPSRV_TIMERMGR_REGISTER_TIMER registering timer: reclaim-expired-leases, using interval: 10000 ms
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: DEBUG DHCPSRV_TIMERMGR_START_TIMER starting timer: reclaim-expired-leases
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: DEBUG DHCPSRV_TIMERMGR_REGISTER_TIMER registering timer: flush-reclaimed-leases, using interval: 25000 ms
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: DEBUG DHCPSRV_TIMERMGR_START_TIMER starting timer: flush-reclaimed-leases
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: INFO  DHCPSRV_CFGMGR_USE_ALLOCATOR using the iterative allocator for V4 leases in subnet 10.10.10.0/24
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: WARN  DHCP4_MULTI_THREADING_INFO enabled: yes, number of threads: 1, queue size: 64
Jul 17 15:12:24 pxeserver kea-dhcp4[2366]: INFO  DHCP4_STARTED Kea DHCPv4 server version 2.4.1 started

Get the iPXE boot files

By default, tftpd-hpa TFTP root is at /srv/tftp. Change directory into that and download the boot files from https://boot.ipxe.org/:

$ cd /srv/tftp/
$ sudo curl -OL https://boot.ipxe.org/ipxe.efi
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1011k  100 1011k    0     0   508k      0  0:00:01  0:00:01 --:--:--  508k
$ sudo curl -OL https://boot.ipxe.org/undionly.kpxe
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 69953  100 69953    0     0  57403      0  0:00:01  0:00:01 --:--:-- 57479
$ ls -la
total 1092
drwxr-xr-x 2 root nogroup    4096 Jul 16 13:48 .
drwxr-xr-x 3 root root       4096 Jul 16 13:45 ..
-rw-r--r-- 1 root root    1035776 Jul 17 15:14 ipxe.efi
-rw-r--r-- 1 root root      69953 Jul 17 15:14 undionly.kpxe

Test drive

Now, connect the blank VM to the same internal network as the Ubuntu VM. Boot that VM up, and it should chain load iPXE, then boot into netboot.xyz.

Further reading

  1. Kea Administrator Reference Manual: https://kea.readthedocs.io/en/kea-2.6.0/index.html
  2. Kea Training Series: https://www.youtube.com/playlist?list=PLUwyH0o3uuIBv1-CJQaM9_L4_wrI0Jatb

/network/

Comments: