Managing DNS zonefiles just got easier and more robust thanks to Gitlab CI.
The presented workflow is based on a Git repository saved in a Gitlab instance and Gitlab CI which runs some scripts in a Docker image. This Docker image contains tools and scripts to build, check and deploy zonefiles to a Knot authoritative DNS server.
How it works
It all starts with a Git repository containing all zonefiles and the Gitlab CI configuration:
*.zone: BIND compatible zonefile
.gitlab-ci.yml: Configuration for Gitlab CI build
In this example Knot is configured with a hidden master which syncs to two slaves.
Each DNS zone is stored in a BIND compatible zonefile, f.e.
example.com.zone. Here is an example (really just a normal BIND zonefile):
$ORIGIN example.com. $TTL 3600 ; SOA @ IN SOA ns.communityrack.org. hostmaster.communityrack.org. ( 1 ; SERIALAUTOUPDATE 3600 600 1209600 3600 ) ; NS IN NS ns.communityrack.org. IN NS ns.communityrack.net. ; Mail @ IN MX mail.example.com. @ IN TXT "v=spf1 a mx ?all" ; Host Resource Records @ IN A 192.0.2.10 @ IN AAAA 2001:db8::10 www IN A 192.0.2.10 www IN AAAA 2001:db8::10 mail IN A 192.0.2.11 mail IN AAAA 2001:db8::11
Please note the special serial and it's magic string
SERIALAUTOUPDATE. This is used later in the CI/CD pipeline to automatically manage the zone serial.
Configuration of the Gitlab CI job happens in a file called
image: communityrack/knotci zonedelivery: script: - buildzones.sh - checkzones.sh - deployzones.sh artifacts: paths: - '*.zone' cache: paths: - .lasthash - .oldserials only: - master
This files is very well documented here: Configuration of your builds with .gitlab-ci.yml.
Docker Image communityrack/knotci
This image is based on the Alpine Linux image and contains some DNS tools and the worker scripts:
FROM gliderlabs/alpine:latest RUN apk-install \ bash \ bind \ bind-tools \ openssh-client \ git \ rsync ADD *.sh /usr/local/bin/ ADD rsyncignore /etc/ WORKDIR /zones
The source can be found on Github: communityrack/knotci.
Build, Check and Deploy Scripts
The heart of the whole chain are the three scripts
All have the detection of which zones have changed in common: As there is no information available which files have changed since the last CI run, this is being detected by using some Git commands.
If there is a file available called
.lasthash, it's content (a hash) is read and used to detect the changed files with
git diff --name-only HEAD "$LASTHASH" -- '*.zone, otherwise it just compares to
HEAD~1. To act on all zonefiles without change detection, all scripts accept the command line parameter
allzones. At the end of the CI run the file
.lasthash is cached (see
.gitlab-ci.yml cache configuration above).
Configuration of the scripts happens in environment variables which can be defined in Gitlab under "My Project -> Settings -> Variables". All configuration parameters are documented in the README.
Here we update the zone serial. This is done by replacing
1 ; SERIALAUTOUPDATE with the current unix timestamp. For zones which didn't change we replace the magic string with the cached serial from
.oldserials. During the script run the serials are all saved to
.oldserials which is cached for the next CI run by Gitlab CI.
After the zones have been modified we check all zones before deploying them. The following checks are carried out:
named-checkzone -i local: This is done on all zones, no mather if they have changed or not
- Comparing serial to the currently active on the hidden master for changed zones
If the zone does not yet exist on the hidden master, the check is skipped. The check also supports serial rollover, it doesn't simply compare if the new serial it's higher but takes the serial maths into consideration.
When all checks pass the zones are deployed to the hidden master by using rsync. For accessing the remote with SSH the script get's the private SSH key from the environment variable
$SSH_PRIVATE_KEY which is passed to a temporary started SSH agent. Only zonefiles are synced, all other files are excluded from the rsync process (see rsyncignore).
Knot DNS Server configuration
This is done using the Knot Puppet module, no special configuration is needed, except the zonefiles must be saved in a separate directory, f.e. in
A sample zone template:
template: - id: master_default file: /var/lib/knot/zones/%s.zone serial-policy: unixtime storage: /var/lib/knot