From 9f5c4bacb0eb1bdc94aec9d3f8c7646eaea18762 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Thu, 14 May 2015 11:14:48 -0500 Subject: [PATCH 1/5] Add `dynamic` parameter to `bind::zone` Add a parameter to `bind::zone` which indicates whether a zone is dynamic or not. This has the effect of allowing puppet to manage the zone file rather than simply initialize it. This change also introduces more appropriate handling of slave and stub zones, so that puppet will not populate a stock zone file, forcing the nameserver to do a zone transfer when a zone is created. Also, there is now a substancial amount of validation in the `bind::zone` class in order to prevent invalid parameter combinations, so that validity may be assumed elsewhere in the manifest and in the configuration template. --- manifests/zone.pp | 137 ++++++++++++++++++++++++---------------- metadata.json | 1 + templates/zone.conf.erb | 34 +++++----- 3 files changed, 101 insertions(+), 71 deletions(-) diff --git a/manifests/zone.pp b/manifests/zone.pp index 6522960..aa7a4d6 100644 --- a/manifests/zone.pp +++ b/manifests/zone.pp @@ -3,6 +3,7 @@ define bind::zone ( $zone_type, $domain = '', + $dynamic = true, $masters = '', $transfer_source = '', $allow_updates = '', @@ -16,74 +17,104 @@ define bind::zone ( $forward = '', $source = '', ) { - $cachedir = $bind::cachedir + # where there is a zone, there is a server + include bind + $cachedir = $::bind::cachedir + $_domain = pick($domain, $name) - if $domain == '' { - $_domain = $name - } else { - $_domain = $domain + # dynamic implies master zone + validate_bool(!($dynamic and $zone_type != 'master')) + + # masters implies slave/stub zone + validate_bool(!($masters != '' and ! member(['slave', 'stub'], $zone_type))) + + # transfer_source implies slave/stub zone + validate_bool(!($transfer_source != '' and ! member(['slave', 'stub'], $zone_type))) + + # allow_updates implies dynamic + validate_bool(!($allow_update != '' and ! $dynamic)) + + # dnssec implies dynamic zone + validate_bool(!($dnssec and ! $dynamic)) + + # key_directory implies dnssec + validate_bool(!($key_directory != '' and ! $dnssec)) + + # allow_notify implies slave/stub zone + validate_bool(!($allow_notify != '' and ! member(['slave', 'stub'], $zone_type))) + + # forwarders implies forward zone + validate_bool(!($forwarders != '' and $zone_type != 'forward')) + + # forward implies forward zone + validate_bool(!($forward != '' and $zone_type != 'forward')) + + # source implies master/hint zone + validate_bool(!($source != '' and ! member(['master', 'hint'], $zone_type))) + + $zone_file_mode = $zone_type ? { + 'master' => $dynamic ? { + true => 'init', + false => 'managed', + }, + 'slave' => 'allowed', + 'hint' => 'managed', + 'stub' => 'allowed', + default => 'absent', } - $has_zone_file = $zone_type ? { - 'master' => true, - 'slave' => true, - 'hint' => true, - 'stub' => true, - default => false, - } - - if $has_zone_file { - if $zone_type == 'master' and $source != '' { - $_source = $source - } else { - $_source = 'puppet:///modules/bind/db.empty' - } - + if member(['init', 'managed', 'allowed'], $zone_file_mode) { file { "${cachedir}/${name}": ensure => directory, - owner => $bind::params::bind_user, - group => $bind::params::bind_group, + owner => $::bind::params::bind_user, + group => $::bind::params::bind_group, mode => '0755', require => Package['bind'], } - file { "${cachedir}/${name}/${_domain}": - ensure => present, - owner => $bind::params::bind_user, - group => $bind::params::bind_group, - mode => '0644', - replace => false, - source => $_source, - audit => [ content ], + if member(['init', 'managed'], $zone_file_mode) { + file { "${cachedir}/${name}/${_domain}": + ensure => present, + owner => $::bind::params::bind_user, + group => $::bind::params::bind_group, + mode => '0644', + replace => ($zone_file_mode == 'managed'), + source => pick($source, 'puppet:///modules/bind/db.empty'), + audit => [ content ], + } } - - if $dnssec { - exec { "dnssec-keygen-${name}": - command => "/usr/local/bin/dnssec-init '${cachedir}' '${name}'\ - '${_domain}' '${key_directory}'", - cwd => $cachedir, - user => $bind::params::bind_user, - creates => "${cachedir}/${name}/${_domain}.signed", - timeout => 0, # crypto is hard - require => [ - File['/usr/local/bin/dnssec-init'], - File["${cachedir}/${name}/${_domain}"] - ], - } - - file { "${cachedir}/${name}/${_domain}.signed": - owner => $bind::params::bind_user, - group => $bind::params::bind_group, - mode => '0644', - audit => [ content ], - } + } elsif $zone_file_mode == 'absent' { + file { "${cachedir}/${name}": + ensure => absent, } } - file { "${bind::confdir}/zones/${name}.conf": + if $dnssec { + exec { "dnssec-keygen-${name}": + command => "/usr/local/bin/dnssec-init '${cachedir}' '${name}'\ + '${_domain}' '${key_directory}'", + cwd => $cachedir, + user => $::bind::params::bind_user, + creates => "${cachedir}/${name}/${_domain}.signed", + timeout => 0, # crypto is hard + require => [ + File['/usr/local/bin/dnssec-init'], + File["${cachedir}/${name}/${_domain}"] + ], + } + + file { "${cachedir}/${name}/${_domain}.signed": + owner => $::bind::params::bind_user, + group => $::bind::params::bind_group, + mode => '0644', + audit => [ content ], + } + } + + file { "${::bind::confdir}/zones/${name}.conf": ensure => present, owner => 'root', - group => $bind::params::bind_group, + group => $::bind::params::bind_group, mode => '0644', content => template('bind/zone.conf.erb'), notify => Service['bind'], diff --git a/metadata.json b/metadata.json index 8d00a00..d825440 100644 --- a/metadata.json +++ b/metadata.json @@ -27,6 +27,7 @@ } ], "dependencies": [ + { "name": "puppetlabs/stdlib" }, { "name": "puppetlabs/concat", "version_requirement": ">=1.0.0 <2.0.0" }, { "name": "ripienaar/module_data" } ] diff --git a/templates/zone.conf.erb b/templates/zone.conf.erb index 98a4fcf..4be5f17 100644 --- a/templates/zone.conf.erb +++ b/templates/zone.conf.erb @@ -2,35 +2,33 @@ # This file managed by puppet - changes will be lost zone "<%= @_domain %>" { type <%= @zone_type %>; -<%- if @has_zone_file -%> -<%- if @dnssec -%> +<%- if @dnssec -%> auto-dnssec maintain; -<%- if @key_directory and @key_directory != '' -%> +<%- if @key_directory and @key_directory != '' -%> key-directory "<%= @key_directory %>"; -<%- else -%> - key-directory "<%= @cachedir %>/<%= @name %>"; -<%- end -%> - file "<%= @cachedir %>/<%= @name %>/<%= @_domain %>.signed"; <%- else -%> + key-directory "<%= @cachedir %>/<%= @name %>"; +<%- end -%> + file "<%= @cachedir %>/<%= @name %>/<%= @_domain %>.signed"; +<%- elsif %w(init managed allowed).include? @zone_file_mode -%> file "<%= @cachedir %>/<%= @name %>/<%= @_domain %>"; -<%- end -%> -<%- unless @zone_type == 'stub' -%> +<%- end -%> +<%- if %w(master slave).include? @zone_type -%> notify <%= @ns_notify ? 'yes' : 'no' %>; -<%- end -%> -<%- if @also_notify and @also_notify != '' -%> +<%- end -%> +<%- if @also_notify and @also_notify != '' -%> also-notify { -<%- Array(@also_notify).each do |server| -%> +<%- Array(@also_notify).each do |server| -%> <%= server %>; -<%- end -%> - }; <%- end -%> -<%- if @allow_notify and @allow_notify != '' -%> + }; +<%- end -%> +<%- if @allow_notify and @allow_notify != '' -%> allow-notify { -<%- Array(@allow_notify).each do |server| -%> +<%- Array(@allow_notify).each do |server| -%> <%= server %>; -<%- end -%> - }; <%- end -%> + }; <%- end -%> <%- if @masters and @masters != '' -%> masters { From aa7b743dd6b48f8d14f20b5822e405daf2995127 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Thu, 14 May 2015 11:27:44 -0500 Subject: [PATCH 2/5] Document new functionality --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 7c88898..b1627fe 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,15 @@ file will not be overwritten. Only the `zone_type` is required. If `domain` is unspecified, the title of the `bind::zone` declaration will be used as the domain. +A master zone with a zone file managed directly by Puppet: + + bind::zone { 'example.org': + zone_type => 'master', + dynamic => false, + source => 'puppet:///dns/db.example.org', + allow_transfers => [ 'secondary-dns', ], + } + A master zone with DNSSec disabled which allows updates using a TSIG key and zone transfers to servers matching an acl: From 54eea45d7d27b5ac57047028da8ee258c01235e1 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Thu, 14 May 2015 15:36:41 -0500 Subject: [PATCH 3/5] Refresh managed zones after a zone file change. --- manifests/zone.pp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/manifests/zone.pp b/manifests/zone.pp index aa7a4d6..56cee53 100644 --- a/manifests/zone.pp +++ b/manifests/zone.pp @@ -83,6 +83,16 @@ define bind::zone ( audit => [ content ], } } + + if $zone_file_mode == 'managed' { + exec { "rndc refresh ${_domain}": + command => "/usr/sbin/rndc refresh ${_domain}", + user => $::bind::params::bind_user, + refreshonly => true, + require => Service['bind'], + subscribe => File["${cachedir}/${name}/${_domain}"], + } + } } elsif $zone_file_mode == 'absent' { file { "${cachedir}/${name}": ensure => absent, From 20e50bf43a74158eaafc8f44cde39815229899fe Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Thu, 14 May 2015 16:05:44 -0500 Subject: [PATCH 4/5] That needs to be a reload --- manifests/zone.pp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manifests/zone.pp b/manifests/zone.pp index 56cee53..189ac1d 100644 --- a/manifests/zone.pp +++ b/manifests/zone.pp @@ -85,8 +85,8 @@ define bind::zone ( } if $zone_file_mode == 'managed' { - exec { "rndc refresh ${_domain}": - command => "/usr/sbin/rndc refresh ${_domain}", + exec { "rndc reload ${_domain}": + command => "/usr/sbin/rndc reload ${_domain}", user => $::bind::params::bind_user, refreshonly => true, require => Service['bind'], From 9f489dcb6f01f8c45979473b051c078c7ab72bf8 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Fri, 15 May 2015 07:42:52 -0500 Subject: [PATCH 5/5] `validate_bool` means something else `validate_bool` validates that the passed value(s) are boolean, not that they are true. Reformulate the calls to `unless` blocks, and remove the check for "dynamic implies master" since setting `dynamic` to true is 1) the default and 2) does not cause any change to configuration text or manifest behavior when the zone is not master. --- manifests/zone.pp | 48 ++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/manifests/zone.pp b/manifests/zone.pp index 189ac1d..097441e 100644 --- a/manifests/zone.pp +++ b/manifests/zone.pp @@ -22,35 +22,41 @@ define bind::zone ( $cachedir = $::bind::cachedir $_domain = pick($domain, $name) - # dynamic implies master zone - validate_bool(!($dynamic and $zone_type != 'master')) + unless !($masters != '' and ! member(['slave', 'stub'], $zone_type)) { + fail("masters may only be provided for bind::zone resources with zone_type 'slave' or 'stub'") + } - # masters implies slave/stub zone - validate_bool(!($masters != '' and ! member(['slave', 'stub'], $zone_type))) + unless !($transfer_source != '' and ! member(['slave', 'stub'], $zone_type)) { + fail("transfer_source may only be provided for bind::zone resources with zone_type 'slave' or 'stub'") + } - # transfer_source implies slave/stub zone - validate_bool(!($transfer_source != '' and ! member(['slave', 'stub'], $zone_type))) + unless !($allow_update != '' and ! $dynamic) { + fail("allow_update may only be provided for bind::zone resources with dynamic set to true") + } - # allow_updates implies dynamic - validate_bool(!($allow_update != '' and ! $dynamic)) + unless !($dnssec and ! $dynamic) { + fail("dnssec may only be true for bind::zone resources with dynamic set to true") + } - # dnssec implies dynamic zone - validate_bool(!($dnssec and ! $dynamic)) + unless !($key_directory != '' and ! $dnssec) { + fail("key_directory may only be provided for bind::zone resources with dnssec set to true") + } - # key_directory implies dnssec - validate_bool(!($key_directory != '' and ! $dnssec)) + unless !($allow_notify != '' and ! member(['slave', 'stub'], $zone_type)) { + fail("allow_notify may only be provided for bind::zone resources with zone_type 'slave' or 'stub'") + } - # allow_notify implies slave/stub zone - validate_bool(!($allow_notify != '' and ! member(['slave', 'stub'], $zone_type))) + unless !($forwarders != '' and $zone_type != 'forward') { + fail("forwarders may only be provided for bind::zone resources with zone_type 'forward'") + } - # forwarders implies forward zone - validate_bool(!($forwarders != '' and $zone_type != 'forward')) + unless !($forward != '' and $zone_type != 'forward') { + fail("forward may only be provided for bind::zone resources with zone_type 'forward'") + } - # forward implies forward zone - validate_bool(!($forward != '' and $zone_type != 'forward')) - - # source implies master/hint zone - validate_bool(!($source != '' and ! member(['master', 'hint'], $zone_type))) + unless !($source != '' and ! member(['master', 'hint'], $zone_type)) { + fail("source may only be provided for bind::zone resources with zone_type 'master' or 'hint'") + } $zone_file_mode = $zone_type ? { 'master' => $dynamic ? {