Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group property management #2

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ The configuration file is NSD-like as described below:
algorithm: <string>
secret: <base64 blob>

If a group property is specified for a given zone (see [section 4.4.2](https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-dns-catalog-zones#section-4.4.2) of the RFC draft), this overrides the `pattern` option defined for the corresponding catalog zone.

## zones2catz

`zones2catz` creates a catalog zone from a comma-separated text file containing one zone per line (and optionally the intended group for the zone) and writes its output to a file or _stdout_.
Expand Down
60 changes: 44 additions & 16 deletions dnscatz/catz2nsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ def axfr(
return zone


def get_catz_zones(catalog_zone: dns.zone.Zone) -> Set[str]:
def get_catz_zones(catalog_zone: dns.zone.Zone) -> Dict:
"""Get zones from catalog zone"""
zones = set()
zones = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps use defaultdict(dict) here to rid of the test at line 232?

for k, v in catalog_zone.nodes.items():
if str(k) == "version":
if rdataset := v.get_rdataset(dns.rdataclass.IN, dns.rdatatype.TXT):
Expand All @@ -223,20 +223,42 @@ def get_catz_zones(catalog_zone: dns.zone.Zone) -> Set[str]:
raise CatalogZoneError(
f"Unsupported catalog zone version ({catz_version})"
)
elif str(k).endswith(".zones"):
rdataset = v.get_rdataset(dns.rdataclass.IN, dns.rdatatype.PTR)
if len(rdataset) != 1:
raise CatalogZoneError("Broken catalog zone (PTR)")
zones.add(str(rdataset[0]).rstrip("."))
elif str(k).startswith("group."):
logging.info("Group property not supported: %s", str(k))
get_zone_property(
zones, str(k), v.get_rdataset(dns.rdataclass.IN, dns.rdatatype.TXT)
)
elif str(k).startswith("coo."):
logging.info("Change of Ownership property not supported: %s", str(k))
elif str(k).startswith("serial."):
logging.info("Serial property not supported: %s", str(k))
elif str(k).endswith(".zones"):
get_zone(
zones, str(k), v.get_rdataset(dns.rdataclass.IN, dns.rdatatype.PTR)
)
return zones


def get_zone_property(zones: Dict, record: str, rdataset: dns.rdataset.Rdataset):
"""Get zone property from correspindig TXT record"""
zone_property = record.split(".")[0]
uuid = record.split(".")[1]
if len(rdataset) != 1:
raise CatalogZoneError("Broken catalog zone (%s/TXT)", zone_property)
if uuid not in zones:
zones[uuid] = {}
zones[uuid][zone_property] = str(rdataset[0]).strip('"')


def get_zone(zones: Dict, record: str, rdataset: dns.rdataset.Rdataset):
"""Get zone from PTR record"""
uuid = record.split(".")[0]
if len(rdataset) != 1:
raise CatalogZoneError("Broken catalog zone (PTR)")
if uuid not in zones:
zones[uuid] = {}
zones[uuid]["zone"] = str(rdataset[0]).rstrip(".")


def get_catz_version(rr) -> Optional[int]:
"""Get catalog zone version from TXT RR"""
if rr.rdtype != dns.rdatatype.TXT:
Expand All @@ -249,7 +271,8 @@ def ensure_unique_zones(catalog_zones: List[CatalogZone]):
"""Ensure zones are not defined in multiple catalogs"""
zone2catalogs = defaultdict(set)
for cz in catalog_zones:
for zone in cz.zones:
for uuid in cz.zones:
zone = cz.zones[uuid]["zone"]
zone2catalogs[zone].add(cz.origin)
errors = 0
for zone, catalogs in zone2catalogs.items():
Expand Down Expand Up @@ -318,15 +341,20 @@ def main() -> None:
all_new_zones = set()

for cz in catalog_zones:
for zone in cz.zones:
for uuid in cz.zones:
zone = cz.zones[uuid]["zone"]
if "group" in cz.zones[uuid]:
group = cz.zones[uuid]["group"]
else:
group = cz.pattern
if zone not in current_zone_patterns:
logger.info("Add zone %s (%s)", zone, cz.pattern)
nsd_control(f"addzone {zone} {cz.pattern}", args.dry_run)
elif cz.pattern != current_zone_patterns[zone]:
logger.info("Update zone %s (%s)", zone, cz.pattern)
nsd_control(f"changezone {zone} {cz.pattern}", args.dry_run)
logger.info("Add zone %s (%s)", zone, group)
nsd_control(f"addzone {zone} {group}", args.dry_run)
elif group != current_zone_patterns[zone]:
logger.info("Update zone %s (%s)", zone, group)
nsd_control(f"changezone {zone} {group}", args.dry_run)
else:
logger.debug("No changes to zone %s (%s)", zone, cz.pattern)
logger.debug("No changes to zone %s (%s)", zone, group)
all_new_zones.add(zone)

del_zones = current_zones - all_new_zones
Expand Down
16 changes: 11 additions & 5 deletions dnscatz/zones2catz.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,17 @@ def generate_catalog_zone(
print(f"{origin} {DEFAULT_TTL} IN NS invalid.")
print(f'version.{origin} {DEFAULT_TTL} IN TXT "{CATZ_VERSION}"')

for zone in zones:
if not zone.endswith("."):
zone += "."
zone_id = uuid.uuid5(uuid.NAMESPACE_DNS, zone)
print(f"{zone_id}.zones.{origin} {DEFAULT_TTL} IN PTR {zone}")
with open(str(zonelist), "r") as csv_file:
csv_reader = csv.DictReader(csv_file, fieldnames=["zone", "group"])
for row in csv_reader:
zone = row["zone"].strip()
if not zone.endswith("."):
zone += "."
zone_id = uuid.uuid5(uuid.NAMESPACE_DNS, zone)
print(f"{zone_id}.zones.{origin} {DEFAULT_TTL} IN PTR {zone}")
if row["group"]:
group = row["group"].strip()
print(f'group.{zone_id}.zones.{origin} {DEFAULT_TTL} IN TXT "{group}"')

if zonelist:
with open(zonelist, mode="r") as csv_file:
Expand Down