modified: README.md
modified: netbox_dns_bridge/notify_handler.py modified: netbox_dns_bridge/request_handler.py modified: netbox_dns_bridge/signals/netbox_dns_zones.py Disable NOTIFY feature by default and add setting to enable it.
This commit is contained in:
1 parent
cb4886f032
commit
73544a4335
4 files changed
+70
-53
No files matched your search
@@ -1,10 +1,10 @@
|
||||
# Netbox DNS Bridge
|
||||
(Formerly Netbox Bind Provisioner)
|
||||
|
||||
The NetBox DNS Bridge plugin extends [NetBox DNS](https://github.com/sys4/netbox-plugin-dns) by
|
||||
embedding a lightweight DNS server directly within NetBox. It acts as a bridge between NetBox DNS
|
||||
and your existing DNS infrastructure, leveraging native DNS mechanisms for seamless integration.
|
||||
These include zone transfers (RFC 5936), catalog zones (RFC 9432), and dynamic updates (RFC 2136).
|
||||
These include zone transfers (RFC 5936), catalog zones (RFC 9432), and notifying clients about zone
|
||||
changes (RFC 1996).
|
||||
|
||||
<a href="https://pypi.org/project/netbox-plugin-dns-bridge/"><img src="https://img.shields.io/pypi/v/netbox-plugin-dns-bridge" alt="PyPi"></a>
|
||||
<a href="https://github.com/suraxius/netbox-plugin-dns-bridge/stargazers"><img src="https://img.shields.io/github/stars/suraxius/netbox-plugin-dns-bridge?style=flat" alt="Stars Badge"></a>
|
||||
@@ -18,12 +18,6 @@ These include zone transfers (RFC 5936), catalog zones (RFC 9432), and dynamic u
|
||||
<a href="https://pepy.tech/project/netbox-plugin-dns-bridge"><img src="https://static.pepy.tech/personalized-badge/netbox-plugin-dns-bridge?period=monthly&left_color=BLACK&right_color=BLUE&left_text=Downloads%2fMonth" alt="Downloads/Month"></a>
|
||||
<a href="https://pepy.tech/project/netbox-plugin-dns-bridge"><img src="https://static.pepy.tech/personalized-badge/netbox-plugin-dns-bridge?period=weekly&left_color=BLACK&right_color=BLUE&left_text=Downloads%2fWeek" alt="Downloads/Week"></a>
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
DISCLAIMER: The dynamic update endpoint isn't here yet. Its planned - without a deadline.
|
||||
|
||||
## Plugin configuration
|
||||
While providing Zone transfers via AXFR, the Server also exposes specialized catalog zones that BIND
|
||||
and other RFC9432 compliant DNS Servers use to automatically discover newly created zones and remove
|
||||
@@ -52,44 +46,55 @@ Parameter | Description
|
||||
--address | IP of interface to bind to (defaults to 0.0.0.0)
|
||||
|
||||
### Plugin settings
|
||||
Plugin settings should be configured under `PLUGINS_CONFIG` in `netbox_dns_bridge`:
|
||||
```json
|
||||
PLUGINS_CONFIG = {
|
||||
'netbox_dns_bridge': {
|
||||
...
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### TSIG Authentication
|
||||
Following sets the TSIG key that allows clients to query the transfer endpoint and also they key to
|
||||
be used to sign NOTIFY messages to clients on zone changes. Each view should have its own unique
|
||||
key to allow the plugin to identify the view the client is trying to access. Re-using the key for
|
||||
multiple views yields unpredicted behavior.
|
||||
```json
|
||||
'tsig_keys': {
|
||||
'the-view-name-here': {
|
||||
'keyname': 'the-tsig-key-name-here',
|
||||
'algorithm': 'hmac-sha256',
|
||||
'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Client notification on zone change
|
||||
To enable the NOTIFY feature, set following:
|
||||
```
|
||||
'notify_clients': True
|
||||
```
|
||||
This causes the plugin to schedule background jobs which send out NOTIFY messages to all clients
|
||||
that have previously successfullly requested a zone transfer from the transfer endpoint.
|
||||
|
||||
After how many hours that are client last checked in on a zone with a SOA or transfer request,
|
||||
should the client be considered dead and no longer be notified. Default is 24 hours.
|
||||
```
|
||||
notify_client_dead_after_hours: 24
|
||||
```
|
||||
|
||||
Setting | Description
|
||||
--------------------| ---------------------------------------------------------
|
||||
tsig_keys | Maps a TSIG Key to be used for each view.
|
||||
notify_clients | When set to true, turns on the notify mechanism to inform downsteams
|
||||
|
||||
## Plugin compatibility
|
||||
This plugin is an extension to the netbox-plugin-dns plugin. As such the versioning of this plugin
|
||||
was changed to match the one of the netbox-plugin-dns plugin closely. To guarantee compatability,
|
||||
ensure that the major and minor version match between both plugins.
|
||||
For example, when using netbox-plugin-dns `v1.5.5` install netbox-plugin-dns-bridge `v1.5.x`.
|
||||
|
||||
## Post 1.0.7 Upgrade Guide
|
||||
Applies only if you have a <= 1.0.7 installation. If you are freshly installing this plugin,
|
||||
go on to [Installation Guide](#installation-guide).
|
||||
|
||||
After version 1.0.7, the project was restructured and renamed. Until that point version updates
|
||||
happened more or less automatically. However, this shift is more of a move from one plugin to
|
||||
another as the package name has changed.
|
||||
|
||||
This should not be an issue since the slave DNS Servers connected to Netbox can operate
|
||||
independently while this plugin is being upgraded.
|
||||
|
||||
Make sure to note down the catalog zone serial number before going further.
|
||||
|
||||
1. Remove Netbox Bind Provisioner package
|
||||
- Remove `netbox-plugin-bind-provisioner` from **local_dependencies.txt**
|
||||
- Uninstall the package: `pip uninstall netbox-plugin-bind-provisioner`
|
||||
2. Install Netbox DNS Bridge package
|
||||
- Install the package `pip install netbox-plugin-dns-bridge`
|
||||
- Put `netbox-plugin-dns-bridge` in your **local_dependencies.txt** so it will be installed on
|
||||
next upgrade.
|
||||
3. Adjust your `configuration.py`
|
||||
- Change the plugin name from `netbox-plugin-bind-provisioner` to `netbox-plugin-dns-bridge`
|
||||
- Change the key in `PLUGIN_CONFIG` from `netbox_plugin_bind_provisioner` to `netbox_dns_bridge`
|
||||
4. Run migrations: `manage.py migrate`
|
||||
5. Restore the catalog zone serial you noted down previously so that your slave dns servers continue
|
||||
to pull the changes: `manage.py dns-settings set catalog-zone-soa-serial yourserialnumber`
|
||||
6. Start the `dns-transfer-endpoint` service.
|
||||
7. Mission accomplished.
|
||||
This plugin extends the netbox-plugin-dns plugin. As such the versioning was changed to match the
|
||||
one of netbox-plugin-dns more closely. To guarantee compatability, ensure that the major and minor
|
||||
version match between both plugins. For example, when using netbox-plugin-dns `v1.5.5` install
|
||||
netbox-plugin-dns-bridge `v1.5.x`.
|
||||
|
||||
## Installation guide
|
||||
This setup provisions a BIND9 server directly with DNS data from NetBox. BIND9 can optionally run on
|
||||
@@ -222,6 +227,7 @@ This guide assumes:
|
||||
########## ZONES ##########
|
||||
view "public" {
|
||||
match-clients { public; };
|
||||
allow-notify { key "public_view_key"; };
|
||||
|
||||
catalog-zones {
|
||||
zone "catz"
|
||||
@@ -240,6 +246,7 @@ This guide assumes:
|
||||
|
||||
view "private" {
|
||||
match-clients { private; };
|
||||
allow-notify { key "private_view_key"; };
|
||||
|
||||
catalog-zones {
|
||||
zone "catz"
|
||||
|
||||
@@ -12,6 +12,7 @@ from .logger import get_logger
|
||||
from .models import SeenTransferClients
|
||||
|
||||
logger = get_logger(__name__)
|
||||
SETTINGS = settings.PLUGINS_CONFIG["netbox_dns_bridge"]
|
||||
|
||||
|
||||
class SendDNSNotify(JobRunner):
|
||||
@@ -19,7 +20,7 @@ class SendDNSNotify(JobRunner):
|
||||
name = "Send DNS NOTIFY"
|
||||
|
||||
def _load_tsig_key(self, view_name: str) -> dns.tsig.Key:
|
||||
tsig_keys = settings.PLUGINS_CONFIG["netbox_dns_bridge"].get("tsig_keys", {})
|
||||
tsig_keys = SETTINGS.get("tsig_keys", {})
|
||||
if view_name not in tsig_keys:
|
||||
raise RuntimeError(f"No TSIG key configured for view '{view_name}'")
|
||||
|
||||
@@ -50,7 +51,8 @@ class SendDNSNotify(JobRunner):
|
||||
view = View.objects.get(name=view_name)
|
||||
tsig_key = self._load_tsig_key(view_name)
|
||||
|
||||
seen_cutoff = timezone.now() - timezone.timedelta(hours=1)
|
||||
cutoff_hours = SETTINGS.get("notify_client_dead_after_hours", 24)
|
||||
seen_cutoff = timezone.now() - timezone.timedelta(hours=cutoff_hours)
|
||||
clients = SeenTransferClients.objects.filter(
|
||||
view=view, last_seen__gte=seen_cutoff
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ import dns.exception
|
||||
import dns.renderer
|
||||
from .logger import get_logger
|
||||
from django.utils import timezone
|
||||
from netbox_dns.models import Zone, Record
|
||||
from netbox_dns.models import Zone, Record, View
|
||||
from netbox_dns_bridge import catalog_zone_manager as catzm
|
||||
from .models import SeenTransferClients
|
||||
|
||||
@@ -103,7 +103,8 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
|
||||
# Ensure rdataset has the same rdclass as the zone
|
||||
if rdataset.rdclass != zone.rdclass:
|
||||
raise ValueError(
|
||||
f"rdataset rdclass {rdataset.rdclass} does not match zone rdclass {zone.rdclass}"
|
||||
f"rdataset rdclass {rdataset.rdclass} does not match "
|
||||
f"zone rdclass {zone.rdclass}"
|
||||
)
|
||||
|
||||
# Check if the rdataset has any rdata before creating an RRset
|
||||
@@ -143,6 +144,13 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
|
||||
wire = response.to_wire(multi=False)
|
||||
self._send_response(wire)
|
||||
|
||||
def _track_seen_client(source_ip: str, view: View) -> None:
|
||||
SeenTransferClients.objects.update_or_create(
|
||||
source_ip=source_ip,
|
||||
view=view,
|
||||
defaults={"last_seen": timezone.now()},
|
||||
)
|
||||
|
||||
def _handle_soa_request(self, query, soa_rrset, zone, peer, nb_view, dname) -> None:
|
||||
# We assume that the SOA rdataset has at least one record (it usually does).
|
||||
soa_rdata = soa_rrset[0] # Get the first SOA record
|
||||
@@ -274,13 +282,6 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
|
||||
)
|
||||
wire = r.get_wire()
|
||||
self._send_response(wire)
|
||||
|
||||
SeenTransferClients.objects.update_or_create(
|
||||
source_ip=peer,
|
||||
view=nb_view,
|
||||
defaults={"last_seen": timezone.now()},
|
||||
)
|
||||
|
||||
logger.info(f"{peer} AXFR {nb_view.name}/{dname}")
|
||||
|
||||
def _handle_dns_query(self, wire) -> None:
|
||||
@@ -361,8 +362,10 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
if qtype == dns.rdatatype.SOA:
|
||||
self._handle_soa_request(query, soa_rrset, zone, peer, nb_view, dname)
|
||||
self._track_seen_client(peer, nb_view)
|
||||
elif qtype == dns.rdatatype.AXFR:
|
||||
self._handle_axfr_request(query, zone, peer, nb_view, dname)
|
||||
self._track_seen_client(peer, nb_view)
|
||||
|
||||
|
||||
class UDPRequestHandler(DNSBaseRequestHandler):
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.db import transaction
|
||||
from django.conf import settings
|
||||
from netbox.context import current_request
|
||||
from netbox_dns.models import Zone
|
||||
from netbox_dns_bridge import catalog_zone_manager as catzm
|
||||
from netbox_dns_bridge import notify_handler
|
||||
|
||||
SETTINGS = settings.PLUGINS_CONFIG["netbox_dns_bridge"]
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Zone)
|
||||
def zone_pre_save(sender, instance, **kwargs):
|
||||
@@ -31,7 +34,9 @@ def zone_post_save(sender, instance, created, **kwargs):
|
||||
|
||||
def _on_commit():
|
||||
catzm.increment_soa_serial()
|
||||
notify_handler.schedule(zone)
|
||||
|
||||
if SETTINGS.get("notify_clients", False):
|
||||
notify_handler.schedule(zone)
|
||||
|
||||
transaction.on_commit(_on_commit)
|
||||
|
||||
|
||||
Reference in new issue
Block a user