Skip to main content

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 https://code.minecraftchest1.us/minecraftchest1/dnscode/issues/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.

<iframe id='kofiframe' src='https://ko-fi.com/minecraftchest1/?hidefeed=true&widget=true&embed=true&preview=true' style='border:none;width:100%;padding:4px;background:#f9f9f9;' height='712' title='minecraftchest1'></iframe>

Metadata

Release files for dnscode 1.7.3.post1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dnscode 1.7.3.post1
File Size Uploaded
dnscode-1.7.3.post1.tar.gz 6.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for dnscode 1.7.3.post1
File Interpreter ABI Platform
dnscode-1.7.3.post1-py3-none-any.whl Python 3 none any Details

Total release size: 6.5 MB

Release files / dnscode-1.7.3.post1.tar.gz

Download URL dnscode-1.7.3.post1.tar.gz
Size 6.4 MB
Tags Source
SHA-256 checksum
How to use checksums
8f59238c794ad903b01b69cda627284105d525159687df4aa3d04bb39b9976c9
BLAKE2b-256 checksum
How to use checksums
e56c0395c987ce8f1798a741197453b788e704b3eef24c4c3be0ce894858b682
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / dnscode-1.7.3.post1-py3-none-any.whl

Download URL dnscode-1.7.3.post1-py3-none-any.whl
Size 17.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e93d4f47fc59eb5ad13211edd3b3696010e8d89aaee16bbdab0a0da2bb16857
BLAKE2b-256 checksum
How to use checksums
f0fcb8450b3bb00b7c45597668e6234808f6dc60a280b5e32c6824f68f622afd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

1.7.3.post1 This release

2 release files

1.7.3

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

1 release file

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.3

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page