Skip to main content

Monitor JSON lease files and sync them to Unbound DNS server

Project description

json-leases-to-unbound

CI codecov

A Python service that monitors JSON lease files and automatically synchronizes them to an Unbound DNS server in real-time. This tool bridges the gap between DHCP/SLAAC lease management systems and local DNS resolution.

Features

  • ๐Ÿ”„ Real-time Monitoring - Watches lease directories for changes using filesystem events
  • ๐ŸŒ IPv4 & IPv6 Support - Handles both A and AAAA DNS records with automatic PTR record generation
  • ๐ŸŽฏ Smart Lease Management - Automatically adds, updates, and removes DNS entries based on lease state
  • ๐Ÿ”Œ Flexible Unbound Integration - Supports local and remote Unbound servers with custom config files
  • ๐Ÿ“ Comprehensive Logging - Configurable log levels for debugging and monitoring
  • โœ… Well Tested - 91% code coverage with 33 passing tests
    • Coverage badge above powered by Codecov (uploaded from GitHub Actions)

How It Works

  1. Monitors a directory containing JSON lease files (typically from SLAAC or DHCP services)
  2. Extracts hostname and IP address information from lease files
  3. Generates DNS records (AAAA/A and PTR) for each active lease
  4. Synchronizes with Unbound DNS server using unbound-control
  5. Updates DNS records when leases change or expire

DNS Record Generation

For each lease, the service creates:

  • AAAA record (or A for IPv4): Maps hostname to IP address
    • Example: test-host.lan IN AAAA 2001:db8::1
  • PTR record: Maps IP address back to hostname
    • Example: 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa PTR test-host.lan

Installation

From Source

git clone https://github.com/junior/json-leases-to-unbound.git
cd json-leases-to-unbound
pip install -e .

Using pip (when published)

pip install json-leases-to-unbound

Usage

Command Line

json-leases-to-unbound [OPTIONS]

Options

Option Default Description
--log-level INFO Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL
--source /run/slaac-resolver/ Source directory containing JSON lease files
--domain lan Default domain suffix for hostnames
--unbound-server None Remote Unbound server (IP:port)
--config-file None Path to Unbound config file

Examples

Monitor default directory with INFO logging:

json-leases-to-unbound

Monitor custom directory with debug logging:

json-leases-to-unbound --source /var/lib/dhcp/leases --log-level DEBUG

Use custom domain:

json-leases-to-unbound --domain home.local

Connect to remote Unbound server:

json-leases-to-unbound --unbound-server 192.168.1.1:8953

Use custom Unbound config:

json-leases-to-unbound --config-file /etc/unbound/unbound.conf

As a Python Module

from json_leases_to_unbound import main

main(
    log_level='INFO',
    source='/run/slaac-resolver/',
    domain='lan',
    unbound_server=None,
    config_file=None
)

JSON Lease File Format

The service expects JSON files with the following structure:

[
  {
    "Address": [8193, 3512, 0, 0, 0, 0, 0, 1],
    "AddressType": "IPv6",
    "Hostname": "test-host",
    "Expire": "2025-12-31T23:59:59Z"
  },
  {
    "Address": [192, 168, 1, 100],
    "AddressType": "IPv4",
    "Hostname": "another-host",
    "Expire": "2025-12-31T23:59:59Z"
  }
]

Field Descriptions

  • Address: Array of integers representing the IP address
    • IPv6: 8 elements (e.g., [8193, 3512, 0, 0, 0, 0, 0, 1] โ†’ 2001:db8::1)
    • IPv4: 4 elements (e.g., [192, 168, 1, 100] โ†’ 192.168.1.100)
  • AddressType: String indicating address type ("IPv4" or "IPv6")
  • Hostname: String hostname for DNS entry
  • Expire: ISO 8601 timestamp for lease expiration

Requirements

System Requirements

  • Python 3.8 or higher
  • unbound-control binary (part of Unbound DNS server)
  • Appropriate permissions to execute unbound-control

Python Dependencies

  • watchdog>=2.1.9,<3.0 - Filesystem monitoring

Development/Testing Dependencies

  • pytest>=7.0.0
  • pytest-mock>=3.10.0
  • pytest-cov>=4.0.0

Development

Setting Up Development Environment

# Clone the repository
git clone https://github.com/junior/json-leases-to-unbound.git
cd json-leases-to-unbound

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install in editable mode with test dependencies
pip install -e ".[test]"

Running Tests

# Run all tests
pytest

# Run with coverage report
pytest --cov=json_leases_to_unbound --cov-report=html

# Run specific test file
pytest tests/test_core.py

# Run with verbose output
pytest -v

See TESTING.md for detailed testing documentation.

Project Structure

