DHCP Service Bot 90736253b1 Initial commit: DHCP Web Manager
Flask-based web UI for managing ISC DHCP server (dhcpd).

Features:
- subnet / static host binding CRUD via web
- listen interface configuration (INTERFACESv4)
- active lease query + pool utilization stats
- service status, reload/restart, syntax check
- user auth + operation logs
- automatic backup before each config change
- safe change pipeline: write file → dhcpd -t → systemctl restart + is-active verify
- error messages include journalctl tail for diagnosis
2026-07-09 17:09:23 +08:00
2026-07-09 17:09:23 +08:00
2026-07-09 17:09:23 +08:00
2026-07-09 17:09:23 +08:00
2026-07-09 17:09:23 +08:00
2026-07-09 17:09:23 +08:00

DHCP Web Manager

一个用 Flask 写的轻量级 ISC DHCP (dhcpd) Web 管理界面,底层直接复用系统自带的 isc-dhcp-server, 不引入额外 daemon。提供 Web 界面来管理 subnet / 静态 host 绑定 / 监听接口 / 查看租约 / 操作日志。

功能

  • 🟢 子网(subnet)增删改:网段、掩码、网关、DNS、地址池、租期
  • 🟢 静态绑定(host):MAC + 固定 IP + 主机名
  • 🟢 监听接口配置(INTERFACESv4
  • 🟢 租约查询(按 IP/MAC/主机名搜索)+ 地址池使用率
  • 🟢 服务状态 + reload / restart + 语法检查
  • 🟢 操作日志(谁在什么时间做了什么)
  • 🟢 用户名密码登录 + 改密
  • 🟢 所有变更自动:写文件 → 备份 → dhcpd -t 语法检查 → systemctl reload isc-dhcp-server

文件结构

dhcp-service/
├── app.py              # Flask 主程序
├── dhcp_ops.py         # dhcpd.conf 读写 + leases 解析 + 服务控制
├── requirements.txt
├── instance/dhcp.db    # SQLite(用户、日志)
├── backups/            # dhcpd.conf 自动备份(最多保留 20 份)
├── templates/          # Jinja2 模板
└── static/style.css

安装

apt-get install -y isc-dhcp-server  # 后端 DHCP 服务
pip3 install -r requirements.txt --break-system-packages

启动

开发模式:

python3 app.py   # 监听 0.0.0.0:5100

生产模式(推荐用 gunicorn):

gunicorn --workers 2 --bind 0.0.0.0:5100 'app:app'

默认账号:admin / admin(首次登录后请到「改密」修改)。

关键设计

配置文件组织

我们改写 /etc/dhcp/dhcpd.conf,但只动我们管理的区段——通过标记:

# 原有的通用配置(option domain-name 等)保持不变
option domain-name "example.org";
default-lease-time 600;
...

# >>> DHCP-WEB-MANAGER BEGIN >>>
authoritative;
subnet 192.168.1.0 netmask 255.255.255.0 {
  option routers 192.168.1.1;
  ...
}
host printer {
  hardware ethernet aa:bb:cc:dd:ee:01;
  fixed-address 192.168.1.50;
}
# <<< DHCP-WEB-MANAGER END <<<

系统原有的 option / 注释 / 不认识的配置都不丢。备份在 backups/dhcpd.conf.<timestamp>.bak(最多 20 份)。

变更流程

所有写操作都走同一个流程(在 app.py: apply_and_reload):

  1. 读现有 subnets/hosts → 追加/替换 → 生成新的 conf 文本
  2. 先备份到 backups/
  3. 写到 /etc/dhcp/dhcpd.conf
  4. dhcpd -t 语法检查 — 失败则回滚到备份(不修改数据库)
  5. 通过则 systemctl reload isc-dhcp-server(不丢连接)

接口绑定

/etc/default/isc-dhcp-server 中的 INTERFACESv4="enp0s31f6"。 保存接口配置会重启(而非 reload)服务,因为接口绑定变更不支持热加载。

租约解析

直接读 /var/lib/dhcp/dhcpd.leases 并 regex 解析 lease 块。注意 dhcpd 每次写入 leases 文件时 会以临时名写入后 rename 替换,所以解析时可能短暂读到空内容——前端解析失败的话就是 0 条,不报错。

部署到生产

按本机惯例,dev 仓库在 /root/dhcp-service/,生产部署在 /root/.openclaw/workspace/dhcp-service/

# 1. 把代码复制到生产目录
mkdir -p /root/.openclaw/workspace/dhcp-service
cp -r /root/dhcp-service/{app.py,dhcp_ops.py,requirements.txt,templates,static} \
      /root/.openclaw/workspace/dhcp-service/

# 2. systemd unit (示例)
cat > /etc/systemd/system/dhcp-web-manager.service <<EOF
[Unit]
Description=DHCP Web Manager (Flask)
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/root/.openclaw/workspace/dhcp-service
ExecStart=/usr/bin/gunicorn --workers 2 --bind 0.0.0.0:5100 'app:app'
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now dhcp-web-manager

访问 http://<server-ip>:5100

注意事项

  • 本程序必须以 root 跑,因为需要写 /etc/dhcp/dhcpd.conf 和控制 systemd 服务。
  • 修改 subnet/host 走 reload(不丢现有租约),但改 INTERFACESv4 必须 restart。
  • dhcpd -t 语法检查失败时不会回滚配置,但也不会 reload——服务继续按旧配置运行, 操作日志记录失败原因。修改者可以修正后重试。
  • 数据库 instance/dhcp.db 不要 commit 到 git。

备份恢复

所有 conf 改动前会自动备份到 backups/,恢复:

ls backups/
cp backups/dhcpd.conf.20260709_120000.bak /etc/dhcp/dhcpd.conf
systemctl reload isc-dhcp-server
S
Description
No description provided
Readme 76 KiB
测试版本 Latest
2026-07-09 17:58:50 +08:00
Languages
Python 58.7%
HTML 30.5%
CSS 10.8%