set -euo pipefail . driglibash-base fakeresolve_ip_list () { if [ "$#" -ne 1 ] ; then die "Usage: fakeresolve_ip_list " fi grep -oP "^$1[[:space:]]+(IN)?[[:space:]]+A{1,4}[[:space:]]+\K[^;\s]+" "$debian_bind_confdir/$server_zone_file" | tr '\n' ';' } prepare () { # Install dependencies apt install -y bind9 &>/dev/null # Create Directories if [ -n "$keydir" ] ; then mkdir -p "$keydir" chown bind:bind "$keydir" -R chown bind:bind "$debian_bind_confdir" -R fi echo 'Sync the git repo' run sudo -u bind git_update.sh -r main -o "-i $DATA_DIR/gitkey" -d "$debian_bind_confdir" 'ssh://git@git.jean-cloud.net:22529/adrian/dnszones.git' cd /etc/bind echo 'Prepare bind: Remove autogenerated part from bind conf files' rm /etc/bind/*.jnl &>/dev/null || true sed -i -n "/$autoconf_separator/q;p" "$debian_bind_confdir"/* echo 'Put the separator back' for file in $( ls "$debian_bind_confdir"/template.db.* | grep -v '.signed$\|.jbk$\|.jnl$') ; do echo "$autoconf_separator" >> "$file" done } restart () { echo 'Check named conf' run named-checkconf "$debian_bind_confdir/named.conf" for db_file in $(list_db_files) ; do domain="$(basename "$db_file")" domain="${domain:3}" run named-checkzone "$domain" "$db_file" done echo 'Restart named' systemctl restart named } # Function that simulate a DNS resolve by reading bind zone file # Returns all the record line: # @ IN A X.X.X.X fakeresolve () { if [ "$#" -ne 1 ] ; then die "Usage: fakeresolve " fi name="$1" zonefile="$debian_bind_confdir/$server_zone_file" # Split full name if there are dots shortname="$name" if [ -n "$(echo "$name" | grep -o '\.')" ] ; then shortname="$(echo "$name" | grep -Po '^.*(?=\.[^\.]+\.[^\.]+$)' || true)" fi grep -v -e '^[[:space:]]*;' "$zonefile" |grep -oP "^[[:space:]]*$shortname\K[[:space:]]*(IN)?[[:space:]]*A{1,4}[[:space:]]*[\S;]+" | sed 's/^/@/' } # Function that add DNS record in the right file addbindline () { if [ "$#" -ne 2 ] ; then die "Usage: addbindline " fi name="$1" target="$2" # extract the truc.com part domain="$(echo "$name" | grep -o '[^\.]\+\.[^\.]\+$' || true)" [ -z "$domain" ] && return 0 # extract the subdomain part (www) shortname="$(echo "$name" | grep -Po '^.*(?=\.[^\.]+\.[^\.]+$)' || true)" # bind DB file bindfile="$debian_bind_confdir/db.$domain" # Only append if db file exists [ ! -f "$bindfile" ] && return 0 # BTW allow ACME DNS update token="#JC-ACME $domain" acme_dns="grant letsencrypt.key. name _acme-challenge.$name. TXT;" sed -i "s/\([[:space:]]*\)$token/\1$acme_dns\n\1$token/" "$debian_bind_confdir/named.conf.local" # TODO check if name already existst with a different target # Hard since we are resolving targets now… # CNAME are forbiden for empty shortnames, so we must resolve the target IPs # For performance reasons, we just put plain IP everywhere # to put aliases in place of ip juste do the following if $shortname is empty: #line_in_file "$shortname CNAME $target." "$bindfile" while read line ; do if [ -n "$shortname" ] ; then line="$(echo "$line" | sed "s/@/$shortname/")" fi line_in_file "$line" "$bindfile" done < <(fakeresolve "$target") } list_template_db_files () { ls "$debian_bind_confdir"/template.db.* } list_db_files () { ls "$debian_bind_confdir"/db.* | grep -v -e '.jbk$' -e '.signed$' -e '.signed.jnl' } create_primary_files () { # Compact the default SOA SOA="$(grep -o '^[^;]*' SOA | sed -z -e 's/[[:space:]]\{2,\}/ /g' -e 's/\n/\\n/')" line_in_file "include \"$DATA_DIR/letsencrypt.key\";" "$debian_bind_confdir/named.conf" for file in $(list_template_db_files) ; do domain="$(basename "$file" | sed 's/template.db.//')" new_db_file="$(echo "$file" | sed 's/template.db./db./')" # Set the default SOA if needed sed "s/^;JC_AUTOSOA$/$SOA/" "$file" > "$new_db_file" # Set serial serial="$(date '+%s')" sed -i "s/\(@ SOA [^(]*( \)[0-9]\+/\1$serial/" "$new_db_file" # Add this dns server if not present if ! grep -q '[^;].*(IN)?.*NS.*' "$new_db_file" ; then cat default_ns >> "$new_db_file" fi # Add DS record #if [ -n "$(ls "$DATA_DIR/keys/K$domain"*.key)" ] ; then #dnssec-dsfromkey "$DATA_DIR/keys/K$domain"*.key | sed "s/${domain}./@/" >> "$new_db_file" #fi # Populate named.conf.local cat >> "$debian_bind_confdir/named.conf.local" <<-EOF zone "$domain" { # Zone file type master; file "$new_db_file"; # Secondary conf # https://kb.isc.org/docs/aa-00723 #allow-update { !{!{$secondary_ips};any;}; key update-key; }; allow-transfer { $secondary_ips }; also-notify { $secondary_ips }; notify yes; # DNSSEC dnssec-policy default; inline-signing yes; key-directory "$DATA_DIR/keys"; # ACME autorizations update-policy { #JC-ACME $domain }; }; EOF done echo 'Find every used domain and add them to bind db' while IFS=';' read -r id username service target do addbindline "$service" "$target" nginxfile="/docker/$service/nginx_server.conf" if [ -f "$nginxfile" ] ; then for name in $(extract_domain_nginx_conf.sh "$nginxfile" | template.sh "/docker/$service/.env") ; do addbindline "$name" "$target" done fi done < <(grep -v '^#' /docker/services.csv) } create_secondary_files () { primary_ips="$(echo "$primary_ips" | sed 's/^;//')" for file in "$debian_bind_confdir"/template.db.* ; do file="$(echo "$file" | sed -e 's/template.db.//' -e "s#$debian_bind_confdir#/var/lib/bind/#")" domain="$(basename "$file")" echo -n " zone \"$domain\" { masters { $primary_ips }; type slave; file \"$file\"; };" >> "$debian_bind_confdir/named.conf.local" done }