powerdns-tools/bin/pdns.sh
2024-02-06 10:30:35 -08:00

233 lines
7.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
#
# powerdns-tools
# https://git.stack-source.com/msb/powerdns-tools
# Copyright (c) 2022 Matthew Saunders Brown <matthewsaundersbrown@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# powerdns-tools include file, used by other powerdns-tools bash scripts
# Must be root, attempt sudo if need be. root is not actually required for the API commands, but we want to restrict access.
if [ "$USER" != "root" ]; then
exec sudo -u root $0 $@
fi
# Load local config
# 'api_key' & 'dns_domain' must be set in the local config.
# If 'account' is set in the local config it is set for all 'zone' & 'comment' PDNS entries.
# 'account' can also be set/overridden on command line.
# Any of the other constants below can be overriden in the local config.
if [[ -f /usr/local/etc/pdns.conf ]]; then
source /usr/local/etc/pdns.conf
fi
# constants
# API URL. Consider putting this behind a proxy with https on the front and then setting api_base_url in pdns.conf
[[ -z $api_base_url ]] && api_base_url=http://127.0.0.1:8081/api/v1/servers/localhost
# Default IP address to use for new zone records
# Defaults to IP of server script is run from
[[ -z $default_ip ]] && default_ip=$(hostname --ip-address)
# Array of allowed Resource Record Types
[[ -z $rr_types ]] && declare -a rr_types=(A AAAA CNAME MX NS PTR SRV TXT)
# Minimum/maximum values for SOA and RR records
[[ -z $min_ttl ]] && min_ttl=300
[[ -z $max_ttl ]] && max_ttl=2419200
[[ -z $min_refresh ]] && min_refresh=300
[[ -z $min_retry ]] && min_retry=300
[[ -z $min_expire ]] && min_expire=86400
# Default values for new zones.
[[ -z $zone_default_ns ]] && zone_default_ns="ns1.$dns_domain"
[[ -z $zone_defaults_mbox ]] && zone_defaults_mbox="hostmaster.$dns_domain"
[[ -z $zone_defaults_ttl ]] && zone_defaults_ttl='3600'
[[ -z $zone_defaults_refresh ]] && zone_defaults_refresh='86400'
[[ -z $zone_defaults_retry ]] && zone_defaults_retry='7200'
[[ -z $zone_defaults_expire ]] && zone_defaults_expire='1209600'
[[ -z $zone_defaults_minimum ]] && zone_defaults_minimum='3600'
readonly zone_defaults_pri='0'
# zone_defaults_pri must be 0, do not change
# The following array specifies default records for new zone records.
# These get inserted automatically whenever a zone is created.
# The format of each record is (name|type|content). TTL is taken from defaults above.
# @ will be replace with the zone (domain name)
# TXT records will get quoted (don't add quotes around the content field here)
# Only MX & SRV records use priority, and it should be specified as part of the content/data field.
[[ -z $default_records ]] && declare -a default_records=("@|A|$default_ip"
"@|MX|10 mail.@"
"@|NS|ns1.$dns_domain"
"@|NS|ns2.$dns_domain"
"@|NS|ns3.$dns_domain"
"@|TXT|v=spf1 a mx -all"
"mail.@|A|$default_ip"
"imap.@|CNAME|mail.@"
"smtp.@|CNAME|mail.@"
"pop.@|CNAME|mail.@"
"www.@|CNAME|@")
# functions
# crude but good enough domain name format validation
function pdns::validate_domain () {
local my_domain=$1
if [[ $my_domain =~ ^(([a-zA-Z0-9](-?[a-zA-Z0-9])*)\.)+[a-zA-Z]{2,}\.?$ ]] ; then
return 0
else
return 1
fi
}
# crude but good enough IP address format validation
function pdns::validate_ip()
{
local ip=$1
local status=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
status=$?
fi
return $status
}
# yesno prompt
#
# Examples:
# loop until y or n: if pdns::yesno "Continue?"; then
# default y: if pdns::yesno "Continue?" Y; then
# default n: if pdns::yesno "Continue?" N; then
function pdns::yesno() {
local prompt default reply
if [ "${2:-}" = "Y" ]; then
prompt="Y/n"
default=Y
elif [ "${2:-}" = "N" ]; then
prompt="y/N"
default=N
else
prompt="y/n"
default=
fi
while true; do
read -p "$1 [$prompt] " -n 1 -r reply
# Default?
if [ -z "$reply" ]; then
reply=$default
fi
# Check if the reply is valid
case "$reply" in
Y*|y*) return 0 ;;
N*|n*) return 1 ;;
esac
done
}
function pdns:getoptions () {
local OPTIND
while getopts "ha:bcd:e:i:l:m:n:o:q:r:s:t:w:xz:" opt ; do
case "${opt}" in
h ) # display help and exit
help
exit
;;
a ) # account
account=${OPTARG}
;;
b ) # bare - create empty zone, do not any default records
bare=true
;;
c ) # csv - output in csv format
csv=true
;;
d ) # domain (alias of zone)
zone=${OPTARG,,}
# pdns stores zone name without trailing dot, remove if found
if [[ ${zone: -1} = '.' ]]; then
zone=${zone::-1}
fi
if ! pdns::validate_domain $zone; then
echo "ERROR: $zone is not a valid domain name."
exit
fi
;;
e ) # max (integer) Maximum number of entries to return
max=${OPTARG}
;;
i ) # IP
ip=${OPTARG}
if ! pdns::validate_ip $ip; then
echo "ERROR: $ip is not a valid IP address."
exit
fi
;;
l ) # ttl - Time To Live
ttl=${OPTARG}
;;
m ) # master - IP of master if this is a slave zone
master=${OPTARG}
;;
n ) # name - hostname for this record
name=${OPTARG,,}
;;
o ) # object_type - Type of data to search for, one of 'all', 'zone', 'record', 'comment'
object=${OPTARG,,}
;;
q ) # query - The string to search for
query=${OPTARG}
;;
r ) # record data
record=${OPTARG}
;;
s ) # status - disabled status, 0 for active 1 for inactive, default 0
status=${OPTARG}
;;
t ) # type
type=${OPTARG^^}
;;
w ) # comment - a note about the record
comment=${OPTARG}
;;
x ) # eXecute - don't prompt for confirmation
execute=true
;;
z ) # zone
zone=${OPTARG,,}
# pdns stores zone name without trailing dot, remove if found
if [[ ${zone: -1} = '.' ]]; then
zone=${zone::-1}
fi
if ! pdns::validate_domain $zone; then
echo "ERROR: $zone is not a valid domain name."
exit
fi
;;
\? )
echo "Invalid option: $OPTARG"
exit 1
;;
: )
echo "Invalid option: $OPTARG requires an argument"
exit 1
;;
esac
done
shift $((OPTIND-1))
}