Last active
October 13, 2023 09:04
-
-
Save alainwolf/4ba6466c60734a254219d47154dbd86c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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