Skip to content

Commit 214df0e

Browse files
committed
feat: add knot nslord backend
- split change tracking into base + PDNS/Knot implementations with NSLord routing - add domain nslord field, serializer support, and migration - implement knot backend (DNSKEY/AXFR/updates) and nslord_knot service/config - update e2e2 and unit tests for knot domains, cert handling, and fail-fast - tune PDNS API timeout and nsmaster transfer intervals
1 parent 22aff8f commit 214df0e

40 files changed

Lines changed: 2927 additions & 201 deletions

.github/workflows/test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ env:
3434
DESECSTACK_NSMASTER_ALSO_NOTIFY:
3535
DESECSTACK_NSMASTER_APIKEY: LLq1orOQuXCINUz4TV
3636
DESECSTACK_NSMASTER_TSIGKEY: +++undefined/undefined/undefined/undefined/undefined/undefined/undefined/undefined+++A==
37+
DESECSTACK_NSLORD_KNOT_UPDATE_KEY_SECRET: insecure
3738
DESECSTACK_IPV4_REAR_PREFIX16: 172.16
3839
DESECSTACK_IPV6_SUBNET: bade:affe:dead:beef:b011::/80
3940
DESECSTACK_IPV6_ADDRESS: bade:affe:dead:beef:b011:0642:ac10:0080
@@ -53,6 +54,19 @@ jobs:
5354
- name: Test desecapi formatting
5455
run: ruff format --check api/
5556

57+
test-watcher:
58+
# runs Knot watcher unit tests
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v6
62+
- uses: actions/setup-python@v5
63+
with:
64+
python-version: "3.12"
65+
- name: Install pytest
66+
run: python3 -m pip install pytest
67+
- name: Run watcher tests
68+
run: python3 -m pytest nslord_knot/tests/test_zone_watch.py
69+
5670
test-missing-migrations:
5771
# test if Django migrations are missing
5872
runs-on: ubuntu-latest

api/api/celery.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pprint
33

44
import django.utils.log
5+
from django.apps import apps as django_apps
56
from celery import Celery
67
from celery.signals import task_failure
78

@@ -26,8 +27,23 @@ def debug_task(self):
2627
print("Request: {0!r}".format(self.request))
2728

2829

30+
logger = logging.getLogger(__name__)
31+
32+
33+
def _configure_logger():
34+
if getattr(_configure_logger, "configured", False):
35+
return
36+
if not django_apps.ready:
37+
return
38+
handler = django.utils.log.AdminEmailHandler()
39+
handler.setFormatter(CeleryFormatter())
40+
logger.addHandler(handler)
41+
_configure_logger.configured = True
42+
43+
2944
@task_failure.connect()
3045
def task_failure(task_id, exception, args, kwargs, traceback, einfo, **other_kwargs):
46+
_configure_logger()
3147
try:
3248
sender = other_kwargs.get("sender").name
3349
except AttributeError:
@@ -49,7 +65,6 @@ def task_failure(task_id, exception, args, kwargs, traceback, einfo, **other_kwa
4965
)
5066

5167

52-
django.setup()
5368
logger = logging.getLogger(__name__)
5469
handler = django.utils.log.AdminEmailHandler()
5570
handler.setFormatter(CeleryFormatter())

api/api/settings.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,28 @@
175175
NSLORD_PDNS_API_TOKEN = os.environ["DESECSTACK_NSLORD_APIKEY"]
176176
NSMASTER_PDNS_API = "http://nsmaster:8081/api/v1/servers/localhost"
177177
NSMASTER_PDNS_API_TOKEN = os.environ["DESECSTACK_NSMASTER_APIKEY"]
178+
NSLORD_KNOT_HOST = os.environ.get("DESECSTACK_NSLORD_KNOT_HOST", "nslord_knot")
179+
NSLORD_KNOT_PORT = int(os.environ.get("DESECSTACK_NSLORD_KNOT_PORT", "53"))
180+
NSLORD_KNOT_TIMEOUT = float(os.environ.get("DESECSTACK_NSLORD_KNOT_TIMEOUT", "5"))
181+
NSLORD_KNOT_UPDATE_KEY_NAME = os.environ.get(
182+
"DESECSTACK_NSLORD_KNOT_UPDATE_KEY_NAME", "nslord-update"
183+
)
184+
NSLORD_KNOT_UPDATE_KEY_SECRET = os.environ.get(
185+
"DESECSTACK_NSLORD_KNOT_UPDATE_KEY_SECRET", ""
186+
)
187+
NSLORD_KNOT_UPDATE_KEY_ALGORITHM = os.environ.get(
188+
"DESECSTACK_NSLORD_KNOT_UPDATE_KEY_ALGORITHM", "hmac-sha256"
189+
)
190+
NSLORD_KNOT_TRANSFER_KEY_NAME = os.environ.get(
191+
"DESECSTACK_NSLORD_KNOT_TRANSFER_KEY_NAME", "nsmaster-xfr"
192+
)
193+
NSLORD_KNOT_TRANSFER_KEY_SECRET = os.environ.get(
194+
"DESECSTACK_NSLORD_KNOT_TRANSFER_KEY_SECRET",
195+
os.environ.get("DESECSTACK_NSMASTER_TSIGKEY", ""),
196+
)
197+
NSLORD_KNOT_TRANSFER_KEY_ALGORITHM = os.environ.get(
198+
"DESECSTACK_NSLORD_KNOT_TRANSFER_KEY_ALGORITHM", "hmac-sha256"
199+
)
178200
CATALOG_ZONE = "catalog.internal"
179201

180202
# Celery
@@ -193,6 +215,7 @@
193215
# pdns accepts request payloads of this size.
194216
# This will hopefully soon be configurable: https://github.com/PowerDNS/pdns/pull/7550
195217
PDNS_MAX_BODY_SIZE = 16 * 1024 * 1024
218+
PDNS_API_TIMEOUT = float(os.environ.get("DESECSTACK_PDNS_API_TIMEOUT", "10"))
196219

197220
# SEPA direct debit settings
198221
SEPA = {

api/desecapi/exception_handlers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from rest_framework.views import exception_handler as drf_exception_handler
77

88
from desecapi import metrics
9-
from desecapi.exceptions import PDNSException
9+
from desecapi.exceptions import KnotException, PDNSException
1010

1111

1212
def exception_handler(exc, context):
@@ -39,6 +39,7 @@ def _500():
3939
IntegrityError: _409,
4040
OSError: _500, # OSError happens on system-related errors, like full disk or getaddrinfo() failure.
4141
PDNSException: _500, # nslord/nsmaster returned an error
42+
KnotException: _500, # knot returned an error
4243
}
4344

4445
for exception_class, handler in handlers.items():

api/desecapi/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class PCHException(ExternalAPIException):
3434
pass
3535

3636

37+
class KnotException(APIException):
38+
pass
39+
40+
3741
class ConcurrencyException(APIException):
3842
status_code = status.HTTP_429_TOO_MANY_REQUESTS
3943
default_detail = "Too many concurrent requests."

0 commit comments

Comments
 (0)