ISC DHCP Web Console
Веб-интерфейс для управления ISC DHCP сервером: просмотр аренд, управление резервациями MAC→IP, история подключений, метки, фоновый ping и статистика устройств.
Аналог DHCP-страницы домашнего роутера, но для self-hosted isc-dhcp-server на Ubuntu / Debian.
Возможности
- Аренды — таблица всех текущих lease’ов: IP, MAC, hostname, vendor (OUI lookup), статус (active / expired / abandoned), времена начала и окончания. Фильтр по статусу, поиск, сортировка.
- Резервации (MAC→IP) — добавление и удаление через web UI с применением через OMAPI (live, без рестарта dhcpd) и записью в
reservations.confдля персистентности. Кнопка «Закрепить» прямо из строки аренды. - Метки — произвольное имя и заметка для любого MAC, отображается в таблицах. Inline-редактирование кликом.
- История — фоновый поллер каждые 60 секунд снимает срез active-аренд, ведёт таблицу устройств и события (
first_seen,ip_change). Авто-pruning старше 90 дней. - Фоновый ping — ICMP-проба всех active-аренд раз в 60с, агрегаты в окне 24ч: uptime %, средняя/последняя RTT, всего проб.
- Wake-on-LAN — кнопка в строке аренды отправляет magic packet.
- Онлайн-индикатор — зелёная/серая точка слева от IP по содержимому
/proc/net/arp. - Backup / Restore — экспорт всех резерваций и меток в JSON, импорт в режиме
mergeилиreplace. - Валидация конфига — перед применением каждой правки запускается
dhcpd -t -cf <config>, при ошибке изменение откатывается.
Архитектура
| Файл | Назначение |
|---|---|
app.py |
Flask приложение, маршруты /api/* |
leases.py |
Парсер /var/lib/dhcp/dhcpd.leases через isc_dhcp_leases |
reservations.py |
Файловое хранилище резерваций (reservations.conf), атомарная запись |
omapi_client.py |
Обёртка над pypureomapi для live-применения резерваций |
validator.py |
Запуск dhcpd -t для валидации конфига перед сохранением |
history.py |
SQLite, таблицы devices и events, фоновый поллер |
labels.py |
Пользовательские метки для MAC, таблица labels |
ping.py |
Фоновый ICMP ping, агрегаты, таблица ping_samples |
presence.py |
Парсер /proc/net/arp для онлайн-индикатора |
wol.py |
Генерация и отправка Wake-on-LAN magic packet |
oui.py |
MAC→vendor через mac-vendor-lookup |
mac.py |
Общая валидация и нормализация MAC-адресов |
db.py |
Общие SQLite helpers (один файл БД на history, labels, ping) |
static/ |
HTML, CSS, JS (без сборки, ванильный JS) |
tests/ |
228 pytest-тестов |
systemd/ |
Unit-файл и пример env |
install.sh |
Установочный скрипт |
Требования
- Ubuntu 24.04 или совместимый Debian-based дистрибутив
isc-dhcp-serverустановлен и запущен- Python 3.12+
- Для фонового ping:
iputils-ping(есть по умолчанию)
Установка (production)
sudo ./install.sh
Скрипт:
- Копирует код в
/opt/dhcp-web-console - Создаёт venv, ставит зависимости из
requirements.txt - Создаёт
/var/lib/dhcp-web-console/под пользователемdhcpd - Устанавливает systemd unit и
/etc/default/dhcp-web-console(если не существует) - Делает
systemctl daemon-reload
После установки:
-
Добавить в
/etc/dhcp/dhcpd.confстроку:include "/var/lib/dhcp-web-console/reservations.conf"; - Перезапустить dhcpd:
sudo systemctl restart isc-dhcp-server - Запустить веб-консоль:
sudo systemctl enable --now dhcp-web-console - Открыть
http://<host>:9990/
Опционально: OMAPI для live-применения резерваций
-
Сгенерировать ключ:
tsig-keygen -a HMAC-MD5 omapi_key -
Добавить в
/etc/dhcp/dhcpd.conf(использовав вывод выше):key omapi_key { algorithm hmac-md5; secret "<BASE64_СЕКРЕТ_ИЗ_ВЫВОДА>"; } omapi-port 7911; omapi-key omapi_key; -
Раскомментировать в
/etc/default/dhcp-web-console:DHCP_OMAPI_KEY_NAME=omapi_key DHCP_OMAPI_KEY_SECRET=<BASE64_СЕКРЕТ> sudo systemctl restart isc-dhcp-server dhcp-web-console
Без этой настройки резервации всё равно работают, но через systemctl reload isc-dhcp-server (или после перезапуска сервиса).
Конфигурация (env vars)
Все переменные опциональны. Указаны значения по умолчанию.
| Переменная | Default | Назначение |
|---|---|---|
HOST |
0.0.0.0 |
Адрес для bind |
PORT |
9990 |
Порт |
DEBUG |
(unset) | Если задан — Flask в debug-режиме |
| Файлы | ||
DHCP_LEASES_FILE |
/var/lib/dhcp/dhcpd.leases |
Где читать leases |
DHCP_RESERVATIONS_FILE |
/etc/dhcp/reservations.conf |
Куда писать резервации |
DHCP_CONFIG_FILE |
/etc/dhcp/dhcpd.conf |
Для валидации dhcpd -t |
DHCPD_BIN |
/usr/sbin/dhcpd |
Бинарь dhcpd |
DHCP_HISTORY_DB |
/var/lib/dhcp-web-console/history.db |
SQLite файл |
| OMAPI | ||
DHCP_OMAPI_HOST |
127.0.0.1 |
|
DHCP_OMAPI_PORT |
7911 |
|
DHCP_OMAPI_KEY_NAME |
(пусто = выключено) | Имя ключа из dhcpd.conf |
DHCP_OMAPI_KEY_SECRET |
(пусто = выключено) | base64 секрет |
DHCP_OMAPI_TIMEOUT |
5 |
Сек |
| История | ||
DHCP_POLL_INTERVAL |
60 |
Сек |
DHCP_HISTORY_RETENTION_DAYS |
90 |
0 = не удалять |
| Ping | ||
DHCP_PING_ENABLED |
1 |
0 отключает фоновый ping |
DHCP_PING_INTERVAL |
60 |
Сек |
DHCP_PING_TIMEOUT |
1 |
Сек на одну пробу |
DHCP_PING_RETENTION_DAYS |
30 |
|
DHCP_PING_STATS_HOURS |
24 |
Окно для агрегатов |
PING_BIN |
ping |
|
| Presence | ||
PRESENCE_TTL |
15 |
Кэш ARP в секундах |
ARP_TABLE_PATH |
/proc/net/arp |
|
| Wake-on-LAN | ||
WOL_BROADCAST |
255.255.255.255 |
|
WOL_PORT |
9 |
|
| Валидация | ||
DHCP_VALIDATE_TIMEOUT |
10 |
Сек на dhcpd -t |
API
Все ответы — JSON. Все MAC принимаются в любом формате (:, -, ., без разделителей), возвращаются в aa:bb:cc:dd:ee:ff.
Аренды
GET /api/leases?state=all|active|expired|abandoned
Возвращает массив объектов: ip, mac, hostname, vendor, start, end, state, binding_state, label, note, online.
Резервации
GET /api/reservations
POST /api/reservations {name, mac, ip, comment?}
DELETE /api/reservations/<mac>
POST возвращает 201 + объект; 400 при ошибке валидации, 502 при сбое OMAPI (с откатом файла).
Метки
GET /api/labels
PUT /api/labels/<mac> {name, note?}
DELETE /api/labels/<mac>
История
GET /api/history/devices?limit=500
GET /api/history/events?limit=200&mac=<mac>
Ping
GET /api/ping/stats
Per-IP: total, success, uptime, avg_rtt_ms, last_rtt_ms, last_success, last_ts, mac, hostname, label.
Wake-on-LAN
POST /api/wol/<mac>
Backup / Restore
GET /api/backup
POST /api/restore?mode=replace|merge
Body restore: {"version": 1, "reservations": [...], "labels": [...]}. Ответ содержит счётчики reservations_added/skipped, labels_added/skipped.
Health
GET /api/health → {"ok": true}
Разработка
git clone <repo>
cd isc_dhcp_web_console
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
pytest
Локальный запуск (без записи в системные файлы):
DHCP_LEASES_FILE=tests/fixtures/sample.leases \
DHCP_RESERVATIONS_FILE=/tmp/res.conf \
DHCP_HISTORY_DB=/tmp/history.db \
DHCP_PING_ENABLED=0 \
PORT=19990 HOST=127.0.0.1 \
.venv/bin/python app.py
Открыть http://127.0.0.1:19990/.
Тесты
228 тестов в tests/, ~16 секунд на полный прогон. Все модули покрыты unit-тестами + API endpoint тесты с Flask test client.
.venv/bin/pytest # все
.venv/bin/pytest tests/test_leases.py -v
Troubleshooting
Ping показывает 100% packet loss для всех устройств. Под systemd с NoNewPrivileges=true setuid не работает. Юнит уже содержит AmbientCapabilities=CAP_NET_RAW. Если ping всё равно не работает, проверьте:
systemctl cat dhcp-web-console | grep -i cap
Если capability нет — пересоздайте юнит из репозитория.
OMAPI пишет connection failed: [Errno 111]. 1. Убедитесь что в dhcpd.conf есть блок key и omapi-port 7911; 2. Проверьте порт: ss -tlnp | grep 7911 3. Проверьте имя ключа и secret в /etc/default/dhcp-web-console совпадают с dhcpd.conf 4. Рестарт: systemctl restart isc-dhcp-server dhcp-web-console
Аренды не отображаются. Сервис запущен от пользователя dhcpd, ему нужен доступ на чтение /var/lib/dhcp/dhcpd.leases. По умолчанию права 0644, проблем быть не должно. Проверьте:
sudo -u dhcpd cat /var/lib/dhcp/dhcpd.leases | head
OUI vendor пуст для всех устройств. При первом запуске mac-vendor-lookup скачивает базу с IEEE. Если нет интернета — vendor будет пустым. Принудительное обновление:
/opt/dhcp-web-console/.venv/bin/python -c "from mac_vendor_lookup import MacLookup; MacLookup().update_vendors()"
Резервация добавилась, но dhcpd её не видит. Если OMAPI не настроен — нужен systemctl reload isc-dhcp-server после каждой правки. Включите OMAPI (см. выше) — изменения станут мгновенными.
Очистить историю.
sudo rm /var/lib/dhcp-web-console/history.db
sudo systemctl restart dhcp-web-console
Безопасность
Авторизации нет — приложение рассчитано на доверенную локальную сеть. Не публикуйте порт 9990 наружу. Если нужно — поставьте nginx с basic auth и/или TLS перед ним.