No description
  • Python 62.6%
  • Volt 24.7%
  • Shell 8.4%
  • PHP 4%
  • Makefile 0.3%
Find a file
Poltergeist 0e3758d55f feat: valid-lease-only filter, override unmanaged records, fix UUID tracking
- is_lease_valid(): filter dynamic leases to state=0 and expire > now.
  Expired/declined leases no longer produce DNS records.
- Override unmanaged BIND records: when a valid lease resolves to a
  (zone, name, type) that already has a manually-created record, update
  the value and take ownership instead of skipping. Same-value records
  are adopted silently.
- get_bind_records: unmanaged changed from set to dict so element access
  is available for in-place override without a second XML scan.
- Fix final_managed_uuids: was incorrectly built from the stale-record
  remainder of managed (records about to be deleted) instead of the kept
  set. Introduce kept_uuids tracked during apply loop. Correct formula:
  final = kept_uuids | added_uuids.
2026-03-16 19:56:40 +00:00
src feat: valid-lease-only filter, override unmanaged records, fix UUID tracking 2026-03-16 19:56:40 +00:00
build_plugin.sh fix: move hook script to /usr/local/share/kea/scripts (Kea enforced path) 2026-03-12 13:15:24 +00:00
Makefile initial commit: os-kea-bind-sync plugin 2026-03-12 12:10:41 +00:00
pkg-descr initial commit: os-kea-bind-sync plugin 2026-03-12 12:10:41 +00:00
README.md feat: replace direct hook injection with os-kea PHP patch mechanism 2026-03-12 12:39:28 +00:00

os-bind-leases

OPNsense plugin that syncs Kea DHCPv4/DHCPv6 leases and static reservations into BIND DNS zones managed by the os-bind plugin.

Features

  • Reads dynamic leases and static reservations from Kea (DHCPv4 + DHCPv6)
  • Constructs FQDNs using domain from lease → DHCP global config → subnet → system domain
  • Creates/updates A, AAAA, and PTR records in matching BIND zones
  • Tracks managed records via [kea-sync] description tag — never touches records it didn't create
  • Triggered on OPNsense startup and optionally via Kea run_script hook on lease events
  • WebUI under Services → Kea BIND Sync

