Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,18 @@ an output CSV path:
python scripts/parked_domain_csv.py domains.txt parked_domains.csv
```

Pass `--dmarc-cname` to override the default DMARC CNAME target:

```bash
python scripts/parked_domain_csv.py domains.txt parked_domains.csv --dmarc-cname reject.dmarc.contoso.com.
```

Each domain receives the following DNS entries:

- SPF record with `-all`
- Null MX record
- DKIM wildcard with an empty key
- DMARC CNAME pointing to a reject policy
- DMARC CNAME pointing to a reject policy (customizable via `--dmarc-cname`)

## License

Expand Down
33 changes: 23 additions & 10 deletions scripts/parked_domain_csv.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import csv
import sys
import argparse

HEADER = [
"Action",
Expand All @@ -11,37 +11,50 @@
"Comments",
]

DEFAULT_DMARC_CNAME = "reject.dmarc.fabrikam.com."

RECORD_TEMPLATE = [
["REMOVE", "{domain}", "@", "MX", "", "10 custmx.cscdns.net.", "Remove existing MX record"],
["ADD", "{domain}", "@", "TXT", "3600", "v=spf1 -all", "SPF hard fail"],
["ADD", "{domain}", "@", "MX", "3600", "0 .", "Null MX (RFC 7505)"],
["ADD", "{domain}", "*._domainkey", "TXT", "3600", "v=DKIM1; p=", "DKIM wildcard with empty key"],
["ADD", "{domain}", "_dmarc", "CNAME", "3600", "reject.dmarc.fabrikam.com.", "DMARC reject policy via CNAME"],
["ADD", "{domain}", "_dmarc", "CNAME", "3600", "{dmarc_target}", "DMARC reject policy via CNAME"],
]


def generate_records(domain: str):
def generate_records(domain: str, dmarc_target: str):
records = [[f"--- {domain} ---", "", "", "", "", "", "Domain configuration"]]
for template in RECORD_TEMPLATE:
value = template[5].format(dmarc_target=dmarc_target)
records.append([
template[0],
domain,
template[2],
template[3],
template[4],
template[5],
value,
template[6],
])
return records


def main():
if len(sys.argv) != 3:
print("Usage: python parked_domain_csv.py domains.txt output.csv")
sys.exit(1)
parser = argparse.ArgumentParser(
description="Generate CSV records to lock down parked or non-mailing domains"
)
parser.add_argument("domain_file", help="Text file containing domain names")
parser.add_argument("output_csv", help="Path to write the output CSV")
parser.add_argument(
"--dmarc-cname",
default=DEFAULT_DMARC_CNAME,
help="DMARC CNAME target (default: %(default)s)",
)

args = parser.parse_args()

domain_file = sys.argv[1]
output_csv = sys.argv[2]
domain_file = args.domain_file
output_csv = args.output_csv
dmarc_cname = args.dmarc_cname

with open(domain_file, "r", encoding="utf-8") as f:
domains = [line.strip() for line in f if line.strip()]
Expand All @@ -50,7 +63,7 @@ def main():
writer = csv.writer(csvfile)
writer.writerow(HEADER)
for domain in domains:
for row in generate_records(domain):
for row in generate_records(domain, dmarc_cname):
writer.writerow(row)


Expand Down
Loading