En esta práctica, vamos a instalar y configurar un servidor DHCP Kea utilizando Docker. Kea es una solución moderna y potente para la gestión de direcciones IP en redes.
A diferencia del antiguo ISC DHCP Server, Kea está construido con un enfoque moderno y modular, permitiendo una mayor flexibilidad y escalabilidad. Kea soporta una amplia gama de características avanzadas, incluyendo soporte para múltiples subredes, reservas de IP, y almacenamiento de datos en bases de datos SQL como MySQL o PostgreSQL.
Empezaremos creando un servidor DHCP Kea y un par de clientes que obtendrán sus direcciones IP del servidor. Posteriormente, iremos ampliando la funcionalidad con características avanzadas como la reserva de IPs por dirección MAC, la configuración de múltiples subredes y el uso de MySQL como almacenamiento de datos…
Para esta práctica, lo primero que haremos será crear la máquina virtual derivada de Alpine_base que será nuestro servidor DHCP. Esta máquina tendrá dos adaptadores de red:
Usaremos la imagen smr-kea-server para crear nuestro contenedor docker;
name: dhcp-kea
services:
dhcp-server:
image: smr/kea-server
container_name: dhcp-kea
network_mode: host
volumes:
- ./config:/etc/kea
- ./lib:/var/lib/kea
restart: always
Detalles importantes
Una vez tenemos nuestro contenedor listo, es hora de configurar el servidor DHCP. Para ello, necesitamos crear dos archivos de configuración: kea-ctrl-agent.conf y kea-dhcp4.conf dentro de nuestra carpeta config.
Copia el siguiente contenido en el archivo kea-ctrl-agent.conf:
{
"Control-agent": {
"http-host": "0.0.0.0",
"http-port": 8000,
"control-sockets": {
"dhcp4": {
"socket-type": "unix",
"socket-name": "/run/kea/control_socket_4"
}
},
"loggers": [
{
"name": "kea-ctrl-agent",
"output_options": [
{
"output": "stdout"
}
],
"severity": "INFO"
}
]
}
}
El contenido del archivo kea-dhcp4.conf será:
{
"Dhcp4": {
# First we set up global values
"valid-lifetime": 4000,
"renew-timer": 1000,
"rebind-timer": 2000,
# Next we set up the interfaces to be used by the server.
"interfaces-config": {
"interfaces": ["eth1"]
},
# And we specify the type of lease database
"lease-database": {
"type": "memfile",
"persist": true,
"name": "/var/lib/kea/dhcp4.leases"
},
# Finally, we list the subnets from which we will be leasing addresses.
"subnet4": [
{
"id": 1,
"subnet": "172.18.2.0/24",
"pools": [
{
"pool": "172.18.2.70 - 172.18.2.90"
}
],
"interface": "eth1"
}
]
}
}
Vamos a explicar brevemente cada concepto:
Además, crearemos el archivo dhcp4.leases (vacío) en ./lib, que será donde kea almacenará las IPs asignadas.
Antes de levantar el entorno de laboratorio final, necesitamos que nuestro servidor DHCP tenga una IP estática y que la red sea interna, de forma que los clientes puedan comunicarse solo dentro del entorno controlado.
Edita el archivo de interfaces de red en Alpine:
sudo nano /etc/network/interfaces
Y asegúrate de tener algo similar a:
auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet static address 172.18.2.2 netmask 255.255.255.0
Explicación:
Guarda los cambios y reinicia la interfaz de red:
sudo service networking restart
Verifica la IP:
ip a
Debe mostrar 172.18.2.2/24 en eth1.
Una vez tenemos nuestro servidor configurado y en marcha, creamos una máquina virtual a partir de Alpine base con red interna (asegúrate de que tiene el mismo nombre de red que el servidor).
Si todo ha ido bien, debería obtener la ip del servidor al arrancar:
udhcpc: started, v1.37.0 udhcpc: broadcasting discover udhcpc: broadcasting discover udhcpc: broadcasting select for 172.18.2.70, server 172.18.2.2 udhcpc: lease of 172.18.2.70 obtained from 172.18.2.2, lease time 4000
Crearemos otra máquina igual a la que llamaremos impresora, para poder probar la reserva de host.
Una reserva de host en un servidor DHCP permite asignar una dirección IP específica a un dispositivo basándose en su dirección MAC. Esto asegura que el dispositivo recibirá siempre la misma IP cada vez que se conecte a la red. Esta función es especialmente útil en escenarios donde se necesita una IP fija, como en servidores, impresoras de red, o cámaras de seguridad, facilitando la gestión y el acceso constante a estos dispositivos sin necesidad de configurarlos manualmente en cada uno de ellos.
Para probar esta funcionalidad, configuraremos Kea para indicar que reserve una IP para la máquina con la MAC de nuestra “impresora”. Ésto se puede hacer mediante la opción reservations en el archivo de configuración kea-dhcp4.conf:
"subnet4": [
{
"id": 1,
"subnet": "172.18.2.0/24",
"pools": [
{
"pool": "172.18.2.70 - 172.18.2.90"
}
],
"interface": "eth1",
"reservations": [
{
"hw-address": "CC:DD:EE:FF:00:11",
"ip-address": "172.18.2.84"
}
]
}
]
Reiniciamos nuestro contenedor docker y al arrancar la máquina virtual “impresora”, debería darnos siempre la 172.168.2.84:
udhcpc: started, v1.37.0 udhcpc: broadcasting discover udhcpc: broadcasting discover udhcpc: broadcasting select for 172.18.2.84, server 172.18.2.2 udhcpc: lease of 172.18.2.84 obtained from 172.18.2.2, lease time 4000
La capacidad de un servidor DHCP para servir diferentes rangos de IP a diferentes subredes permite una gestión más eficiente y flexible de redes complejas. Esto es útil en entornos donde existen múltiples subredes que requieren configuraciones de red específicas, como oficinas con departamentos separados, redes de invitados y redes internas. Al administrar varios rangos de IP, el servidor DHCP puede asignar automáticamente las direcciones adecuadas a cada dispositivo según la subred a la que pertenece, lo que facilita la segmentación de la red, mejora la seguridad y optimiza el uso de los recursos de red.
Vamos a simular un entorno con diferentes subredes. Para ello, crearemos 2 adaptadores de red en nuestro servidor. Ambos estarán en “Red interna”, con nombre intnet1 y intnet2 respectívamente. El primer cliente lo añadiremos a la red intnet1 y la impresora a intnet2. La primera subred tendrá el rango de direcciones 172.18.2.0/24 y la segunda 172.18.3.0/24.
Además, tendremos que configurar IP fija para ambos adaptadores de red de nuestro servidor:
auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet static address 172.18.2.2 netmask 255.255.255.0 auto eth2 iface eth2 inet static address 172.18.3.2 netmask 255.255.255.0
{
"Dhcp4": {
"valid-lifetime": 4000,
"renew-timer": 1000,
"rebind-timer": 2000,
"interfaces-config": {
"interfaces": ["eth1", "eth2"]
},
"lease-database": {
"type": "memfile",
"persist": true,
"name": "/var/lib/kea/dhcp4.leases"
},
"subnet4": [
{
"id": 1,
"subnet": "172.18.2.0/24",
"pools": [
{
"pool": "172.18.2.70 - 172.18.2.90"
}
],
"interface": "eth1",
"reservations": [
{
"hw-address": "08:00:27:CD:6D:00",
"ip-address": "172.18.2.84"
}
]
},
{
"id": 2,
"subnet": "172.18.3.0/24",
"pools": [
{
"pool": "172.18.3.70 - 172.18.3.90"
}
],
"interface": "eth2"
}
]
}
}
Lo último será cambiar los nombres de red de nuestros dos clientes: intnet1 a la impresora (para que le de la IP fija que configuramos) y intnet2 a la otra máquina.
En nuestra configuración de Kea, indicamos al principio que la base de datos utilizada para almacenar la información de los arrendamientos de IP era en memoria mediante la opción lease-database:
"lease-database": {
"type": "memfile",
"persist": true,
"name": "/var/lib/kea/dhcp4.leases"
}
De hecho, si arrancamos el contenedor Docker y hacemos varias peticiones al servidor para que sirva IPs, podemos ver en el contenido de ese archivo (/var/lib/kea/dhcp4.leases) las IPs que ha ido sirviendo el servidor:
address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id 172.18.2.70,00:11:22:33:44:55,,4000,1724234915,1,0,0,2d1a2137a76e,0,,0 172.18.3.70,66:77:88:99:aa:bb,,4000,1724234927,2,0,0,7366e6e03c6d,0,,0 172.18.3.84,cc:dd:ee:ff:00:11,,4000,1724234943,2,0,0,d3b9c1306036,0,,0 172.18.3.84,cc:dd:ee:ff:00:11,,4000,1724235702,2,0,0,d3b9c1306036,0,,0 172.18.3.70,66:77:88:99:aa:bb,,4000,1724235760,2,0,0,7366e6e03c6d,0,,0 172.18.2.70,00:11:22:33:44:55,,4000,1724235778,1,0,0,2d1a2137a76e,0,,0
Podemos variar este comportamiento para hacer que toda esa información se almacene en una bbdd, lo que ofrece varios beneficios:
Para implementar este cambio, realizaremos los siguientes ajustes:
Uno de los primeros problemas que debemos enfrentar es garantizar que, al iniciar el servidor Kea, la base de datos ya esté lista. Esto es importante porque, si Kea intenta conectarse a la base de datos antes de que esta esté completamente operativa, generará errores de conexión. Para solucionar esto, normalmente podríamos usar una herramienta como kea-admin para crear la base de datos y las tablas necesarias antes de que el servidor Kea comience a funcionar.
Sin embargo, utilizar kea-admin complica el proceso de creación de contenedores. Esto nos obliga a buscar una solución que sea más sencilla y automática dentro del entorno de Docker.
En lugar de utilizar kea-admin, optamos por una solución más directa: crear la base de datos y las tablas necesarias a través de un dhcpdb_create.zip. Este script contendrá todas las instrucciones necesarias para crear la base de datos y las tablas que Kea necesita para funcionar correctamente.
Guardaremos este script en una carpeta llamada script dentro de nuestro proyecto. Luego, aprovechamos una funcionalidad de MySQL en Docker: cuando iniciamos un contenedor de MySQL, el propio contenedor ejecuta automáticamente cualquier script que esté en la carpeta docker-entrypoint-initdb.d.
Al mapear la carpeta script a docker-entrypoint-initdb.d en nuestro archivo docker-compose.yml, logramos que MySQL ejecute el script automáticamente al iniciarse el contenedor. De esta forma, la base de datos y las tablas estarán listas antes de que Kea intente conectarse, resolviendo el problema de dependencia entre los dos servicios.
Este enfoque no solo simplifica el proceso de creación de la base de datos, sino que también asegura que el contenedor MySQL siempre tenga la estructura correcta sin necesidad de ejecutar comandos adicionales o complicar el flujo de creación de contenedores.
En nuestro entorno de Docker, es crucial asegurarnos de que el contenedor kea-server no intente conectarse a la base de datos MySQL hasta que esta esté completamente lista y aceptando conexiones. Para lograr esto, utilizamos la funcionalidad de healthcheck en Docker, que nos permite verificar si un servicio está listo antes de que otro servicio dependiente intente conectarse:
dhcp-mysql:
image: mysql:8.0
container_name: kea-mysql
networks:
dhcp-net:
ipv4_address: 172.18.4.3
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: dhcp_kea
volumes:
- ./mysql:/var/lib/mysql
- ./script:/docker-entrypoint-initdb.d
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u root --password=root
interval: 10s
retries: 5
start_period: 30s
timeout: 10s
Luego, añadimos la clave depends_on en el contenedor kea-server. Esto garantiza que kea-server no se cargue hasta que el contenedor kea-mysql pase las comprobaciones de salud.
dhcp-server:
build: .
depends_on:
dhcp-mysql:
condition: service_healthy
container_name: kea-server
networks:
dhcp-1floor:
ipv4_address: 172.18.2.2
dhcp-2floor:
ipv4_address: 172.18.3.2
dhcp-net:
ipv4_address: 172.18.4.2
volumes:
- ./config:/etc/kea
Fíjate que en nuestra configuración de Docker, utilizamos la opción build para crear una imagen personalizada del contenedor kea-server. Esta opción nos permite definir instrucciones adicionales en un Dockerfile que ajusta la imagen base según nuestras necesidades. En este caso, estamos utilizando un Dockerfile que extiende la imagen base de Kea para añadir algunas herramientas adicionales que requerimos en nuestro entorno:
FROM docker.cloudsmith.io/isc/docker/kea-dhcp4:2.7.0 RUN apk update && apk add --no-cache bash curl mysql-client
La opción build es necesaria porque estamos personalizando la imagen base de Kea. La imagen oficial no incluye todas las herramientas que necesitamos, como el cliente MySQL o Bash. En lugar de crear un contenedor desde una imagen que no está completamente adaptada a nuestras necesidades, utilizamos esta opción para construir una imagen que ya contiene todas las herramientas necesarias, lo que hace más eficiente el desarrollo y la ejecución de nuestra aplicación.
Al agregar estas utilidades directamente en la imagen, no tenemos que instalarlas cada vez que el contenedor se inicie, lo que ahorra tiempo y evita problemas de configuración en el futuro.
Por último, sólo nos queda cambiar la configuración de kea en el archivo kea-dhcp4.conf para indicar que vamos a utilizar MySql como almacenamiento:
"lease-database": {
"type": "mysql",
"host": "kea-mysql",
"user": "root",
"password": "root",
"name": "dhcp_kea"
},
Una vez que hemos configurado Kea para que utilice MySQL como almacenamiento, verificamos que las asignaciones de direcciones IP (leases) se están guardando correctamente en la base de datos. Para hacer esta comprobación, podemos realizar una consulta SQL sencilla sobre la tabla lease4, que es donde se almacenan las direcciones IPv4 asignadas.
Para ello, accedemos a cualquier cliente para pedir una IP al servidor DHCP. A continuación, entramos al contenedor mysql y ejecutamos:
mysql -u root -p
Una vez estamos conectados a MySql:
use dhcp_kea;
SELECT * FROM lease4;
Esto mostrará todas las entradas de direcciones IPv4 asignadas, junto con información relevante como el tiempo de expiración de la lease, la dirección MAC del cliente, entre otros detalles.
Configura un servidor Kea DHCP para una oficina de tres pisos. Cada piso debe tener la siguiente configuración:
Piso 1:
Subred: 192.168.1.0/24 Dispositivos: 3 PCs y una impresora Reserva de IP para una impresora: 192.168.1.20
Piso 2:
Subred: 192.168.2.0/24 Dispositivos: 2 PCs Reserva de IP para una impresora: 192.168.2.15
Piso 3:
Subred: 192.168.3.0/24 Dispositivos: 3 PCs
El almacenamiento de las asignaciones debe hacerse en una bbdd MySql.
Asegúrate de que los PCs y las impresoras obtengan las configuraciones de IP adecuadas.