Tabla de Contenidos

06 - DHCP: Kea

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…

Configuración básica

docker-compose

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

configuración de Kea

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"
            }
        ]
    }
}

En esta práctica, no nos detendremos en los detalles específicos de cada opción en el archivo de configuración kea-ctrl-agent.conf. Dado que el archivo es relativamente sencillo y se utiliza para configurar aspectos básicos del agente de control de Kea, la explicación detallada de cada opción no es necesaria para los objetivos de esta práctica. Nos centraremos en la funcionalidad general del servidor DHCP y la interacción con los clientes.

Si quieres saber más de cómo configurar este archivo, visita este enlace

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:

Aquí tienes la documentación completa de cómo configurar Kea

Además, crearemos el archivo dhcp4.leases (vacío) en ./lib, que será donde kea almacenará las IPs asignadas.

Asegúrate que los archivos tengan los permisos adecuados, en especial el de escritura en dhcp4.leases

Cambiando la configuración del servidor

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.

Configurar IP estática en la VM del servidor

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.

Clientes

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.

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"
              }
            ]
        }
    ]

En hw-address debes poner la MAC de tu máquina virtual que simula la impresora.

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

Subredes

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"
      }
    ]
  }
}

Fíjate que en el archivo de configuración configuramos las dos interfaces: eth1 y eth2. Ésto es para indicar a Kea que el servidor DHCP servirá IPs por ambas interfaces

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.

Administración BBDD

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.

Práctica

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.