json-leases-to-unbound/
โ”œโ”€โ”€ json_leases_to_unbound/
โ”‚   โ”œโ”€โ”€ __init__.py          # Package initialization
โ”‚   โ”œโ”€โ”€ __main__.py          # Entry point for python -m
โ”‚   โ”œโ”€โ”€ cli.py               # Command-line interface
โ”‚   โ””โ”€โ”€ core.py              # Core functionality
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ conftest.py          # Pytest fixtures
โ”‚   โ”œโ”€โ”€ test_cli.py          # CLI tests
โ”‚   โ”œโ”€โ”€ test_core.py         # Core functionality tests
โ”‚   โ””โ”€โ”€ README.md            # Testing documentation
โ”œโ”€โ”€ pyproject.toml           # Project metadata and build config
โ”œโ”€โ”€ requirements.txt         # Dependencies
โ”œโ”€โ”€ README.md                # This file
โ””โ”€โ”€ TESTING.md              # Testing guide

How It Works Internally

Monitoring Process

  1. Initial Scan: Processes all existing lease files in the source directory
  2. Event Loop: Watches for filesystem events (create, modify, delete)
  3. Change Detection: Compares new lease data with active leases in memory
  4. DNS Updates: Generates and applies appropriate unbound-control commands

Lease State Management

The service maintains an in-memory cache of active leases keyed by filename and hostname. When changes are detected:

  • New Lease: Adds DNS records to Unbound
  • Modified Lease: Removes old records and adds new ones
  • Deleted File: Removes all DNS records associated with that file
  • Expired/Missing Lease: Removes DNS records for leases no longer in file

Unbound Control Integration

The service discovers the unbound-control binary automatically in common locations:

  • /usr/sbin/unbound-control
  • /usr/bin/unbound-control
  • /usr/local/sbin/unbound-control
  • Any location in PATH

Commands used:

  • unbound-control local_datas - Add DNS records
  • unbound-control local_datas_remove - Remove DNS records

Use Cases

SLAAC (Stateless Address Autoconfiguration)

Monitor IPv6 SLAAC leases and provide DNS resolution for auto-configured addresses:

json-leases-to-unbound --source /run/slaac-resolver/ --domain lan

DHCPv4/DHCPv6 Integration

Integrate with DHCP servers that export lease information as JSON:

json-leases-to-unbound --source /var/lib/dhcp/leases/ --domain home.local

Multi-Site DNS Management

Connect to remote Unbound servers for centralized DNS management:

json-leases-to-unbound \
  --source /var/lib/leases/ \
  --unbound-server 10.0.0.1:8953 \
  --domain site1.internal

Troubleshooting

Service Not Finding unbound-control

Ensure unbound-control is installed and in PATH:

which unbound-control

If not found, install Unbound:

# Debian/Ubuntu
sudo apt-get install unbound

# Fedora/RHEL
sudo dnf install unbound

# macOS
brew install unbound

Permission Denied Errors

Ensure the user running the service has permissions to:

  • Read the source lease directory
  • Execute unbound-control
  • Access Unbound's control socket (typically /var/run/unbound/unbound.ctl)

You may need to add the user to the appropriate group:

sudo usermod -a -G unbound your-username

Lease Files Not Being Processed

Check log output with DEBUG level:

json-leases-to-unbound --log-level DEBUG --source /your/path

Verify JSON file format matches the expected structure.

DNS Records Not Appearing

Test unbound-control manually:

# Add a test record
echo "test.lan IN A 192.168.1.100" | sudo unbound-control local_datas

# Query the record
dig @localhost test.lan

# Remove the record
echo "test.lan IN A 192.168.1.100" | sudo unbound-control local_datas_remove

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Workflow

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Run tests (pytest)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Code Style

  • Follow PEP 8 guidelines
  • Add tests for new functionality
  • Update documentation as needed
  • Maintain or improve code coverage

License

MIT License - see LICENSE file for details

Author

Junior - cjuniorfox@gmail.com

Links

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

json_leases_to_unbound-0.0.1.tar.gz (15.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

json_leases_to_unbound-0.0.1-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file json_leases_to_unbound-0.0.1.tar.gz.

File metadata

  • Download URL: json_leases_to_unbound-0.0.1.tar.gz
  • Upload date:
  • Size: 15.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for json_leases_to_unbound-0.0.1.tar.gz
Algorithm Hash digest
SHA256 8e2ed6e82251ad9632327a6e769b0c1c265b3fb565d1848203f62755da62c38e
MD5 3fc9e37ba714f577741c781d74dd39d6
BLAKE2b-256 f745da5f06fbda9d34f0970c2c72f8c8d1a09faa99ec21d98776638705e4e663

See more details on using hashes here.

Provenance

The following attestation bundles were made for json_leases_to_unbound-0.0.1.tar.gz:

Publisher: publish.yml on cjuniorfox/json-leases-to-unbound

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file json_leases_to_unbound-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for json_leases_to_unbound-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2f058950f184d829aebac6d37e72c9a443be749a5ec237cbd9d68ffb07df2bdc
MD5 2d767c791523a6be75108410d27fe432
BLAKE2b-256 cab6eddff371a9d702e85362a34c085c8d3c717960fb0616a36c517e31ae9ba6

See more details on using hashes here.

Provenance

The following attestation bundles were made for json_leases_to_unbound-0.0.1-py3-none-any.whl:

Publisher: publish.yml on cjuniorfox/json-leases-to-unbound

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page