Skip to content

Instantly share code, notes, and snippets.

@kjd
Last active March 11, 2026 08:18
Show Gist options
  • Select an option

  • Save kjd/4ab344767ecda2ebf07b94b1008ad001 to your computer and use it in GitHub Desktop.

Select an option

Save kjd/4ab344767ecda2ebf07b94b1008ad001 to your computer and use it in GitHub Desktop.
Fetch the root zone, validating it with DNSSEC and ZONEMD
#!/usr/bin/env python3
# /// script
# dependencies = ['dnspython', 'cryptography', 'certifi']
# ///
"""Fetch, validate, and save the current DNS root zone.
Downloads the root zone from IANA, validates DNSSEC signatures on the
ZONEMD record against IANA trust anchors, verifies the zone digest, and
writes the result to root.zone in the current directory.
Invocation with standard Python:
pip install dnspython cryptography certifi
python3 fetch_root_zone.py
Invocation using uv:
uv run fetch_root_zone.py
"""
import ssl
import time
import urllib.request
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
import certifi
import dns.dnssec
import dns.name
import dns.rdata
import dns.rdataclass
import dns.rdatatype
import dns.zone
ZONE_URL = "https://www.internic.net/domain/root.zone"
TRUST_ANCHOR_URL = "https://data.iana.org/root-anchors/root-anchors.xml"
_ssl_ctx = ssl.create_default_context(cafile=certifi.where())
def _fetch(url, retries=5):
for attempt in range(retries):
try:
with urllib.request.urlopen(url, timeout=30, context=_ssl_ctx) as r:
return r.read()
except Exception:
if attempt == retries - 1:
raise
time.sleep(2 ** (attempt + 1))
def _parse_trust_anchors(xml_data):
now = datetime.now(timezone.utc)
ds_records = []
for kd in ET.fromstring(xml_data).findall("KeyDigest"):
valid_from = datetime.fromisoformat(kd.get("validFrom"))
valid_until = kd.get("validUntil")
if valid_until and now > datetime.fromisoformat(valid_until):
continue
if now < valid_from:
continue
ds_text = (
f"{kd.findtext('KeyTag')} {kd.findtext('Algorithm')} "
f"{kd.findtext('DigestType')} {kd.findtext('Digest')}"
)
ds_records.append(
dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, ds_text)
)
assert ds_records, "No valid trust anchors found"
return ds_records
def _validate_dnssec(zone, trust_anchors):
origin = dns.name.root
node = zone.find_node(origin)
get = lambda rdtype, covers=dns.rdatatype.NONE: node.get_rdataset(
dns.rdataclass.IN, rdtype, covers
)
dnskeys = get(dns.rdatatype.DNSKEY)
assert any(
dns.dnssec.make_ds(origin, k, ds.digest_type) == ds
for k in dnskeys
for ds in trust_anchors
), "No DNSKEY matches any trust-anchor DS record"
dns.dnssec.validate(
(origin, get(dns.rdatatype.ZONEMD)),
(origin, get(dns.rdatatype.RRSIG, dns.rdatatype.ZONEMD)),
{origin: dnskeys},
origin=origin,
)
if __name__ == "__main__":
zone_data = _fetch(ZONE_URL)
zone = dns.zone.from_text(
zone_data.decode("ascii"),
origin=dns.name.root,
relativize=False,
check_origin=True,
)
_validate_dnssec(zone, _parse_trust_anchors(_fetch(TRUST_ANCHOR_URL)))
zone.verify_digest()
with open("root.zone", "wb") as f:
f.write(zone_data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment