Configure DNS using simple python scripts https://pypi.org/project/dnscode/
Find a file
Wyatt Jackson 5f6cd51fc3
Some checks failed
ci/woodpecker/push/package-deploy Pipeline failed
Bump to 1.7.3-post1
2025-09-09 23:25:06 -05:00
.woodpecker Disable broken docs workflow 2025-04-09 12:26:48 -05:00
docs Add latex and man docs outputs. 2025-08-28 20:53:28 -05:00
doxygen-awesome-css@568f56cde6 New doxygen theme. 2025-01-31 09:12:32 -06:00
src/dnscode Fix quoting on TXT records. 2025-09-06 21:09:36 -05:00
.editorconfig Initial "Commit. 2024-11-04 12:20:23 -06:00
.gitignore Setup for packaging. 2025-01-13 13:35:19 -06:00
.gitmodules New doxygen theme. 2025-01-31 09:12:32 -06:00
Doxyfile Add latex and man output to doxygen. 2025-08-28 20:53:51 -05:00
LICENSE Setup for packaging. 2025-01-13 13:35:19 -06:00
pyproject.toml Bump to 1.7.3-post1 2025-09-09 23:25:06 -05:00
README.md Update README 2025-09-09 23:13:56 -05:00
requirements.txt Build unit tests. 2025-01-23 08:31:31 -06:00
test_dnscode.py Update tests for SOA negative TTL 2025-01-27 10:42:11 -06:00

DNScode

Simplifying DNS Zone management


About

DNScode is a project to help simplify DNS zone management, when using plain text files with servers like BIND and NSD. It provides a framework for programmatically generating zone files with Python, allowing for more flexibility, compared to other DNS as code solutions.

Why

I created this project to help me simplify my personal DNS records for my domains. What started out as a way to simply create a list of subdomains to point to a single host, turned into a flexible library for DNS zone generation. While still meeting my goal of a simple list to update, I am also able to use it for LetsEncrypt DNS challenges using my own self hosted DNS, without being forced to use BIND and rfc2136. And if I do decide to setup DNSSEC at some later point, I can build automation into my existing DNS tooling, simplifying management and reducing the likely hood of mistakes. All without loosing any flexibility.

Features

  • Support for most major record types.
  • Support for custom records.
  • Multiple zones in one file.
  • Dynamic and static records.
  • Simple and intuitive.

Installation

# Create working directory
mkdir dnsproject
cd dnsproject

# Create virtual envrionment (optional, but highly recomended)
python3 -m venv .venv
source .venv/bin/activate

# Install the dnscode package
pip install dnscode

Usage

Import the dnscode package into a python script, create a zone object, then add records into the zone. Records can be added either via helper functions in the dnscode.Zone class, or by manually creating record objects and adding them through dnscode.Zone.add(). Both methods are shown in the example below.

Once the zone is setup, you can save it as a text file using dnscode.Zone.save_file(). Currently, it also outputs to to STDOUT. See minecraftchest1/dnscode#5 for details.

API docs at https://dnscode.minecraftchest1.us/classdnscode_1_1dnscode_1_1Zone

import dnscode

zone = dnscode.Zone(origin='example.com')				# Create zone object
zone.new_SOA(mname='ns1.minecraftchest1.us.',			# Create SOA
	rname='admin.minecraftchest1.us.',
	refresh=onemonth, retry=oneday, ttl=oneday)
zone.new_A(name='myhost', ttl=3600, host='0.0.0.0')		#New A record
zone.new_AAAA(name='myhost', ttl-3600, hosts='::1')

more_hosts = [
	'host1',
	'host2',
	'host3',
]

zone.new_A(name=more_hosts, ttl=60, host='0.0.0.0')
zone.new_AAAA(name=more_hosts, ttl=60, host='::!')

# More helper functions in the docs

cname = dnscode.CNAME(name='mycname', ttl=60, host='example.com')
# More record objects in the docs.
zone.add(cname)

zone.save_file('example.zone')

Certbot integration

Certbot supports validation using external scripts. Here is a quick example on how to configure your project and certbot for DNS validation.

Create the following scripts. These are the hooks that certbot will call to perform DNS validation.

/path/to/validation.sh

#!/bin/bash

# Certbot only returns the domain to be validated. We must add the prefix manually.
NEW_DOMAIN="_acme-challenge.${CERTBOT_DOMAIN}."

# Call your script with the record to add.
/path/to/project/.venv/bin/python3 /path/to/project/zone.py ${NEW_DOMAIN} TXT "${CERTBOT_VALIDATION}"
echo 'Propagation delay'
# Not optional, but encouraged. Exact time depends on your DNS setup.
sleep 15

/path/to/cleanup.sh

#!/bin/bash
# No cleanup required. The zone we added during validation will get removed next time the script is run.
# You could run it here if you want.
echo 'Nothing to do.'

Add the following somewhere to your script.

# Add record from command line args.
#
# NOTE: This implementation only allows for passing at most one record at a time.
#
# NOTE: This provides no validation or sanitation. More checks should be added if
#       you can't guarantee the validity of the provided arguments.

if len(sys.argv) >= 4:						# Check that enough args were provided
    r = dnscode.Record()
    r.name  = sys.argv[1]
    r.rtype = sys.argv[2]
    if sys.argv[2] == 'TXT':				# Add quoted if the record type is TXT
        r.data  = f"\"{sys.argv[3]}\""
    else:
        r.data  = sys.argv[3]
    zone.add(r)

Tell certbot to use the above hooks for validation. For example:

/path/to/certbot certonly --manual --preferred-challenges=dns --manual-auth-hook /path/to/validation.sh --manual-cleanup-hook /path/to/cleanup.sh -d 'example.com' --cert-name 'example-cert'

One drawback with this implementation is that only one domain can be validated at one. Further work is required for multi domain certs.

Donte

If you find this project useful, feel free to leave me a tip with Ko-Fi.