DHCP server with ISC Kea
(Written by Bert Van Vreckem, https://github.com/bertvv/)
ISC DHCP has been de facto the default implementation of DHCP for Linux. Most Linux distributions ship with ISC DHCP as the default DHCP server. However, since the end of 2022, ISC has announced the end of maintenance for the ISC DHCP server.
ISC has developed a new DHCP server, Kea, which should replace ISC DHCP in most cases.
Starting with Enterprise Linux 10, ISC DHCP is no longer available in the repositories. Red Hat recommends moving to Kea instead, or to another DHCP server, like dhcpdcd
.
Learning goals:
- Installing Kea
- Configuring Kea to provide network configuration to clients in a simple local network
installing isc kea on enterprise linux
On EL 10 (released in 2025), ISC Kea is available in the default repositories as package kea
. In EL 9, it isn't available yet, but you can install it from the EPEL (Extra Packages for Enterprise Linux) repository. To activate the repository, run:
Then, you can install Kea with:
The configuration files are located in /etc/kea/
:
[vagrant@el ~]$ tree /etc/kea/
/etc/kea/
├── kea-ctrl-agent.conf
├── keactrl.conf
├── kea-dhcp4.conf
├── kea-dhcp6.conf
└── kea-dhcp-ddns.conf
0 directories, 5 files
The contents of the configuration files are in JSON format (with comments). Examples of configuration files for several different use cases can be found in /usr/share/doc/kea/examples/
.
installing isc kea on debian based distributions
On Debian 12 (bookworm) and 13 (trixie), KEA is available as the package kea
.
After installation, three systemd services are started and enabled, viz. kea-dhcp4-server
, kea-dhcp6-server
, and kea-dhcp-ddns-server
. However, no subnet declarations are defined, so the servers will not listen or respond to any queries.
vagrant@debian12:~$ systemctl list-units kea-dhcp*
UNIT LOAD ACTIVE SUB DESCRIPTION
kea-dhcp-ddns-server.service loaded active running Kea DDNS Service
kea-dhcp4-server.service loaded active running Kea IPv4 DHCP daemon
kea-dhcp6-server.service loaded active running Kea IPv6 DHCP daemon
Configuration is stored in the /etc/kea
directory:
vagrant@debian12:~$ tree /etc/kea/
/etc/kea/
├── kea-ctrl-agent.conf
├── kea-dhcp4.conf
├── kea-dhcp6.conf
└── kea-dhcp-ddns.conf
1 directory, 4 files
configuring kea for a simple local network
On EL, the default configuration file contains a lot of comments and examples that are not needed for a simple local network. We suggest that you put it aside and start with the contents of /usr/share/doc/kea/examples/kea4/single-subnet.json
.
[vagrant@kea-el ~]$ sudo cp /etc/kea/kea-dhcp4.conf /usr/share/doc/kea/kea-dhcp4.conf.rpmnew
[vagrant@kea-el ~]$ sudo cp /usr/share/doc/kea/examples/kea4/single-subnet.json /etc/kea/kea-dhcp4.conf
Then, edit /etc/kea/kea-dhcp4.conf
to adjust the configuration to your network. In the example setup below, the local network has the following properties:
- The network IP is 192.168.42.0/24
- The Kea server has IP 192.168.42.254 on network interface
eth1
- The default gateway is 192.168.42.254 (i.e. the host running Kea is also acting as the default gateway)
- The DNS server is 10.0.2.3
- Clients will receive an IP address in the range 192.168.42.101-252
The configuration file should look like this:
// Kea DHCPv4 configuration for a single subnet
{ "Dhcp4":
{
// Kea is told to only listen on eth1
"interfaces-config": {
"interfaces": [ "eth1" ]
},
// Leases will be kept in memory.
"lease-database": {
"type": "memfile",
"lfc-interval": 3600
},
// Addresses will be assigned with a default lease time of 8 hours,
// minimal lease time of 4 hours and maximum 10 hours.
"valid-lifetime": 28800,
"min-valid-lifetime": 14400,
"max-valid-lifetime": 36000,
// Subnet declaration for the LAN
"subnet4": [
{
"pools": [ { "pool": "192.168.42.101 - 192.168.42.252" } ],
"subnet": "192.168.42.0/24",
"interface": "eth1",
"option-data": [
{
"name": "domain-name-servers",
"data": "10.0.2.3"
},
{
"name": "routers",
"data": "192.168.42.254"
}
]
}
],
// Logging configuration. Messages with at least informational level (info,
// warn, error and fatal) are logged to syslog.
"loggers": [
{
"name": "kea-dhcp4",
"output_options": [
{
"output": "syslog"
}
],
"severity": "INFO"
}
]
}
}
After editing the configuration file, be sure to first check the syntax with kea-dhcp4 -t
. In the example below, we get a warning about extraneous commas (which is easily fixed), but the configuration is otherwise correct:
[vagrant@kea-el ~]$ kea-dhcp4 -t /etc/kea/kea-dhcp4.conf
2024-11-24 14:26:17.056 WARN [kea-dhcp4.dhcp4/5824.140563970693248] DHCP4_CONFIG_SYNTAX_WARNING configuration syntax warning: /etc/kea/kea-dhcp4.conf:38.30: Extraneous comma. A piece of configuration may have been omitted.
2024-11-24 14:26:17.056 WARN [kea-dhcp4.dhcp4/5824.140563970693248] DHCP4_CONFIG_SYNTAX_WARNING configuration syntax warning: /etc/kea/kea-dhcp4.conf:42.36: Extraneous comma. A piece of configuration may have been omitted.
2024-11-24 14:26:17.057 INFO [kea-dhcp4.hosts/5824.140563970693248] HOSTS_BACKENDS_REGISTERED the following host backend types are available: mysql postgresql
2024-11-24 14:26:17.059 INFO [kea-dhcp4.dhcpsrv/5824.140563970693248] DHCPSRV_CFGMGR_ADD_IFACE listening on interface eth1
2024-11-24 14:26:17.060 INFO [kea-dhcp4.dhcpsrv/5824.140563970693248] DHCPSRV_CFGMGR_SOCKET_TYPE_DEFAULT "dhcp-socket-type" not specified , using default socket type raw
2024-11-24 14:26:17.061 INFO [kea-dhcp4.dhcpsrv/5824.140563970693248] DHCPSRV_CFGMGR_NEW_SUBNET4 a new subnet has been added to configuration: 192.168.42.0/24 with params: valid-lifetime=28800
If the syntax check is successful, you can start the Kea DHCP server with systemctl start
or systemctl enable --now
.
[vagrant@kea-el ~]$ sudo systemctl enable --now kea-dhcp4
Created symlink /etc/systemd/system/multi-user.target.wants/kea-dhcp4.service → /usr/lib/systemd/system/kea-dhcp4.service.
Check with systemctl status
and sudo ss -ulnp
if the service is running and listening on the correct port (67) and interface (eth1
).
[vagrant@kea-el ~]$ systemctl status kea-dhcp4.service
● kea-dhcp4.service - Kea DHCPv4 Server
Loaded: loaded (/usr/lib/systemd/system/kea-dhcp4.service; enabled; preset: disabled)
Active: active (running) since Sun 2024-11-24 14:35:58 UTC; 51min ago
Docs: man:kea-dhcp4(8)
Main PID: 6065 (kea-dhcp4)
Tasks: 5 (limit: 11128)
Memory: 2.1M
CPU: 257ms
CGroup: /system.slice/kea-dhcp4.service
└─6065 /usr/sbin/kea-dhcp4 -c /etc/kea/kea-dhcp4.conf
[vagrant@kea-el ~]$ sudo ss -uln | grep ':67'
UNCONN 0 0 192.168.42.254:67 0.0.0.0:*
Finally, you can test the DHCP server by connecting a client to the network and checking if it receives an IP address. Follow the logs with journalctl -flu kea-dhcp4 -f
to observe the interaction between a client and the Kea service. Optionally, you can also use tcpdump
to see the DHCP packets.
[vagrant@kea-el ~]$ sudo tcpdump -w kea-dhcp.pcap -v -n -i eth1 port 67 or port 68
dropped privs to tcpdump
tcpdump: listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
4 packets captured
4 packets received by filter
0 packets dropped by kernel
Cancel the tcpdump
command with Ctrl-C
after you've seen the four packets. Afterwards, open the file with wireshark
or use tcpdump
to print the contents:
[vagrant@kea-el ~]$ tcpdump -r /vagrant/kea-dhcp.pcap -ne#
reading from file /vagrant/kea-dhcp.pcap, link-type EN10MB (Ethernet), snapshot length 262144
1 15:31:59.764913 08:00:27:78:d1:ec > Broadcast, ethertype IPv4 (0x0800), length 329: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 08:00:27:78:d1:ec, length 287
2 15:31:59.765589 08:00:27:aa:8c:22 > 08:00:27:78:d1:ec, ethertype IPv4 (0x0800), length 336: 192.168.42.254.bootps > 192.168.42.101.bootpc: BOOTP/DHCP, Reply, length 294
3 15:31:59.766029 08:00:27:78:d1:ec > Broadcast, ethertype IPv4 (0x0800), length 341: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 08:00:27:78:d1:ec, length 299
4 15:31:59.766198 08:00:27:aa:8c:22 > 08:00:27:78:d1:ec, ethertype IPv4 (0x0800), length 336: 192.168.42.254.bootps > 192.168.42.101.bootpc: BOOTP/DHCP, Reply, length 294
The first packet is the dhcp discover from the client, the second packet is the dhcp offer from the server, the third packet is the dhcp request from the client, and the fourth packet is the dhcp ack from the server. You can see more details when you open the file in wireshark
.
In this example, the host with mac address 08:00:27:78:d1:ec
received the reserved ip address 192.168.42.101.
practice: DHCP server with ISC Kea
In this exercise, we are going to set up an Enterprise Linux 10 VM as a router and dhcp server. Use e.g. Vagrant to quickly set up a VirtualBox VM with a NAT interface (usually eth0
or enp0s3
) and a Host-Only Interface (eth1
or enp0s8
). The Host-Only Interface will be used to connect the client VMs. The NAT interface will provide Internet access to the VMs.
-
Set up the Enterprise Linux VM (e.g. AlmaLinux 10). Give it a static IP address 192.168.42.254/24 on the second Host-Only Interface
eth1
(orenp0s8
, depending on the VM configuration). Vagrant will also automatically connect the VM to a NAT interfaceeth0
(orenp0s3
), providing Internet access. The VM's IP address on this interface are provided by VirtualBox, but they are predictable: the IP address will be 10.0.2.15, the default gateway 10.0.2.2 and DNS server 10.0.2.3. This DNS server will also be used by the client VMs.Enable routing on the machine and NAT address translation so it can provide Internet access to client machines on the network. Follow the instructions in the router with nat section of the chapter on firewalls.
-
Install Kea and copy an appropriate example configuration file to
/etc/kea
. Back up the original configuration file first. -
Add a subnet declaration for the 192.168.42.0/24 network. Specify a range (100-252) and set this VM as the default gateway.
-
Update the options for this subnet. Set 10.0.2.3 as the global DNS server, linux-training.be as the domain name, default lease time of 2 hours and maximum lease time of 4 hours.
-
Check the configuration file syntax and start the service. Check that the service is listening on the appropriate NIC and port.
-
Start tcpdump to listen for DHCP requests on the host-only interface.
-
Attach a client VM (e.g. a Kali Linux VM) to the host-only network and start it. Check if the client gets an IP address from the DHCP server, and that the gateway and DNS server are set correctly.
-
From the client VM, ping the default gateway/dhcp server on both interfaces (192.168.42.254 and 10.0.2.15), the DNS server 10.0.2.3 and the dhcp server's external gateway 10.0.2.2. Use
dig
to test that name resolution works. Access a website from the client VM to verify that Internet access is working. -
Add a client reservation the client VM. Find the MAC address and assign a fixed IP address to it. Give it a longer default and maximum lease time than the global settings (e.g. 4 and 10 hours, respectively). Restart the dhcp server and re-attach the client VM to the network. Check if the client gets the reserved IP address and can access the internet.
Repeat the exercise for a VM with a Debian-based distribution (e.g. Debian 12/13 or Ubuntu 24.04) with ip address 192.168.42.253/24. What happens if both VMs are active at the same time?
solutions: DHCP server with ISC Kea
An implementation of this exercise can be found at https://github.com/HoGentTIN/linux-training-labs/tree/main/kea
-
Set up the Enterprise Linux VM (e.g. AlmaLinux 10). Enable routing on the machine and NAT address translation so it can provide Internet access to client machines on the network.
Perform the following commands as root.
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p firewall-cmd --zone=external --change-interface=enp0s3 --permanent firewall-cmd --zone=internal --change-interface=enp0s8 --permanent firewall-cmd --reload
Check the network settings:
vagrant@kea-el:~$ ip -br a lo UNKNOWN 127.0.0.1/8 ::1/128 enp0s3 UP 10.0.2.15/24 fd17:625c:f037:2:a00:27ff:fe43:cbc1/64 fe80::a00:27ff:fe43:cbc1/64 enp0s8 UP 192.168.42.254/24 fe80::1af6:e3ad:a03b:7523/64 vagrant@kea-el:~$ cat /proc/sys/net/ipv4/ip_forward 1 vagrant@kea-el:~$ sudo firewall-cmd --get-active-zones external interfaces: enp0s3 internal interfaces: enp0s8 public (default) vagrant@kea-el:~$ sudo firewall-cmd --zone=external --query-masquerade yes
-
Install Kea and copy an appropriate example configuration file to
/etc/kea
. Back up the original configuration file first.Perform the following commands as root:
-
Add a subnet declaration for the 192.168.42.0/24 network. Specify a range (100-252) and set this VM as the default gateway.
The complete configuration file follows in step 4.
-
Update the options for this subnet. Set 10.0.2.3 as the global DNS server, linux-training.be as the domain name, default lease time of 2 hours and maximum lease time of 4 hours.
{ "Dhcp4": { "interfaces-config": { "interfaces": [ "enp0s8" ] }, // We need to specify the database used to store leases. // We'll use memfile because it doesn't require any prior set up. "lease-database": { "type": "memfile", "lfc-interval": 3600 }, // Addresses will be assigned with a lifetime of 4000 seconds. "valid-lifetime": 4000, // The following list defines subnets. We have only one subnet // here. We tell Kea that it is directly available over local interface. "subnet4": [ { "pools": [ { "pool": "192.168.42.100 - 192.168.42.252" } ], "id": 1, "subnet": "192.168.42.0/24", "interface": "enp0s8", "valid-lifetime": 7200, "max-valid-lifetime": 14400, "option-data": [ { "name": "domain-name", "data": "linux-training.be" }, { "name": "domain-name-servers", "data": "10.0.2.3" }, { "name": "routers", "data": "192.168.42.254" } ] } ], // The following configures logging. It assumes that messages with at // least informational level (info, warn, error and fatal) should be // logged to stdout. Alternatively, you can specify stderr here, a filename // or 'syslog', which will store output messages via syslog. "loggers": [ { "name": "kea-dhcp4", "output-options": [ { "output": "stdout" } ], "severity": "INFO" } ] } }
-
Check the configuration file syntax and start the service. Check that the service is listening on the appropriate NIC and port.
vagrant@kea-el:~$ sudo kea-dhcp4 -t /etc/kea/kea-dhcp4.conf 2025-08-13 14:41:39.301 INFO [kea-dhcp4.hosts/6564.139635720923328] HOSTS_BACKENDS_REGISTERED the following host backend types are available: mysql postgresql 2025-08-13 14:41:39.301 WARN [kea-dhcp4.dhcpsrv/6564.139635720923328] DHCPSRV_MT_DISABLED_QUEUE_CONTROL disabling dhcp queue control when multi-threading is enabled. 2025-08-13 14:41:39.301 WARN [kea-dhcp4.dhcp4/6564.139635720923328] DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED Multi-threading is enabled and host reservations lookup is always performed first. 2025-08-13 14:41:39.301 INFO [kea-dhcp4.dhcpsrv/6564.139635720923328] DHCPSRV_CFGMGR_NEW_SUBNET4 a new subnet has been added to configuration: 192.168.42.0/24 with params: valid-lifetime=7200 2025-08-13 14:41:39.302 INFO [kea-dhcp4.dhcpsrv/6564.139635720923328] DHCPSRV_CFGMGR_SOCKET_TYPE_SELECT using socket type raw 2025-08-13 14:41:39.302 INFO [kea-dhcp4.dhcpsrv/6564.139635720923328] DHCPSRV_CFGMGR_ADD_IFACE listening on interface enp0s8 2025-08-13 14:41:39.302 INFO [kea-dhcp4.dhcpsrv/6564.139635720923328] DHCPSRV_CFGMGR_SOCKET_TYPE_DEFAULT "dhcp-socket-type" not specified , using default socket type raw vagrant@kea-el:~$ sudo systemctl enable --now kea-dhcp4 Created symlink '/etc/systemd/system/multi-user.target.wants/kea-dhcp4.service' → '/usr/lib/systemd/system/kea-dhcp4.service'. vagrant@kea-el:~$ systemctl status kea-dhcp4 ● kea-dhcp4.service - Kea DHCPv4 Server Loaded: loaded (/usr/lib/systemd/system/kea-dhcp4.service; enabled; preset: disabled) Active: active (running) since Wed 2025-08-13 14:23:30 UTC; 19min ago Invocation: 1bc367b61aa34cafb2550850dcc963b5 Docs: man:kea-dhcp4(8) Main PID: 6455 (kea-dhcp4) Tasks: 7 (limit: 18764) Memory: 2.5M (peak: 5.9M) CPU: 126ms CGroup: /system.slice/kea-dhcp4.service └─6455 /usr/sbin/kea-dhcp4 -c /etc/kea/kea-dhcp4.conf vagrant@kea-el:~$ sudo ss -ulnp | grep :67 UNCONN 0 0 192.168.42.254:67 0.0.0.0:* users:(("kea-dhcp4",pid=6455,fd=11))
-
Start tcpdump to listen for DHCP requests on the host-only interface.
See the example of using
tcpdump
in the previous sections. -
Attach a client VM (e.g. a Kali Linux VM) to the host-only network and start it. Check if the client gets an IP address from the DHCP server, and that the gateway and DNS server are set correctly.
On the server, we follow the system logs and see that a client:
Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.851 INFO [kea-dhcp4.dhcp4/6455.139934786459328] DHCP4_QUERY_LABEL received query: [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956 Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.851 INFO [kea-dhcp4.packets/6455.139934786459328] DHCP4_PACKET_RECEIVED [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956: DHCPDISCOVER (type 1) received from 0.0.0.0 to 255.255.255.255 on interface enp0s8 Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.852 INFO [kea-dhcp4.leases/6455.139934786459328] DHCP4_LEASE_OFFER [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956: lease 192.168.42.100 will be offered Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.852 INFO [kea-dhcp4.packets/6455.139934786459328] DHCP4_PACKET_SEND [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956: trying to send packet DHCPOFFER (type 2) from 192.168.42.254:67 to 192.168.42.100:68 on interface enp0s8 Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.855 INFO [kea-dhcp4.dhcp4/6455.139934778066624] DHCP4_QUERY_LABEL received query: [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956 Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.855 INFO [kea-dhcp4.packets/6455.139934778066624] DHCP4_PACKET_RECEIVED [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956: DHCPREQUEST (type 3) received from 0.0.0.0 to 255.255.255.255 on interface enp0s8 Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.856 INFO [kea-dhcp4.leases/6455.139934778066624] DHCP4_LEASE_ALLOC [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956: lease 192.168.42.100 has been allocated for 7200 seconds Aug 13 14:23:38 kea-el kea-dhcp4[6455]: 2025-08-13 14:23:38.856 INFO [kea-dhcp4.packets/6455.139934778066624] DHCP4_PACKET_SEND [hwtype=1 08:00:27:4d:16:e9], cid=[01:08:00:27:4d:16:e9], tid=0x3f082956: trying to send packet DHCPACK (type 5) from 192.168.42.254:67 to 192.168.42.100:68 on interface enp0s8
On the client, we check the IP config:
┌──(kali@kali)-[~] └─$ ip a show dev eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:4d:16:e9 brd ff:ff:ff:ff:ff:ff inet 192.168.42.100/24 brd 192.168.42.255 scope global dynamic noprefixroute eth1 valid_lft 6954sec preferred_lft 6954sec inet6 fe80::81b4:b70a:c98a:bd5c/64 scope link noprefixroute valid_lft forever preferred_lft forever ┌──(kali@kali)-[~] └─$ ip r default via 192.168.42.254 dev eth1 proto dhcp src 192.168.42.100 metric 101 192.168.42.0/24 dev eth1 proto kernel scope link src 192.168.42.100 metric 101 ┌──(kali@kali)-[~] └─$ cat /etc/resolv.conf # Generated by NetworkManager search linux-training.be nameserver 10.0.2.3
-
From the client VM, ping the default gateway/dhcp server on both interfaces (192.168.42.254 and 10.0.2.15), the DNS server 10.0.2.3 and the dhcp server's external gateway 10.0.2.2. Use
dig
to test that name resolution works. Access a website from the client VM to verify that Internet access is working. -
Add a client reservation the client VM. Find the MAC address and assign a fixed IP address to it. Give it a longer default and maximum lease time than the global settings (e.g. 4 and 10 hours, respectively). Restart the dhcp server and re-attach the client VM to the network. Check if the client gets the reserved IP address and can access the internet.