Requirements

  • OPNsense 23.x or later
  • os-bind plugin installed and configured with at least one zone
  • os-kea plugin installed with kea-ctrl-agent running (default: http://127.0.0.1:8000)

Installation

git clone ssh://git@forgejo.zint.de:2222/l0rn/os-bind-leases.git
cd os-bind-leases
sh build_plugin.sh

Copy and install on OPNsense:

scp os-kea-bind-sync-1.0.0.pkg root@<opnsense-ip>:/tmp/
ssh root@<opnsense-ip> pkg add /tmp/os-kea-bind-sync-1.0.0.pkg

The post-install script will automatically apply patches to os-kea and restart configd.

Option B — Manual (development/testing)

Copy the plugin files directly onto your OPNsense box:

# On your workstation, from the repo root:
scp -r src/etc/          root@<opnsense-ip>:/
scp -r src/usr/          root@<opnsense-ip>:/usr/
scp -r src/opnsense/mvc/ root@<opnsense-ip>:/usr/local/opnsense/mvc/
scp -r src/opnsense/scripts/ root@<opnsense-ip>:/usr/local/opnsense/scripts/
scp -r src/opnsense/service/ root@<opnsense-ip>:/usr/local/opnsense/service/

Then on OPNsense:

# Make scripts executable
chmod +x /usr/local/opnsense/scripts/OPNsense/KeaBindSync/*.py
chmod +x /usr/local/opnsense/scripts/OPNsense/KeaBindSync/hook.sh
chmod +x /etc/rc.syshook.d/start/99-keabindsync
chmod +x /etc/rc.syshook.d/update/50-keabindsync-repair
chmod +x /etc/rc.syshook.d/early/50-keabindsync-repair

# Create the status directory
mkdir -p /var/db/keabindsync

# Apply patches to os-kea PHP/XML files
python3 /usr/local/opnsense/scripts/OPNsense/KeaBindSync/patch.py apply

# Reload configd so it picks up the new actions
pluginctl -s configd restart

# Clear Volt template cache and reload the web UI
rm -rf /var/cache/opnsense/volt/*
pluginctl -s webgui restart

Kea Hook Setup

The plugin injects the Kea run_script hook by patching the os-kea PHP/XML source files directly. This means the hook is included every time Kea regenerates its configuration — no manual re-patching needed after config changes.

Enable via Kea DHCP Settings

After installing the plugin, go to Services → Kea DHCP → Settings and enable the "Sync Leases to BIND (via os-bind-leases)" checkbox. Save and apply. The hook will be included in the next Kea config generation.

Manual patch management

From Services → Kea BIND Sync → Sync Status you can also:

  • Apply Patches — re-apply patches to os-kea files (e.g. after a manual os-kea reinstall)
  • Remove Patches — restore original os-kea files (restores .bak.keabindsync backups)

Or from the CLI:

python3 /usr/local/opnsense/scripts/OPNsense/KeaBindSync/patch.py apply
python3 /usr/local/opnsense/scripts/OPNsense/KeaBindSync/patch.py remove

How the hook persists

The patcher (patch.py) modifies three files inside the os-kea plugin:

File Change
generalSettings4.xml / generalSettings6.xml Adds a "Sync Leases to BIND" checkbox to the Kea settings form
KeaDhcpv4.xml / KeaDhcpv6.xml Adds registerBindSync BooleanField to the model
KeaDhcpv4.php / KeaDhcpv6.php Injects hook entry into config generation, conditional on the checkbox

Because the hook is injected at PHP config-generation time, it survives every Kea config apply — OPNsense regenerates the JSON config from PHP, and the hook is always included when the checkbox is checked.

After a firmware update that replaces os-kea files, patches are automatically re-applied by /etc/rc.syshook.d/update/50-keabindsync-repair and /etc/rc.syshook.d/early/50-keabindsync-repair.

Backups of the original os-kea files are kept with a .bak.keabindsync suffix and are restored when patches are removed.

Verify hook is active

Check the generated Kea config:

grep -r "run_script" /usr/local/etc/kea/

Configuration

Settings are in Services → Kea BIND Sync → Settings:

Setting Default Description
Enabled Yes Enable/disable the plugin
Kea API URL http://127.0.0.1:8000 kea-ctrl-agent endpoint
Sync PTR records Yes Create PTR records in reverse zones if they exist
Default TTL 300 TTL (seconds) for created records

How it works

  1. Queries Kea for all active leases (lease4-get-all, lease6-get-all) and static reservations (reservation-get-all per subnet, or falls back to config-embedded reservations if host_cmds hook isn't loaded)
  2. Builds an FQDN for each host using this priority:
    • Hostname already contains . → use as-is
    • domain-name option-data on the global DHCP config
    • domain-name option-data on the subnet
    • System domain from OPNsense config
  3. Finds the best matching BIND zone by longest suffix match
  4. Creates/updates A, AAAA, and PTR records — all tagged [kea-sync] in the description
  5. Deletes any [kea-sync] records that no longer have a matching lease
  6. Never modifies records without the [kea-sync] tag
  7. Writes config.xml and calls configctl bind reconfigure

Logs: /var/log/keabindsync.log Status: /var/db/keabindsync/status.json Lock: /var/run/keabindsync.lock (prevents concurrent syncs)


Troubleshooting

Sync does nothing / no records created

  • Check that BIND zones are enabled in the os-bind plugin
  • Verify Kea leases have hostnames set (lease4-get-all via curl)
  • Check /var/log/keabindsync.log

Hook not firing after Kea restart

  • Make sure the "Sync Leases to BIND" checkbox is enabled in Services → Kea DHCP → Settings
  • Re-apply patches: python3 /usr/local/opnsense/scripts/OPNsense/KeaBindSync/patch.py apply

reservation-get-all errors in log

  • The host_cmds hook is not loaded in Kea — plugin falls back to config-embedded reservations automatically

Permission denied writing config.xml

  • Scripts must run as root. configd runs as root by default; verify with ps aux | grep configd

Patches lost after os-kea firmware update

  • Re-apply via the WebUI (Apply Patches button) or CLI: patch.py apply
  • The rc.syshook.d/update hook should handle this automatically on next update