2019-01-06 16:34:39 +01:00
<!DOCTYPE html>
< html lang = "en-us" >
< head >
< meta charset = "utf-8" / >
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" / >
< meta name = "twitter:card" content = "summary" / >
< meta name = "twitter:title" content = "DNS zone versioning" / >
< meta name = "twitter:description" content = "" / >
< meta name = "twitter:site" content = "@" / >
< meta property = "og:title" content = "DNS zone versioning · Sysadmining. All day. Every day." / >
< meta property = "og:site_name" content = "Sysadmining. All day. Every day." / >
< meta property = "og:url" content = "https://www.captainark.net/2018/04/14/dns-zone-versioning/" / >
< meta property = "og:image" content = "/images/cover.jpg" / >
< meta property = "og:description" content = "" / >
< meta property = "og:type" content = "article" / >
< meta property = "article:published_time" content = "2018-04-14T00:00:00+01:00" / >
< title > DNS zone versioning · Sysadmining. All day. Every day.< / title >
< meta name = "description" content = "I&rsquo;ve been using PowerDNS with a SQL backend as a hidden master DNS server for a few years now .
I& rsquo;ve been wanting to write a quick shell script to ve" />
< meta name = "HandheldFriendly" content = "True" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" / >
2019-01-07 13:56:01 +01:00
< link rel = "shortcut icon" href = "https://www.captainark.net/images/favicon.ico" >
< link rel = "apple-touch-icon" href = "https://www.captainark.net/images/apple-touch-icon.png" / >
2019-01-06 16:34:39 +01:00
2019-01-07 13:56:01 +01:00
< link rel = "stylesheet" type = "text/css" href = "https://www.captainark.net/css/screen.css" / >
< link rel = "stylesheet" type = "text/css" href = "https://www.captainark.net/css/nav.css" / >
2019-01-06 22:45:23 +01:00
< link rel = "stylesheet" type = "text/css" href = "https://fonts.googleapis.com/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400|Inconsolata:700,400" / >
2019-01-06 16:34:39 +01:00
2019-01-06 18:41:13 +01:00
< link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/solarized-light.min.css" integrity = "sha384-bFKDPkG3geCujYJIbPornilfOgmYQoPS45Oh/8daqqo1SUwNY06OeHorpgnNvx82" crossorigin = "anonymous" >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js" integrity = "sha384-BlPof9RtjBqeJFskKv3sK3dh4Wk70iKlpIe92FeVN+6qxaGUOUu+mZNpALZ+K7ya" crossorigin = "anonymous" > < / script >
2019-01-06 16:34:39 +01:00
2019-01-07 13:56:01 +01:00
< script type = "text/javascript" src = "https://www.captainark.net/js/hjsload.js" > < / script >
2019-01-06 16:34:39 +01:00
2019-01-07 13:56:01 +01:00
< link href = "https://www.captainark.net/index.xml" rel = "alternate" type = "application/rss+xml" title = "Sysadmining. All day. Every day." / >
2019-01-06 16:34:39 +01:00
< meta name = "generator" content = "Hugo 0.53" / >
< link rel = "canonical" href = "https://www.captainark.net/2018/04/14/dns-zone-versioning/" / >
< script type = "application/ld+json" >
{
"@context": "https://schema.org",
"@type": "Article",
"publisher": {
"@type": "Organization",
"name": ,
"logo": https://www.captainark.net/images/logo.png
},
"author": {
"@type": "Person",
"name": ,
"image": {
"@type": "ImageObject",
"url": https://www.captainark.net/images/author.jpg,
"width": 250,
"height": 250
},
"url": https://www.captainark.net,
"sameAs": [
],
"description": Geek | Gamer | TV Shows Aficionado
},
"headline": DNS zone versioning,
"name": DNS zone versioning,
"wordCount": 621,
"timeRequired": "PT3M",
"inLanguage": {
"@type": "Language",
"alternateName": en
},
"url": https://www.captainark.net/2018/04/14/dns-zone-versioning/,
"datePublished": 2018-04-14T00:00Z,
"dateModified": 2018-04-14T00:00Z,
"description": ,
"mainEntityOfPage": {
"@type": "WebPage",
"@id": https://www.captainark.net/2018/04/14/dns-zone-versioning/
}
}
< / script >
2019-01-06 17:58:41 +01:00
2019-01-07 13:56:01 +01:00
< script type = "text/javascript" src = "https://www.captainark.net/js/stats.js" > < / script >
2019-01-06 16:34:39 +01:00
2019-01-06 17:58:41 +01:00
2019-01-06 16:34:39 +01:00
< link rel = "stylesheet" href = "https://cdn.jsdelivr.net/npm/fork-awesome@1.1.5/css/fork-awesome.min.css" integrity = "sha256-P64qV9gULPHiZTdrS1nM59toStkgjM0dsf5mK/UwBV4=" crossorigin = "anonymous" >
< / head >
< body class = "nav-closed" >
< div class = "nav" >
< h3 class = "nav-title" > Menu< / h3 >
< a href = "#" class = "nav-close" >
< span class = "hidden" > Close< / span >
< / a >
< ul >
< h3 > This site< / h3 >
< li class = "nav-opened" role = "presentation" >
2019-01-07 13:56:01 +01:00
< a href = "https://www.captainark.net/" > Home< / a >
2019-01-06 16:34:39 +01:00
< / li >
< li class = "nav-opened" role = "presentation" >
2019-01-07 13:56:01 +01:00
< a href = "https://www.captainark.net/about" > About< / a >
2019-01-06 16:34:39 +01:00
< / li >
< li class = "nav-opened" role = "presentation" >
2019-01-07 13:56:01 +01:00
< a href = "https://www.captainark.net/resume" > Resume< / a >
2019-01-06 16:34:39 +01:00
< / li >
< h3 > Other services< / h3 >
< li class = "nav-opened" role = "presentation" >
< a href = "https://git.captainark.net" > Gitea< / a >
< / li >
< li class = "nav-opened" role = "presentation" >
< a href = "https://pics.captainark.net" > Chevereto< / a >
< / li >
< li class = "nav-opened" role = "presentation" >
< a href = "https://paste.captainark.net" > Privatebin< / a >
< / li >
< li class = "nav-opened" role = "presentation" >
< a href = "https://chat.captainark.net" > Rocket.Chat< / a >
< / li >
< / ul >
2019-01-07 13:56:01 +01:00
< a class = "subscribe-button icon-feed" href = "https://www.captainark.net/index.xml" > Subscribe< / a >
2019-01-06 16:34:39 +01:00
< / div >
< span class = "nav-cover" > < / span >
< div class = "site-wrapper" >
< header class = "main-header post-head no-cover" >
< nav class = "main-nav clearfix" >
2019-01-07 13:56:01 +01:00
< a class = "blog-logo" href = "https://www.captainark.net/" > < img src = "https://www.captainark.net/images/logo.png" alt = "Home" / > < / a >
2019-01-06 16:34:39 +01:00
< a class = "menu-button" href = "#" > < span class = "burger" > ☰ < / span > < span class = "word" > Menu< / span > < / a >
< / nav >
< / header >
< main class = "content" role = "main" >
< article class = "post post" >
< header class = "post-header" >
< h1 class = "post-title" > DNS zone versioning< / h1 >
< small > < / small >
< section class = "post-meta" >
< time class = "post-date" datetime = "2018-04-14T00:00:00+01:00" >
14 April 2018
< / time >
< / section >
< / header >
< section class = "post-content" >
< p > I’ ve been using < a href = "https://doc.powerdns.com/md/" > PowerDNS< / a > with a SQL backend as a hidden master DNS server for a few years now.< / p >
< p > I’ ve been wanting to write a quick shell script to version my DNS zones for a while, and since I’ ve finally taken the time to do so today, I figured I’ d share it here.< / p >
< p > The script uses PowerDNS API to list the configured zones. It then exports them to a file in an AXFR-like format, commits and finally pushes them on a git repository< / p >
< h1 id = "configuration" > Configuration< / h1 >
< h2 id = "powerdns" > PowerDNS< / h2 >
< p > For the script to work, we have to activate PowerDNS’ API.< / p >
< p > To do so, let’ s create a < code > /etc/powerdns/pdns.d/api.conf< / code > file with the following content :< / p >
< pre > < code > api=yes
api-key=mysupersecretapikey
webserver=yes
webserver-address=10.0.0.10
webserver-allow-from=10.0.0.0/8
webserver-port=8081
< / code > < / pre >
< p > You should change < em > mysupersecretapikey< / em > to an actual secret.< / p >
< p > You should also adapt the < code > webserver-address< / code > and < code > webserver-allow-from< / code > to reflect your network configuration.< / p >
< p > Once the file is created, we have to restart pdnsd :< / p >
< pre > < code > systemctl restart pdns.service
< / code > < / pre >
< p > < strong > N.B. :< / strong > As with all my other articles, I’ m assuming here you’ re running Debian. The path of the configuration file you have to create or edit might not be the same if you’ re running another distribution or if you’ ve installed PowerDNS from source.< / p >
< h2 id = "jq" > jq< / h2 >
< p > < a href = "https://stedolan.github.io/jq/" > jq< / a > is required for the script to work, so let’ s install it !< / p >
< pre > < code > apt install jq
< / code > < / pre >
< h2 id = "git" > Git< / h2 >
< p > We now have to create a git repository to host our zone files.< / p >
< p > To do so, you can follow my < a href = "https://www.captainark.net/2016/01/31/private-git-repo/" > previous tutorial< / a > on the subject if you want.< / p >
< p > I’ ve personnaly migrated my git repos to a self-hosted < a href = "https://gogs.io/" > Gogs< / a > installation a while back.< / p >
< p > If you don’ t care about your zones content being public (it already is, technically), you could create a GitHub repo for that use (or on any other available git hosting).< / p >
< p > Once you’ ve created your repo, you should clone it on the machine that will run the script. For me, the path to the repo will be < code > /home/captainark/backup/dnsexport< / code > .< / p >
< pre > < code > apt install git
mkdir ~/backup & & cd ~/backup
git clone ssh://git@git.captainark.net/captainark/dnsexport.git
< / code > < / pre >
< p > You should also create a < code > ~/.gitconfig< / code > for the user that will run the script with the following parameters configured :< / p >
< pre > < code > [user]
email = captainark@captainark.net
name = CaptainArk
[push]
default = simple
< / code > < / pre >
< p > Also, make sure your user can push to the remote server before running the script. The following should work :< / p >
< pre > < code > cd ~/backup/dnsexport
echo '# DNSEXPORT' > README.md
git add README.md
git commit README.md -m 'adding README'
git push
< / code > < / pre >
< h1 id = "script" > Script< / h1 >
< p > Once we’ ve finished configuring PowerDNS and Git, we can run the script.< / p >
< p > You can copy the following to < code > ~/bin/dnsexport< / code > :< / p >
< pre > < code class = "language-bash" > #!/bin/bash
ApiKey=" mysupersecretapikey"
PdnsUrl=" 10.0.0.10:8081"
PdnsServerName=" localhost"
PdnsZoneUrl=" http://${PdnsUrl}/api/v1/servers/${PdnsServerName}/zones"
ZoneList=$(/usr/bin/curl -sH " X-API-Key: ${ApiKey}" ${PdnsZoneUrl} | jq -r '.[].id')
ExportFolder=" /home/captainark/backup/dnsexport"
updateremote() {
cd $ExportFolder
git add db.${Zone%.}
git commit -m " Automated commit due to modification on ${Zone%.} at $(date -Iseconds)"
git push
cd -
}
for Zone in ${ZoneList}; do
ZoneFile=" ${ExportFolder}/db.${Zone%.}"
CurrentShaSum=$(/usr/bin/sha256sum ${ZoneFile})
/usr/bin/curl -o ${ZoneFile} -sH " X-API-Key: ${ApiKey}" ${PdnsZoneUrl}/${Zone}/export
NewShaSum=$(/usr/bin/sha256sum ${ZoneFile})
[[ ${NewShaSum% *} != ${CurrentShaSum% *} ]] & & updateremote
done
< / code > < / pre >
< p > It’ s nothing fancy, but it does the job.< / p >
< p > You’ ll have to adapt the < code > ApiKey< / code > , < code > PdnsUrl< / code > and < code > ExportFolder< / code > variables to your configuration.< / p >
< p > Once that’ s done, let’ s fix the permissions on the script :< / p >
< pre > < code > chmod 700 ~/bin/dnsexport
< / code > < / pre >
< p > You should run the script manually once to make sure everything is working OK. If it is, you should see a new commit on the repo for each zone you have configured in PowerDNS.< / p >
< p > Once the script has executed once without issue, you can schedule it regularly. I have it running every 10 minutes in my user’ s crontab :< / p >
< pre > < code > crontab -e
# DNSEXPORT
*/10 * * * * /home/captainark/bin/dnsexport
< / code > < / pre >
< h1 id = "conclusion" > Conclusion< / h1 >
< p > That’ s all !< / p >
< p > As always, if you’ ve found this article useful, please feel free to make use of the comments section below !< / p >
< p > Hopefully it won’ t take as long before I write another article here next time !< / p >
< / section >
< footer class = "post-footer" >
< figure class = "author-image" >
< a class = "img" href = "https://www.captainark.net/" style = "background-image: url(/images/author.jpg)" > < span class = "hidden" > Antoine Joubert's Picture< / span > < / a >
< / figure >
< section class = "author" >
< h4 > < a href = "https://www.captainark.net/" > Antoine Joubert< / a > < / h4 >
< p > Geek | Gamer | TV Shows Aficionado< / p >
< div class = "author-meta" >
< span class = "author-location icon-location" > Angers, France< / span >
< span class = "author-link icon-link" > < a href = "https://www.captainark.net" > https://www.captainark.net< / a > < / span >
< / div >
< / section >
2019-01-06 17:14:04 +01:00
<!-- isso -->
< script data-isso = "https://www.captainark.net/comments/" src = "https://www.captainark.net/comments/js/embed.min.js" > < / script >
< noscript > Please enable JavaScript to view comments< / noscript >
< section id = "isso-thread" > < / section >
<!-- end isso -->
2019-01-06 16:34:39 +01:00
< / footer >
< / article >
< / main >
< aside class = "read-next" >
2019-01-07 13:56:01 +01:00
< a class = "read-next-story" style = "no-cover" href = "https://www.captainark.net/2018/11/27/self-hosted-report-uri/" >
2019-01-06 16:34:39 +01:00
< section class = "post" >
< h2 > Self-hosted report-uri< / h2 >
< / section >
< / a >
2019-01-07 13:56:01 +01:00
< a class = "read-next-story prev" style = "no-cover" href = "https://www.captainark.net/2017/11/19/installing-ghost/" >
2019-01-06 16:34:39 +01:00
< section class = "post" >
2019-01-06 18:06:10 +01:00
< h2 > Installing Ghost< / h2 >
2019-01-06 16:34:39 +01:00
< / section >
< / a >
< / aside >
< center >
< a class = "fa-icons" href = "mailto:contact@captainark.net" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-envelope fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< a class = "fa-icons" href = "https://twitter.com/captainark" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-twitter fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< a class = "fa-icons" href = "https://social.captainark.net/users/captainark" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-mastodon-alt fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< a class = "fa-icons" href = "https://github.com/captainark" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-github fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< a class = "fa-icons" href = "https://www.last.fm/user/captainark" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-lastfm fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< a class = "fa-icons" href = "https://steamcommunity.com/id/captainark" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-steam fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< a class = "fa-icons" href = "https://www.twitch.tv/captainark" >
< span class = "fa-stack fa-lg" >
< i class = "fa fa-circle fa-stack-2x" > < / i >
< i class = "fa fa-twitch fa-stack-1x fa-inverse" > < / i >
< / span >
< / a >
< / center >
< footer class = "site-footer clearfix" >
< section class = "copyright" > < a href = "" > Sysadmining. All day. Every day.< / a > © 2015 - 2019< / section >
< section class = "poweredby" > Proudly generated by < a class = "icon-hugo" href = "http://gohugo.io" > HUGO< / a > , with < a class = "icon-theme" href = "https://github.com/vjeantet/hugo-theme-casper" > Casper< / a > theme< / section >
< / footer >
< / div >
2019-01-06 22:45:23 +01:00
< script src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" integrity = "sha384-CgeP3wqr9h5YanePjYLENwCTSSEz42NJkbFpAFgHWQz7u3Zk8D00752ScNpXqGjS" crossorigin = "anonymous" > < / script >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/fitvids/1.1.0/jquery.fitvids.min.js" integrity = "sha384-2/VQUb0aZHixKnNLh7pD38DZk+acGpEw5LeHieWVDPR0h/H326kp/1qnRPDYmFXM" crossorigin = "anonymous" > < / script >
2019-01-07 13:56:01 +01:00
< script type = "text/javascript" src = "https://www.captainark.net/js/index.js" > < / script >
2019-01-06 16:34:39 +01:00
< / body >
< / html >