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.
*.zone
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.
.gitlab-ci.yml
Configuration of the Gitlab CI job happens in a file called .gitlab-ci.yml
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 buildzones.sh
, checkzones.sh
and deployzones.sh
.
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.
buildzones.sh
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.
checkzones.sh
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.
deployzones.sh
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 /var/lib/knot/zones
.
A sample zone template:
template:
- id: master_default
file: /var/lib/knot/zones/%s.zone
serial-policy: unixtime
storage: /var/lib/knot