From 3698bb4e5392c5f3d5b000a1db6ad75f25618901 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 23 Oct 2022 13:33:43 +0200 Subject: [PATCH 1/3] Add README.md with usage documentation --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7bb1a01 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# CAcert DNS zones + +CAcert runs its own public DNS nameservers on ns1 and ns2 in its BIT datacenter +rack in Ede. + +We use [PowerDNS](https://doc.powerdns.com/authoritative/index.html) installed +on Debian systems. + +This repository contains a Python script `update-zones.py` that is used for +updating DNS information from this Git repository. + +The canonical URL for this repository is +[https://code.cacert.org/critical/dns-zones.git](https://code.cacert.org/critical/dns-zones.git). + +## Prerequisites + +The server certificate for https://code.cacert.org/ needs to be trusted. +Therefore the CAcert root CA certificate needs to be put into +`/usr/local/share/ca-certificates` and hast to be registered as trusted by +running + +```shell +update-ca-certificates +``` + +The `update-zones.py` script needs `git`, `pdnsutil` and `python3`. We only use +the Python 3 standard library and no external dependencies. The script uses +`/usr/lib/sendmail` to send change mails. Mail sending has been tested with +ssmtp and exim4. + +To make sure that all these prerequisites are met, you may run + +```shell +apt install python3 ca-certificates pdns-server git mail-transport-agent +``` + +## Cloning the repository + +The git configuration on ns1 and ns2 has been adapted to allow remembering the +credentials to clone the repository. A separate user pdnssync has been setup to +allow cloning the repository. + +This repository is meant to be cloned on the CAcert DNS servers ns1 and ns2. + +```shell +cd ~ +git config --global credential.helper store +git config --global pull.ff only +git clone https://code.cacert.org/critical/dns-zones.git +``` + +Credentials will only be asked for the initial clone. The credential helper +records them in in `~/.git-credentials`. + +## Updating zones + +The user running the update needs read access to the configuration in +`/etc/powerdns` (either member of the pdns group or root). + +``` +$ cd ~/dns-zones +$ git pull +$ ./update-zones.py +``` + +The `update-zones.py` tracks the local status in a branch (default +'provisioned') that is updated when + +* a) zone changes have been applied +* b) the running PowerDNS is responsible as secondary nameserver + +The `update-zones.py` script should be run on both nameservers. From 5f7fb5235d928b9984bc2e1dd5d1b373e45414a6 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 23 Oct 2022 13:34:01 +0200 Subject: [PATCH 2/3] Remove the import_zone script This commit removes the older import_zone script to avoid accidential usage. --- import_zone | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 import_zone diff --git a/import_zone b/import_zone deleted file mode 100644 index c4d6153..0000000 --- a/import_zone +++ /dev/null @@ -1,21 +0,0 @@ -set -x -domain=$1 -now=$(date +"%Y%m%d_%H%M%S") -pdnsutil list-zone $1 > $1.$now -grep -v SOA $1 >$1.new -grep SOA $1.$now >> $1.new -pdnsutil load-zone $1 $1.new -pdnsutil increase-serial $1 -mysqldump powerdns > powerdns.dump.$now - -echo "From: Nameserver NS2 " > mail.txt -echo "To: critical-admin@cacert.org" >> mail.txt -echo "Subject: Update of zone $1" >> mail.txt -#echo "$1 has now the following zonefile-data:" >>mail.txt -pdnsutil list-zone $1 > $1.now -echo >>mail.txt -diff -u0 -b $1.$now $1.now >>mail.txt -echo " ------------------------- " >> mail.txt -pdnsutil list-zone $1 >> mail.txt - -cat mail.txt | ssmtp -6 critical-admin@cacert.org From 2c896a85ac98b15393ffdcea4641d0751d525b55 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 23 Oct 2022 13:52:05 +0200 Subject: [PATCH 3/3] Add support for secondary nameservers Fixes #4 --- update-zones.py | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/update-zones.py b/update-zones.py index 23481cb..655a306 100755 --- a/update-zones.py +++ b/update-zones.py @@ -44,11 +44,13 @@ def git_changed_files(reference_branch, target_branch): return output.strip().splitlines() -def pdns_managed_zones(): +def pdns_managed_zones(secondary_only=False): + command = ["pdnsutil", "list-all-zones"] + if secondary_only: + command += ["slave"] + try: - all_zones = run( - ["pdnsutil", "list-all-zones"], check=True, capture_output=True, text=True - ) + all_zones = run(command, check=True, capture_output=True, text=True) zones = all_zones.stdout.strip().splitlines() except CalledProcessError as e: print( @@ -75,6 +77,14 @@ def calculate_changed_zones(files, zones): return sorted(set(files).intersection(zones)) +def remove_secondary_zones(changed_zones): + """ + Remove DNS zones from the given set where the current server is a secondary DNS server. + """ + secondary_zones = pdns_managed_zones(secondary_only=True) + return sorted(set(changed_zones).difference(secondary_zones)) + + def generate_diff(zone, reference_branch, target_branch): diffresult = run( ["git", "diff", f"{reference_branch}..{target_branch}", "--", zone], @@ -190,6 +200,19 @@ def get_changelog(reference_branch, target_branch): return r.stdout.strip() +def update_reference_branch(reference_branch, target_branch): + """ + Update the local git reference branch to track the target branch. + """ + run( + ["git", "branch", "-D", reference_branch], + check=True, + stdout=DEVNULL, + stderr=DEVNULL, + ) + run(["git", "branch", reference_branch, target_branch], check=True) + + def main(reference_branch, target_branch, audit_email_address, audit_sender_address): changed_files = git_changed_files( reference_branch=reference_branch, target_branch=target_branch @@ -206,8 +229,13 @@ def main(reference_branch, target_branch, audit_email_address, audit_sender_addr changed_zones = calculate_changed_zones(changed_files, zones_in_pdns) + changed_zones = remove_secondary_zones(changed_zones) + if not changed_zones: print("no zones changed") + + update_reference_branch(reference_branch, target_branch) + return diffs = [] @@ -246,13 +274,7 @@ def main(reference_branch, target_branch, audit_email_address, audit_sender_addr changelog = get_changelog(reference_branch, target_branch) - run( - ["git", "branch", "-D", reference_branch], - check=True, - stdout=DEVNULL, - stderr=DEVNULL, - ) - run(["git", "branch", reference_branch, target_branch], check=True) + update_reference_branch(reference_branch, target_branch) send_audit_mail(diffs, audit_email_address, audit_sender_address, changelog)