Compare commits

..
4 Commits
Author SHA1 Message Date
Sven Luethi 962be7a048 modified: src/netbox_plugin_bind_provisioner/management/commands/bind-transfer-endpoint.py
Fixes broken dns responses to UDP endpoint (denyRequest function replaced)
2025-11-25 00:03:36 +01:00
Sven Luethi 204a946a84 modified: README.md
Removes the warning for step 4 from the installation guide. The warning said not to run migrations
as the plugin did not have its own models. Since v0.9.6 it does so this was forgotten to be removed.
2025-11-24 13:51:42 +01:00
Sven Luethi d72dc5a115 modified: .github/workflows/python-publish.yml 2025-11-23 16:45:46 +01:00
Sven Luethi b659268117 modified: .github/workflows/python-publish.yml
Update flow
2025-11-23 16:43:27 +01:00
3 changed files with 38 additions and 43 deletions

No files matched your search

+9 -28
View File
@@ -11,14 +11,17 @@ name: Upload Python Package
on: on:
release: release:
types: [published] types: [published]
# push:
# branches:
# - main
permissions: permissions:
contents: read contents: read
jobs: jobs:
release-build: build-release:
name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -30,7 +33,7 @@ jobs:
run: | run: |
# NOTE: put your own distribution build steps here. # NOTE: put your own distribution build steps here.
python -m pip install build python -m pip install build
python -m build python -m build --wheel
- name: Upload distributions - name: Upload distributions
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -39,32 +42,10 @@ jobs:
path: dist/ path: dist/
pypi-publish: pypi-publish:
runs-on: ubuntu-latest name: Upload
needs: environment: pypi
- release-build
permissions: permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write id-token: write
# Dedicated environments with protections for publishing are strongly recommended.
# For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
environment:
name: pypi
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
# url: https://pypi.org/p/YOURPROJECT
#
# ALTERNATIVE: if your GitHub Release name is the PyPI project version string
# ALTERNATIVE: exactly, uncomment the following line instead:
# url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}
steps: steps:
- name: Retrieve release distributions - name: Publish package distributions to PyPI
uses: actions/download-artifact@v4
with:
name: release-dists
path: dist/
- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
+1 -1
View File
@@ -109,7 +109,7 @@ This guide assumes:
This allows the plugin to map requests to the right dns view using the tsig This allows the plugin to map requests to the right dns view using the tsig
signature from each request. signature from each request.
4. Run migrations (This step is not yet required. Just adding for completeness) 4. Run migrations
``` ```
python3 netbox/manage.py migrate python3 netbox/manage.py migrate
``` ```
@@ -260,17 +260,15 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
# Replace the rdataset for the given name and type # Replace the rdataset for the given name and type
zone.replace_rdataset(name, rdataset) zone.replace_rdataset(name, rdataset)
return zone return zone
# def denyRequest(self, query, rcode: dns.rcode = dns.rcode.REFUSED):
def denyRequest(self, query, rcode: dns.rcode = dns.rcode.REFUSED): # response = dns.message.make_response(query)
response = dns.message.make_response(query) # response.set_rcode(rcode)
response.set_rcode(rcode) # wire = response.to_wire(multi=False)
wire = response.to_wire(multi=False) # length = len(wire).to_bytes(2, byteorder="big")
length = len(wire).to_bytes(2, byteorder="big") # self.request.sendall(length + wire)
self.request.sendall(length + wire)
def denyRequestBadTSIG(self, wire, tsig_error: dns.rcode): def denyRequestBadTSIG(self, wire, tsig_error: dns.rcode):
@@ -292,6 +290,13 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
class UDPRequestHandler(DNSBaseRequestHandler): class UDPRequestHandler(DNSBaseRequestHandler):
def denyRequest(self, query, rcode: dns.rcode = dns.rcode.REFUSED):
response = dns.message.make_response(query)
response.set_rcode(rcode)
wire = response.to_wire(multi=False)
sock = self.request[1]
sock.sendto(wire, self.client_address)
def handle(self): def handle(self):
data, sock = self.request data, sock = self.request
peer = self.client_address[0] peer = self.client_address[0]
@@ -301,7 +306,7 @@ class UDPRequestHandler(DNSBaseRequestHandler):
query = dns.message.from_wire( query = dns.message.from_wire(
data, data,
keyring=self.server.keyring, keyring=self.server.keyring,
continue_on_error=True, continue_on_error=False,
ignore_trailing=True ignore_trailing=True
) )
except Exception as e: except Exception as e:
@@ -328,12 +333,13 @@ class UDPRequestHandler(DNSBaseRequestHandler):
return return
# Identify TSIG key used # Identify TSIG key used
key_name = query.keyname.canonicalize().to_text() if query.keyname else None if not query.had_tsig:
if not key_name:
logger.warning(f"Request denied from {peer}: No TSIG key used") logger.warning(f"Request denied from {peer}: No TSIG key used")
self.denyRequest(query) self.denyRequest(query)
return return
key_name = query.keyname.canonicalize().to_text()
# Check if key matches a view # Check if key matches a view
nb_view = self.server.tsig_view_map.get(key_name) nb_view = self.server.tsig_view_map.get(key_name)
if not nb_view: if not nb_view:
@@ -387,6 +393,13 @@ class UDPRequestHandler(DNSBaseRequestHandler):
class TCPRequestHandler(DNSBaseRequestHandler): class TCPRequestHandler(DNSBaseRequestHandler):
def denyRequest(self, query, rcode: dns.rcode = dns.rcode.REFUSED):
response = dns.message.make_response(query)
response.set_rcode(rcode)
wire = response.to_wire(multi=False)
length = len(wire).to_bytes(2, byteorder="big")
self.request.sendall(length + wire)
def handle(self): def handle(self):
MAX_WIRE = 65535 MAX_WIRE = 65535
# MAX_WIRE = 2000 # For testing fragmentation # MAX_WIRE = 2000 # For testing fragmentation
@@ -443,12 +456,13 @@ class TCPRequestHandler(DNSBaseRequestHandler):
return return
# Identify TSIG key used # Identify TSIG key used
key_name = query.keyname.canonicalize().to_text() if query.keyname else None if not query.had_tsig:
if not key_name:
logger.warning(f"Request denied from {peer}: No TSIG key used") logger.warning(f"Request denied from {peer}: No TSIG key used")
self.denyRequest(query) self.denyRequest(query)
return return
key_name = query.keyname.canonicalize().to_text()
# Check if the key matches a view # Check if the key matches a view
nb_view = self.server.tsig_view_map.get(key_name) nb_view = self.server.tsig_view_map.get(key_name)
if not nb_view: if not nb_view:
@@ -630,7 +644,7 @@ class Command(BaseCommand):
name=key_name_obj, secret=secret, algorithm=algorithm_str name=key_name_obj, secret=secret, algorithm=algorithm_str
) )
self.tsig_view_map[key_name_str] = nb_view self.tsig_view_map[key_name_str] = nb_view
logger.debug(f"Loaded TSIG key: {key_name_str} (view: {nb_view.name})") logger.debug(f"Loaded TSIG key: {key_name_str} view: {nb_view.name}")
if not self.keyring: if not self.keyring:
msg = "No TSIG keys found in database." msg = "No TSIG keys found in database."