Skip to content

Instantly share code, notes, and snippets.

@alainwolf
Last active October 13, 2023 09:04
Show Gist options
  • Save alainwolf/4ba6466c60734a254219d47154dbd86c to your computer and use it in GitHub Desktop.
Save alainwolf/4ba6466c60734a254219d47154dbd86c to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# Create new domains (or sub-domains) in PowerDNS authoritative servers
# using pdnsutil
#
# Features:
# * DNSSEC (incl. delegation in parent zone, if on same server);
# * NSEC3;
# * CAA;
# * SPF, DKIM and DMARC;
# * TSIG keys for use with AXFR, notify and RFC2136 DNS dynamic updates;
#
# Usage:
# ./pdns_create_new_zone.sh "example.net"
# ./pdns_create_new_zone.sh "sub.example.net"
#
# Exit on errors
set -e
# Error on undefined variables
set -u
ZONE=$1
# ------------------------------------------------------------------------------
# Settings
# ------------------------------------------------------------------------------
# The PowerDNS command and control utility
pdnsutil='sudo /usr/bin/pdnsutil'
# DNS master hostname
soa_master="eko.${ZONE}."
# Administrative mail address
soa_contact="hostmaster.${ZONE}."
# SOA serial number (0 is recommended for PowerDNS to handle this automatically)
soa_serial=0
# SOA timings in seconds
soa_refresh=28800 # 8 hours
soa_retry=3600 # 1 hour
soa_expire=2419200 # 28 days
soa_negative=900 # 15 minutes
# Default TTL for DNS records
rr_ttl=3600 # 1 hour
# An existing zone who's records will serve as template for ...
# * The vanity name servers IP addresses
# * The DomainKey (DKIM) record
# * The DMARC record
# * A SPF record to include (typically "_spf.hoster-domain.tld")
hoster='urown.net'
# List of name servers host-names (minimum 3, maximum 7)
ns_hosts='eko pace norris'
# List of mail exchangers fully qualified domain names.
mx_hosts='pace.urown.net norris.urown.net'
# SPF DNS record (Suggestion: Just include the hoster SPF record)
spf='"v=spf1 include:_spf.urown.net ~all"'
# DKIM ID of the key (will be looked up in the hoster domain)
dkim_id='2017'
# List of certificate authorities allowed to issue certificates.
caa_issuer="letsencrypt.org $hoster"
caa_issuerwild="letsencrypt.org $hoster"
# Settings for NSEC3
nsec3_hash_algo=1
nsec3_optout=0
nsec3_iterations=10
nsec3_salt_lenght=16
# Algorithm to use for creating TSIG keys.
tsig_algo="hmac-sha256"
# Names of TSIG keys.
dnsupdate_key="$(pwgen --no-capitalize --no-numerals --ambiguous 8 1)"
axfr_master_key="$(pwgen --no-capitalize --no-numerals --ambiguous 8 1)"
axfr_slave_key="$(pwgen --no-capitalize --no-numerals --ambiguous 8 1)"
# ------------------------------------------------------------------------------
# End of Settings
# ------------------------------------------------------------------------------
# Set sorting
LC_CTYPE=C
# We must be root
sudo --validate
# We need to be root
if [[ ${UID} -gt 0 ]]; then
echo "Sorry, need to be root"
exit 1
fi
nsec3_salt()
{
/usr/bin/tr --complement --delete 'A-F0-9' < /dev/urandom | \
/usr/bin/head -c ${nsec3_salt_lenght}
}
echo "Salting ..."
nsec3_salt="$(nsec3_salt)"
echo -n "Creating TSIG key for AXFR transfers ... "
pdnsutil generate-tsig-key "${axfr_master_key}" "${tsig_algo}"
echo -n "Creating TSIG key for AXFR notify ... "
pdnsutil generate-tsig-key "${axfr_slave_key}" "${tsig_algo}"
echo -n "Creating TSIG key for RFC2136 dynamic DNS updates ... "
pdnsutil generate-tsig-key "${dnsupdate_key}" "${tsig_algo}"
#echo "Creating new zone $ZONE"
$pdnsutil create-zone "$ZONE"
echo "Setting $ZONE as 'native'"
$pdnsutil set-kind "$ZONE" native
echo "Setting $ZONE account as $USER"
$pdnsutil set-account "$ZONE" "$USER"
echo "Adding meta-data to $ZONE ..."
$pdnsutil set-meta "$ZONE" ALLOW-AXFR-FROM AUTO-NS
$pdnsutil set-meta "$ZONE" API-RECTIFY 1
$pdnsutil set-meta "$ZONE" AXFR-MASTER-TSIG "${axfr_master_key}"
$pdnsutil set-meta "$ZONE" SOA-EDIT INCEPTION-INCREMENT
$pdnsutil set-meta "$ZONE" SOA-EDIT-API INCEPTION-INCREMENT
$pdnsutil set-meta "$ZONE" SOA-EDIT-DNSUPDATE SOA-EDIT-INCREASE
$pdnsutil set-meta "$ZONE" TSIG-ALLOW-AXFR "${axfr_slave_key}"
$pdnsutil set-meta "$ZONE" TSIG-ALLOW-DNSUPDATE "${dnsupdate_key}"
$pdnsutil set-meta "$ZONE" NOTIFY-DNSUPDATE 1
$pdnsutil set-meta "$ZONE" FORWARD-DNSUPDATE ''
#echo "Modifying SOA record"
soa_record="$soa_master $soa_contact $soa_serial $soa_refresh $soa_retry $soa_expire $soa_negative"
$pdnsutil replace-rrset "$ZONE" @ SOA "$soa_record"
echo "Adding name servers and glue ..."
for ns_host in $ns_hosts ; do
#echo "Adding glue for name server $ns_host"
$pdnsutil add-record "$ZONE" "$ns_host" A "${rr_ttl}" "$( dig +short A "${ns_host}.${hoster}" )"
$pdnsutil add-record "$ZONE" "$ns_host" AAAA "${rr_ttl}" "$( dig +short AAAA "${ns_host}.${hoster}" )"
#echo "Adding $ns_host as vanity name server"
$pdnsutil add-record "$ZONE" @ NS "${rr_ttl}" "${ns_host}.$ZONE"
done
echo "Adding mail exchangers ..."
for mx_host in $mx_hosts ; do
#echo "Adding mail server $mx_host as mail exchanger"
$pdnsutil add-record "$ZONE" @ MX "${rr_ttl}" "10 ${mx_host}"
done
echo "Adding SPF, DKIM and DMARC records ..."
#echo "Adding SPF record"
$pdnsutil add-record "$ZONE" @ TXT "${rr_ttl}" "${spf}"
#echo "Adding DKIM record"
dkim_record=$( dig +short TXT ${dkim_id}._domainkey.${hoster} )
$pdnsutil add-record "$ZONE" ${dkim_id}._domainkey TXT "${rr_ttl}" "${dkim_record}"
#echo "Adding DMARC record"
dmarc_record=$( dig +short TXT _dmarc.${hoster} )
$pdnsutil add-record "$ZONE" _dmarc TXT "${rr_ttl}" "${dmarc_record}"
echo "Adding mail-clients autoconfiguration records"
#autoconf_record=$( dig +short A autoconfig.${hoster} )
$pdnsutil add-record "$ZONE" autoconfig CNAME "${rr_ttl}" "autoconfig.${hoster}."
$pdnsutil add-record "$ZONE" autodiscover CNAME "${rr_ttl}" "autodiscover.${hoster}."
echo "Adding CAA records ..."
for ca in $caa_issuer ; do
#echo "Adding $caa_issuer as allowed certificates issuer."
caa_record="0 issue \"${ca}\""
$pdnsutil add-record "$ZONE" @ CAA "${rr_ttl}" "${caa_record}"
done
for ca in $caa_issuerwild ; do
#echo "Adding $ca as allowed wildcard certificates issuer."
caa_record="0 issuewild \"${ca}\""
$pdnsutil add-record "$ZONE" @ CAA "${rr_ttl}" "${caa_record}"
done
caa_record="0 iodef \"mailto:hostmaster.${ZONE}\""
$pdnsutil add-record "$ZONE" @ CAA "${rr_ttl}" "${caa_record}"
#echo "Securing the zone with DNSSEC ..."
$pdnsutil secure-zone "$ZONE"
echo -n "Setting up NSEC3 ... "
nsec3param="$nsec3_hash_algo $nsec3_optout $nsec3_iterations $nsec3_salt"
echo "$nsec3param"
$pdnsutil set-nsec3 "$ZONE" "${nsec3param}"
echo -n "AXFR transfers: "
$pdnsutil activate-tsig-key "${ZONE}" "${axfr_master_key}" master
echo -n "AXFR master notify: "
$pdnsutil activate-tsig-key "${ZONE}" "${axfr_slave_key}" slave
echo -n "Rectifying Zone ... "
$pdnsutil rectify-zone "$ZONE"
# Are we in control of the parent domain?
parent_zone="${ZONE#*.}"
zone_name="${ZONE%$parent_zone}"
our_zones=$( $pdnsutil list-all-zones )
echo "parent: $parent_zone"
echo "child: $zone_name"
#echo "our zones: $our_zones"
echo -n "Checking if we handle the parent zone ... "
while read -r line; do
#echo "$line"
if [[ $line == "$parent_zone" ]]; then
echo "yes, we do!"
echo "Creating delegation on parent zone $parent_zone"
echo "Adding name server delegations ..."
for ns_host in $ns_hosts ; do
echo "Adding $ns_host delegation to the parent zone ..."
$pdnsutil add-record "$parent_zone" "$zone_name" NS "${rr_ttl}" "${ns_host}.$ZONE"
echo "Adding glue for $ns_host in the parent zone"
$pdnsutil add-record "$parent_zone" "${ns_host}.${zone_name}" A "${rr_ttl}" "$( dig +short A "${ns_host}.${hoster}" )"
$pdnsutil add-record "$parent_zone" "${ns_host}.${zone_name}" AAAA "${rr_ttl}" "$( dig +short AAAA "${ns_host}.${hoster}" )"
done
echo "Adding DS (delegation signer) key to parent zone ..."
# Get the DS record for the child zone
ds_rr_content="$( pdnsutil export-zone-ds "$ZONE" |grep "( SHA256 digest )" | cut -d " " -f 4-7 )"
# Publish the child DS in the parent zone
$pdnsutil add-record "$parent_zone" "$zone_name" "DS" "$ds_rr_content"
$pdnsutil rectify-zone "$parent_zone"
$pdnsutil increase-serial "$parent_zone"
$pdnsutil check-zone "$parent_zone"
continue # while .. do
fi
done <<< "$our_zones"
echo -n "Checking if everything is correct ... "
$pdnsutil check-zone "$ZONE"
echo '***** All done. Your zone is ready *****'
echo
echo "Here is the DNS name-server information for your registrar:"
for ns_host in $ns_hosts ; do
echo '------------------------------------------------------------'
echo "Nameserver: ${ns_host}.$ZONE"
echo "IPv4 address: $( dig +short A "${ns_host}.${hoster}" )"
echo "IPv6 address: $( dig +short AAAA "${ns_host}.${hoster}" )"
echo
done
echo "Here is the DNSSEC information for your registrar:"
echo '------------------------------------------------------------------------------'
$pdnsutil export-zone-ds "$ZONE"
echo '------------------------------------------------------------------------------'
echo "This is your TSIG key for DNS updates (RFC2136):"
$pdnsutil list-tsig-keys | grep "${dnsupdate_key}"
echo '------------------------------------------------------------------------------'
echo "This is your TSIG key for receiving AXFR transfers:"
$pdnsutil list-tsig-keys| grep "${axfr_master_key}"
echo '------------------------------------------------------------------------------'
echo "This is the TSIG used by the master to notify slaves:"
$pdnsutil list-tsig-keys| grep "${axfr_slave_key}"
echo '------------------------------------------------------------------------------'
echo "Analyze DNSSEC Delegation (DNSViz): http://dnsviz.net/d/${ZONE}/dnssec/"
echo "Analyze security properties (Hardenize): https://www.hardenize.com/?host=${ZONE}"
echo '------------------------------------------------------------------------------'
echo 'Have a nice day.'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment