How to Write a Python Script to Audit SOA Expiry Timers at Scale
Checking Start of Authority (SOA) expiry timers across hundreds of domains manually is inefficient and prone to human error. A robust python soa audit script automates this process, querying authoritative nameservers directly to validate refresh, retry, expire, and minimum TTL values. Whether you manage a corporate portfolio or handle enterprise client migrations, programmatic DNS validation ensures your secondary zones never silently fail.
To quickly check an individual domain before writing code, you can use the interactive SOA Lookup tool to inspect raw record fields and verify baseline parameters instantly.
Understanding the SOA Record Structure
The Start of Authority record dictates how a zone is propagated and maintained across primary and secondary nameservers. Before writing your python soa audit script, you must understand the individual timers contained within the RDATA portion of the record.
Key Fields in an SOA Record
- MNAME: The primary master nameserver for this zone.
- RNAME: The email address of the administrator responsible for the zone, with the
@replaced by a dot. - SERIAL: The version number of the zone file, incremented whenever changes are made.
- REFRESH: The time interval before a secondary nameserver should query the primary to check for zone updates.
- RETRY: The time interval that a secondary nameserver should wait before retrying a failed refresh attempt.
- EXPIRE: The upper limit time interval that can elapse before a secondary nameserver stops treating its zone data as authoritative if it cannot reach the primary.
- MINIMUM: The default Time to Live (TTL) for resource records in the zone that do not specify their own TTL.
Setting Up Your Python Environment
To query DNS records programmatically in Python, you need a reliable library. While Python's built-in socket library can handle basic forward lookups, it cannot parse complex DNS resource records like SOA timers. Instead, you should use dnspython, the industry standard library for DNS operations.
Installing Dependencies
Open your terminal and install the required library using pip:
pip install dnspython
Verify your installation by running a quick interactive Python check or by executing a one-liner command in your shell:
python3 -c "import dns.resolver; print(dns.resolver.__version__)"
Writing the Python SOA Audit Script
Now, let's write a complete, production-ready script. This script reads a list of domains from a text file, queries the authoritative nameservers, extracts the SOA timers, and flags any domains with risky or non-standard expiry settings.
Complete Python Script Example
Create a file named audit_soa.py and add the following code:
import sys
import dns.resolver
import dns.exception
# Configuration thresholds (in seconds)
MIN_EXPIRE_SECONDS = 604800 # 7 days
MAX_EXPIRE_SECONDS = 2419200 # 28 days
def audit_domain(domain):
resolver = dns.resolver.Resolver()
resolver.timeout = 5
resolver.lifetime = 5
try:
# Query the SOA record for the domain
answers = resolver.resolve(domain, 'SOA')
for rdata in answers:
print(f"[+] Domain: {domain}")
print(f" Primary NS : {rdata.mname}")
print(f" Admin Email: {rdata.rname}")
print(f" Serial : {rdata.serial}")
print(f" Refresh : {rdata.refresh}s")
print(f" Retry : {rdata.retry}s")
print(f" Expire : {rdata.expire}s")
print(f" Minimum TTL: {rdata.minimum}s")
# Validate expiry timer thresholds
if rdata.expire < MIN_EXPIRE_SECONDS:
print(f" [WARNING] Expire timer is too low ({rdata.expire}s). Secondary zones may drop quickly during outages.")
elif rdata.expire > MAX_EXPIRE_SECONDS:
print(f" [NOTICE] Expire timer is quite high ({rdata.expire}s).")
print("-" * 50)
except dns.resolver.NoAnswer:
print(f"[-] Error: No SOA record found for {domain}")
except dns.resolver.NXDOMAIN:
print(f"[-] Error: Domain {domain} does not exist")
except dns.exception.Timeout:
print(f"[-] Error: Query timed out for {domain}")
except Exception as e:
print(f"[-] Unexpected error for {domain}: {str(e)}")
def main():
# Example domain list input
domains = ["example.com", "192.0.2.1.example.com"]
print(f"Starting SOA audit for {len(domains)} domains...")
print("=" * 50)
for domain in domains:
audit_domain(domain)
if __name__ == "__main__":
main()
Running the Script
Execute the script from your terminal:
python3 audit_soa.py
Sample output:
Starting SOA audit for 1 domains...
==================================================
[+] Domain: example.com
Primary NS : a.iana-servers.net.
Admin Email: noc.dns.icann.org.
Serial : 2023101501
Refresh : 7200s
Retry : 600s
Expire : 1209600s
Minimum TTL: 3600s
------------------------------------------------==
Advanced Scaling: Asynchronous Queries
If you need to audit thousands of domains, sequential execution will take too long. You can scale your python soa audit script by using Python's asyncio library along with dns.asyncresolver.
import asyncio
import dns.asyncresolver
async def audit_async(domain):
resolver = dns.asyncresolver.Resolver()
try:
answers = await resolver.resolve(domain, 'SOA')
for rdata in answers:
print(f"{domain} -> Expire: {rdata.expire}s")
except Exception:
print(f"{domain} -> Query Failed")
async def main():
domains = ["example.com", "test.example.com"]
await asyncio.gather(*(audit_async(d) for d in domains))
if __name__ == '__main__':
asyncio.run(main())
Comparison of DNS Query Approaches
| Approach | Speed | Complexity | Best Use Case |
|---|---|---|---|
Standard socket |
Slow | High (Manual Parsing) | Minimalist scripts without dependencies |
dnspython (Sync) |
Moderate | Low | Small to medium domain lists (< 500) |
dnspython (Async) |
Fast | Medium | Large enterprise portfolios (> 500) |
Common Mistakes and How to Fix Them
When writing automated DNS auditing tools, network engineers frequently encounter a few specific pitfalls.
- Ignoring DNS Timeouts: Network congestion or blocked UDP ports can hang your script indefinitely. Always set explicit
timeoutandlifetimeparameters on your resolver instance. - Forgetting the Trailing Dot: When comparing primary nameserver strings returned by the SOA record against your inventory, remember that
dnspythonreturns fully qualified domain names ending with a dot (e.g.,ns1.example.com.). Normalize strings before making comparisons. - Hardcoding Public Resolvers: While querying 8.8.8.8 works for standard lookups, SOA records retrieved from public forwarders might return cached data with incorrect timers. For true audits, query the authoritative nameservers directly.
Pre-Flight Checklist for Your Audit Script
Before deploying your script to a production automation pipeline, verify the following items:
- Installed the correct version of
dnspythonin your virtual environment. - Implemented robust exception handling for
NXDOMAIN,NoAnswer, and timeouts. - Configured logging to output results to a CSV or JSON file for downstream analysis.
- Defined clear business thresholds for minimum and maximum expire timers.