align-catalog-zone.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. from django.conf import settings
  2. from django.core.management import BaseCommand
  3. from desecapi.exceptions import PDNSException
  4. from desecapi.pdns import (
  5. _pdns_delete,
  6. _pdns_get,
  7. _pdns_post,
  8. NSLORD,
  9. NSMASTER,
  10. pdns_id,
  11. construct_catalog_rrset,
  12. )
  13. class Command(BaseCommand):
  14. # https://tools.ietf.org/html/draft-muks-dnsop-dns-catalog-zones-04
  15. help = "Generate a catalog zone on nsmaster, based on zones known on nslord."
  16. def add_arguments(self, parser):
  17. pass
  18. def handle(self, *args, **options):
  19. catalog_zone_id = pdns_id(settings.CATALOG_ZONE)
  20. # Fetch zones from NSLORD
  21. response = _pdns_get(NSLORD, "/zones").json()
  22. zones = {zone["name"] for zone in response}
  23. # Retrieve catalog zone serial (later reused for recreating the catalog zone, for allow for smooth rollover)
  24. try:
  25. response = _pdns_get(NSMASTER, f"/zones/{catalog_zone_id}")
  26. serial = response.json()["serial"]
  27. except PDNSException as e:
  28. if e.response.status_code == 404:
  29. serial = None
  30. else:
  31. raise e
  32. # Purge catalog zone if exists
  33. try:
  34. _pdns_delete(NSMASTER, f"/zones/{catalog_zone_id}")
  35. except PDNSException as e:
  36. if e.response.status_code != 404:
  37. raise e
  38. # Create new catalog zone
  39. rrsets = [
  40. construct_catalog_rrset(
  41. subname="", qtype="NS", rdata="invalid."
  42. ), # as per the specification
  43. construct_catalog_rrset(
  44. subname="version", qtype="TXT", rdata='"2"'
  45. ), # as per the specification
  46. *(construct_catalog_rrset(zone=zone) for zone in zones),
  47. ]
  48. data = {
  49. "name": settings.CATALOG_ZONE + ".",
  50. "kind": "MASTER",
  51. "dnssec": False, # as per the specification
  52. "nameservers": [],
  53. "rrsets": rrsets,
  54. }
  55. if serial is not None:
  56. data["serial"] = (
  57. serial + 1
  58. ) # actually, pdns does increase this as well, but let's not rely on this
  59. _pdns_post(NSMASTER, "/zones?rrsets=false", data)
  60. print(f"Aligned catalog zone ({len(zones)} member zones).")