├── .fixtures.yml ├── .gitignore ├── .ruby-version ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── README.md ├── Rakefile ├── manifests ├── acl.pp ├── collector.pp ├── init.pp ├── key.pp ├── member.pp ├── record.pp ├── record │ ├── a.pp │ ├── aaaa.pp │ ├── cname.pp │ ├── mx.pp │ ├── ns.pp │ ├── ptr.pp │ ├── ptr │ │ └── by_ip.pp │ ├── srv.pp │ └── txt.pp ├── server.pp ├── server │ ├── config.pp │ ├── default.pp │ ├── install.pp │ ├── options.pp │ ├── params.pp │ ├── service.pp │ └── view.pp ├── tsig.pp └── zone.pp ├── metadata.json ├── spec ├── acceptance │ ├── basic_dns_spec.rb │ └── nodesets │ │ ├── centos-66-x64.yml │ │ ├── centos-70-x64.yml │ │ ├── debian-78-x64.yml │ │ ├── ubuntu-server-1204-x86.yml │ │ └── ubuntu-server-1404-x64.yml ├── classes │ ├── .gitkeep │ ├── coverage_spec.rb │ ├── dns__server__config_spec.rb │ ├── dns__server__install_spec.rb │ ├── dns__server__service_spec.rb │ ├── dns__server_spec.rb │ └── server │ │ └── default_spec.rb ├── defines │ ├── .gitkeep │ ├── dns__acl_spec.rb │ ├── dns__key_spec.rb │ ├── dns__record__a_spec.rb │ ├── dns__record__aliases.spec.rb │ ├── dns__record__mx_spec.rb │ ├── dns__record__ns_spec.rb │ ├── dns__record__ptr__by_ip_spec.rb │ ├── dns__record__txt_spec.rb │ ├── dns__record_spec.rb │ ├── dns__server__options_spec.rb │ ├── dns__tsig_spec.rb │ └── dns__zone_spec.rb ├── fixtures │ └── manifests │ │ └── init.pp ├── hosts │ └── example_spec.rb ├── spec_helper.rb └── spec_helper_acceptance.rb ├── templates ├── acl.erb ├── default.debian.erb ├── default.redhat.erb ├── key.erb ├── named.conf.default-zones.erb ├── named.conf.erb ├── named.conf.options.erb ├── secret.erb ├── tsig.erb ├── view.erb ├── zone.erb ├── zone_file.erb └── zone_record.erb └── tests └── init.pp /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | forge_modules: 3 | stdlib: "puppetlabs/stdlib" 4 | concat: 5 | repo: "puppetlabs/concat" 6 | ref: "2.2.0" 7 | symlinks: 8 | dns: "#{source_dir}" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .bundle 3 | .*.sw? 4 | pkg 5 | .rspec_system 6 | .vagrant 7 | /.project 8 | log/ 9 | spec/fixtures/modules/ 10 | /*.lock 11 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.5 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | bundler_args: --without development --jobs=3 --retry=3 4 | 5 | sudo: false 6 | 7 | rvm: 8 | - 1.9.3 9 | - 2.0.0 10 | - 2.1 11 | 12 | env: 13 | - PUPPET_VERSION="~> 2.7.0" 14 | - PUPPET_VERSION="~> 3.1.0" 15 | - PUPPET_VERSION="~> 3.2.0" 16 | - PUPPET_VERSION="~> 3.3.0" 17 | - PUPPET_VERSION="~> 3.4.0" 18 | - PUPPET_VERSION="~> 3.5.0" 19 | 20 | before_install: rm Gemfile.lock || true 21 | 22 | script: bundle exec rake test 23 | 24 | matrix: 25 | exclude: 26 | - rvm: 1.9.3 27 | env: PUPPET_VERSION="~> 2.7.0" 28 | - rvm: 2.0.0 29 | env: PUPPET_VERSION="~> 2.7.0" 30 | - rvm: 2.0.0 31 | env: PUPPET_VERSION="~> 3.1.0" 32 | - rvm: 2.1 33 | env: PUPPET_VERSION="~> 2.7.0" 34 | - rvm: 2.1 35 | env: PUPPET_VERSION="~> 3.1.0" 36 | - rvm: 2.1 37 | env: PUPPET_VERSION="~> 3.2.0" 38 | - rvm: 2.1 39 | env: PUPPET_VERSION="~> 3.3.0" 40 | - rvm: 2.1 41 | env: PUPPET_VERSION="~> 3.4.0" 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [2.1.0](https://github.com/ajjahn/puppet-dns/tree/2.1.0) (2017-01-26) 4 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v2.0.2...2.1.0) 5 | 6 | **Closed issues:** 7 | 8 | - Function Call, validate\_re\(\): input needs to be a String, not a NilClass at modules/dns/manifests/server/default.pp:29:3 [\#190](https://github.com/ajjahn/puppet-dns/issues/190) 9 | - Getting puppet evaluation error about $enable\_zone\_write [\#184](https://github.com/ajjahn/puppet-dns/issues/184) 10 | - `dns::server::defaults` class fails with puppetlabs-stdlib 4.10.0 [\#181](https://github.com/ajjahn/puppet-dns/issues/181) 11 | - Setup Travis CI Releases to Forge [\#177](https://github.com/ajjahn/puppet-dns/issues/177) 12 | - TXT record types should properly format the data value [\#170](https://github.com/ajjahn/puppet-dns/issues/170) 13 | - Tags and Forge releases for 2.0.1 and 2.0.2 [\#167](https://github.com/ajjahn/puppet-dns/issues/167) 14 | - How to handle class B nets [\#166](https://github.com/ajjahn/puppet-dns/issues/166) 15 | - Any thoughts on pointing cfg\_dir to different directory? [\#163](https://github.com/ajjahn/puppet-dns/issues/163) 16 | - cut releases more frequently. :\) [\#157](https://github.com/ajjahn/puppet-dns/issues/157) 17 | - Support for Views [\#156](https://github.com/ajjahn/puppet-dns/issues/156) 18 | - statistics-channel option broken [\#148](https://github.com/ajjahn/puppet-dns/issues/148) 19 | - named.conf not including options [\#101](https://github.com/ajjahn/puppet-dns/issues/101) 20 | - doesnt work with dynamic dns [\#54](https://github.com/ajjahn/puppet-dns/issues/54) 21 | 22 | **Merged pull requests:** 23 | 24 | - Remove Ruby 1.8 from the build matrix [\#194](https://github.com/ajjahn/puppet-dns/pull/194) ([solarkennedy](https://github.com/solarkennedy)) 25 | - fixing path issue which prevents working when path is not /etc/bind [\#193](https://github.com/ajjahn/puppet-dns/pull/193) ([ppouliot](https://github.com/ppouliot)) 26 | - 2nd chance: feat query\_log - optional parameter query\_log\_enable to enable query log [\#192](https://github.com/ajjahn/puppet-dns/pull/192) ([eumel8](https://github.com/eumel8)) 27 | - Add support for stub zones [\#191](https://github.com/ajjahn/puppet-dns/pull/191) ([jjthiessen](https://github.com/jjthiessen)) 28 | - Make data\_dir configurable in defined resource types. [\#189](https://github.com/ajjahn/puppet-dns/pull/189) ([n00by](https://github.com/n00by)) 29 | - Fix template formatting for [\#188](https://github.com/ajjahn/puppet-dns/pull/188) ([n00by](https://github.com/n00by)) 30 | - Fix statistics-channels location outside named.conf.options, add supp… [\#187](https://github.com/ajjahn/puppet-dns/pull/187) ([kwisatz](https://github.com/kwisatz)) 31 | - feat re-implement serial number in dns zone as optional parameter [\#186](https://github.com/ajjahn/puppet-dns/pull/186) ([eumel8](https://github.com/eumel8)) 32 | - Ensure `validate\_re` calls are wrapped in `if` checks to avoid passing undef [\#182](https://github.com/ajjahn/puppet-dns/pull/182) ([jearls](https://github.com/jearls)) 33 | - Ignore /\*.lock files [\#175](https://github.com/ajjahn/puppet-dns/pull/175) ([sspreitzer](https://github.com/sspreitzer)) 34 | - Make lint happy: change variables class\_\[ABC\]\_\* to class\_\[abc\]\_\* [\#174](https://github.com/ajjahn/puppet-dns/pull/174) ([jearls](https://github.com/jearls)) 35 | - Fix typos in `dns::collector` and `dns::zone` [\#173](https://github.com/ajjahn/puppet-dns/pull/173) ([jearls](https://github.com/jearls)) 36 | - Allow dynamic dns, fixes ajjahn/puppet-dns\#54 [\#172](https://github.com/ajjahn/puppet-dns/pull/172) ([sspreitzer](https://github.com/sspreitzer)) 37 | - issue \#170: Produce proper DNS quoted strings for TXT and SPF records [\#171](https://github.com/ajjahn/puppet-dns/pull/171) ([jearls](https://github.com/jearls)) 38 | - Correct indentation of template. [\#169](https://github.com/ajjahn/puppet-dns/pull/169) ([MemberIT](https://github.com/MemberIT)) 39 | - Bugfix/template named options [\#168](https://github.com/ajjahn/puppet-dns/pull/168) ([MemberIT](https://github.com/MemberIT)) 40 | - feature TSIG configuration [\#129](https://github.com/ajjahn/puppet-dns/pull/129) ([eumel8](https://github.com/eumel8)) 41 | 42 | ## [v2.0.2](https://github.com/ajjahn/puppet-dns/tree/v2.0.2) (2016-05-24) 43 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v2.0.0...v2.0.2) 44 | 45 | **Closed issues:** 46 | 47 | - Is dependency on electrical-file\_concat still required? [\#160](https://github.com/ajjahn/puppet-dns/issues/160) 48 | - seemingly random order within the zonefile [\#154](https://github.com/ajjahn/puppet-dns/issues/154) 49 | - Concat dependency causing builds to fail [\#142](https://github.com/ajjahn/puppet-dns/issues/142) 50 | - Version update [\#141](https://github.com/ajjahn/puppet-dns/issues/141) 51 | 52 | **Merged pull requests:** 53 | 54 | - Remove unneeded dependency on electrical/file\_concat module [\#165](https://github.com/ajjahn/puppet-dns/pull/165) ([jearls](https://github.com/jearls)) 55 | - Add `reverse =\> reverse` option to dns::zone [\#162](https://github.com/ajjahn/puppet-dns/pull/162) ([jearls](https://github.com/jearls)) 56 | - Add `notify\_source` and `transfer\_source` to `dns::server::options` [\#161](https://github.com/ajjahn/puppet-dns/pull/161) ([jearls](https://github.com/jearls)) 57 | - Adjust Gemfile to Fix Tests [\#159](https://github.com/ajjahn/puppet-dns/pull/159) ([solarkennedy](https://github.com/solarkennedy)) 58 | - Removed 'ensure' setting from concat::fragment statements [\#158](https://github.com/ajjahn/puppet-dns/pull/158) ([Loewe88](https://github.com/Loewe88)) 59 | - Comparison of: String \>= Integer, is not possible [\#155](https://github.com/ajjahn/puppet-dns/pull/155) ([wazoo](https://github.com/wazoo)) 60 | - Added Package require [\#153](https://github.com/ajjahn/puppet-dns/pull/153) ([mooreandrew](https://github.com/mooreandrew)) 61 | - Allow query zone [\#152](https://github.com/ajjahn/puppet-dns/pull/152) ([gcmalloc](https://github.com/gcmalloc)) 62 | - Fix default spec, a class not a define [\#151](https://github.com/ajjahn/puppet-dns/pull/151) ([solarkennedy](https://github.com/solarkennedy)) 63 | - Control whether DNS-SEC support is enabled/disabled [\#146](https://github.com/ajjahn/puppet-dns/pull/146) ([evidex](https://github.com/evidex)) 64 | - \[WIP\] Fix fixtures [\#145](https://github.com/ajjahn/puppet-dns/pull/145) ([solarkennedy](https://github.com/solarkennedy)) 65 | - Empty Zone Generation control [\#144](https://github.com/ajjahn/puppet-dns/pull/144) ([evidex](https://github.com/evidex)) 66 | - Add support for delegation-only zone types. [\#143](https://github.com/ajjahn/puppet-dns/pull/143) ([evidex](https://github.com/evidex)) 67 | - Add `all` and `first` values to `ptr` parameter of `dns::record::a` [\#138](https://github.com/ajjahn/puppet-dns/pull/138) ([jearls](https://github.com/jearls)) 68 | 69 | ## [v2.0.0](https://github.com/ajjahn/puppet-dns/tree/v2.0.0) (2015-12-03) 70 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v1.2.0...v2.0.0) 71 | 72 | **Closed issues:** 73 | 74 | - Outdated dependencies make this module incompatible with other modules. [\#120](https://github.com/ajjahn/puppet-dns/issues/120) 75 | - Fatal Regression in \#112- bad config means bind will not start. [\#115](https://github.com/ajjahn/puppet-dns/issues/115) 76 | - Adding record to multiple zones or all zones? [\#105](https://github.com/ajjahn/puppet-dns/issues/105) 77 | - Large Number of Records? [\#104](https://github.com/ajjahn/puppet-dns/issues/104) 78 | - Tag New Release [\#97](https://github.com/ajjahn/puppet-dns/issues/97) 79 | - SOA has additional "." [\#93](https://github.com/ajjahn/puppet-dns/issues/93) 80 | - Error finding a dependency. [\#78](https://github.com/ajjahn/puppet-dns/issues/78) 81 | - Allow "type forward" without file-statement [\#64](https://github.com/ajjahn/puppet-dns/issues/64) 82 | - 'dnssec-validation auto' not supported in Debian Squeeze \(Bind 9.7.3\) [\#52](https://github.com/ajjahn/puppet-dns/issues/52) 83 | 84 | **Merged pull requests:** 85 | 86 | - Test NS records, provide example for README [\#140](https://github.com/ajjahn/puppet-dns/pull/140) ([roderickm](https://github.com/roderickm)) 87 | - Properly escape the { and } in the listen-on-v6 regexp check. [\#139](https://github.com/ajjahn/puppet-dns/pull/139) ([jearls](https://github.com/jearls)) 88 | - fix variable access preference with @preference [\#136](https://github.com/ajjahn/puppet-dns/pull/136) ([timogoebel](https://github.com/timogoebel)) 89 | - fixes for puppet future parser support [\#135](https://github.com/ajjahn/puppet-dns/pull/135) ([timogoebel](https://github.com/timogoebel)) 90 | - Make "listen-on-v6" a configurable option [\#134](https://github.com/ajjahn/puppet-dns/pull/134) ([djm256](https://github.com/djm256)) 91 | - Allow the dns::zone::slave\_masters parameter to be an array [\#133](https://github.com/ajjahn/puppet-dns/pull/133) ([jearls](https://github.com/jearls)) 92 | - params.pp: excluded dnssec-tools from $necessary\_package for debian 8 [\#131](https://github.com/ajjahn/puppet-dns/pull/131) ([Gril258](https://github.com/Gril258)) 93 | - Added support to modify service startup [\#130](https://github.com/ajjahn/puppet-dns/pull/130) ([Cicco0](https://github.com/Cicco0)) 94 | - add updated Gemfile.lock [\#128](https://github.com/ajjahn/puppet-dns/pull/128) ([jearls](https://github.com/jearls)) 95 | - Added initial acceptance test framework [\#126](https://github.com/ajjahn/puppet-dns/pull/126) ([solarkennedy](https://github.com/solarkennedy)) 96 | - Fix the `directory` option in named.conf.options [\#125](https://github.com/ajjahn/puppet-dns/pull/125) ([darkfoxprime](https://github.com/darkfoxprime)) 97 | - Make dnssec validation a configurable option. [\#124](https://github.com/ajjahn/puppet-dns/pull/124) ([darkfoxprime](https://github.com/darkfoxprime)) 98 | - fix zone template's @allow\_transfer check [\#123](https://github.com/ajjahn/puppet-dns/pull/123) ([darkfoxprime](https://github.com/darkfoxprime)) 99 | - Correct path for named.conf.options in tests/init.pp [\#122](https://github.com/ajjahn/puppet-dns/pull/122) ([darkfoxprime](https://github.com/darkfoxprime)) 100 | - Remove invalid reference to dns::server::options::forwarder [\#121](https://github.com/ajjahn/puppet-dns/pull/121) ([darkfoxprime](https://github.com/darkfoxprime)) 101 | - Named.options fix [\#119](https://github.com/ajjahn/puppet-dns/pull/119) ([tedivm](https://github.com/tedivm)) 102 | - Fixes \#93 - Avoid the extra dot in the soa [\#117](https://github.com/ajjahn/puppet-dns/pull/117) ([oloc](https://github.com/oloc)) 103 | - Updated concat module version [\#116](https://github.com/ajjahn/puppet-dns/pull/116) ([tedivm](https://github.com/tedivm)) 104 | - Fix comment syntax in named.conf template [\#114](https://github.com/ajjahn/puppet-dns/pull/114) ([jaxim](https://github.com/jaxim)) 105 | - Add param to manage packages [\#113](https://github.com/ajjahn/puppet-dns/pull/113) ([jaxim](https://github.com/jaxim)) 106 | - issue 101: take control of named.conf. [\#112](https://github.com/ajjahn/puppet-dns/pull/112) ([jearls](https://github.com/jearls)) 107 | - Add notify to server options & also\_notify to server and zone options. [\#110](https://github.com/ajjahn/puppet-dns/pull/110) ([jearls](https://github.com/jearls)) 108 | - Remove dnssec-tools from RedHat package list. [\#108](https://github.com/ajjahn/puppet-dns/pull/108) ([jearls](https://github.com/jearls)) 109 | - Added file\_concat as a dependent module [\#103](https://github.com/ajjahn/puppet-dns/pull/103) ([solarkennedy](https://github.com/solarkennedy)) 110 | - Use resource names instead of hosts for the aliases of dns record types. With spec test file. [\#100](https://github.com/ajjahn/puppet-dns/pull/100) ([jearls](https://github.com/jearls)) 111 | - zone files should only be created or modified for master zones [\#99](https://github.com/ajjahn/puppet-dns/pull/99) ([jearls](https://github.com/jearls)) 112 | - spec tests: fix invalid range in regexp [\#98](https://github.com/ajjahn/puppet-dns/pull/98) ([jearls](https://github.com/jearls)) 113 | - Fixed a bug where key did not work on redhat due to incorrect pkg name [\#87](https://github.com/ajjahn/puppet-dns/pull/87) ([fhaynes](https://github.com/fhaynes)) 114 | - Bind stats [\#77](https://github.com/ajjahn/puppet-dns/pull/77) ([gcmalloc](https://github.com/gcmalloc)) 115 | 116 | ## [v1.2.0](https://github.com/ajjahn/puppet-dns/tree/v1.2.0) (2015-04-10) 117 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v1.1.0...v1.2.0) 118 | 119 | **Closed issues:** 120 | 121 | - Custom NS not supported- can't properly handle domain forwarding [\#95](https://github.com/ajjahn/puppet-dns/issues/95) 122 | - Error: Could not set 'present' on ensure: No such file or directory - /etc/bind/named.conf.options20150404-12319-h6cff6.lock [\#94](https://github.com/ajjahn/puppet-dns/issues/94) 123 | - dnssec-tools not available in centos 7 epel [\#83](https://github.com/ajjahn/puppet-dns/issues/83) 124 | - Invalid relationship errors with concat [\#81](https://github.com/ajjahn/puppet-dns/issues/81) 125 | - Dependency required for repository "epel" on CentOS [\#79](https://github.com/ajjahn/puppet-dns/issues/79) 126 | - New Release 1.1.0 [\#75](https://github.com/ajjahn/puppet-dns/issues/75) 127 | 128 | **Merged pull requests:** 129 | 130 | - Added NS record type [\#96](https://github.com/ajjahn/puppet-dns/pull/96) ([tedivm](https://github.com/tedivm)) 131 | - Added in feature allowing for global allow-transfer [\#90](https://github.com/ajjahn/puppet-dns/pull/90) ([fhaynes](https://github.com/fhaynes)) 132 | - Fixed a bug where the secret line was not ending a ; [\#89](https://github.com/ajjahn/puppet-dns/pull/89) ([fhaynes](https://github.com/fhaynes)) 133 | - Fixed a bug where the key was being written with }: and not }; [\#88](https://github.com/ajjahn/puppet-dns/pull/88) ([fhaynes](https://github.com/fhaynes)) 134 | - fixed params.pp for rhel 7 and added fixes for concat issues [\#84](https://github.com/ajjahn/puppet-dns/pull/84) ([ITBlogger](https://github.com/ITBlogger)) 135 | - Added a description to make RHEL/CentOS users aware that EPEL is required. [\#82](https://github.com/ajjahn/puppet-dns/pull/82) ([robertdebock](https://github.com/robertdebock)) 136 | - Test check\_names\_response with wrong string [\#76](https://github.com/ajjahn/puppet-dns/pull/76) ([roderickm](https://github.com/roderickm)) 137 | 138 | ## [v1.1.0](https://github.com/ajjahn/puppet-dns/tree/v1.1.0) (2015-02-03) 139 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v1.0.0...v1.1.0) 140 | 141 | **Closed issues:** 142 | 143 | - Version 2.0.0 [\#38](https://github.com/ajjahn/puppet-dns/issues/38) 144 | 145 | **Merged pull requests:** 146 | 147 | - EL Compatible [\#74](https://github.com/ajjahn/puppet-dns/pull/74) ([roderickm](https://github.com/roderickm)) 148 | - cleanup of inline rdocs in `dns::server::options` class [\#73](https://github.com/ajjahn/puppet-dns/pull/73) ([talisto](https://github.com/talisto)) 149 | - allow port to be customized in dns::server::options [\#72](https://github.com/ajjahn/puppet-dns/pull/72) ([talisto](https://github.com/talisto)) 150 | - MX preference fix, unique alias, add tests [\#71](https://github.com/ajjahn/puppet-dns/pull/71) ([roderickm](https://github.com/roderickm)) 151 | - Add listen-on option \(with tests\) [\#70](https://github.com/ajjahn/puppet-dns/pull/70) ([roderickm](https://github.com/roderickm)) 152 | - Use the new build env on Travis [\#68](https://github.com/ajjahn/puppet-dns/pull/68) ([joshk](https://github.com/joshk)) 153 | - Zone with "type forward" are now without "file"-line [\#66](https://github.com/ajjahn/puppet-dns/pull/66) ([fr3dm4n](https://github.com/fr3dm4n)) 154 | - fix README example [\#65](https://github.com/ajjahn/puppet-dns/pull/65) ([rkcpi](https://github.com/rkcpi)) 155 | - Update README.md [\#62](https://github.com/ajjahn/puppet-dns/pull/62) ([kylecannon](https://github.com/kylecannon)) 156 | 157 | ## [v1.0.0](https://github.com/ajjahn/puppet-dns/tree/v1.0.0) (2014-10-19) 158 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v0.1.4...v1.0.0) 159 | 160 | **Closed issues:** 161 | 162 | - Error 400 on SERVER: Duplicate declaration: Dns::Record::A\[server1\] is already declared [\#44](https://github.com/ajjahn/puppet-dns/issues/44) 163 | - Change zone-serial only on record updates \(this a solution\) [\#24](https://github.com/ajjahn/puppet-dns/issues/24) 164 | - Possibility to set forwarders [\#22](https://github.com/ajjahn/puppet-dns/issues/22) 165 | - Provide a feature to set the /etc/bind/named.conf.options file [\#21](https://github.com/ajjahn/puppet-dns/issues/21) 166 | - module not found when installing from the forge using puppet module install [\#15](https://github.com/ajjahn/puppet-dns/issues/15) 167 | 168 | **Merged pull requests:** 169 | 170 | - Updated docs [\#60](https://github.com/ajjahn/puppet-dns/pull/60) ([solarkennedy](https://github.com/solarkennedy)) 171 | - Create ns.pp [\#53](https://github.com/ajjahn/puppet-dns/pull/53) ([gilneidp](https://github.com/gilneidp)) 172 | - Fix 'Usage' section in dns::server::options [\#51](https://github.com/ajjahn/puppet-dns/pull/51) ([strangeman](https://github.com/strangeman)) 173 | - Fix 'Usage' section in dns::acl [\#50](https://github.com/ajjahn/puppet-dns/pull/50) ([strangeman](https://github.com/strangeman)) 174 | - Spec refactor [\#49](https://github.com/ajjahn/puppet-dns/pull/49) ([danzilio](https://github.com/danzilio)) 175 | - Reformatted dns::key and wrote tests for it [\#48](https://github.com/ajjahn/puppet-dns/pull/48) ([danzilio](https://github.com/danzilio)) 176 | - allow recursion [\#47](https://github.com/ajjahn/puppet-dns/pull/47) ([gcmalloc](https://github.com/gcmalloc)) 177 | - Adding a forward option for a zone. [\#46](https://github.com/ajjahn/puppet-dns/pull/46) ([gcmalloc](https://github.com/gcmalloc)) 178 | - Update zone-serial only on changing zone-records \(sed version\) [\#45](https://github.com/ajjahn/puppet-dns/pull/45) ([kubashin-a](https://github.com/kubashin-a)) 179 | - Use FQDN as PTR name instead of octet [\#43](https://github.com/ajjahn/puppet-dns/pull/43) ([kubashin-a](https://github.com/kubashin-a)) 180 | - El compatible [\#41](https://github.com/ajjahn/puppet-dns/pull/41) ([sereinity](https://github.com/sereinity)) 181 | - Solved Syntax error at 'inherits' in ::dns::server::options.pp:18 [\#40](https://github.com/ajjahn/puppet-dns/pull/40) ([n1tr0g](https://github.com/n1tr0g)) 182 | - Params refactor for future OS support with tests... on top of danzilio's refactor [\#34](https://github.com/ajjahn/puppet-dns/pull/34) ([solarkennedy](https://github.com/solarkennedy)) 183 | - Allow transfer... on top of danzilios refactor [\#33](https://github.com/ajjahn/puppet-dns/pull/33) ([solarkennedy](https://github.com/solarkennedy)) 184 | - Update zone\_file.erb [\#31](https://github.com/ajjahn/puppet-dns/pull/31) ([seanscottking](https://github.com/seanscottking)) 185 | - Update Modulefile [\#30](https://github.com/ajjahn/puppet-dns/pull/30) ([seanscottking](https://github.com/seanscottking)) 186 | - ACL [\#29](https://github.com/ajjahn/puppet-dns/pull/29) ([danzilio](https://github.com/danzilio)) 187 | - Refactored the module with a better Gemfile and Rakefile. [\#28](https://github.com/ajjahn/puppet-dns/pull/28) ([danzilio](https://github.com/danzilio)) 188 | - Template changes [\#27](https://github.com/ajjahn/puppet-dns/pull/27) ([ppouliot](https://github.com/ppouliot)) 189 | - Add possibility to set forwarders [\#23](https://github.com/ajjahn/puppet-dns/pull/23) ([zeleznypa](https://github.com/zeleznypa)) 190 | - Added support for SRV DNS record types. [\#20](https://github.com/ajjahn/puppet-dns/pull/20) ([samcday](https://github.com/samcday)) 191 | 192 | ## [v0.1.4](https://github.com/ajjahn/puppet-dns/tree/v0.1.4) (2013-02-12) 193 | [Full Changelog](https://github.com/ajjahn/puppet-dns/compare/v0.1.3...v0.1.4) 194 | 195 | ## [v0.1.3](https://github.com/ajjahn/puppet-dns/tree/v0.1.3) (2013-01-14) 196 | **Closed issues:** 197 | 198 | - Named.conf Updates [\#13](https://github.com/ajjahn/puppet-dns/issues/13) 199 | - Building PTR Records Fails With Same Resource Defined In Seperate Zones [\#12](https://github.com/ajjahn/puppet-dns/issues/12) 200 | - Zone regenerates w/ every Puppet run [\#3](https://github.com/ajjahn/puppet-dns/issues/3) 201 | 202 | **Merged pull requests:** 203 | 204 | - add supoprt for managing slave zones [\#14](https://github.com/ajjahn/puppet-dns/pull/14) ([aussielunix](https://github.com/aussielunix)) 205 | - MX Records need a host field [\#11](https://github.com/ajjahn/puppet-dns/pull/11) ([aaronbbrown](https://github.com/aaronbbrown)) 206 | - Syntax error when using strings [\#10](https://github.com/ajjahn/puppet-dns/pull/10) ([aaronbbrown](https://github.com/aaronbbrown)) 207 | - Dependency version [\#9](https://github.com/ajjahn/puppet-dns/pull/9) ([aaronbbrown](https://github.com/aaronbbrown)) 208 | - A few Modulefile corrections [\#8](https://github.com/ajjahn/puppet-dns/pull/8) ([aaronbbrown](https://github.com/aaronbbrown)) 209 | - Changed single quotes to doubles [\#7](https://github.com/ajjahn/puppet-dns/pull/7) ([zodeus](https://github.com/zodeus)) 210 | - .IN-ADDR.ARPA is missing when a PTR record is created with an A record [\#2](https://github.com/ajjahn/puppet-dns/pull/2) ([guillaumerose](https://github.com/guillaumerose)) 211 | - Fix bug in mx record [\#1](https://github.com/ajjahn/puppet-dns/pull/1) ([dvigueras](https://github.com/dvigueras)) 212 | 213 | 214 | 215 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Checklist 2 | ================================================= 3 | 4 | - Fork the repository on GitHub. 5 | 6 | - Make changes on a branch *with tests* 7 | 8 | - Run tests `bundle exec rake spec` 9 | 10 | - Check for style `bundle exec rake lint` 11 | 12 | - Push your changes to a topic branch in your fork of the 13 | repository. (the format ticket/1234-short_description_of_change is 14 | usually preferred for this project). 15 | 16 | - Submit a pull request to the repository 17 | 18 | 19 | Testing 20 | ======= 21 | 22 | Getting Started 23 | --------------- 24 | 25 | Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby 26 | package manager such as [bundler](http://bundler.io/) what Ruby packages, 27 | or Gems, are required to build, develop, and test this software. 28 | 29 | Please make sure you have [bundler installed](http://bundler.io/#getting-started) 30 | on your system, then use it to install all dependencies needed for this project, 31 | by running 32 | 33 | ```shell 34 | % bundle install 35 | Fetching gem metadata from https://rubygems.org/........ 36 | Fetching gem metadata from https://rubygems.org/.. 37 | Using rake (10.1.0) 38 | Using builder (3.2.2) 39 | -- 8><-- many more --><8 -- 40 | Using rspec-system-puppet (2.2.0) 41 | Using serverspec (0.6.3) 42 | Using rspec-system-serverspec (1.0.0) 43 | Using bundler (1.3.5) 44 | Your bundle is complete! 45 | Use `bundle show [gemname]` to see where a bundled gem is installed. 46 | ``` 47 | 48 | NOTE some systems may require you to run this command with sudo. 49 | 50 | 51 | Running Tests 52 | ------------- 53 | 54 | ```shell 55 | $ bundle exec rake spec 56 | Cloning into 'spec/fixtures/modules/stdlib'... 57 | remote: Counting objects: 5550, done. 58 | remote: Total 5550 (delta 0), reused 0 (delta 0) 59 | Receiving objects: 100% (5550/5550), 1.09 MiB | 271.00 KiB/s, done. 60 | Resolving deltas: 100% (2302/2302), done. 61 | Checking connectivity... done. 62 | HEAD is now at 9e8127b Merge pull request #313 from mhaskel/spec_updates 63 | Cloning into 'spec/fixtures/modules/concat'... 64 | remote: Counting objects: 1467, done. 65 | remote: Compressing objects: 100% (87/87), done. 66 | remote: Total 1467 (delta 52), reused 2 (delta 0) 67 | Receiving objects: 100% (1467/1467), 319.82 KiB | 256.00 KiB/s, done. 68 | Resolving deltas: 100% (688/688), done. 69 | ..................................................................... 70 | 71 | Finished in 4.59 seconds 72 | 69 examples, 0 failures 73 | 74 | Total resources: 72 75 | Touched resources: 22 76 | Resource coverage: 30.56% 77 | ``` 78 | 79 | Writing Tests 80 | ------------ 81 | See the [tutorial](http://rspec-puppet.com/tutorial/) 82 | 83 | 84 | Integration tests 85 | ----------------- 86 | 87 | The unit tests just check the code runs, not that it does exactly what 88 | we want on a real machine. For that we're using 89 | [Beaker](https://github.com/puppetlabs/beaker). 90 | This fires up a new virtual machine (using vagrant) and runs a series of 91 | simple tests against it after applying the module. You can run this 92 | with: 93 | 94 | bundle exec rake beaker 95 | 96 | This will run the tests on an Ubuntu 12.04 virtual machine. You can also 97 | run the integration tests against Centos 6.6 with: 98 | 99 | BEAKER_set=centos-66-x64 bundle exec rake beaker 100 | 101 | Or with Ubuntu 12.04 with: 102 | 103 | BEAKER_set=ubuntu-server-12-x64 bundle exec rake beaker 104 | 105 | If you need to inspect a vm manually afterwards, you can ask beaker to not 106 | destroy the box: 107 | 108 | bundle exec rake BEAKER_destroy=no beaker 109 | 110 | Then vagrant ssh to the box that was left behind 111 | 112 | vagrant global-status 113 | vagrant ssh (box-id) 114 | 115 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | group :development do 4 | gem "beaker", "> 2.0.0" 5 | gem "beaker-rspec", ">= 5.1.0" 6 | gem "pry" 7 | gem "puppet-blacksmith" 8 | gem "serverspec" 9 | gem "vagrant-wrapper" 10 | end 11 | 12 | group :test do 13 | gem "json" 14 | gem "json_pure", "~> 1.8.3" 15 | # Pin for 1.8.7 compatibility for now 16 | gem "rake", '< 11.0.0' 17 | gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.7.0' 18 | gem "puppet-lint" 19 | 20 | # Pin for 1.8.7 compatibility for now 21 | gem "rspec", '< 3.2.0' 22 | gem "rspec-core", "3.1.7" 23 | gem "rspec-puppet", "< 2.6.0" 24 | 25 | gem "puppet-syntax" 26 | gem "puppetlabs_spec_helper", "< 2.1.1" 27 | end 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Puppet DNS (BIND9) Module 2 | 3 | [![Build Status](https://travis-ci.org/ajjahn/puppet-dns.png?branch=master)](https://travis-ci.org/ajjahn/puppet-dns) 4 | 5 | Module for provisioning DNS (bind9) 6 | 7 | Supports: 8 | 9 | * Ubuntu: 14.04, 12.04 10 | * CentOS: 7.x, 6.x 11 | 12 | Patches to support other operating systems are welcome. 13 | 14 | This module depends on concat (https://github.com/puppetlabs/puppet-concat). 15 | 16 | This module ''will'' overwrite all bind configuration, it is not safe to apply 17 | to a server with an existing bind configuration. 18 | 19 | ## Installation 20 | 21 | Clone this repo to your Puppet modules directory 22 | 23 | git clone git://github.com/ajjahn/puppet-dns.git dns 24 | 25 | or 26 | 27 | puppet module install ajjahn/dns 28 | 29 | ## Usage 30 | 31 | Tweak and add the following to your site manifest: 32 | 33 | ```puppet 34 | node 'server.example.com' { 35 | include dns::server 36 | 37 | # Forwarders 38 | dns::server::options { '/etc/bind/named.conf.options': 39 | forwarders => [ '8.8.8.8', '8.8.4.4' ] 40 | } 41 | 42 | # Forward Zone 43 | dns::zone { 'example.com': 44 | soa => 'ns1.example.com', 45 | soa_email => 'admin.example.com', 46 | nameservers => ['ns1'] 47 | } 48 | 49 | # Reverse Zone 50 | dns::zone { '1.168.192.IN-ADDR.ARPA': 51 | soa => 'ns1.example.com', 52 | soa_email => 'admin.example.com', 53 | nameservers => ['ns1'] 54 | } 55 | 56 | # A Records: 57 | dns::record::a { 58 | 'huey': 59 | zone => 'example.com', 60 | data => ['98.76.54.32']; 61 | 'duey': 62 | zone => 'example.com', 63 | data => ['12.34.56.78', '12.23.34.45']; 64 | 'luey': 65 | zone => 'example.com', 66 | data => ['192.168.1.25'], 67 | ptr => true; # Creates a matching reverse zone record. Make sure you've added the proper reverse zone in the manifest. 68 | } 69 | 70 | # MX Records: 71 | dns::record::mx { 72 | 'mx,0': 73 | zone => 'example.com', 74 | preference => 0, 75 | data => 'ASPMX.L.GOOGLE.com'; 76 | 'mx,10': 77 | zone => 'example.com', 78 | preference => 10, 79 | data => 'ALT1.ASPMX.L.GOOGLE.com'; 80 | } 81 | 82 | # NS Records: 83 | dns::record::ns { 84 | 'example.com': 85 | zone => 'example.com', 86 | data => 'ns3'; 87 | 'delegation-to-ns4-jp-example-net': 88 | zone => 'example.com', 89 | host => 'delegated-zone', 90 | data => 'ns4.jp.example.net.'; 91 | } 92 | 93 | # CNAME Record: 94 | dns::record::cname { 'www': 95 | zone => 'example.com', 96 | data => 'huey.example.com', 97 | } 98 | 99 | # TXT Record: 100 | dns::record::txt { 'www': 101 | zone => 'example.com', 102 | data => 'Hello World', 103 | } 104 | 105 | # TSIG 106 | dns::tsig { 'ns3' : 107 | ensure => present, 108 | algorithm => "hmac-md5", 109 | secret => "La/E5CjG9O+os1jq0a2jdA==", 110 | server => "192.168.1.3" 111 | } 112 | 113 | } 114 | ``` 115 | 116 | You can also declare forwarders for a specific zone, if you don't have one in the dns::option. 117 | 118 | ```puppet 119 | dns::zone { 'example.com': 120 | soa => 'ns1.example.com', 121 | soa_email => 'admin.example.com', 122 | allow_forwarder => ['8.8.8.8'], 123 | forward_policy => 'first', 124 | nameservers => ['ns1'], 125 | } 126 | ``` 127 | 128 | You can change the checking of the domain name. The policy can be either warn fail or ignore. 129 | 130 | ```puppet 131 | dns::server::options { '/etc/bind/named.conf.options': 132 | check_names_master => 'fail', 133 | check_names_slave => 'warn', 134 | forwarders => [ '8.8.8.8', '4.4.4.4' ], 135 | } 136 | ``` 137 | 138 | You can enable the report of bind stats trough the `statistics-channels` using: 139 | 140 | ```puppet 141 | dns::server::options { '/etc/bind/named.conf.options': 142 | check_names_master => 'fail', 143 | check_names_slave => 'warn', 144 | forwarders => [ '8.8.8.8', '4.4.4.4' ], 145 | statistic_channel_ip => '127.0.0.1', 146 | statistic_channel_port => 8053 147 | } 148 | ``` 149 | 150 | You can also create dynamic zones. Mind they are only created once by puppet and never replaced unless allow_update is empty. 151 | 152 | ```puppet 153 | dns::zone { 154 | soa => 'ns1.example.com', 155 | soa_email => 'admin.example.com', 156 | allow_forwarder => ['8.8.8.8'], 157 | allow_update => ['192.168.1.2', '192.168.1.3'], 158 | forward_policy => 'first', 159 | nameservers => ['ns1'], 160 | } 161 | ``` 162 | 163 | ### Exported resource patterns 164 | 165 | ```puppet 166 | node default { 167 | # Other nodes export an A record for their hostname 168 | @@dns::record::a { $::hostname: 169 | zone => $::domain, 170 | data => $::ipaddress, 171 | } 172 | } 173 | 174 | node 'ns1.xkyle.com' { 175 | dns::zone { $::domain: 176 | soa => $::fqdn, 177 | soa_email => "admin.${::domain}", 178 | nameservers => [ 'ns1' ], 179 | } 180 | # Collect all the records from other nodes 181 | Dns::Record::A <<||>> 182 | } 183 | ``` 184 | 185 | ## Contributing 186 | 187 | 1. Fork it 188 | 2. Create your feature branch (`git checkout -b my-new-feature`) 189 | 3. Commit your changes (`git commit -am 'Added some feature'`) 190 | 4. Push to the branch (`git push origin my-new-feature`) 191 | 5. Create new Pull Request 192 | 193 | ## Authors 194 | 195 | Note: This module is a merge of the work from the following authors: 196 | * [ajjahn](https://github.com/ajjahn/puppet-dns) 197 | * [Danzilio](https://github.com/danzilio) 198 | * [solarkennedy](https://github.com/solarkennedy) 199 | 200 | ## License 201 | 202 | This module is released under the MIT license: 203 | 204 | * [http://www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT) 205 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'puppetlabs_spec_helper/rake_tasks' 2 | require 'puppet-lint/tasks/puppet-lint' 3 | require 'puppet-syntax/tasks/puppet-syntax' 4 | 5 | # These two gems aren't always present, for instance 6 | # on Travis with --without development 7 | begin 8 | require 'puppetlabs_spec_helper/rake_tasks' 9 | require 'puppet_blacksmith/rake_tasks' 10 | rescue LoadError 11 | end 12 | 13 | PuppetLint.configuration.send("disable_80chars") 14 | PuppetLint.configuration.log_format = "%{path}:%{line}:%{check}:%{KIND}:%{message}" 15 | 16 | # Forsake support for Puppet 2.6.2 for the benefit of cleaner code. 17 | # http://puppet-lint.com/checks/class_parameter_defaults/ 18 | PuppetLint.configuration.send('disable_80chars') 19 | PuppetLint.configuration.send('disable_class_parameter_defaults') 20 | # http://puppet-lint.com/checks/class_inherits_from_params_class/ 21 | PuppetLint.configuration.send('disable_class_inherits_from_params_class') 22 | 23 | exclude_paths = [ 24 | "pkg/**/*", 25 | "vendor/**/*", 26 | "spec/**/*", 27 | ] 28 | PuppetLint.configuration.ignore_paths = exclude_paths 29 | PuppetSyntax.exclude_paths = exclude_paths 30 | 31 | ENV['BEAKER_set'] ||= 'ubuntu-server-1204-x86' 32 | desc "Run acceptance tests" 33 | RSpec::Core::RakeTask.new(:acceptance) do |t| 34 | t.pattern = 'spec/acceptance' 35 | end 36 | 37 | desc "Run syntax, lint, and spec tests." 38 | task :test => [ 39 | :syntax, 40 | :lint, 41 | :spec, 42 | ] 43 | -------------------------------------------------------------------------------- /manifests/acl.pp: -------------------------------------------------------------------------------- 1 | # defined type allows you to declare a BIND ACL. 2 | # 3 | # Parameters: 4 | # 5 | # $ensure = ensure the persence or absence of the acl. 6 | # $aclname = the name given to the ACL. This must be unique. This defaults to 7 | # the namevar. 8 | # $data = an array of IP addresses or subnets using CIDR notation. 9 | # 10 | # Usage: 11 | # 12 | # dns::acl { 'trusted': 13 | # ensure => present, 14 | # data => [ '10.0.0.0/8', '172.16.2.0/24', ] 15 | # } 16 | # 17 | define dns::acl ( 18 | $ensure = present, 19 | $aclname = $name, 20 | $data = [], 21 | ) { 22 | include dns::server::params 23 | 24 | validate_string($aclname) 25 | validate_array($data) 26 | 27 | concat::fragment { "named.conf.local.acl.${name}.include": 28 | target => "${dns::server::params::cfg_dir}/named.conf.local", 29 | order => 2, 30 | content => template("${module_name}/acl.erb"), 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /manifests/collector.pp: -------------------------------------------------------------------------------- 1 | #== Class dns::collector 2 | # 3 | # ? 4 | class dns::collector { 5 | Dns::Member <<| |>> { 6 | require => Class['dns::server'], 7 | notify => Class['dns::server::service'] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /manifests/init.pp: -------------------------------------------------------------------------------- 1 | # == Class dns 2 | # 3 | # Currently does nothing 4 | # 5 | class dns { 6 | # include dns::install 7 | # include dns::config 8 | # include dns::service 9 | } 10 | -------------------------------------------------------------------------------- /manifests/key.pp: -------------------------------------------------------------------------------- 1 | # == Class define::key 2 | # 3 | define dns::key { 4 | include dns::server::params 5 | $cfg_dir = $dns::server::params::cfg_dir # Used in a template 6 | 7 | file { "/tmp/${name}-secret.sh": 8 | ensure => file, 9 | mode => '0777', 10 | content => template('dns/secret.erb'), 11 | notify => Exec["dnssec-keygen-${name}"], 12 | } 13 | 14 | exec { "dnssec-keygen-${name}": 15 | command => "/usr/sbin/dnssec-keygen -a HMAC-MD5 -r /dev/urandom -b 128 -n USER ${name}", 16 | cwd => "${cfg_dir}/bind.keys.d", 17 | require => [ 18 | Package['dnssec-tools'], 19 | File["${cfg_dir}/bind.keys.d"], 20 | ], 21 | refreshonly => true, 22 | notify => Exec["get-secret-from-${name}"], 23 | } 24 | 25 | exec { "get-secret-from-${name}": 26 | command => "/tmp/${name}-secret.sh", 27 | cwd => "${cfg_dir}/bind.keys.d", 28 | creates => "${cfg_dir}/bind.keys.d/${name}.secret", 29 | require => [ 30 | Exec["dnssec-keygen-${name}"], 31 | File["${cfg_dir}/bind.keys.d"], 32 | File["/tmp/${name}-secret.sh"], 33 | ], 34 | refreshonly => true, 35 | } 36 | 37 | file { "${cfg_dir}/bind.keys.d/${name}.secret": 38 | require => Exec["get-secret-from-${name}"], 39 | } 40 | 41 | concat { "${cfg_dir}/bind.keys.d/${name}.key": 42 | owner => $dns::server::params::owner, 43 | group => $dns::server::params::group, 44 | mode => '0644', 45 | notify => Class['dns::server::service'] 46 | } 47 | 48 | Concat::Fragment { 49 | target => "${cfg_dir}/bind.keys.d/${name}.key", 50 | require => [ 51 | Exec["get-secret-from-${name}"], 52 | File["${cfg_dir}/bind.keys.d/${name}.secret"], 53 | ], 54 | } 55 | 56 | concat::fragment { "${name}.key-header": 57 | order => 1, 58 | content => template('dns/key.erb'), 59 | } 60 | 61 | concat::fragment { "${name}.key-secret": 62 | order => 2, 63 | source => "${cfg_dir}/bind.keys.d/${name}.secret", 64 | } 65 | 66 | concat::fragment { "${name}.key-footer": 67 | order => 3, 68 | content => '};', 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /manifests/member.pp: -------------------------------------------------------------------------------- 1 | # == Class dns::member 2 | # 3 | define dns::member ($domain, $hostname, $ipaddress) { 4 | dns::record::a { $hostname: 5 | zone => $domain, 6 | data => $ipaddress, 7 | ptr => true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /manifests/record.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::record 2 | # 3 | # This is a private class to arbitary dns records. 4 | # 5 | define dns::record ( 6 | $zone, 7 | $host, 8 | $data, 9 | $record = 'A', 10 | $dns_class = 'IN', 11 | $ttl = '', 12 | $preference = false, 13 | $order = 9, 14 | $data_dir = $::dns::server::params::data_dir, 15 | ) { 16 | 17 | $zone_file_stage = "${data_dir}/db.${zone}.stage" 18 | 19 | # lint:ignore:only_variable_string 20 | if "${ttl}" !~ /^[0-9SsMmHhDdWw]+$/ and $ttl != '' { 21 | # lint:endignore:only_variable_string 22 | fail("Define[dns::record]: TTL ${ttl} must be an integer within 0-2147483647 or explicitly specified time units, e.g. 1h30m.") 23 | } 24 | 25 | if is_integer($ttl) and !(($ttl + 0) >= 0 and ($ttl+ 0) <= 2147483647) { 26 | fail("Define[dns::record]: TTL ${ttl} must be an integer within 0-2147483647 or explicitly specified time units, e.g. 1h30m.") 27 | } 28 | 29 | concat::fragment{"db.${zone}.${name}.record": 30 | target => $zone_file_stage, 31 | order => $order, 32 | content => template("${module_name}/zone_record.erb") 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /manifests/record/a.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::record::a 2 | # 3 | # Wrapper for dns::record to set an A record, optionally 4 | # also setting a PTR at the same time. 5 | # 6 | define dns::record::a ( 7 | $zone, 8 | $data, 9 | $ttl = '', 10 | $ptr = false, 11 | $host = $name, 12 | $data_dir = $::dns::server::config::data_dir, 13 | ) { 14 | 15 | $alias = "${name},A,${zone}" 16 | 17 | dns::record { $alias: 18 | zone => $zone, 19 | host => $host, 20 | ttl => $ttl, 21 | data => $data, 22 | data_dir => $data_dir, 23 | } 24 | 25 | if $ptr == 'all' { 26 | dns::record::ptr::by_ip { $data: 27 | host => $host, 28 | zone => $zone, 29 | data_dir => $data_dir, 30 | } 31 | } elsif $ptr == 'first' or str2bool($ptr) { 32 | $ip = inline_template('<%= @data.kind_of?(Array) ? @data.first : @data %>') 33 | dns::record::ptr::by_ip { $ip: 34 | host => $host, 35 | zone => $zone, 36 | data_dir => $data_dir, 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /manifests/record/aaaa.pp: -------------------------------------------------------------------------------- 1 | # == Define: dns::record::aaaa 2 | # 3 | # Wrapper of dns::record to set AAAA records 4 | # 5 | define dns::record::aaaa ( 6 | $zone, 7 | $data, 8 | $ttl = '', 9 | $host = $name, 10 | $data_dir = $::dns::server::config::data_dir, 11 | ) { 12 | 13 | $alias = "${name},AAAA,${zone}" 14 | 15 | dns::record { $alias: 16 | zone => $zone, 17 | host => $host, 18 | ttl => $ttl, 19 | record => 'AAAA', 20 | data => $data, 21 | data_dir => $data_dir, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /manifests/record/cname.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::record::dname 2 | # 3 | # Wrapper for dns::record to set a CNAME 4 | # 5 | define dns::record::cname ( 6 | $zone, 7 | $data, 8 | $ttl = '', 9 | $host = $name, 10 | $data_dir = $::dns::server::config::data_dir, 11 | ) { 12 | 13 | $alias = "${name},CNAME,${zone}" 14 | 15 | $qualified_data = $data ? { 16 | '@' => $data, 17 | /\.$/ => $data, 18 | default => "${data}." 19 | } 20 | 21 | dns::record { $alias: 22 | zone => $zone, 23 | host => $host, 24 | ttl => $ttl, 25 | record => 'CNAME', 26 | data => $qualified_data, 27 | data_dir => $data_dir, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /manifests/record/mx.pp: -------------------------------------------------------------------------------- 1 | # == Define: dns::record::mx 2 | # 3 | # Wrapper for dns::record to set an MX record. 4 | # 5 | define dns::record::mx ( 6 | $zone, 7 | $data, 8 | $ttl = '', 9 | $preference = 10, 10 | $host = '@', 11 | $data_dir = $::dns::server::config::data_dir, 12 | ) { 13 | 14 | $alias = "${name},${zone},MX,${preference},${data}" 15 | 16 | validate_string($zone) 17 | validate_string($data) 18 | validate_string($host) 19 | 20 | if !is_domain_name($zone) or $zone =~ /^[0-9\.]+$/ { 21 | fail("Define[dns::record::mx]: MX zone ${zone} must be a valid domain name.") 22 | } 23 | # Highest label (top-level domain) must be alphabetic 24 | if $zone =~ /\./ and $zone !~ /\.[A-Za-z]+$/ { 25 | fail("Define[dns::record::mx]: MX zone ${zone} must be a valid domain name.") 26 | } 27 | # RR data must be a valid hostname, not entirely numeric values 28 | if !is_domain_name($data) or $data =~ /^[0-9\.]+$/ { 29 | fail("Define[dns::record::mx]: MX data ${data} must be a valid hostname.") 30 | } 31 | if !is_integer($preference) or $preference < 0 or $preference > 65536 { 32 | fail("Define[dns::record::mx]: preference ${preference} must be an integer within 0-65536.") 33 | } 34 | if !is_domain_name($host) and $host != '@' { 35 | # Blank labels are permitted in BIND zone files, but they are not handled 36 | # by this puppet module because it does not render concat fragments in an 37 | # explicit order. Since BIND will substitute the last valid label, the 38 | # resulting blank substitution would be unpredictable. 39 | # Use @ to substitute the zone origin. 40 | fail("Define[dns::record::mx]: MX host label ${host} must be a valid hostname or '@' to signify \$ORIGIN.") 41 | } 42 | 43 | dns::record { $alias: 44 | zone => $zone, 45 | host => $host, 46 | ttl => $ttl, 47 | record => 'MX', 48 | preference => $preference, 49 | data => "${data}.", 50 | order => 2, 51 | data_dir => $data_dir, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /manifests/record/ns.pp: -------------------------------------------------------------------------------- 1 | # == Define: dns::record::ns 2 | # 3 | # Wrapper of dns::record to set NS records 4 | # 5 | define dns::record::ns ( 6 | $zone, 7 | $data, 8 | $ttl = '', 9 | $host = $name, 10 | $data_dir = $::dns::server::config::data_dir, 11 | ) { 12 | 13 | $alias = "${host},${zone},NS,${data}" 14 | 15 | validate_string($zone) 16 | validate_string($data) 17 | validate_string($host) 18 | 19 | if !is_domain_name($zone) or $zone =~ /^[0-9\.]+$/ { 20 | fail("Define[dns::record::ns]: NS zone ${zone} must be a valid domain name.") 21 | } 22 | # Highest label (top-level domain) must be alphabetic 23 | if $zone =~ /\./ and $zone !~ /\.[A-Za-z]+$/ { 24 | fail("Define[dns::record::ns]: NS zone ${zone} must be a valid domain name.") 25 | } 26 | # RR data must be a valid hostname, not entirely numeric values 27 | if !is_domain_name($data) or $data =~ /^[0-9\.]+$/ { 28 | fail("Define[dns::record::ns]: NS data ${data} must be a valid hostname.") 29 | } 30 | 31 | dns::record { $alias: 32 | zone => $zone, 33 | host => $host, 34 | ttl => $ttl, 35 | record => 'NS', 36 | data => $data, 37 | data_dir => $data_dir, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /manifests/record/ptr.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::record::prt 2 | # 3 | # Wrapper for dns::record to set PTRs 4 | # 5 | define dns::record::ptr ( 6 | $zone, 7 | $data, 8 | $ttl = '', 9 | $host = $name, 10 | $data_dir = $::dns::server::config::data_dir, 11 | ) { 12 | 13 | $alias = "${name},PTR,${zone}" 14 | 15 | dns::record { $alias: 16 | zone => $zone, 17 | host => $host, 18 | ttl => $ttl, 19 | record => 'PTR', 20 | data => "${data}.", 21 | data_dir => $data_dir, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /manifests/record/ptr/by_ip.pp: -------------------------------------------------------------------------------- 1 | # == Definition: dns::record::ptr::by_ip 2 | # 3 | # This type is defined to allow the dns::record::a resource type to 4 | # use pre-4.x 'iteration' to create reverse ptr records for hosts with 5 | # multiple ip addresses. 6 | # 7 | # === Parameters 8 | # 9 | # * `$ip` 10 | # The ip address for the ptr record. Defaults to the resource title. 11 | # 12 | # * `$host` 13 | # The host pointed to by this ptr record. If `$zone` is a non-empty 14 | # domain, it will be appended to the value of `$host` in the `PTR` 15 | # record; if `$zone` is undefined or empty, then `$host` 16 | # must include the domain name (but must *not* include any trailing `.`). 17 | # If `$host` is `@` and `$zone` is non-empty, `$zone` will be used by 18 | # itself in the `PTR` record. 19 | # 20 | # * `$ttl` 21 | # The TTL of the records to be created. Defaults to undefined. 22 | # 23 | # * `$zone` 24 | # The domain of the host pointed to by this ptr record, with *no* trailing 25 | # `.`. If not defined, `$host` is assumed to be fully-qualified. 26 | # 27 | # === Actions 28 | # 29 | # Reformats the $ip, $host, $zone parameters to create a `dns::record::ptr` 30 | # resource 31 | # 32 | # === Examples 33 | # 34 | # @example 35 | # dns::record::ptr::by_ip { '192.168.128.42': 36 | # host => 'server', 37 | # zone => 'example.com', 38 | # } 39 | # 40 | # turns into: 41 | # 42 | # @example 43 | # dns::record::ptr { '42.128.168.192.IN-ADDR.ARPA': 44 | # host => '42', 45 | # zone => '128.168.192.IN-ADDR.ARPA', 46 | # data => 'server.example.com', 47 | # } 48 | # 49 | # --- 50 | # @example 51 | # dns::record::ptr::by_ip { [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ]: 52 | # host => 'multihomed-server.example.com', 53 | # } 54 | # 55 | # turns into: 56 | # 57 | # @example 58 | # dns::record::ptr { '68.128.168.192.IN-ADDR.ARPA': 59 | # host => '68', 60 | # zone => '128.168.192.IN-ADDR.ARPA', 61 | # data => 'multihomed-server.example.com', 62 | # 63 | # @example 64 | # dns::record::ptr { '69.128.168.192.IN-ADDR.ARPA': 65 | # host => '69', 66 | # zone => '128.168.192.IN-ADDR.ARPA', 67 | # data => 'multihomed-server.example.com', 68 | # 69 | # @example 70 | # dns::record::ptr { '70.128.168.192.IN-ADDR.ARPA': 71 | # host => '70', 72 | # zone => '128.168.192.IN-ADDR.ARPA', 73 | # data => 'multihomed-server.example.com', 74 | # } 75 | 76 | define dns::record::ptr::by_ip ( 77 | $host, 78 | $zone = undef, 79 | $ttl = undef, 80 | $ip = $name, 81 | $data_dir = $::dns::server::config::data_dir, 82 | ) { 83 | 84 | # split the IP address up into three host/zone pairs based on class A, B, or C splits: 85 | # For IP address A.B.C.D, 86 | # class C => host D / zone C.B.A.IN-ADDR.ARPA 87 | # class B => host D.C / zone B.A.IN-ADDR.ARPA 88 | # class A => host D.C.B / zone A.IN-ADDR.ARPA 89 | $class_c_zone = inline_template('<%= @ip.split(".").reverse()[1..3].join(".") %>.IN-ADDR.ARPA') 90 | $class_c_host = inline_template('<%= @ip.split(".").reverse()[0..0].join(".") %>') 91 | $class_b_zone = inline_template('<%= @ip.split(".").reverse()[2..3].join(".") %>.IN-ADDR.ARPA') 92 | $class_b_host = inline_template('<%= @ip.split(".").reverse()[0..1].join(".") %>') 93 | $class_a_zone = inline_template('<%= @ip.split(".").reverse()[3..3].join(".") %>.IN-ADDR.ARPA') 94 | $class_a_host = inline_template('<%= @ip.split(".").reverse()[0..2].join(".") %>') 95 | 96 | # choose the most specific defined reverse zone file (class C, then B, then A). 97 | # Default to class C if none are defined. 98 | if defined(Dns::Zone[$class_c_zone]) { 99 | $reverse_zone = $class_c_zone 100 | $octet = $class_c_host 101 | } elsif defined(Dns::Zone[$class_b_zone]) { 102 | $reverse_zone = $class_b_zone 103 | $octet = $class_b_host 104 | } elsif defined(Dns::Zone[$class_a_zone]) { 105 | $reverse_zone = $class_a_zone 106 | $octet = $class_a_host 107 | } else { 108 | $reverse_zone = $class_c_zone 109 | $octet = $class_c_host 110 | } 111 | 112 | if $zone != undef and $zone != '' { 113 | if $host == '@' { 114 | $fqdn = $zone 115 | } else { 116 | $fqdn = "${host}.${zone}" 117 | } 118 | } else { 119 | $fqdn = $host 120 | } 121 | 122 | dns::record::ptr { "${octet}.${reverse_zone}": 123 | host => $octet, 124 | zone => $reverse_zone, 125 | ttl => $ttl, 126 | data => $fqdn, 127 | data_dir => $data_dir, 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /manifests/record/srv.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::server:srv 2 | # 3 | # Wrapper for dns::zone to set SRV records 4 | # 5 | define dns::record::srv ( 6 | $zone, 7 | $service, 8 | $pri, 9 | $weight, 10 | $port, 11 | $target, 12 | $proto = 'tcp', 13 | $ttl = '', 14 | $data_dir = $::dns::server::config::data_dir, 15 | ) { 16 | 17 | $alias = "${service}:${proto}@${target}:${port},${pri},${weight},SRV,${zone}" 18 | 19 | $host = "_${service}._${proto}.${zone}." 20 | 21 | dns::record { $alias: 22 | zone => $zone, 23 | host => $host, 24 | ttl => $ttl, 25 | record => 'SRV', 26 | data => "${pri}\t${weight}\t${port}\t${target}", 27 | data_dir => $data_dir, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /manifests/record/txt.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::record::txt 2 | # 3 | # Wrapper for dns::record for TXT records 4 | # 5 | # === Parameters 6 | # 7 | # [*zone*] 8 | # The zone in which this record should be created. 9 | # [*data*] 10 | # The value of the TXT record. (See the @Formatting section below) 11 | # [*ttl*] 12 | # The time-to-live value for the record. Defaults to an empty string, 13 | # which means BIND will use the zone's TTL setting. 14 | # [*host*] 15 | # The host name for this record. Defaults to the resource title. 16 | # 17 | # === Formatting 18 | # 19 | # DNS `TXT` and `SPF` records have specific formatting applied to their 20 | # *data* values, based on the requirements in 21 | # [RFC1035](https://tools.ietf.org/html/rfc1035.html) sections 22 | # [3.3](https://tools.ietf.org/html/rfc1035.html#section-3.3), 23 | # [3.3.14](https://tools.ietf.org/html/rfc1035.html#section-3.3.14), and 24 | # [5](https://tools.ietf.org/html/rfc1035.html#section-5). 25 | # 26 | # When the `TXT` or `SPF` record is created in the zone file, the data 27 | # value will be split into multiple strings of no more than 255 characters 28 | # each; within each character string, double-quotes (`"`) and backslashes 29 | # (`\\`) will be escaped by adding a backslash before them (`\\#` / `\\\\`); 30 | # each individual string will be surrounded by double-quotes; and the strings 31 | # will be joined back together with spaces in between. 32 | # 33 | # === Examples 34 | # 35 | # dns::record::txt { 'txt1': 36 | # zone => 'example.com', 37 | # data => 'this is a test record', 38 | # } 39 | # 40 | # will generate: 41 | # 42 | # txt1 IN TXT "this is a test record" 43 | # 44 | # --- 45 | # 46 | # dns::record::txt { 'txt2': 47 | # zone => 'example.com', 48 | # data => 'this is "another" test record', 49 | # } 50 | # 51 | # will generate: 52 | # 53 | # txt2 IN TXT "this is \"another\" test record" 54 | # 55 | # --- 56 | # 57 | # dns::record::txt { 'txt3.example.com': 58 | # zone => 'example.com', 59 | # data => 'this is a' + ' very'*60 + ' long test record', 60 | # host => 'txt3', 61 | # } 62 | # 63 | # will generate: 64 | # 65 | # txt3 IN TXT "this is a very very very...very " "very very...very long test record" 66 | # 67 | 68 | define dns::record::txt ( 69 | $zone, 70 | $data, 71 | $ttl = '', 72 | $host = $name, 73 | $data_dir = $::dns::server::config::data_dir, 74 | ) { 75 | 76 | $alias = "${name},TXT,${zone}" 77 | 78 | dns::record { $alias: 79 | zone => $zone, 80 | host => $host, 81 | ttl => $ttl, 82 | record => 'TXT', 83 | data => $data, 84 | data_dir => $data_dir, 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /manifests/server.pp: -------------------------------------------------------------------------------- 1 | # == Class dns::server 2 | # 3 | class dns::server ( 4 | $service = $dns::server::params::service, 5 | 6 | $necessary_packages = $dns::server::params::necessary_packages, 7 | $ensure_packages = $dns::server::params::ensure_packages, 8 | 9 | $cfg_dir = $dns::server::params::cfg_dir, 10 | $cfg_file = $dns::server::params::cfg_file, 11 | $data_dir = $dns::server::params::data_dir, 12 | $owner = $dns::server::params::owner, 13 | $group = $dns::server::params::group, 14 | 15 | $enable_default_zones = true, 16 | ) inherits dns::server::params { 17 | class { 'dns::server::install': 18 | necessary_packages => $necessary_packages, 19 | ensure_packages => $ensure_packages, 20 | } -> class { 'dns::server::config': 21 | cfg_dir => $cfg_dir, 22 | cfg_file => $cfg_file, 23 | data_dir => $data_dir, 24 | owner => $owner, 25 | group => $group, 26 | enable_default_zones => $enable_default_zones, 27 | } ~> class { 'dns::server::service': 28 | service => $service, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /manifests/server/config.pp: -------------------------------------------------------------------------------- 1 | # == Class dns::server 2 | # 3 | class dns::server::config ( 4 | $cfg_dir = $dns::server::params::cfg_dir, 5 | $cfg_file = $dns::server::params::cfg_file, 6 | $data_dir = $dns::server::params::data_dir, 7 | $owner = $dns::server::params::owner, 8 | $group = $dns::server::params::group, 9 | $enable_default_zones = true, 10 | ) inherits dns::server::params { 11 | 12 | file { $cfg_dir: 13 | ensure => directory, 14 | owner => $owner, 15 | group => $group, 16 | mode => '0755', 17 | } 18 | 19 | file { $data_dir: 20 | ensure => directory, 21 | owner => $owner, 22 | group => $group, 23 | mode => '0755', 24 | } 25 | 26 | file { "${cfg_dir}/bind.keys.d/": 27 | ensure => directory, 28 | owner => $owner, 29 | group => $group, 30 | mode => '0755', 31 | } 32 | 33 | file { $cfg_file: 34 | ensure => present, 35 | owner => $owner, 36 | group => $group, 37 | mode => '0644', 38 | content => template("${module_name}/named.conf.erb"), 39 | require => [ 40 | File[$cfg_dir], 41 | Class['dns::server::install'] 42 | ], 43 | notify => Class['dns::server::service'], 44 | } 45 | 46 | concat { "${cfg_dir}/named.conf.local": 47 | owner => $owner, 48 | group => $group, 49 | mode => '0644', 50 | notify => Class['dns::server::service'] 51 | } 52 | 53 | concat::fragment{'named.conf.local.header': 54 | target => "${cfg_dir}/named.conf.local", 55 | order => 1, 56 | content => "// File managed by Puppet.\n" 57 | } 58 | 59 | # Configure default zones with a concat so we could add more zones in it 60 | concat {$dns::server::params::rfc1912_zones_cfg: 61 | owner => $owner, 62 | group => $group, 63 | mode => '0644', 64 | ensure_newline => true, 65 | notify => Class['dns::server::service'], 66 | } 67 | 68 | concat::fragment {'default-zones.header': 69 | target => $dns::server::params::rfc1912_zones_cfg, 70 | order => '00', 71 | content => template('dns/named.conf.default-zones.erb'), 72 | } 73 | 74 | include dns::server::default 75 | 76 | } 77 | -------------------------------------------------------------------------------- /manifests/server/default.pp: -------------------------------------------------------------------------------- 1 | # == Class: dns::server::default 2 | # 3 | class dns::server::default ( 4 | 5 | $default_file = $dns::server::params::default_file, 6 | $default_template = $dns::server::params::default_template, 7 | 8 | $resolvconf = undef, 9 | $options = undef, 10 | $rootdir = undef, 11 | $enable_zone_write = undef, 12 | $enable_sdb = undef, 13 | $disable_named_dbus = undef, 14 | $keytab_file = undef, 15 | $disable_zone_checking = undef, 16 | 17 | ) inherits dns::server::params { 18 | 19 | validate_absolute_path( $default_file ) 20 | 21 | if $resolvconf != undef { 22 | validate_re( $resolvconf, '^(yes|no)$', 'The resolvconf value is not type of a string yes / no.' ) 23 | } 24 | 25 | if $rootdir != undef { 26 | validate_absolute_path( $rootdir ) 27 | } 28 | 29 | if $enable_zone_write != undef { 30 | validate_re( $enable_zone_write, '^(yes|no|\s*)$', 'The enable_zone_write value is not type of a string yes / no or empty.' ) 31 | } 32 | 33 | if $enable_sdb != undef { 34 | validate_re( $enable_sdb, '^(yes|no|1|0|\s*)$', 'The enable_sdb value is not type of a string yes / no / 1 / 0 or empty.' ) 35 | } 36 | 37 | if $keytab_file != undef { 38 | validate_absolute_path( $keytab_file ) 39 | } 40 | 41 | if $disable_zone_checking != undef { 42 | validate_re( $disable_zone_checking, '^(yes|no|\s*)$', 'The disable_zone_checking value is not type of a string yes / no or empty.' ) 43 | } 44 | 45 | file { $default_file: 46 | ensure => present, 47 | owner => $::dns::server::params::owner, 48 | group => $::dns::server::params::group, 49 | mode => '0644', 50 | content => template("${module_name}/${default_template}"), 51 | notify => Class['dns::server::service'], 52 | require => Package[$::dns::server::params::necessary_packages] 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /manifests/server/install.pp: -------------------------------------------------------------------------------- 1 | # == Class dns::server 2 | # 3 | class dns::server::install ( 4 | $necessary_packages = $dns::server::params::necessary_packages, 5 | $ensure_packages = $dns::server::params::ensure_packages, 6 | ) inherits dns::server::params { 7 | 8 | package { $necessary_packages : 9 | ensure => $ensure_packages, 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /manifests/server/options.pp: -------------------------------------------------------------------------------- 1 | # == Define: dns::server::options 2 | # 3 | # BIND server template-based configuration definition. 4 | # 5 | # === Parameters 6 | # 7 | # [*allow_recursion*] 8 | # Array of IP addresses which are allowed to make recursive queries. 9 | # Default: empty, meaning "localnets; localhost" 10 | # 11 | # [*allow_query*] 12 | # Array of IP addresses which are allowed to ask ordinary DNS questions. 13 | # Default: empty, meaning "any" 14 | # 15 | # [*also_notify*] 16 | # The list of servers to which additional zone-change notifications 17 | # should be sent. 18 | # Default: empty, meaning no additional servers 19 | # 20 | # [*check_names_master*] 21 | # Restrict the character set and syntax of master zones. 22 | # Default: undefined, meaning "fail" 23 | # 24 | # [*check_names_slave*] 25 | # Restrict the character set and syntax of slave zones. 26 | # Default: undefined, meaning "warn" 27 | # 28 | # [*check_names_response*] 29 | # Restrict the character set and syntax of network responses. 30 | # Default: undefined, meaning "ignore" 31 | # 32 | # [*control_channel_ip*] 33 | # String of one ip for which the control api is bound. 34 | # Default: undef, meaning the control channel is disable, 35 | # both control_channel_port and control_channel_ip must be defined 36 | # for the control api to be enabled 37 | # 38 | # [*control_channel_port*] 39 | # String of one port for which the control api is bound. 40 | # Default: undef, meaning the control channel is disable 41 | # both control_channel_port and control_channel_ip must be defined 42 | # for the control api to be enabled 43 | # 44 | # [*control_channel_allow*] 45 | # Array of IPs that are allowed to query the controls channel. 46 | # Default: undef, meaning all IPs that can reach control_channel_ip are allowed 47 | # to query it 48 | # 49 | # [*data_dir*] 50 | # Bind data directory. 51 | # Default: `/etc/bind/zones` in Debian, `/var/named` in RedHat. 52 | # 53 | # [*dnssec_validation*] 54 | # Controls DNS-SEC validation. String of "yes", "auto", "no", or 55 | # "absent" (to prevent the `dnssec-validation` option from being 56 | # included). Default is "absent" on RedHat 5 (whose default bind 57 | # package is too old to include dnssec validation), and "auto" on 58 | # Debian and on RedHat 6 and above. 59 | # Note: If *dnssec_enable* is set to false, this option is ignored. 60 | # 61 | # [*dnssec_enable*] 62 | # Controls whether to enable/disable DNS-SEC support. Boolean. 63 | # Default is false on RedHat 5 (for the same reasons as 64 | # dnssec_validation above), and true on Debian and on RedHat 6 65 | # and above. 66 | # 67 | # [*forward_policy*] 68 | # The forwarding policy to use. Must be `first` or `only`. 69 | # If not defined, the `named` default of `first` will be used. 70 | # 71 | # [*forwarders*] 72 | # Array of forwarders IP addresses. Default: empty 73 | # 74 | # [*listen_on*] 75 | # Array of IP addresses on which to listen. Default: empty, meaning "any" 76 | # 77 | # [*listen_on_ipv6*] 78 | # Array of IPv6 addresses on which to listen. Default: empty, meaning "any" 79 | # 80 | # [*listen_on_port*]: 81 | # UDP/TCP port number to use for receiving and sending traffic. 82 | # Default: undefined, meaning 53 83 | # 84 | # [*log_categories*] 85 | # Logging categories to use. It is a hash of arrays. Each key of the hash is a category 86 | # and its value is the array of options. If `query_log_enable` is set to true, then 87 | # this value is ignored. 88 | # Default: empty. 89 | # 90 | # [*log_channels*] 91 | # Logging channels to use. It is a hash of arrays. Each key of the hash is a channel 92 | # and its value is the array of options. If `query_log_enable` is set to true, then 93 | # this value is ignored. 94 | # Default: empty. 95 | # 96 | # [*no_empty_zones*] 97 | # Controls whether to enable/disable empty zones. Boolean values. 98 | # Default: false, meaning enable empty zones 99 | # 100 | # [*notify_source*] 101 | # The source IP address from which to send notifies. 102 | # Default: undef, meaning the primary IP address of the DNS server, 103 | # as determined by BIND. 104 | # 105 | # [*query_log_enable*] 106 | # If `true`, query logging will be turned on and directed to the 107 | # `named_querylog` file in the `working_dir` directory. If `false` 108 | # or not defined, query logging will not be configured. 109 | # 110 | # [*statistic_channel_ip*] 111 | # String of one ip for which the statistic api is bound. 112 | # Default: undef, meaning the statistic channel is disable, 113 | # both statistic_channel_port and statistic_channel_ip must be defined 114 | # for the statistic api to be enabled 115 | # 116 | # [*statistic_channel_port*] 117 | # String of one port for which the statistic api is bound. 118 | # Default: undef, meaning the statistic channel is disable 119 | # both statistic_channel_port and statistic_channel_ip must be defined 120 | # for the statistic api to be enabled 121 | # 122 | # [*statistic_channel_allow*] 123 | # Array of IPs that are allowed to query the statistics channel. 124 | # Default: undef, meaning all IPs that can reach statistic_channel_ip are allowed 125 | # to query it 126 | # 127 | # [*transfers*] 128 | # Array of IP addresses or "none" allowed to transfer. Default: empty 129 | # 130 | # [*transfer_source*] 131 | # The source IP address from which to respond to transfer requests. 132 | # Default: undef, meaning the primary IP address of the DNS server, 133 | # as determined by BIND. 134 | # 135 | # [*zone_notify*] 136 | # Controls notifications when a zone for which this server is 137 | # authoritative changes. String of yes (send notifications to zone's 138 | # NS records and to also-notify list), no (no notifications are sent), 139 | # master-only (only send notifications for master zones), or explicit 140 | # (send notifications only to also-notify list). 141 | # Default: undef, meaning the BIND default of "yes" 142 | # 143 | # [*working_dir*] 144 | # The working directory where the query log will be stored. 145 | # Default: `/var/cache/bind` in Debian, `${data_dir}/data` in RedHat 146 | # 147 | # [*extra_options* ] 148 | # Hash with other options that will be included. 149 | # Default: empty. 150 | # 151 | # === Examples 152 | # 153 | # dns::server::options { '/etc/bind/named.conf.options': 154 | # forwarders => [ '8.8.8.8', '8.8.4.4' ], 155 | # } 156 | # 157 | include dns::server::params 158 | define dns::server::options ( 159 | $allow_query = [], 160 | $allow_recursion = [], 161 | $also_notify = [], 162 | $check_names_master = undef, 163 | $check_names_slave = undef, 164 | $check_names_response = undef, 165 | $control_channel_ip = undef, 166 | $control_channel_port = undef, 167 | $control_channel_allow = undef, 168 | $data_dir = $::dns::server::params::data_dir, 169 | $dnssec_validation = $::dns::server::params::default_dnssec_validation, 170 | $dnssec_enable = $::dns::server::params::default_dnssec_enable, 171 | $forward_policy = undef, 172 | $forwarders = [], 173 | $listen_on = [], 174 | $listen_on_ipv6 = [], 175 | $listen_on_port = undef, 176 | $log_channels = {}, 177 | $log_categories = {}, 178 | $no_empty_zones = false, 179 | $notify_source = undef, 180 | $query_log_enable = undef, 181 | $statistic_channel_ip = undef, 182 | $statistic_channel_port = undef, 183 | $statistic_channel_allow = undef, 184 | $transfers = [], 185 | $transfer_source = undef, 186 | $working_dir = $::dns::server::params::working_dir, 187 | $zone_notify = undef, 188 | $extra_options = {}, 189 | ) { 190 | $valid_check_names = ['fail', 'warn', 'ignore'] 191 | $valid_forward_policy = ['first', 'only'] 192 | $cfg_dir = $::dns::server::params::cfg_dir 193 | 194 | if ! defined(Class['::dns::server']) { 195 | fail('You must include the ::dns::server base class before using any dns options defined resources') 196 | } 197 | 198 | validate_string($forward_policy) 199 | if $forward_policy != undef and !member($valid_forward_policy, $forward_policy) { 200 | fail("The forward_policy must be ${valid_forward_policy}") 201 | } 202 | validate_array($forwarders) 203 | validate_array($transfers) 204 | validate_array($listen_on) 205 | validate_array($listen_on_ipv6) 206 | validate_array($allow_recursion) 207 | if $check_names_master != undef and !member($valid_check_names, $check_names_master) { 208 | fail("The check name policy check_names_master must be ${valid_check_names}") 209 | } 210 | if $check_names_slave != undef and !member($valid_check_names, $check_names_slave) { 211 | fail("The check name policy check_names_slave must be ${valid_check_names}") 212 | } 213 | if $check_names_response != undef and !member($valid_check_names, $check_names_response) { 214 | fail("The check name policy check_names_response must be ${valid_check_names}") 215 | } 216 | validate_array($allow_query) 217 | 218 | if $statistic_channel_port != undef and !is_numeric($statistic_channel_port) { 219 | fail('The statistic_channel_port is not a number') 220 | } 221 | 222 | if $statistic_channel_ip != undef and (!is_string($statistic_channel_ip) or !is_ip_address($statistic_channel_ip)) { 223 | fail('The statistic_channel_ip is not an ip string') 224 | } 225 | 226 | if $statistic_channel_allow != undef { 227 | validate_array($statistic_channel_allow) 228 | } 229 | 230 | if $control_channel_port != undef and !is_numeric($control_channel_port) { 231 | fail('The control_channel_port is not a number') 232 | } 233 | 234 | if $control_channel_ip != undef and (!is_string($control_channel_ip) or !is_ip_address($control_channel_ip)) { 235 | fail('The control_channel_ip is not an ip string') 236 | } 237 | 238 | if $control_channel_allow != undef { 239 | validate_array($control_channel_allow) 240 | } 241 | 242 | validate_array($also_notify) 243 | 244 | $valid_zone_notify = ['yes', 'no', 'explicit', 'master-only'] 245 | if $zone_notify != undef and !member($valid_zone_notify, $zone_notify) { 246 | fail("The zone_notify must be ${valid_zone_notify}") 247 | } 248 | 249 | $valid_dnssec_validation = ['yes', 'no', 'auto', 'absent'] 250 | if $dnssec_validation != undef and !member($valid_dnssec_validation, $dnssec_validation) { 251 | fail("The dnssec_validation must be ${valid_dnssec_validation}") 252 | } 253 | 254 | validate_bool($no_empty_zones) 255 | 256 | validate_bool($dnssec_enable) 257 | if (! $dnssec_enable) and ($dnssec_validation != undef) { 258 | warning('dnssec_enable is false. dnssec_validation will be ignored.') 259 | } 260 | 261 | if $notify_source != undef and !is_ip_address($notify_source) { 262 | fail('The notify_source is not an ip string') 263 | } 264 | 265 | if $transfer_source != undef and !is_ip_address($transfer_source) { 266 | fail('The transfer_source is not an ip string') 267 | } 268 | 269 | # validate these, just in case they're overridden 270 | validate_absolute_path($data_dir) 271 | validate_absolute_path($working_dir) 272 | 273 | validate_hash($log_channels) 274 | validate_hash($log_categories) 275 | 276 | validate_hash($extra_options) 277 | 278 | file { $title: 279 | ensure => present, 280 | owner => $::dns::server::params::owner, 281 | group => $::dns::server::params::group, 282 | mode => '0644', 283 | require => [File[$cfg_dir], Class['::dns::server::install']], 284 | content => template("${module_name}/named.conf.options.erb"), 285 | notify => Class['dns::server::service'], 286 | } 287 | 288 | } 289 | -------------------------------------------------------------------------------- /manifests/server/params.pp: -------------------------------------------------------------------------------- 1 | # == Class dns::server::params 2 | # 3 | class dns::server::params { 4 | case $::osfamily { 5 | 'Debian': { 6 | $cfg_dir = '/etc/bind' 7 | $cfg_file = '/etc/bind/named.conf' 8 | $data_dir = '/var/lib/bind/zones' 9 | $working_dir = '/var/cache/bind' 10 | $root_hint = "${cfg_dir}/db.root" 11 | $rfc1912_zones_cfg = "${cfg_dir}/named.conf.default-zones" 12 | $rndc_key_file = "${cfg_dir}/rndc.key" 13 | $group = 'bind' 14 | $owner = 'bind' 15 | $package = 'bind9' 16 | $service = 'bind9' 17 | $default_file = '/etc/default/bind9' 18 | $default_template = 'default.debian.erb' 19 | $default_dnssec_enable = true 20 | $default_dnssec_validation = 'auto' 21 | if versioncmp( $::operatingsystemmajrelease, '8' ) >= 0 { 22 | $necessary_packages = [ 'bind9', 'bind9utils' ] 23 | } else { 24 | $necessary_packages = [ 'bind9', 'bind9utils', 'dnssec-tools' ] 25 | } 26 | } 27 | 'RedHat': { 28 | $cfg_dir = '/etc/named' 29 | $cfg_file = '/etc/named.conf' 30 | $data_dir = '/var/named' 31 | $working_dir = "${data_dir}/data" 32 | $root_hint = "${data_dir}/named.ca" 33 | $rfc1912_zones_cfg = '/etc/named.rfc1912.zones' 34 | $rndc_key_file = '/etc/named.root.key' 35 | $group = 'named' 36 | $owner = 'named' 37 | $package = 'bind' 38 | $service = 'named' 39 | $necessary_packages = [ 'bind', ] 40 | $default_file = '/etc/sysconfig/named' 41 | $default_template = 'default.redhat.erb' 42 | if $::operatingsystemmajrelease =~ /^[1-5]$/ { 43 | $default_dnssec_enable = false 44 | $default_dnssec_validation = 'absent' 45 | } else { 46 | $default_dnssec_enable = true 47 | $default_dnssec_validation = 'auto' 48 | } 49 | } 50 | default: { 51 | fail("dns::server is incompatible with this osfamily: ${::osfamily}") 52 | } 53 | } 54 | $ensure_packages = latest 55 | } 56 | -------------------------------------------------------------------------------- /manifests/server/service.pp: -------------------------------------------------------------------------------- 1 | # == Class dns::server::service 2 | # 3 | class dns::server::service ( 4 | $service = $dns::server::params::service 5 | ) inherits dns::server::params { 6 | 7 | service { $service: 8 | ensure => running, 9 | hasstatus => true, 10 | hasrestart => true, 11 | enable => true, 12 | require => Class['dns::server::config'] 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /manifests/server/view.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::server::view 2 | # 3 | # `dns::server::view` defines a DNS view. 4 | # 5 | # Zones to the view could be added by using the `zones` parameter of 6 | # `dns::server::view` or by declaring `dns::zone` resources with its `view` 7 | # parameter set to this resource name. 8 | # 9 | # === Parameters 10 | # 11 | # [*ensure*] 12 | # If the view should be `present` or `absent. Defaults to `present`. 13 | # 14 | # [*enable_default_zones*] 15 | # Boolean indicating if the default zones (`named.conf.default-zones`) should 16 | # be included in this view or not. Defaults to `true`. 17 | # 18 | # [*match_clients*] 19 | # Array (of strings) with the `match-clients` for the zone. Defaults to empty. 20 | # 21 | # [*match_destinations*] 22 | # Array (of strings) with the `match-destinations` for the view. Defaults to empty. 23 | # 24 | # [*match_recursive_only*] 25 | # Value for the `match-recursive-only` option of the view. Defaults to undef 26 | # (the option is not configured). Valid values are `yes` and `no`. 27 | # 28 | # [*options* ] 29 | # Hash with additional options that should be configured in the view. 30 | # Defaults to empty (no additional option is added). It should be a hash where 31 | # every value should be a string or an array. 32 | # 33 | # [*order*] 34 | # Order of different views could be important (for example, if `match_clients` 35 | # of a view is a superset of the `match_clients` of another). This parameter 36 | # could be used to force a different order of the alphatical one. 37 | # Defaults to `50`. 38 | # 39 | # [*viewname*] 40 | # Name of the view. Defaults to `$name`. 41 | # 42 | # [*zones*] 43 | # Hash of zones resources that should be included in this view. 44 | # Defaults to empty (no zone is added). 45 | # 46 | define dns::server::view ( 47 | $ensure = 'present', 48 | $enable_default_zones = true, 49 | $match_clients = [], 50 | $match_destinations = [], 51 | $match_recursive_only = undef, 52 | $options = {}, 53 | $order = '50', 54 | $viewname = $name, 55 | $zones = {}, 56 | ) { 57 | include ::dns::server::params 58 | 59 | $valid_ensure = ['present', 'absent'] 60 | $valid_yes_no = ['yes', 'no'] 61 | if !member($valid_ensure, $ensure) { 62 | fail("ensure parameter must be ${valid_ensure}") 63 | } 64 | validate_bool($enable_default_zones) 65 | validate_array($match_clients) 66 | validate_array($match_destinations) 67 | if $match_recursive_only { 68 | if !member($valid_yes_no, $match_recursive_only) { 69 | fail("match_recursive_only parameter must be ${valid_yes_no}") 70 | } 71 | } 72 | validate_hash($options) 73 | validate_string($order) 74 | validate_string($viewname) 75 | validate_hash($zones) 76 | 77 | $rfc1912_zones_cfg = $dns::server::params::rfc1912_zones_cfg 78 | 79 | concat { "${dns::server::params::cfg_dir}/view-${name}.conf": 80 | ensure => $ensure, 81 | owner => $dns::server::params::owner, 82 | group => $dns::server::params::group, 83 | mode => '0644', 84 | ensure_newline => true, 85 | notify => Class['dns::server::service'], 86 | } 87 | 88 | if $ensure == 'present' { 89 | concat::fragment {"view-${name}.header": 90 | target => "${dns::server::params::cfg_dir}/view-${name}.conf", 91 | order => '00', 92 | content => template("${module_name}/view.erb"), 93 | } 94 | 95 | concat::fragment {"view-${name}.tail": 96 | target => "${dns::server::params::cfg_dir}/view-${name}.conf", 97 | order => '99', 98 | content => '}; ', 99 | } 100 | 101 | # Include view configuration in main config 102 | concat::fragment {"named.conf.local.view.${name}.include": 103 | target => "${dns::server::params::cfg_dir}/named.conf.local", 104 | order => $order, 105 | content => "include \"${dns::server::params::cfg_dir}/view-${name}.conf\";\n", 106 | require => Concat["${dns::server::params::cfg_dir}/view-${name}.conf"], 107 | } 108 | 109 | # Create zone config 110 | create_resources(dns::zone, $zones, { view => $name }) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /manifests/tsig.pp: -------------------------------------------------------------------------------- 1 | # defined type allows you to declare a BIND TSIG. 2 | # 3 | # Parameters: 4 | # 5 | # $ensure = ensure the persence or absence of the acl. 6 | # $keyname = the name given to the TSIG KEY. This must be unique. This defaults to 7 | # the namevar. 8 | # $algorithm = Defined algorithm of the key (default: hmac-md5) 9 | # $server = related string or array of ip addresses to this key 10 | # $secret = shared secret of the key 11 | # 12 | # Usage: 13 | # 14 | # dns::tsig { 'ns3': 15 | # ensure => present, 16 | # algorithm => "hmac-md5" 17 | # secret => "dTIxGBPjkT/8b6BYHTUA==" 18 | # } 19 | # 20 | define dns::tsig ( 21 | $keyname = $name, 22 | $algorithm = 'hmac-md5', 23 | $server = undef, 24 | $secret = undef, 25 | $ensure = present 26 | ) { 27 | 28 | $cfg_dir = $dns::server::params::cfg_dir # Used in a template 29 | validate_string($name) 30 | 31 | if $ensure == 'present' { 32 | concat::fragment { "named.conf.local.tsig.${name}.include": 33 | target => "${cfg_dir}/named.conf.local", 34 | order => 4, 35 | content => template("${module_name}/tsig.erb"), 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /manifests/zone.pp: -------------------------------------------------------------------------------- 1 | # == Define dns::zone 2 | # 3 | # `dns::zone` defines a DNS zone and creates both the zone entry 4 | # in the `named.conf` files and the standardized zone file header 5 | # to which the zone records will be added. 6 | # 7 | # === BIND Time Values 8 | # 9 | # BIND time values are numeric strings and default to representing a 10 | # number of seconds (e.g. a time value of `3600` equals one hour). 11 | # Time values may optionally be followed by a suffix of `m`, `h`, 12 | # `d`, or `w`, meaning the time is in minutes, hours, days, or weeks, 13 | # respectively. For example, one week could be represented as any of 14 | # `1w`, `7d`, `168h`, `10080m`, or `604800`. 15 | # 16 | # === FQDN Values 17 | # 18 | # The `dns::zone` type will append the final `.` dot to any value which 19 | # is listed in the *Parameters* section as an *FQDN* (Fully Qualified 20 | # Domain Name). This has two implications: First, any FQDN must *not* 21 | # include the trailing `.` dot; second, any parameter listed as an FQDN 22 | # _must_ be fully-qualified, and will not allow the hostname-only form 23 | # of the name (e.g. in the `example.net` domain, `ns1.example.net` could 24 | # not be referred to as `ns1` in any parameter that requires an FQDN). 25 | # 26 | # === Access Control Lists 27 | # 28 | # Any parameter which is an Access Control List is an array that may 29 | # contain the pre-defined ACL names `none`, `any`, `localhost`, or 30 | # `localnets`, or may contain ip addresses, optionally precededed by a 31 | # `!` exclamation mark and optionally followed by a `/` and a prefix 32 | # length for a subnet match. 33 | # 34 | # === Parameters 35 | # 36 | # [*allow_transfer*] 37 | # An array that indicates what other DNS servers may initiate a 38 | # zone transfer of this zone. The array may contain IP addresses, 39 | # optionally followed by a `/` and a prefix length for a subnet match, 40 | # and optionally prefixed by a `!` exclamation mark (to indicate that 41 | # the IP address - or addresses, with a subnet match - are denied 42 | # rather than allowed) or the special ACL names `any`, `localhost`, 43 | # `localnets`, or `none`: `any` matches any host; `localhost` matches 44 | # only the name server itself; `localnets` matches the name server 45 | # and any request from the name server's subnet(s); and `none` is the 46 | # equivalent of `!any` - it denies all requests. The first entry in 47 | # the array that matches the requester's address will be used. 48 | # 49 | # [*allow_forwarder*] 50 | # An array of IP addresses and optional port numbers to which queries 51 | # for this zone will be forwarded (based on the *forward_policy* 52 | # setting). If the optional port number is included, it must be 53 | # separated from the IP address by the word `port` - for example, `[ 54 | # '192.168.100.102 port 1234' ]`. Defaults to an empty array, which 55 | # means no forwarding will be done. 56 | # 57 | # [*allow_query*] 58 | # An array of IP addresses from which queries should be allowed 59 | # Defaults to an empty array, which allows all ip to query the zone 60 | # 61 | # [*allow_update*] 62 | # An array of IP addresses from which updates should be allowed. 63 | # Defaults to an empty array, which allows updates to the zone. 64 | # If Array has entries, then zone file initial creation is allowed 65 | # but content will not be replaced. This capability allows dynamic 66 | # updates. 67 | # 68 | # [*also_notify*] 69 | # This is an array of IP addresses and optional port numbers to 70 | # which this DNS server will send notifies when the master DNS server 71 | # reloads the zone file. See the *allow_forwarder* parameter for how 72 | # to include the optional port numbers. 73 | # 74 | # [*data_dir*] 75 | # Bind data directory. 76 | # Default: /etc/bind/zones 77 | # 78 | # [*default_zone*] 79 | # When it is set to `true` the zone is added to the `named.conf.default-zones` 80 | # file instead of `named.conf.local` or a view file. Defaults to `false`. This 81 | # parameter should not be set to true when `view` parameter is also used. 82 | # 83 | # [*forward_policy*] 84 | # Either `first` or `only`. If the *allow_forwarder* array is not 85 | # empty, this setting defines how query forwarding is handled. With a 86 | # value of `only`, the DNS server will forward the request and return 87 | # the response to the client immediately. With a value of `first`, 88 | # the DNS server will forward the request; if the forwarder server 89 | # returns a not-found response, the DNS server will attempt to answer 90 | # the request itself. 91 | # 92 | # [*nameservers*] 93 | # An array containing the FQDN's of each name server for this zone. 94 | # This will be used to create the `NS` records for the zone file. 95 | # Defaults to an array containing the `$::fqdn` fact. 96 | # 97 | # [*reverse*] 98 | # If `true`, the zone will have `.in-addr.arpa` appended to it. 99 | # If set to the string `reverse`, the `.`-separated components of 100 | # the zone will be reversed, and then have `.in-addr.arpa` appended 101 | # to it. 102 | # Defaults to `false`. 103 | # 104 | # [*serial*] 105 | # Optional set or time bases auto generated serial numver of zone file 106 | # 107 | # [*slave_masters*] 108 | # If *zone_type* is set to `slave` or `stub`, this holds an array or string 109 | # containing the IP addresses of the DNS servers from which this slave 110 | # transfers the zone. If a string, the IP addresses must be separated 111 | # by semicolons (`;`). 112 | # 113 | # [*soa*] 114 | # The authoritative name server for this zone. Defaults to the 115 | # `$::fqdn` fact. 116 | # 117 | # [*soa_email*] 118 | # The point-of-contact authoritative name server for this zone, in 119 | # the form __`.`__ (__ may not contain 120 | # `.` dots). Defaults to `root.` followed by the `$::fqdn` fact. 121 | # 122 | # [*view*] 123 | # The view which this zone belongs to. Defaults to undef (it does not belong to 124 | # any view and the configuration is added to named.conf.local. This parameter 125 | # should not be used with `default_zone` set to true. 126 | # 127 | # [*zone_expire*] 128 | # This is the maximum amount of time after the last successful refresh 129 | # of the zone for which the slave will continue to respond to DNS 130 | # queries for records in this zone. 131 | # 132 | # [*zone_minimum*] 133 | # Despite _minimum_ in the parameter name, this is the maximum time 134 | # that a _negative_ for this zone (e.g. a host not found response) 135 | # may be cached by other resolvers. 136 | # 137 | # [*zone_notify*] 138 | # One of `yes`, `no`, or `explicit`. With a value of `explicit`, 139 | # when sending notifies, the DNS server will send them only to the 140 | # slaves listed in the `also_notify` list. With `yes`, the DNS server 141 | # will send notifies to the `also_notify` list _and_ to all nameservers 142 | # listed in the `NS` records for the zone _except_ for the nameserver 143 | # listed in the `soa` parameter and the sending name server. With `no`, 144 | # the DNS server will never send notifies for this zone. 145 | # 146 | # [*zone_refresh*] 147 | # The minimum time between when slaves will check back with the zone 148 | # master(s) to check if the zone has been updated. See *Time values* 149 | # above. Defaults to 604800 seconds (7 days). 150 | # 151 | # [*zone_retry*] 152 | # If a slave tries and fails to contact the master(s), this is the 153 | # time the slave will wait before retrying. 154 | # 155 | # [*zone_ttl*] 156 | # The default TTL (time-to-live) for the zone records. See *Time 157 | # values* above. Defaults to 604800 seconds (7 days). 158 | # 159 | # [*zone_type*] 160 | # The type of DNS zone being described. Can be one of `master`, `slave` 161 | # (requires *slave_masters* to be set), `stub` (requires *slave_masters* 162 | # to be set), `delegation-only`, or `forward` (requires both 163 | # *allow_forwarder* and *forward_policy* to be set). 164 | # Defaults to `master`. 165 | # 166 | define dns::zone ( 167 | $soa = $::fqdn, 168 | $soa_email = "root.${::fqdn}", 169 | $zone_ttl = '604800', 170 | $zone_refresh = '604800', 171 | $zone_retry = '86400', 172 | $zone_expire = '2419200', 173 | $zone_minimum = '604800', 174 | $nameservers = [ $::fqdn ], 175 | $reverse = false, 176 | $serial = false, 177 | $zone_type = 'master', 178 | $allow_transfer = [], 179 | $allow_forwarder = [], 180 | $allow_query =[], 181 | $allow_update =[], 182 | $forward_policy = 'first', 183 | $slave_masters = undef, 184 | $zone_notify = undef, 185 | $also_notify = [], 186 | $ensure = present, 187 | $data_dir = $::dns::server::params::data_dir, 188 | $view = undef, 189 | $default_zone = false, 190 | ) { 191 | 192 | $cfg_dir = $dns::server::params::cfg_dir 193 | 194 | validate_array($allow_transfer) 195 | validate_array($allow_forwarder) 196 | if !member(['first', 'only'], $forward_policy) { 197 | fail('The forward policy can only be set to either first or only') 198 | } 199 | validate_array($allow_query) 200 | 201 | validate_array($also_notify) 202 | $valid_zone_notify = ['yes', 'no', 'explicit', 'master-only'] 203 | if $zone_notify != undef and !member($valid_zone_notify, $zone_notify) { 204 | fail("The zone_notify must be ${valid_zone_notify}") 205 | } 206 | 207 | $zone = $reverse ? { 208 | 'reverse' => join(reverse(split("arpa.in-addr.${name}", '\.')), '.'), 209 | true => "${name}.in-addr.arpa", 210 | default => $name 211 | } 212 | 213 | validate_string($zone_type) 214 | $valid_zone_type_array = ['master', 'slave', 'stub', 'forward', 'delegation-only'] 215 | if !member($valid_zone_type_array, $zone_type) { 216 | $valid_zone_type_array_str = join($valid_zone_type_array, ',') 217 | fail("The zone_type must be one of [${valid_zone_type_array}]") 218 | } 219 | 220 | $zone_file = "${data_dir}/db.${name}" 221 | $zone_file_stage = "${zone_file}.stage" 222 | 223 | validate_array($allow_update) 224 | # Replace when updates allowed 225 | if empty($allow_update) { 226 | $zone_replace = true 227 | } else { 228 | $zone_replace = false 229 | } 230 | 231 | if $view { 232 | validate_string($view) 233 | } 234 | 235 | validate_bool($default_zone) 236 | 237 | if $view and $default_zone == true { 238 | fail('view and default parameters are mutually excluding') 239 | } 240 | 241 | if $ensure == absent { 242 | file { $zone_file: 243 | ensure => absent, 244 | } 245 | } elsif $zone_type == 'master' { 246 | # Zone Database 247 | 248 | # Create "fake" zone file without zone-serial 249 | concat { $zone_file_stage: 250 | owner => $dns::server::params::owner, 251 | group => $dns::server::params::group, 252 | mode => '0644', 253 | replace => $zone_replace, 254 | require => Class['dns::server'], 255 | notify => Exec["test-${zone}"] 256 | } 257 | concat::fragment{"db.${name}.soa": 258 | target => $zone_file_stage, 259 | order => 1, 260 | content => template("${module_name}/zone_file.erb") 261 | } 262 | 263 | # Test staging zone file before the real zones are touched. 264 | # This is why the staging zone file is given a bogus serial number. 265 | # That way it's a valid zone, but has a uniquely replaceable ID. 266 | 267 | exec { "test-${zone}": 268 | command => "named-checkzone ${zone} ${zone_file_stage}", 269 | path => ['/bin', '/sbin', '/usr/bin', '/usr/sbin'], 270 | refreshonly => true, 271 | provider => posix, 272 | user => $dns::server::params::owner, 273 | group => $dns::server::params::group, 274 | require => Class['dns::server::install'], 275 | notify => Exec["bump-${zone}-serial"], 276 | } 277 | 278 | 279 | 280 | # Generate real zone from stage file by changing bogus serial number 281 | # to current timestamp. A real zone file will be updated only at change of 282 | # the stage file, thanks to this serial is updated only in case of need. 283 | 284 | $zone_serial = $serial ? { 285 | false => inline_template('<%= Time.now.to_i %>'), 286 | default => $serial 287 | } 288 | exec { "bump-${zone}-serial": 289 | command => "sed '8s/00000000001/${zone_serial}/' ${zone_file_stage} > ${zone_file}", 290 | path => ['/bin', '/sbin', '/usr/bin', '/usr/sbin'], 291 | refreshonly => true, 292 | provider => posix, 293 | user => $dns::server::params::owner, 294 | group => $dns::server::params::group, 295 | require => Class['dns::server::install'], 296 | notify => Class['dns::server::service'], 297 | } 298 | } else { 299 | # For any zone file that is not a master zone, we should make sure 300 | # we don't have a staging file 301 | concat { $zone_file_stage: 302 | ensure => absent 303 | } 304 | } 305 | 306 | # Include Zone in named.conf.local or view file 307 | $target = $default_zone ? { 308 | true => $dns::server::params::rfc1912_zones_cfg, 309 | default => $view ? { 310 | undef => "${cfg_dir}/named.conf.local", 311 | '' => "${cfg_dir}/named.conf.local", 312 | default => "${cfg_dir}/view-${view}.conf", 313 | } 314 | } 315 | concat::fragment{"named.conf.local.${name}.include": 316 | target => $target, 317 | order => 3, 318 | content => template("${module_name}/zone.erb"), 319 | } 320 | 321 | } 322 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ajjahn-dns", 3 | "version": "2.1.1", 4 | "author": "Adam Jahn", 5 | "summary": "Module for provisioning DNS (bind9)", 6 | "license": "Apache-2.0", 7 | "source": "https://github.com/ajjahn/puppet-dns", 8 | "project_page": "https://github.com/ajjahn/puppet-dns", 9 | "issues_url": "https://github.com/ajjahn/puppet-dns/issues", 10 | "description": "Module for provisioning DNS (bind9)", 11 | "operatingsystem_support": [ 12 | { 13 | "operatingsystem": "RedHat", 14 | "operatingsystemrelease": [ 15 | "5.0", 16 | "6.0" 17 | ] 18 | }, 19 | { 20 | "operatingsystem": "Ubuntu", 21 | "operatingsystemrelease": [ 22 | "12.04", 23 | "10.04" 24 | ] 25 | } 26 | ], 27 | "dependencies": [ 28 | { 29 | "name": "puppetlabs/concat", 30 | "version_requirement": ">=2.0.0" 31 | }, 32 | { 33 | "name": "puppetlabs/stdlib", 34 | "version_requirement": ">=2.4.0 <6.0.0" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /spec/acceptance/basic_dns_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'basic dns' do 4 | 5 | context 'default parameters' do 6 | let(:pp) {" 7 | include dns::server 8 | dns::server::options { '/etc/bind/named.conf.options': 9 | forwarders => [ '8.8.8.8', '8.8.4.4' ] 10 | } 11 | "} 12 | it 'should apply with no errors' do 13 | apply_manifest(pp, :catch_failures=>true) 14 | end 15 | it 'should be idempotent' do 16 | apply_manifest(pp, :catch_changes=>true) 17 | end 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-66-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-66-x64: 3 | roles: 4 | - master 5 | platform: el-6-x86_64 6 | box: puppetlabs/centos-6.6-64-nocm 7 | box_url: https://vagrantcloud.com/puppetlabs/boxes/centos-6.6-64-nocm 8 | hypervisor: vagrant 9 | CONFIG: 10 | log_level: verbose 11 | type: foss 12 | color: false 13 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-70-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-70-x64: 3 | roles: 4 | - master 5 | platform: el-7-x86_64 6 | box: puppetlabs/centos-7.0-64-nocm 7 | box_url: https://vagrantcloud.com/puppetlabs/boxes/centos-7.0-64-nocm 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: verbose 11 | type: foss 12 | color: false 13 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-78-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-78-x64: 3 | roles: 4 | - master 5 | platform: debian-7-amd64 6 | box: puppetlabs/debian-7.8-64-nocm 7 | box_url: https://vagrantcloud.com/puppetlabs/boxes/debian-7.8-64-nocm 8 | hypervisor: vagrant 9 | 10 | CONFIG: 11 | log_level: verbose 12 | type: foss 13 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-server-1204-x86.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-1204-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-1204-amd64 6 | box: puppetlabs/ubuntu-12.04-64-nocm 7 | box_url: https://vagrantcloud.com/puppetlabs/boxes/ubuntu-12.04-64-nocm 8 | hypervisor: vagrant 9 | 10 | CONFIG: 11 | log_level: verbose 12 | type: foss 13 | color: false 14 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-server-1404-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-1404-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-1404-amd64 6 | box: puppetlabs/ubuntu-14.04-64-nocm 7 | box_url: https://vagrantcloud.com/puppetlabs/ubuntu-14.04-64-nocm 8 | hypervisor: vagrant 9 | 10 | CONFIG: 11 | log_level: verbose 12 | type: foss 13 | color: false 14 | -------------------------------------------------------------------------------- /spec/classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajjahn/puppet-dns/3b996e1783a7aa4396a23246d8c4a62c4749f5e0/spec/classes/.gitkeep -------------------------------------------------------------------------------- /spec/classes/coverage_spec.rb: -------------------------------------------------------------------------------- 1 | at_exit { RSpec::Puppet::Coverage.report! } 2 | -------------------------------------------------------------------------------- /spec/classes/dns__server__config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::server::config', :type => :class do 4 | 5 | context "on an unsupported OS" do 6 | let :facts do { :osfamily => 'Solaris', :concat_basedir => '/dne', } end 7 | it { should raise_error(/dns::server is incompatible with this osfamily/) } 8 | end 9 | 10 | context "on a Debian OS" do 11 | let (:facts) {{ 12 | :osfamily => 'Debian', 13 | :operatingsystemrelease => '6', 14 | :concat_basedir => '/dne', 15 | }} 16 | it { should contain_file('/etc/bind/').with_owner('bind') } 17 | it { should contain_file('/etc/bind/named.conf').with_content(/^include "\/etc\/bind\/named.conf.options";$/) } 18 | end 19 | 20 | context "on a RedHat OS" do 21 | let (:facts) {{ 22 | :osfamily => 'RedHat', 23 | :concat_basedir => '/dne', 24 | }} 25 | it { should contain_file('/etc/named.conf').with_owner('named') } 26 | it { should contain_file('/etc/named.conf').with_content(/^include "\/etc\/named\/named.conf.options";$/) } 27 | end 28 | end 29 | 30 | -------------------------------------------------------------------------------- /spec/classes/dns__server__install_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::server::install', :type => :class do 4 | context "on an unsupported OS" do 5 | it{ should raise_error(/dns::server is incompatible with this osfamily/) } 6 | end 7 | 8 | context "on a Debian OS with default params" do 9 | let(:facts) {{ :osfamily => 'Debian' }} 10 | it { should contain_class('dns::server::params') } 11 | ['bind9', 'bind9utils', 'dnssec-tools'].each do |package| 12 | it do 13 | should contain_package(package).with({ 14 | 'ensure' => 'latest', 15 | }) 16 | end 17 | end 18 | end 19 | 20 | context "on a Debian OS with non-default params" do 21 | let(:facts) {{ :osfamily => 'Debian' }} 22 | let(:params) {{ :ensure_packages => 'present' }} 23 | it { should contain_class('dns::server::params') } 24 | ['bind9', 'bind9utils', 'dnssec-tools'].each do |package| 25 | it do 26 | should contain_package(package).with({ 27 | 'ensure' => 'present', 28 | }) 29 | end 30 | end 31 | end 32 | 33 | context "on a RedHat OS with default params" do 34 | let(:facts) {{ :osfamily => 'RedHat' }} 35 | it { should contain_class('dns::server::params') } 36 | it do 37 | should contain_package('bind').with({ 38 | 'ensure' => 'latest', 39 | }) 40 | end 41 | end 42 | 43 | context "on a RedHat OS with non-default params" do 44 | let(:facts) {{ :osfamily => 'RedHat' }} 45 | let(:params) {{ :ensure_packages => 'present' }} 46 | it { should contain_class('dns::server::params') } 47 | it do 48 | should contain_package('bind').with({ 49 | 'ensure' => 'present', 50 | }) 51 | end 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /spec/classes/dns__server__service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::server::service' do 4 | let(:facts) {{ :concat_basedir => '/mock_dir' }} 5 | 6 | context 'on a supported OS' do 7 | let(:facts) {{ :osfamily => 'Debian' }} 8 | it { should contain_service('bind9').with_require('Class[Dns::Server::Config]') } 9 | end 10 | 11 | context 'on an unsupported OS' do 12 | let(:facts) {{ :osfamily => 'Solaris' }} 13 | it { should raise_error(/dns::server is incompatible with this osfamily: Solaris/) } 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /spec/classes/dns__server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::server' do 4 | 5 | context "By Default" do 6 | let(:facts) {{ :osfamily => 'Debian', :concat_basedir => '/dne' }} 7 | it { should contain_class('dns::server::install') } 8 | it { should contain_class('dns::server::config') } 9 | it { should contain_class('dns::server::service') } 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /spec/classes/server/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::server::default' do 4 | 5 | context "on an unsupported OS" do 6 | it{ should raise_error(/dns::server is incompatible with this osfamily/) } 7 | end 8 | 9 | context 'by default on debian' do 10 | 11 | let(:facts) {{ :osfamily => 'Debian', :concat_basedir => '/tmp' }} 12 | 13 | context "passing correct values and paths" do 14 | 15 | context 'passing `no` to resolvconf' do 16 | let(:params) {{ :resolvconf => 'no' }} 17 | it { should contain_file('/etc/default/bind9').with_content(/RESOLVCONF=no/) } 18 | end 19 | 20 | context 'passing `yes` to resolvconf' do 21 | let(:params) {{ :resolvconf => 'yes' }} 22 | it { should contain_file('/etc/default/bind9').with_content(/RESOLVCONF=yes/) } 23 | end 24 | 25 | context 'passing `-u bind -4` to options' do 26 | let(:params) {{ :options => '-u bind -4' }} 27 | it { should contain_file('/etc/default/bind9').with_content(/OPTIONS="-u bind -4"/) } 28 | end 29 | 30 | context 'passing `-u bind -6` to options' do 31 | let(:params) {{ :options => '-u bind -6' }} 32 | it { should contain_file('/etc/default/bind9').with_content(/OPTIONS="-u bind -6"/) } 33 | end 34 | 35 | context "requires bind9 and dnssec-tools package" do 36 | it do 37 | should contain_file('/etc/default/bind9').with({ 38 | 'require' => ['Package[bind9]', 'Package[bind9utils]', 'Package[dnssec-tools]'], 39 | }) 40 | end 41 | end 42 | 43 | 44 | end 45 | 46 | context "passing wrong values and paths" do 47 | 48 | context 'passing wrong value to resolvconf for hit an error' do 49 | let(:params) {{ :resolvconf => 'WrongValue' }} 50 | it{ should raise_error(/The resolvconf value is not type of a string yes \/ no./)} 51 | end 52 | 53 | end 54 | 55 | end 56 | 57 | context 'by default on redhat' do 58 | 59 | let(:facts) {{ :osfamily => 'RedHat', :concat_basedir => '/tmp' }} 60 | 61 | context "passing correct values and paths" do 62 | 63 | context 'passing path `/chroot` to rootdir' do 64 | let(:params) {{ :rootdir => '/chroot' }} 65 | it { should contain_file('/etc/sysconfig/named').with_content(/ROOTDIR="\/chroot"/) } 66 | end 67 | 68 | context 'passing `-u named` to options' do 69 | let(:params) {{ :options => '-u named' }} 70 | it { should contain_file('/etc/sysconfig/named').with_content(/OPTIONS="-u named"/) } 71 | end 72 | 73 | context 'passing `yes` to enable_zone_write' do 74 | let(:params) {{ :enable_zone_write => 'yes' }} 75 | it { should contain_file('/etc/sysconfig/named').with_content(/ENABLE_ZONE_WRITE=yes/) } 76 | end 77 | 78 | context 'passing `no` to enable_zone_write' do 79 | let(:params) {{ :enable_zone_write => 'no' }} 80 | it { should contain_file('/etc/sysconfig/named').with_content(/ENABLE_ZONE_WRITE=no/) } 81 | end 82 | 83 | context 'passing `yes` to enable_sdb' do 84 | let(:params) {{ :enable_sdb => 'yes' }} 85 | it { should contain_file('/etc/sysconfig/named').with_content(/ENABLE_SDB=yes/) } 86 | end 87 | 88 | context 'passing `no` to enable_sdb' do 89 | let(:params) {{ :enable_sdb => 'no' }} 90 | it { should contain_file('/etc/sysconfig/named').with_content(/ENABLE_SDB=no/) } 91 | end 92 | 93 | context 'passing `1` to enable_sdb' do 94 | let(:params) {{ :enable_sdb => '1' }} 95 | it { should contain_file('/etc/sysconfig/named').with_content(/ENABLE_SDB=1/) } 96 | end 97 | 98 | context 'passing `0` to enable_sdb' do 99 | let(:params) {{ :enable_sdb => '0' }} 100 | it { should contain_file('/etc/sysconfig/named').with_content(/ENABLE_SDB=0/) } 101 | end 102 | 103 | context 'passing `yes` to disable_named_dbus' do 104 | let(:params) {{ :disable_named_dbus => 'yes' }} 105 | it { should contain_file('/etc/sysconfig/named').with_content(/DISABLE_NAMED_DBUS=yes/) } 106 | end 107 | 108 | context 'passing `no` to disable_named_dbus' do 109 | let(:params) {{ :disable_named_dbus => 'no' }} 110 | it { should contain_file('/etc/sysconfig/named').with_content(/DISABLE_NAMED_DBUS=no/) } 111 | end 112 | 113 | context 'passing path `/usr/local/samba/private/dns.keytab` to keytab_file' do 114 | let(:params) {{ :keytab_file => '/usr/local/samba/private/dns.keytab' }} 115 | it { should contain_file('/etc/sysconfig/named').with_content(/KEYTAB_FILE="\/usr\/local\/samba\/private\/dns.keytab/) } 116 | end 117 | 118 | context 'passing `yes` to disable_zone_checking' do 119 | let(:params) {{ :disable_zone_checking => 'yes' }} 120 | it { should contain_file('/etc/sysconfig/named').with_content(/DISABLE_ZONE_CHECKING=yes/) } 121 | end 122 | 123 | context 'passing `no` to disable_zone_checking' do 124 | let(:params) {{ :disable_zone_checking => 'no' }} 125 | it { should contain_file('/etc/sysconfig/named').with_content(/DISABLE_ZONE_CHECKING=no/) } 126 | end 127 | 128 | context "requires bind package" do 129 | it do 130 | should contain_file('/etc/sysconfig/named').with({ 131 | 'require' => 'Package[bind]', 132 | }) 133 | end 134 | end 135 | 136 | end 137 | 138 | context "passing wrong values and paths" do 139 | 140 | context 'passing wrong value to rootdir for hit an error' do 141 | let(:params) {{ :rootdir => 'chroot' }} 142 | it{ should raise_error(/"chroot" is not an absolute path./)} 143 | end 144 | 145 | context 'passing wrong value to enable_zone_write for hit an error' do 146 | let(:params) {{ :enable_zone_write => 'WrongValue' }} 147 | it{ should raise_error(/The enable_zone_write value is not type of a string yes \/ no./)} 148 | end 149 | 150 | context 'passing wrong value to enable_sdb for hit an error' do 151 | let(:params) {{ :enable_sdb => 'WrongValue' }} 152 | it{ should raise_error(/The enable_sdb value is not type of a string yes \/ no \/ 1 \/ 0 or empty./)} 153 | end 154 | 155 | context 'passing wrong value to keytab_file for hit an error' do 156 | let(:params) {{ :keytab_file => 'usr/local/samba/private/dns.keytab' }} 157 | it{ should raise_error(/"usr\/local\/samba\/private\/dns.keytab" is not an absolute path./)} 158 | end 159 | 160 | context 'passing wrong value to disable_zone_checking for hit an error' do 161 | let(:params) {{ :disable_zone_checking => 'chroot' }} 162 | it{ should raise_error(/The disable_zone_checking value is not type of a string yes \/ no or empty./)} 163 | end 164 | 165 | end 166 | 167 | end 168 | 169 | end 170 | -------------------------------------------------------------------------------- /spec/defines/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajjahn/puppet-dns/3b996e1783a7aa4396a23246d8c4a62c4749f5e0/spec/defines/.gitkeep -------------------------------------------------------------------------------- /spec/defines/dns__acl_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::acl' do 4 | let(:title) { 'trusted' } 5 | let(:facts) { { 6 | :concat_basedir => '/tmp', 7 | :osfamily => 'Debian', 8 | } } 9 | 10 | context 'passing a string to data' do 11 | let :params do 12 | { :data => '192.168.0.0/24' } 13 | end 14 | it { should raise_error(Puppet::Error, /is not an Array/) } 15 | end 16 | 17 | context 'passing an array to data' do 18 | let :params do 19 | { :data => [ '192.168.0.0/24' ] } 20 | end 21 | it { should_not raise_error } 22 | it { 23 | should contain_concat__fragment('named.conf.local.acl.trusted.include'). 24 | with_content(/acl trusted/) 25 | } 26 | it { 27 | should contain_concat__fragment('named.conf.local.acl.trusted.include'). 28 | with_content(/192.168.0.0\/24;/) 29 | } 30 | end 31 | 32 | end 33 | 34 | -------------------------------------------------------------------------------- /spec/defines/dns__key_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::key' do 4 | let(:title) { 'rspec-key' } 5 | let(:default_facts) { { :concat_basedir => '/tmp' } } 6 | 7 | context "On a Debian OS" do 8 | let (:facts) do 9 | default_facts.merge({ 10 | :osfamily => 'Debian' 11 | }) 12 | end 13 | it { should contain_file('/tmp/rspec-key-secret.sh').with_notify('Exec[dnssec-keygen-rspec-key]') } 14 | it { should contain_exec('dnssec-keygen-rspec-key').with_command(/USER rspec-key$/) } 15 | it { should contain_exec('get-secret-from-rspec-key').with_command('/tmp/rspec-key-secret.sh') } 16 | it { should contain_exec('get-secret-from-rspec-key').with_creates('/etc/bind/bind.keys.d/rspec-key.secret') } 17 | it { should contain_exec('get-secret-from-rspec-key').with_require(['Exec[dnssec-keygen-rspec-key]', 'File[/etc/bind/bind.keys.d]', 'File[/tmp/rspec-key-secret.sh]']) } 18 | it { should contain_file('/etc/bind/bind.keys.d/rspec-key.secret').with_require('Exec[get-secret-from-rspec-key]') } 19 | it { should contain_concat('/etc/bind/bind.keys.d/rspec-key.key') } 20 | ['rspec-key.key-header', 'rspec-key.key-secret', 'rspec-key.key-footer'].each do |fragment| 21 | it { should contain_concat__fragment(fragment).with_target('/etc/bind/bind.keys.d/rspec-key.key') } 22 | it { should contain_concat__fragment(fragment).with_require(['Exec[get-secret-from-rspec-key]', 'File[/etc/bind/bind.keys.d/rspec-key.secret]']) } 23 | end 24 | it { should contain_concat__fragment('rspec-key.key-secret').with_source('/etc/bind/bind.keys.d/rspec-key.secret') } 25 | end 26 | 27 | context "On a RedHat OS" do 28 | let (:facts) do 29 | default_facts.merge({ 30 | :osfamily => 'RedHat' 31 | }) 32 | end 33 | it { should contain_file('/tmp/rspec-key-secret.sh').with_notify('Exec[dnssec-keygen-rspec-key]') } 34 | it { should contain_exec('dnssec-keygen-rspec-key').with_command(/USER rspec-key$/) } 35 | it { should contain_exec('get-secret-from-rspec-key').with_command('/tmp/rspec-key-secret.sh') } 36 | it { should contain_exec('get-secret-from-rspec-key').with_creates('/etc/named/bind.keys.d/rspec-key.secret') } 37 | it { should contain_exec('get-secret-from-rspec-key').with_require(['Exec[dnssec-keygen-rspec-key]', 'File[/etc/named/bind.keys.d]', 'File[/tmp/rspec-key-secret.sh]']) } 38 | it { should contain_file('/etc/named/bind.keys.d/rspec-key.secret').with_require('Exec[get-secret-from-rspec-key]') } 39 | it { should contain_concat('/etc/named/bind.keys.d/rspec-key.key') } 40 | ['rspec-key.key-header', 'rspec-key.key-secret', 'rspec-key.key-footer'].each do |fragment| 41 | it { should contain_concat__fragment(fragment).with_target('/etc/named/bind.keys.d/rspec-key.key') } 42 | it { should contain_concat__fragment(fragment).with_require(['Exec[get-secret-from-rspec-key]', 'File[/etc/named/bind.keys.d/rspec-key.secret]']) } 43 | end 44 | it { should contain_concat__fragment('rspec-key.key-secret').with_source('/etc/named/bind.keys.d/rspec-key.secret') } 45 | end 46 | end 47 | 48 | -------------------------------------------------------------------------------- /spec/defines/dns__record__a_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record::a', :type => :define do 4 | let(:title) { 'atest' } 5 | let(:facts) { { :concat_basedir => '/tmp' } } 6 | 7 | context 'passing a single ip address with ptr=>false' do 8 | let :params do { 9 | :host => 'atest', 10 | :zone => 'example.com', 11 | :data => '192.168.128.42', 12 | :ptr => false, 13 | } end 14 | it { should_not raise_error } 15 | it { should contain_concat__fragment('db.example.com.atest,A,example.com.record').with_content(/^atest\s+IN\s+A\s+192\.168\.128\.42$/) } 16 | it { should_not contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.42.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record') } 17 | end 18 | 19 | context 'passing a single ip address with ptr=>true' do 20 | let :params do { 21 | :host => 'atest', 22 | :zone => 'example.com', 23 | :data => '192.168.128.42', 24 | :ptr => true, 25 | } end 26 | it { should_not raise_error } 27 | it { should contain_concat__fragment('db.example.com.atest,A,example.com.record').with_content(/^atest\s+IN\s+A\s+192\.168\.128\.42$/) } 28 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.42.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^42\s+IN\s+PTR\s+atest\.example\.com\.$/) } 29 | end 30 | 31 | context 'passing a single ip address with ptr=>all' do 32 | let :params do { 33 | :host => 'atest', 34 | :zone => 'example.com', 35 | :data => '192.168.128.42', 36 | :ptr => 'all', 37 | } end 38 | it { should_not raise_error } 39 | it { should contain_concat__fragment('db.example.com.atest,A,example.com.record').with_content(/^atest\s+IN\s+A\s+192\.168\.128\.42$/) } 40 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.42.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^42\s+IN\s+PTR\s+atest\.example\.com\.$/) } 41 | end 42 | 43 | context 'passing multiple ip addresses with ptr=>false' do 44 | let :params do { 45 | :host => 'atest', 46 | :zone => 'example.com', 47 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 48 | :ptr => false, 49 | } end 50 | it { should_not raise_error } 51 | it { should contain_concat__fragment('db.example.com.atest,A,example.com.record').with_content(/^atest\s+IN\s+A\s+192\.168\.128\.68\natest\s+IN\s+A\s+192\.168\.128\.69\natest\s+IN\s+A\s+192\.168\.128\.70$/) } 52 | it { should_not contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record') } 53 | end 54 | 55 | context 'passing multiple ip addresses with ptr=>true' do 56 | let :params do { 57 | :host => 'atest', 58 | :zone => 'example.com', 59 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 60 | :ptr => true, 61 | } end 62 | it { should_not raise_error } 63 | it { should contain_concat__fragment('db.example.com.atest,A,example.com.record').with_content(/^atest\s+IN\s+A\s+192\.168\.128\.68\natest\s+IN\s+A\s+192\.168\.128\.69\natest\s+IN\s+A\s+192\.168\.128\.70$/) } 64 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^68\s+IN\s+PTR\s+atest\.example\.com\.$/) } 65 | end 66 | 67 | context 'passing multiple ip addresses with ptr=>all' do 68 | let :params do { 69 | :host => 'atest', 70 | :zone => 'example.com', 71 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 72 | :ptr => 'all', 73 | } end 74 | it { should_not raise_error } 75 | it { should contain_concat__fragment('db.example.com.atest,A,example.com.record').with_content(/^atest\s+IN\s+A\s+192\.168\.128\.68\natest\s+IN\s+A\s+192\.168\.128\.69\natest\s+IN\s+A\s+192\.168\.128\.70$/) } 76 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^68\s+IN\s+PTR\s+atest\.example\.com\.$/) } 77 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.69.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^69\s+IN\s+PTR\s+atest\.example\.com\.$/) } 78 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.70.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^70\s+IN\s+PTR\s+atest\.example\.com\.$/) } 79 | end 80 | 81 | context 'passing ptr=>true with class A network defined' do 82 | let :params do { 83 | :host => 'atest', 84 | :zone => 'example.com', 85 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 86 | :ptr => 'all', 87 | } end 88 | let :pre_condition do [ 89 | 'dns::zone { "192.IN-ADDR.ARPA": }', 90 | ] end 91 | it { should_not raise_error } 92 | it { should contain_concat__fragment('db.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,192.IN-ADDR.ARPA.record').with_content(/^68\.128\.168\s+IN\s+PTR\s+atest\.example\.com\.$/) } 93 | end 94 | 95 | context 'passing ptr=>true with class B network defined' do 96 | let :params do { 97 | :host => 'atest', 98 | :zone => 'example.com', 99 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 100 | :ptr => 'all', 101 | } end 102 | let :pre_condition do [ 103 | 'dns::zone { "168.192.IN-ADDR.ARPA": }', 104 | ] end 105 | it { should_not raise_error } 106 | it { should contain_concat__fragment('db.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,168.192.IN-ADDR.ARPA.record').with_content(/^68\.128\s+IN\s+PTR\s+atest\.example\.com\.$/) } 107 | end 108 | 109 | context 'passing ptr=>true with class C network defined' do 110 | let :params do { 111 | :host => 'atest', 112 | :zone => 'example.com', 113 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 114 | :ptr => 'all', 115 | } end 116 | let :pre_condition do [ 117 | 'dns::zone { "128.168.192.IN-ADDR.ARPA": }', 118 | ] end 119 | it { should_not raise_error } 120 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^68\s+IN\s+PTR\s+atest\.example\.com\.$/) } 121 | end 122 | 123 | context 'passing ptr=>true with class A and class B network defined' do 124 | let :params do { 125 | :host => 'atest', 126 | :zone => 'example.com', 127 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 128 | :ptr => 'all', 129 | } end 130 | let :pre_condition do [ 131 | 'dns::zone { "192.IN-ADDR.ARPA": }', 132 | 'dns::zone { "168.192.IN-ADDR.ARPA": }', 133 | ] end 134 | it { should_not raise_error } 135 | it { should contain_concat__fragment('db.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,168.192.IN-ADDR.ARPA.record').with_content(/^68\.128\s+IN\s+PTR\s+atest\.example\.com\.$/) } 136 | end 137 | 138 | context 'passing ptr=>true with class A and class C network defined' do 139 | let :params do { 140 | :host => 'atest', 141 | :zone => 'example.com', 142 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 143 | :ptr => 'all', 144 | } end 145 | let :pre_condition do [ 146 | 'dns::zone { "192.IN-ADDR.ARPA": }', 147 | 'dns::zone { "128.168.192.IN-ADDR.ARPA": }', 148 | ] end 149 | it { should_not raise_error } 150 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^68\s+IN\s+PTR\s+atest\.example\.com\.$/) } 151 | end 152 | 153 | context 'passing ptr=>true with class B and class C network defined' do 154 | let :params do { 155 | :host => 'atest', 156 | :zone => 'example.com', 157 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 158 | :ptr => 'all', 159 | } end 160 | let :pre_condition do [ 161 | 'dns::zone { "168.192.IN-ADDR.ARPA": }', 162 | 'dns::zone { "128.168.192.IN-ADDR.ARPA": }', 163 | ] end 164 | it { should_not raise_error } 165 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^68\s+IN\s+PTR\s+atest\.example\.com\.$/) } 166 | end 167 | 168 | context 'passing ptr=>true with class A, class B and class C network defined' do 169 | let :params do { 170 | :host => 'atest', 171 | :zone => 'example.com', 172 | :data => [ '192.168.128.68', '192.168.128.69', '192.168.128.70' ], 173 | :ptr => 'all', 174 | } end 175 | let :pre_condition do [ 176 | 'dns::zone { "192.IN-ADDR.ARPA": }', 177 | 'dns::zone { "168.192.IN-ADDR.ARPA": }', 178 | 'dns::zone { "128.168.192.IN-ADDR.ARPA": }', 179 | ] end 180 | it { should_not raise_error } 181 | it { should contain_concat__fragment('db.128.168.192.IN-ADDR.ARPA.68.128.168.192.IN-ADDR.ARPA,PTR,128.168.192.IN-ADDR.ARPA.record').with_content(/^68\s+IN\s+PTR\s+atest\.example\.com\.$/) } 182 | end 183 | 184 | end 185 | 186 | -------------------------------------------------------------------------------- /spec/defines/dns__record__aliases.spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record::aaaa', :type => :define do 4 | let(:facts) { { :concat_basedir => '/tmp' } } 5 | 6 | context 'letting the host be defined by the resource name' do 7 | let :params do { 8 | :zone => 'example.com', 9 | :title => 'foo' , 10 | :data => ['::1'] , 11 | } end 12 | it { should_not raise_error } 13 | it { should contain_concat__fragment('db.example.com.foo,AAAA,example.com.record') 14 | .with_content(/^foo\s+IN\s+AAAA\s+::1$/) 15 | } 16 | end 17 | 18 | context 'assigning a different host than the resource name' do 19 | let :params do { 20 | :zone => 'example.com', 21 | :title => 'foo' , 22 | :host => 'bar' , 23 | :data => ['::1'] , 24 | } end 25 | it { should_not raise_error } 26 | it { should contain_concat__fragment('db.example.com.foo,AAAA,example.com.record') 27 | .with_content(/^bar\s+IN\s+AAAA\s+::1$/) 28 | } 29 | end 30 | 31 | end 32 | 33 | describe 'dns::record::a', :type => :define do 34 | let(:facts) { { :concat_basedir => '/tmp' } } 35 | 36 | context 'letting the host be defined by the resource name' do 37 | let :params do { 38 | :zone => 'example.com', 39 | :title => 'foo' , 40 | :data => ['1.2.3.4'] , 41 | } end 42 | it { should_not raise_error } 43 | it { should contain_concat__fragment('db.example.com.foo,A,example.com.record') 44 | .with_content(/^foo\s+IN\s+A\s+1\.2\.3\.4$/) 45 | } 46 | end 47 | 48 | context 'assigning a different host than the resource name' do 49 | let :params do { 50 | :zone => 'example.com', 51 | :title => 'foo' , 52 | :host => 'bar' , 53 | :data => ['1.2.3.4'] , 54 | } end 55 | it { should_not raise_error } 56 | it { should contain_concat__fragment('db.example.com.foo,A,example.com.record') 57 | .with_content(/^bar\s+IN\s+A\s+1\.2\.3\.4$/) 58 | } 59 | end 60 | 61 | end 62 | 63 | describe 'dns::record::cname', :type => :define do 64 | let(:facts) { { :concat_basedir => '/tmp' } } 65 | 66 | context 'letting the host be defined by the resource name' do 67 | let :params do { 68 | :zone => 'example.com', 69 | :title => 'foo' , 70 | :data => 'baz.example.com', 71 | } end 72 | it { should_not raise_error } 73 | it { should contain_concat__fragment('db.example.com.foo,CNAME,example.com.record') 74 | .with_content(/^foo\s+IN\s+CNAME\s+baz\.example\.com\.$/) 75 | } 76 | end 77 | 78 | context 'assigning a different host than the resource name' do 79 | let :params do { 80 | :zone => 'example.com', 81 | :title => 'foo' , 82 | :host => 'bar' , 83 | :data => 'baz.example.com', 84 | } end 85 | it { should_not raise_error } 86 | it { should contain_concat__fragment('db.example.com.foo,CNAME,example.com.record') 87 | .with_content(/^bar\s+IN\s+CNAME\s+baz\.example\.com\.$/) 88 | } 89 | end 90 | 91 | end 92 | 93 | describe 'dns::record::mx', :type => :define do 94 | let(:facts) { { :concat_basedir => '/tmp' } } 95 | 96 | context 'letting the host be defined by the resource name' do 97 | let :params do { 98 | :zone => 'example.com', 99 | :title => 'foo' , 100 | :data => 'baz.example.com', 101 | :preference => 10, 102 | } end 103 | it { should_not raise_error } 104 | it { should contain_concat__fragment('db.example.com.foo,MX,example.com.record') 105 | .with_content(/^foo\s+IN\s+MX\s+10\s+baz\.example\.com\.$/) 106 | } 107 | end 108 | 109 | context 'assigning a different host than the resource name' do 110 | let :params do { 111 | :zone => 'example.com', 112 | :title => 'foo' , 113 | :host => 'bar' , 114 | :data => 'baz.example.com', 115 | :preference => 10, 116 | } end 117 | it { should_not raise_error } 118 | it { should contain_concat__fragment('db.example.com.foo,MX,example.com.record') 119 | .with_content(/^bar\s+IN\s+MX\s+10\s+baz\.example\.com\.$/) 120 | } 121 | end 122 | 123 | end 124 | 125 | 126 | describe 'dns::record::ns', :type => :define do 127 | let(:facts) { { :concat_basedir => '/tmp' } } 128 | 129 | context 'letting the host be defined by the resource name' do 130 | let :params do { 131 | :zone => 'example.com', 132 | :title => 'foo' , 133 | :data => 'baz.example.com.', 134 | } end 135 | it { should_not raise_error } 136 | it { should contain_concat__fragment('db.example.com.foo,NS,example.com.record') 137 | .with_content(/^foo\s+IN\s+NS\s+baz\.example\.com\.$/) 138 | } 139 | end 140 | 141 | context 'assigning a different host than the resource name' do 142 | let :params do { 143 | :zone => 'example.com', 144 | :title => 'foo' , 145 | :host => 'bar' , 146 | :data => 'baz.example.com.', 147 | } end 148 | it { should_not raise_error } 149 | it { should contain_concat__fragment('db.example.com.foo,NS,example.com.record') 150 | .with_content(/^bar\s+IN\s+NS\s+baz\.example\.com\.$/) 151 | } 152 | end 153 | 154 | end 155 | 156 | 157 | describe 'dns::record::ptr', :type => :define do 158 | let(:facts) { { :concat_basedir => '/tmp' } } 159 | 160 | context 'letting the host be defined by the resource name' do 161 | let :params do { 162 | :zone => '0.0.127.IN-ADDR.ARPA', 163 | :title => '1' , 164 | :data => 'localhost', 165 | } end 166 | it { should_not raise_error } 167 | it { should contain_concat__fragment('db.0.0.127.IN-ADDR.ARPA.1,PTR,0.0.127.IN-ADDR.ARPA.record') 168 | .with_content(/^1\s+IN\s+PTR\s+localhost\.$/) 169 | } 170 | end 171 | 172 | context 'assigning a different host than the resource name' do 173 | let :params do { 174 | :zone => '0.0.127.IN-ADDR.ARPA', 175 | :title => 'foo' , 176 | :host => '1' , 177 | :data => 'localhost', 178 | } end 179 | it { should_not raise_error } 180 | it { should contain_concat__fragment('db.0.0.127.IN-ADDR.ARPA.foo,PTR,0.0.127.IN-ADDR.ARPA.record') 181 | .with_content(/^1\s+IN\s+PTR\s+localhost\.$/) 182 | } 183 | end 184 | 185 | end 186 | 187 | 188 | describe 'dns::record::txt', :type => :define do 189 | let(:facts) { { :concat_basedir => '/tmp' } } 190 | 191 | context 'letting the host be defined by the resource name' do 192 | let :params do { 193 | :zone => 'example.com', 194 | :title => 'foo' , 195 | :data => 'baz', 196 | } end 197 | it { should_not raise_error } 198 | it { should contain_concat__fragment('db.example.com.foo,TXT,example.com.record') 199 | .with_content(/^foo\s+IN\s+TXT\s+"baz"$/) 200 | } 201 | end 202 | 203 | context 'assigning a different host than the resource name' do 204 | let :params do { 205 | :zone => 'example.com', 206 | :title => 'foo' , 207 | :host => 'bar' , 208 | :data => 'baz.example.com', 209 | } end 210 | it { should_not raise_error } 211 | it { should contain_concat__fragment('db.example.com.foo,TXT,example.com.record') 212 | .with_content(/^bar\s+IN\s+TXT\s+"baz"$/) 213 | } 214 | end 215 | 216 | end 217 | 218 | -------------------------------------------------------------------------------- /spec/defines/dns__record__mx_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record::mx', :type => :define do 4 | let(:title) { 'mxtest' } 5 | let(:facts) { { :concat_basedir => '/tmp' } } 6 | 7 | context 'passing an implicit origin' do 8 | let :params do { 9 | :zone => 'example.com', 10 | :data => 'mailserver.example.com' 11 | } end 12 | it { should_not raise_error } 13 | it { should contain_concat__fragment('db.example.com.mxtest,example.com,MX,10,mailserver.example.com.record').with_content(/^@\s+IN\s+MX\s+10\s+mailserver\.example\.com\.$/) } 14 | end 15 | 16 | context 'passing an explicit origin and preference' do 17 | let :params do { 18 | :zone => 'example.com', 19 | :data => 'ittybittymx.example.com', 20 | :host => 'branchoffice', 21 | :preference => 22 22 | } end 23 | it { should_not raise_error } 24 | it { should contain_concat__fragment('db.example.com.mxtest,example.com,MX,22,ittybittymx.example.com.record').with_content(/^branchoffice\s+IN\s+MX\s+22\s+ittybittymx\.example\.com\.$/) } 25 | end 26 | 27 | context 'passing a wrong (out-of-range) preference' do 28 | let :params do { 29 | :zone => 'example.com', 30 | :data => 'badpref.example.com', 31 | :preference => 65537 32 | } end 33 | it { should raise_error(Puppet::Error, /must be an integer within 0-65536/) } 34 | end 35 | 36 | context 'passing a wrong (string) preference' do 37 | let :params do { 38 | :zone => 'example.com', 39 | :data => 'worsepref.example.com', 40 | :preference => 'highest' 41 | } end 42 | it { should raise_error(Puppet::Error, /must be an integer within 0-65536/) } 43 | end 44 | 45 | context 'passing a wrong (numeric top-level domain) zone' do 46 | let :params do { 47 | :zone => 'one.618', 48 | :data => 'goldenratio.example.com' 49 | } end 50 | it { should raise_error(Puppet::Error, /must be a valid domain name/) } 51 | end 52 | 53 | context 'passing a wrong (numeric) zone' do 54 | let :params do { 55 | :zone => 123, 56 | :data => 'badzone.example.com' 57 | } end 58 | it { should raise_error(Puppet::Error, /must be a valid domain name/) } 59 | end 60 | 61 | context 'passing a wrong (IP address) zone' do 62 | let :params do { 63 | :zone => '192.168.1.1', 64 | :data => 'ipaddrzone.example.com' 65 | } end 66 | it { should raise_error(Puppet::Error, /must be a valid domain name/) } 67 | end 68 | 69 | context 'passing wrong (numeric) data' do 70 | let :params do { 71 | :zone => 'example.com', 72 | :data => 456 73 | } end 74 | it { should raise_error(Puppet::Error, /must be a valid hostname/) } 75 | end 76 | 77 | context 'passing wrong (IP address) data' do 78 | let :params do { 79 | :zone => 'example.com', 80 | :data => '192.168.4.4' 81 | } end 82 | it { should raise_error(Puppet::Error, /must be a valid hostname/) } 83 | end 84 | 85 | end 86 | 87 | -------------------------------------------------------------------------------- /spec/defines/dns__record__ns_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record::ns', :type => :define do 4 | let(:title) { 'example.com' } 5 | let(:facts) { { :concat_basedir => '/dne' } } 6 | 7 | context 'passing an implicit host' do 8 | let :params do { 9 | :zone => 'example.com', 10 | :data => 'ns3' 11 | } end 12 | it { should_not raise_error } 13 | it { should contain_concat__fragment('db.example.com.example.com,example.com,NS,ns3.record').with_content(/^example.com\s+IN\s+NS\s+ns3$/) } 14 | end 15 | 16 | context 'passing an explicit host' do 17 | let :params do { 18 | :zone => 'example.com', 19 | :host => 'delegated-zone', 20 | :data => 'ns4.jp.example.net.' 21 | } end 22 | it { should_not raise_error } 23 | it { should contain_concat__fragment('db.example.com.delegated-zone,example.com,NS,ns4.jp.example.net..record').with_content(/^delegated-zone\s+IN\s+NS\s+ns4.jp.example.net\.$/) } 24 | end 25 | 26 | context 'passing a wrong (numeric top-level domain) zone' do 27 | let :params do { 28 | :zone => 'six.022', 29 | :data => 'avogadro.example.com' 30 | } end 31 | it { should raise_error(Puppet::Error, /must be a valid domain name/) } 32 | end 33 | 34 | context 'passing a wrong (numeric) zone' do 35 | let :params do { 36 | :zone => 789, 37 | :data => 'badzone.example.com' 38 | } end 39 | it { should raise_error(Puppet::Error, /must be a valid domain name/) } 40 | end 41 | 42 | context 'passing a wrong (IP address) zone' do 43 | let :params do { 44 | :zone => '192.168.2.1', 45 | :data => 'ipaddrzone.example.com' 46 | } end 47 | it { should raise_error(Puppet::Error, /must be a valid domain name/) } 48 | end 49 | 50 | context 'passing wrong (numeric) data' do 51 | let :params do { 52 | :zone => 'example.com', 53 | :data => 443 54 | } end 55 | it { should raise_error(Puppet::Error, /must be a valid hostname/) } 56 | end 57 | 58 | context 'passing wrong (IP address) data' do 59 | let :params do { 60 | :zone => 'example.com', 61 | :data => '192.168.4.5' 62 | } end 63 | it { should raise_error(Puppet::Error, /must be a valid hostname/) } 64 | end 65 | end 66 | 67 | -------------------------------------------------------------------------------- /spec/defines/dns__record__ptr__by_ip_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record::ptr::by_ip', :type => :define do 4 | let(:title) { '192.0.2.15' } 5 | let(:facts) { { :concat_basedir => '/tmp' } } 6 | 7 | context 'passing a valid host and zone' do 8 | let :params do { 9 | :host => 'test1', 10 | :zone => 'example.com', 11 | } end 12 | it { should_not raise_error } 13 | it { should contain_dns__record__ptr('15.2.0.192.IN-ADDR.ARPA').with({ 14 | 'host' => '15', 15 | 'zone' => '2.0.192.IN-ADDR.ARPA', 16 | 'data' => 'test1.example.com', 17 | }) } 18 | end 19 | 20 | context 'passing a valid host and empty zone' do 21 | let :params do { 22 | :host => 'test2.example.com', 23 | :zone => '', 24 | } end 25 | it { should_not raise_error } 26 | it { should contain_dns__record__ptr('15.2.0.192.IN-ADDR.ARPA').with({ 27 | 'host' => '15', 28 | 'zone' => '2.0.192.IN-ADDR.ARPA', 29 | 'data' => 'test2.example.com', 30 | }) } 31 | end 32 | 33 | context 'passing a valid host but not passing a zone' do 34 | let :params do { 35 | :host => 'test3.example.com', 36 | } end 37 | it { should_not raise_error } 38 | it { should contain_dns__record__ptr('15.2.0.192.IN-ADDR.ARPA').with({ 39 | 'host' => '15', 40 | 'zone' => '2.0.192.IN-ADDR.ARPA', 41 | 'data' => 'test3.example.com', 42 | }) } 43 | end 44 | 45 | context 'passing a host of `@` and a valid zone' do 46 | let :params do { 47 | :host => '@', 48 | :zone => 'example.com', 49 | } end 50 | it { should_not raise_error } 51 | it { should contain_dns__record__ptr('15.2.0.192.IN-ADDR.ARPA').with({ 52 | 'host' => '15', 53 | 'zone' => '2.0.192.IN-ADDR.ARPA', 54 | 'data' => 'example.com', 55 | }) } 56 | end 57 | 58 | context 'passing a host of `@` and an empty zone' do 59 | let :params do { 60 | :host => '@', 61 | :zone => '', 62 | } end 63 | it { should_not raise_error } 64 | it { should contain_dns__record__ptr('15.2.0.192.IN-ADDR.ARPA').with({ 65 | 'host' => '15', 66 | 'zone' => '2.0.192.IN-ADDR.ARPA', 67 | 'data' => '@', 68 | }) } 69 | end 70 | 71 | context 'passing a host of `@` but not passing a zone' do 72 | let :params do { 73 | :host => '@', 74 | } end 75 | it { should_not raise_error } 76 | it { should contain_dns__record__ptr('15.2.0.192.IN-ADDR.ARPA').with({ 77 | 'host' => '15', 78 | 'zone' => '2.0.192.IN-ADDR.ARPA', 79 | 'data' => '@', 80 | }) } 81 | end 82 | 83 | end 84 | 85 | -------------------------------------------------------------------------------- /spec/defines/dns__record__txt_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record::txt', :type => :define do 4 | let(:title) { 'txttest' } 5 | let(:facts) { { :concat_basedir => '/tmp' } } 6 | 7 | context 'passing a simple string should result in a quoted string' do 8 | let :params do { 9 | :host => 'txttest', 10 | :zone => 'example.com', 11 | :data => 'testing', 12 | } end 13 | it { should_not raise_error } 14 | it { should contain_concat__fragment('db.example.com.txttest,TXT,example.com.record').with_content(/^txttest\s+IN\s+TXT\s+"testing"$/) } 15 | end 16 | 17 | context 'passing a string that includes a quote character should result in the dns module escaping the quote' do 18 | let :params do { 19 | :host => 'txttest', 20 | :zone => 'example.com', 21 | :data => 'this is a "test"', 22 | } end 23 | it { should_not raise_error } 24 | it { should contain_concat__fragment('db.example.com.txttest,TXT,example.com.record').with_content(/^txttest\s+IN\s+TXT\s+"this is a \\"test\\""$/) } 25 | end 26 | 27 | context 'passing a long string should result in the dns module splitting that string into multiple quoted strings' do 28 | let :params do { 29 | :host => 'txttest', 30 | :zone => 'example.com', 31 | :data => 'this is a ' + 'very '*60 + 'long test', 32 | } end 33 | it { should_not raise_error } 34 | it { should contain_concat__fragment('db.example.com.txttest,TXT,example.com.record').with_content(/^txttest\s+IN\s+TXT\s+"this is a very.*" ".*very long test"$/) } 35 | end 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /spec/defines/dns__record_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::record', :type => :define do 4 | let(:title) { 'recordtest' } 5 | let(:facts) { { :concat_basedir => '/tmp' } } 6 | 7 | context 'passing a LOC record' do 8 | let :params do { 9 | :zone => 'example.com', 10 | :host => 'saturnv', 11 | :dns_class => 'IN', 12 | :record => 'LOC', 13 | :data => '34 42 40.126 N 86 39 21.248 W 203m 10m 100m 10m', 14 | :ttl => '1h45m10s' 15 | } end 16 | it { should_not raise_error } 17 | it { should contain_concat__fragment('db.example.com.recordtest.record').with_content(/^saturnv\s+1h45m10s\s+IN\s+LOC\s+34 42 40.126 N 86 39 21.248 W 203m 10m 100m 10m$/) } 18 | end 19 | 20 | context 'passing a wrong (out-of-range) TTL' do 21 | let :params do { 22 | :zone => 'example.com', 23 | :host => 'badttl', 24 | :dns_class => 'IN', 25 | :record => 'A', 26 | :data => '172.16.104.1', 27 | :ttl => 2147483648 28 | } end 29 | it { should raise_error(Puppet::Error, /must be an integer within 0-2147483647/) } 30 | end 31 | 32 | context 'passing a wrong (string) TTL' do 33 | let :params do { 34 | :zone => 'example.com', 35 | :host => 'textttl', 36 | :dns_class => 'IN', 37 | :record => 'A', 38 | :data => '172.16.104.2', 39 | :ttl => '4scoreand7years' 40 | } end 41 | it { should raise_error(Puppet::Error, /explicitly specified time units/) } 42 | end 43 | 44 | end 45 | 46 | -------------------------------------------------------------------------------- /spec/defines/dns__server__options_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::server::options', :type => :define do 4 | let :pre_condition do 5 | 'class { "::dns::server": }' 6 | end 7 | 8 | let(:facts) { { :osfamily => 'Debian', :concat_basedir => '/tmp' } } 9 | 10 | let(:title) { '/etc/bind/named.conf.options' } 11 | 12 | context 'passing valid array to forwarders' do 13 | let :params do 14 | { :forwarders => [ '8.8.8.8', '4.4.4.4' ] } 15 | end 16 | it { should contain_file('/etc/bind/named.conf.options') } 17 | it { should contain_file('/etc/bind/named.conf.options').with_content(/8\.8\.8\.8;$/) } 18 | it { should contain_file('/etc/bind/named.conf.options').with_content(/4\.4\.4\.4;$/) } 19 | it { should contain_file('/etc/bind/named.conf.options').with_ensure("present") } 20 | it { should contain_file('/etc/bind/named.conf.options').with_owner("bind") } 21 | it { should contain_file('/etc/bind/named.conf.options').with_group("bind") } 22 | end 23 | 24 | context 'passing valid array to transfers' do 25 | let :params do 26 | { :transfers => ['192.168.0.3', '192.168.0.4'] } 27 | end 28 | it { should contain_file('/etc/bind/named.conf.options') } 29 | it { should contain_file('/etc/bind/named.conf.options').with_content(/192\.168\.0\.3;$/) } 30 | it { should contain_file('/etc/bind/named.conf.options').with_content(/192\.168\.0\.4;$/) } 31 | it { should contain_file('/etc/bind/named.conf.options').with_ensure("present") } 32 | it { should contain_file('/etc/bind/named.conf.options').with_owner("bind") } 33 | it { should contain_file('/etc/bind/named.conf.options').with_group("bind") } 34 | it { should contain_file('/etc/bind/named.conf.options').with_content(/allow-transfer/) } 35 | end 36 | 37 | context 'passing a string to forwarders' do 38 | let :params do 39 | { :forwarders => '8.8.8.8' } 40 | end 41 | it { should raise_error(Puppet::Error, /is not an Array/) } 42 | end 43 | 44 | context 'passing a string to transfers' do 45 | let :params do 46 | { :transfers => '192.168.0.3' } 47 | end 48 | it { should raise_error(Puppet::Error, /is not an Array/) } 49 | end 50 | 51 | context 'passing valid array to listen_on' do 52 | let :params do 53 | { :listen_on => [ '10.11.12.13', '192.168.1.2' ] } 54 | end 55 | it { should contain_file('/etc/bind/named.conf.options').with_content(/10\.11\.12\.13;$/) } 56 | it { should contain_file('/etc/bind/named.conf.options').with_content(/192\.168\.1\.2;$/) } 57 | end 58 | 59 | context 'passing custom port to listen_on_port' do 60 | let :params do 61 | { :listen_on_port => 5300 } 62 | end 63 | it { should contain_file('/etc/bind/named.conf.options').with_content(/port 5300;/) } 64 | end 65 | 66 | context 'passing a string to listen_on' do 67 | let :params do 68 | { :listen_on => '10.9.8.7' } 69 | end 70 | it { should raise_error(Puppet::Error, /is not an Array/) } 71 | end 72 | 73 | context 'when passing valid array to listen_on_ipv6' do 74 | let :params do 75 | { :listen_on_ipv6 => [ '2001:db8:1::1', '2001:db8:2::/124' ] } 76 | end 77 | it { should contain_file('/etc/bind/named.conf.options').with_content(/2001:db8:1::1;$/) } 78 | it { should contain_file('/etc/bind/named.conf.options').with_content(/2001:db8:2::\/124;$/) } 79 | end 80 | 81 | context 'when passing a string to listen_on_ipv6' do 82 | let :params do 83 | { :listen_on_ipv6 => '2001:db8:1::1' } 84 | end 85 | it { should raise_error(Puppet::Error, /is not an Array/) } 86 | end 87 | 88 | context 'when the listen_on_ipv6 option is not provided' do 89 | let(:params) { {} } 90 | it { should contain_file('/etc/bind/named.conf.options').with_content(/listen-on-v6 \{.+?any;.+?\}/) } 91 | end 92 | 93 | context 'passing a string to recursion' do 94 | let :params do 95 | { :allow_recursion => '8.8.8.8' } 96 | end 97 | it { should raise_error(Puppet::Error, /is not an Array/) } 98 | end 99 | 100 | context 'passing a valid recursion allow range' do 101 | let :params do 102 | { :allow_recursion => ['10.0.0.1'] } 103 | end 104 | it { should contain_file('/etc/bind/named.conf.options').with_content(/10\.0\.0\.1;$/) } 105 | it { should contain_file('/etc/bind/named.conf.options').with_content(/allow-recursion \{$/) } 106 | end 107 | 108 | context 'passing a wrong string to slave name' do 109 | let :params do 110 | { :check_names_slave => '8.8.8.8' } 111 | end 112 | it { should raise_error(Puppet::Error, /The check name policy/) } 113 | end 114 | 115 | context 'passing a wrong string to master name' do 116 | let :params do 117 | { :check_names_master => '8.8.8.8' } 118 | end 119 | it { should raise_error(Puppet::Error, /The check name policy/) } 120 | end 121 | 122 | context 'passing a wrong string to response name' do 123 | let :params do 124 | { :check_names_response => '8.8.8.8' } 125 | end 126 | it { should raise_error(Puppet::Error, /The check name policy/) } 127 | end 128 | 129 | context 'passing a valid string to a check name' do 130 | let :params do 131 | { :check_names_master => 'warn', 132 | :check_names_slave => 'ignore', 133 | :check_names_response => 'warn', 134 | } 135 | end 136 | it { should contain_file('/etc/bind/named.conf.options') } 137 | it { should contain_file('/etc/bind/named.conf.options').with_content(/check-names master warn;/) } 138 | it { should contain_file('/etc/bind/named.conf.options').with_content(/check-names slave ignore;$/) } 139 | it { should contain_file('/etc/bind/named.conf.options').with_content(/check-names response warn;$/) } 140 | end 141 | 142 | context 'passing no string to check name' do 143 | it { should contain_file('/etc/bind/named.conf.options').without_content(/check-names master/)} 144 | it { should contain_file('/etc/bind/named.conf.options').without_content(/check-names slave/)} 145 | it { should contain_file('/etc/bind/named.conf.options').without_content(/check-names response/)} 146 | end 147 | 148 | context 'passing a string to the allow query' do 149 | let :params do 150 | { :allow_query => '8.8.8.8' } 151 | end 152 | it { should raise_error(Puppet::Error, /is not an Array/) } 153 | end 154 | 155 | context 'passing a valid array to the allow query' do 156 | let :params do 157 | { :allow_query => ['8.8.8.8'] } 158 | end 159 | it { should contain_file('/etc/bind/named.conf.options').with_content(/8\.8\.8\.8;/) } 160 | it { should contain_file('/etc/bind/named.conf.options').with_content(/allow-query/) } 161 | end 162 | 163 | context 'passing no statistic channel ip' do 164 | let :params do 165 | {} 166 | end 167 | it { should_not contain_file('/etc/bind/named.conf.options').with_content(/statistics-channels/) } 168 | end 169 | 170 | context 'passing a valid ip and a valid port' do 171 | let :params do 172 | { :statistic_channel_ip => '127.0.0.1', 173 | :statistic_channel_port => 12455 } 174 | end 175 | it { should contain_file('/etc/bind/named.conf.options').with_content(/statistics-channels/) } 176 | it { should contain_file('/etc/bind/named.conf.options').with_content(/inet 127\.0\.0\.1 port 12455;/) } 177 | end 178 | 179 | context 'passing no zone_notify setting' do 180 | let :params do 181 | {} 182 | end 183 | it { should contain_file('/etc/bind/named.conf.options').without_content(/^\s*notify /) } 184 | end 185 | 186 | context 'passing a wrong zone_notify setting' do 187 | let :params do 188 | { :zone_notify => 'maybe' } 189 | end 190 | it { should raise_error(Puppet::Error, /The zone_notify/) } 191 | end 192 | 193 | context 'passing yes to zone_notify' do 194 | let :params do 195 | { :zone_notify => 'yes' } 196 | end 197 | it { should contain_file('/etc/bind/named.conf.options').with_content(/^\s*notify yes;/) } 198 | end 199 | 200 | context 'passing no to zone_notify' do 201 | let :params do 202 | { :zone_notify => 'no' } 203 | end 204 | it { should contain_file('/etc/bind/named.conf.options').with_content(/^\s*notify no;/) } 205 | end 206 | 207 | context 'passing master-only to zone_notify' do 208 | let :params do 209 | { :zone_notify => 'master-only' } 210 | end 211 | it { should contain_file('/etc/bind/named.conf.options').with_content(/^\s*notify master-only;/) } 212 | end 213 | 214 | context 'passing explicit to zone_notify' do 215 | let :params do 216 | { :zone_notify => 'explicit' } 217 | end 218 | it { should contain_file('/etc/bind/named.conf.options').with_content(/^\s*notify explicit;/) } 219 | end 220 | 221 | context 'passing no also_notify setting' do 222 | let :params do 223 | {} 224 | end 225 | it { should contain_file('/etc/bind/named.conf.options').without_content(/^\s*also-notify /) } 226 | end 227 | 228 | context 'passing a string to also_notify' do 229 | let :params do 230 | { :also_notify => '8.8.8.8' } 231 | end 232 | it { should raise_error(Puppet::Error, /is not an Array/) } 233 | end 234 | 235 | context 'passing a valid array to also_notify' do 236 | let :params do 237 | { :also_notify => [ '8.8.8.8' ] } 238 | end 239 | it { should contain_file('/etc/bind/named.conf.options').with_content(/^\s*also-notify \{/) } 240 | it { should contain_file('/etc/bind/named.conf.options').with_content(/8\.8\.8\.8;/) } 241 | end 242 | 243 | context 'default value of dnssec_validation on RedHat 5' do 244 | let :facts do 245 | { :osfamily => 'RedHat', :operatingsystemmajrelease => '5', :concat_basedir => '/tmp' } 246 | end 247 | it { should contain_file('/etc/bind/named.conf.options').without_content(/dnssec-validation/) } 248 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable no/) } 249 | end 250 | 251 | context 'default value of dnssec_validation on RedHat 6' do 252 | let :facts do 253 | { :osfamily => 'RedHat', :operatingsystemmajrelease => '6', :concat_basedir => '/tmp' } 254 | end 255 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-validation auto/) } 256 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable yes/) } 257 | end 258 | 259 | context 'default value of dnssec_validation on Debian' do 260 | let :facts do 261 | { :osfamily => 'Debian', :concat_basedir => '/tmp' } 262 | end 263 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-validation auto/) } 264 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable yes/) } 265 | end 266 | 267 | context 'passing `false` to dnssec_enable' do 268 | let :params do 269 | { :dnssec_enable => false} 270 | end 271 | it { should contain_file('/etc/bind/named.conf.options').without_content(/dnssec-validation/) } 272 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable no/) } 273 | end 274 | 275 | context 'passing `absent` to dnssec_validation' do 276 | let :params do 277 | { :dnssec_validation => 'absent' } 278 | end 279 | it { should contain_file('/etc/bind/named.conf.options').without_content(/dnssec-validation/) } 280 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable yes/) } 281 | end 282 | 283 | context 'passing `auto` to dnssec_validation' do 284 | let :params do 285 | { :dnssec_validation => 'auto' } 286 | end 287 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-validation auto/) } 288 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable yes/) } 289 | end 290 | 291 | context 'passing `yes` to dnssec_validation' do 292 | let :params do 293 | { :dnssec_validation => 'yes' } 294 | end 295 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-validation yes/) } 296 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-enable yes/) } 297 | end 298 | 299 | context 'passing `no` to dnssec_validation' do 300 | let :params do 301 | { :dnssec_validation => 'no' } 302 | end 303 | it { should contain_file('/etc/bind/named.conf.options').with_content(/dnssec-validation no/) } 304 | end 305 | context 'with not empty zone generation' do 306 | let :params do 307 | { :no_empty_zones => true } 308 | end 309 | 310 | it { should contain_file('/etc/bind/named.conf.options').with_content(/empty-zones-enable no/) } 311 | 312 | end 313 | 314 | context 'passing no notify_source' do 315 | let :params do 316 | {} 317 | end 318 | it { should_not contain_file('/etc/bind/named.conf.options').with_content(/notify-source/) } 319 | end 320 | 321 | context 'passing notify_source a valid ip' do 322 | let :params do 323 | { :notify_source => '127.0.0.1' } 324 | end 325 | it { should contain_file('/etc/bind/named.conf.options').with_content(/notify-source 127\.0\.0\.1;/) } 326 | end 327 | 328 | context 'passing notify_source an invalid string' do 329 | let :params do 330 | { :notify_source => 'fooberry' } 331 | end 332 | it { should raise_error(Puppet::Error, /is not an ip/) } 333 | end 334 | 335 | context 'passing no transfer_source' do 336 | let :params do 337 | {} 338 | end 339 | it { should_not contain_file('/etc/bind/named.conf.options').with_content(/transfer-source/) } 340 | end 341 | 342 | context 'passing transfer_source a valid ip' do 343 | let :params do 344 | { :transfer_source => '127.0.0.1' } 345 | end 346 | it { should contain_file('/etc/bind/named.conf.options').with_content(/transfer-source 127\.0\.0\.1;/) } 347 | end 348 | 349 | context 'passing transfer_source an invalid string' do 350 | let :params do 351 | { :transfer_source => 'fooberry' } 352 | end 353 | it { should raise_error(Puppet::Error, /is not an ip/) } 354 | end 355 | 356 | context 'passing a non-default data directory' do 357 | let :params do 358 | { :data_dir => '/foo/bar' } 359 | end 360 | it { should contain_file('/etc/bind/named.conf.options').with_content(/directory *"\/foo\/bar"/) } 361 | end 362 | 363 | context 'passing a non-absolute data directory' do 364 | let :params do 365 | { :data_dir => 'foo/bar' } 366 | end 367 | it { should raise_error(Puppet::Error, /is not an absolute/) } 368 | end 369 | 370 | context 'passing a non-default working directory' do 371 | let :params do 372 | { :working_dir => '/foo/bar', 373 | :query_log_enable => true 374 | } 375 | end 376 | it { should contain_file('/etc/bind/named.conf.options').with_content(/\/foo\/bar\/named_querylog/) } 377 | end 378 | 379 | context 'passing a non-absolute working directory' do 380 | let :params do 381 | { :working_dir => 'foo/bar', 382 | :query_log_enable => true 383 | } 384 | end 385 | it { should raise_error(Puppet::Error, /is not an absolute/) } 386 | end 387 | 388 | context 'not passing forward_policy' do 389 | it { should contain_file('/etc/bind/named.conf.options').without_content(/ forward /) } 390 | end 391 | 392 | context 'passing forward_policy as `only`' do 393 | let :params do 394 | { :forward_policy => 'only' } 395 | end 396 | it { should contain_file('/etc/bind/named.conf.options').with_content(/ forward *only *;/) } 397 | end 398 | 399 | context 'passing forward_policy as `first`' do 400 | let :params do 401 | { :forward_policy => 'first' } 402 | end 403 | it { should contain_file('/etc/bind/named.conf.options').with_content(/ forward *first *;/) } 404 | end 405 | 406 | context 'passing forward_policy as an invalid string' do 407 | let :params do 408 | { :forward_policy => 'snozberry' } 409 | end 410 | it { should raise_error(Puppet::Error, /The forward_policy must be/) } 411 | end 412 | 413 | context 'passing forward_policy as an invalid type' do 414 | let :params do 415 | { :forward_policy => ['first'] } 416 | end 417 | it { should raise_error(Puppet::Error, /is not a string/) } 418 | end 419 | 420 | end 421 | 422 | -------------------------------------------------------------------------------- /spec/defines/dns__tsig_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::tsig' do 4 | let(:facts) {{ :osfamily => 'Debian', :concat_basedir => '/mock_dir' }} 5 | let(:title) { 'ns3' } 6 | let :pre_condition do 7 | 'class { "::dns::server::config": }' 8 | end 9 | 10 | context 'passing valid array to server' do 11 | let :params do 12 | { :server => [ '192.168.0.1', '192.168.0.2' ], 13 | :algorithm => 'hmac-md5', 14 | :secret => 'La/E5CjG9O+os1jq0a2jdA==' } 15 | end 16 | it { should_not raise_error } 17 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include') } 18 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include').with_content(/key ns3\. \{/) } 19 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include').with_content(/server 192\.168\.0\.1/) } 20 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include').with_content(/server 192\.168\.0\.2/) } 21 | end 22 | 23 | context 'passing valid string to server' do 24 | let :params do 25 | { :server => '192.168.0.1', 26 | :algorithm => 'hmac-md5', 27 | :secret => 'La/E5CjG9O+os1jq0a2jdA==' } 28 | end 29 | it { should_not raise_error } 30 | 31 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include') } 32 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include').with_content(/key ns3\. \{/) } 33 | it { should contain_concat__fragment('named.conf.local.tsig.ns3.include').with_content(/server 192\.168\.0\.1/) } 34 | end 35 | 36 | end 37 | 38 | -------------------------------------------------------------------------------- /spec/defines/dns__zone_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dns::zone' do 4 | let(:pre_condition) { 'include dns::server::params' } 5 | let(:title) { 'test.com' } 6 | let(:facts) {{ :osfamily => 'Debian', :concat_basedir => '/mock_dir' }} 7 | 8 | describe 'passing something other than an array to $allow_query ' do 9 | let(:params) {{ :allow_query => '127.0.0.1' }} 10 | it { should raise_error(Puppet::Error, /is not an Array/) } 11 | end 12 | 13 | describe 'passing an array to $allow_query' do 14 | let(:params) {{ :allow_query => ['192.0.2.0', '2001:db8::/32'] }} 15 | it { should_not raise_error } 16 | it { 17 | should contain_concat__fragment('named.conf.local.test.com.include'). 18 | with_content(/allow-query/) 19 | } 20 | it { 21 | should contain_concat__fragment('named.conf.local.test.com.include'). 22 | with_content(/192\.0\.2\.0;/) 23 | } 24 | it { 25 | should contain_concat__fragment('named.conf.local.test.com.include'). 26 | with_content(/2001:db8::\/32/) 27 | } 28 | end 29 | 30 | describe 'passing something other than an array to $allow_transfer' do 31 | let(:params) {{ :allow_transfer => '127.0.0.1' }} 32 | it { should raise_error(Puppet::Error, /is not an Array/) } 33 | end 34 | 35 | describe 'passing something other than an array to $allow_forwarder' do 36 | let(:params) {{ :allow_forwarder => '127.0.0.1' }} 37 | it { should raise_error(Puppet::Error, /is not an Array/) } 38 | end 39 | 40 | describe 'passing an array to $allow_transfer and $allow_forwarder' do 41 | let(:params) do { 42 | :allow_transfer => ['192.0.2.0', '2001:db8::/32'], 43 | :allow_forwarder => ['8.8.8.8', '208.67.222.222'] 44 | } 45 | end 46 | it { should_not raise_error } 47 | it { 48 | should contain_concat__fragment('named.conf.local.test.com.include'). 49 | with_content(/allow-transfer/) 50 | } 51 | it { 52 | should contain_concat__fragment('named.conf.local.test.com.include'). 53 | with_content(/192\.0\.2\.0/) 54 | } 55 | it { 56 | should contain_concat__fragment('named.conf.local.test.com.include'). 57 | with_content(/forwarders/) 58 | } 59 | it { 60 | should contain_concat__fragment('named.conf.local.test.com.include'). 61 | with_content(/forward first;/) 62 | } 63 | it { 64 | should contain_concat__fragment('named.conf.local.test.com.include'). 65 | with_content(/8.8.8.8/) 66 | } 67 | it { 68 | should contain_concat__fragment('named.conf.local.test.com.include'). 69 | with_content(/2001:db8::\/32/) 70 | } 71 | it { should contain_concat('/var/lib/bind/zones/db.test.com.stage') } 72 | it { should contain_concat__fragment('db.test.com.soa'). 73 | with_content(/00000000001/) 74 | } 75 | it { should contain_exec('bump-test.com-serial'). 76 | with_refreshonly('true') 77 | } 78 | end 79 | 80 | context 'when ask to have a only forward policy' do 81 | let :params do 82 | { :allow_transfer => [], 83 | :allow_forwarder => ['8.8.8.8', '208.67.222.222'], 84 | :forward_policy => 'only' 85 | } 86 | end 87 | it 'should have a forward only policy' do 88 | should contain_concat__fragment('named.conf.local.test.com.include'). 89 | with_content(/forward only;/) 90 | end 91 | end 92 | 93 | context 'with no explicit forward policy or forwarder' do 94 | let(:params) {{ :allow_transfer => ['192.0.2.0', '2001:db8::/32'] }} 95 | it 'should not have any forwarder configuration' do 96 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 97 | with_content(/forward/) 98 | end 99 | end 100 | 101 | context 'with a delegation-only zone' do 102 | let :params do 103 | { :zone_type => 'delegation-only' 104 | } 105 | end 106 | it 'should only have a type delegation-only entry' do 107 | should contain_concat__fragment('named.conf.local.test.com.include'). 108 | with_content(/zone \"test.com\" \{\s*type delegation-only;\s*\}/) 109 | end 110 | end 111 | 112 | 113 | context 'with a forward zone' do 114 | let :params do 115 | { :allow_transfer => ['123.123.123.123'], 116 | :allow_forwarder => ['8.8.8.8', '208.67.222.222'], 117 | :forward_policy => 'only', 118 | :zone_type => 'forward' 119 | } 120 | end 121 | it 'should have a type forward entry' do 122 | should contain_concat__fragment('named.conf.local.test.com.include'). 123 | with_content(/type forward/) 124 | end 125 | it 'should not have allow_tranfer entry' do 126 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 127 | with_content(/allow-transfer/) 128 | end 129 | it 'should not have file entry' do 130 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 131 | with_content(/file/) 132 | end 133 | it 'should have a forward-policy entry' do 134 | should contain_concat__fragment('named.conf.local.test.com.include'). 135 | with_content(/forward only/) 136 | end 137 | it 'should have a forwarders entry' do 138 | should contain_concat__fragment('named.conf.local.test.com.include'). 139 | with_content(/forwarders/) 140 | end 141 | it 'should have an "absent" zone file concat' do 142 | should contain_concat('/var/lib/bind/zones/db.test.com.stage').with({ 143 | :ensure => "absent" 144 | }) 145 | end 146 | end 147 | 148 | context 'with a slave zone' do 149 | let :params do 150 | { :slave_masters => ['123.123.123.123'], 151 | :zone_type => 'slave' 152 | } 153 | end 154 | it 'should have a type slave entry' do 155 | should contain_concat__fragment('named.conf.local.test.com.include'). 156 | with_content(/type slave/) 157 | end 158 | it 'should have file entry' do 159 | should contain_concat__fragment('named.conf.local.test.com.include'). 160 | with_content(/file/) 161 | end 162 | it 'should have masters entry' do 163 | should contain_concat__fragment('named.conf.local.test.com.include'). 164 | with_content(/masters.*123.123.123.123 *;/) 165 | end 166 | it 'should not have allow_tranfer entry' do 167 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 168 | with_content(/allow-transfer/) 169 | end 170 | it 'should not have any forward information' do 171 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 172 | with_content(/forward/) 173 | end 174 | it 'should have an "absent" zone file concat' do 175 | should contain_concat('/var/lib/bind/zones/db.test.com.stage').with({ 176 | :ensure => "absent" 177 | }) 178 | end 179 | end 180 | 181 | context 'with a slave zone with multiple masters' do 182 | let :params do 183 | { :slave_masters => ['123.123.123.123', '234.234.234.234'], 184 | :zone_type => 'slave' 185 | } 186 | end 187 | it 'should have masters entry with all masters joined by ;' do 188 | should contain_concat__fragment('named.conf.local.test.com.include'). 189 | with_content(/masters.*123.123.123.123 *;[ \n]*234.234.234.234 *;/) 190 | end 191 | end 192 | 193 | context 'with a stub zone' do 194 | let :params do 195 | { :slave_masters => ['123.123.123.123'], 196 | :zone_type => 'stub' 197 | } 198 | end 199 | it 'should have a type stub entry' do 200 | should contain_concat__fragment('named.conf.local.test.com.include'). 201 | with_content(/type stub/) 202 | end 203 | it 'should have file entry' do 204 | should contain_concat__fragment('named.conf.local.test.com.include'). 205 | with_content(/file/) 206 | end 207 | it 'should have masters entry' do 208 | should contain_concat__fragment('named.conf.local.test.com.include'). 209 | with_content(/masters.*123.123.123.123 *;/) 210 | end 211 | it 'should not have allow_tranfer entry' do 212 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 213 | with_content(/allow-transfer/) 214 | end 215 | it 'should not have any forward information' do 216 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 217 | with_content(/forward/) 218 | end 219 | it 'should have an "absent" zone file concat' do 220 | should contain_concat('/var/lib/bind/zones/db.test.com.stage').with({ 221 | :ensure => "absent" 222 | }) 223 | end 224 | end 225 | 226 | context 'with a stub zone with multiple masters' do 227 | let :params do 228 | { :slave_masters => ['123.123.123.123', '234.234.234.234'], 229 | :zone_type => 'stub' 230 | } 231 | end 232 | it 'should have masters entry with all masters joined by ;' do 233 | should contain_concat__fragment('named.conf.local.test.com.include'). 234 | with_content(/masters.*123.123.123.123 *;[ \n]*234.234.234.234 *;/) 235 | end 236 | end 237 | 238 | context 'with a master zone' do 239 | let :params do 240 | { :allow_transfer => ['8.8.8.8','8.8.4.4'], 241 | :allow_forwarder => ['8.8.8.8', '208.67.222.222'], 242 | :forward_policy => 'only', 243 | :zone_type => 'master' 244 | } 245 | end 246 | it 'should have a type master entry' do 247 | should contain_concat__fragment('named.conf.local.test.com.include'). 248 | with_content(/type master/) 249 | end 250 | it 'should have file entry' do 251 | should contain_concat__fragment('named.conf.local.test.com.include'). 252 | with_content(/file/) 253 | end 254 | it 'should not have masters entry' do 255 | should_not contain_concat__fragment('named.conf.local.test.com.include'). 256 | with_content(/masters/) 257 | end 258 | it 'should have allow_tranfer entry' do 259 | should contain_concat__fragment('named.conf.local.test.com.include'). 260 | with_content(/allow-transfer/) 261 | end 262 | it 'should have a forward-policy entry' do 263 | should contain_concat__fragment('named.conf.local.test.com.include'). 264 | with_content(/forward /) 265 | end 266 | it 'should have a forwarders entry' do 267 | should contain_concat__fragment('named.conf.local.test.com.include'). 268 | with_content(/forwarders/) 269 | end 270 | it 'should have a zone file concat' do 271 | should contain_concat('/var/lib/bind/zones/db.test.com.stage').with({ 272 | :ensure => "present" 273 | }) 274 | end 275 | end 276 | 277 | context 'passing no zone_notify setting' do 278 | let :params do 279 | {} 280 | end 281 | it { should contain_concat__fragment('named.conf.local.test.com.include').without_content(/ notify /) } 282 | end 283 | 284 | context 'passing a wrong zone_notify setting' do 285 | let :params do 286 | { :zone_notify => 'maybe' } 287 | end 288 | it { should raise_error(Puppet::Error, /The zone_notify/) } 289 | end 290 | 291 | context 'passing yes to zone_notify' do 292 | let :params do 293 | { :zone_notify => 'yes' } 294 | end 295 | it { should contain_concat__fragment('named.conf.local.test.com.include').with_content(/ notify yes;/) } 296 | end 297 | 298 | context 'passing no to zone_notify' do 299 | let :params do 300 | { :zone_notify => 'no' } 301 | end 302 | it { should contain_concat__fragment('named.conf.local.test.com.include').with_content(/ notify no;/) } 303 | end 304 | 305 | context 'passing master-only to zone_notify' do 306 | let :params do 307 | { :zone_notify => 'master-only' } 308 | end 309 | it { should contain_concat__fragment('named.conf.local.test.com.include').with_content(/ notify master-only;/) } 310 | end 311 | 312 | context 'passing explicit to zone_notify' do 313 | let :params do 314 | { :zone_notify => 'explicit' } 315 | end 316 | it { should contain_concat__fragment('named.conf.local.test.com.include').with_content(/ notify explicit;/) } 317 | end 318 | 319 | context 'passing no also_notify setting' do 320 | let :params do 321 | {} 322 | end 323 | it { should contain_concat__fragment('named.conf.local.test.com.include').without_content(/ also-notify /) } 324 | end 325 | 326 | context 'passing a string to also_notify' do 327 | let :params do 328 | { :also_notify => '8.8.8.8' } 329 | end 330 | it { should raise_error(Puppet::Error, /is not an Array/) } 331 | end 332 | 333 | context 'passing a valid array to also_notify' do 334 | let :params do 335 | { :also_notify => [ '8.8.8.8' ] } 336 | end 337 | it { should contain_concat__fragment('named.conf.local.test.com.include').with_content(/ also-notify \{/) } 338 | it { should contain_concat__fragment('named.conf.local.test.com.include').with_content(/8\.8\.8\.8;/) } 339 | end 340 | 341 | context 'passing true to reverse' do 342 | let(:title) { '10.23.45' } 343 | let :params do 344 | { :reverse => true } 345 | end 346 | it { should contain_concat__fragment('named.conf.local.10.23.45.include').with_content(/zone "10\.23\.45\.in-addr\.arpa"/) } 347 | it { should contain_concat__fragment('db.10.23.45.soa').with_content(/\$ORIGIN\s+10\.23\.45\.in-addr\.arpa\./) } 348 | end 349 | 350 | context 'passing reverse to reverse' do 351 | let(:title) { '10.23.45' } 352 | let :params do 353 | { :reverse => 'reverse' } 354 | end 355 | it { should contain_concat__fragment('named.conf.local.10.23.45.include').with_content(/zone "45\.23\.10\.in-addr\.arpa"/) } 356 | it { should contain_concat__fragment('db.10.23.45.soa').with_content(/\$ORIGIN\s+45\.23\.10\.in-addr\.arpa\./) } 357 | end 358 | 359 | describe 'passing something other than an array to $allow_update ' do 360 | let(:params) {{ :allow_update => '127.0.0.1' }} 361 | it { should raise_error(Puppet::Error, /is not an Array/) } 362 | end 363 | 364 | describe 'passing an empty array to $allow_update' do 365 | let(:params) {{ :allow_update => [] }} 366 | it { should_not raise_error } 367 | it { 368 | should contain_concat('/var/lib/bind/zones/db.test.com.stage'). 369 | with({ :replace => true }) 370 | } 371 | end 372 | 373 | describe 'passing an array to $allow_update' do 374 | let(:params) {{ :allow_update => ['192.0.2.0', '2001:db8::/32'] }} 375 | it { should_not raise_error } 376 | it { 377 | should contain_concat('/var/lib/bind/zones/db.test.com.stage'). 378 | with({ :replace => false }) 379 | } 380 | it { 381 | should contain_concat__fragment('named.conf.local.test.com.include'). 382 | with_content(/allow-update/) 383 | } 384 | it { 385 | should contain_concat__fragment('named.conf.local.test.com.include'). 386 | with_content(/192\.0\.2\.0;/) 387 | } 388 | it { 389 | should contain_concat__fragment('named.conf.local.test.com.include'). 390 | with_content(/2001:db8::\/32/) 391 | } 392 | end 393 | end 394 | -------------------------------------------------------------------------------- /spec/fixtures/manifests/init.pp: -------------------------------------------------------------------------------- 1 | node 'testhost.example.com' { 2 | 3 | include dns::server 4 | 5 | dns::zone { 'example.com': 6 | soa => 'ns1.example.com', 7 | soa_email => 'admin.example.com', 8 | nameservers => [ 'ns1.example.com', ] 9 | } 10 | dns::zone { '1.168.192.IN-ADDR.ARPA': 11 | soa => 'ns1.example.com', 12 | soa_email => 'admin.example.com', 13 | nameservers => [ 'ns1.example.com', ] 14 | } 15 | dns::record::a { 'ns1': 16 | zone => 'example.com', 17 | data => [ '192.168.1.1', ], 18 | ptr => true; 19 | } 20 | } 21 | 22 | node default {} -------------------------------------------------------------------------------- /spec/hosts/example_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'testhost.example.com' do 4 | 5 | let(:facts) {{ :osfamily => 'RedHat', :concat_basedir => '/dne', :define_fact => "" }} 6 | 7 | context 'When given connected records that depend on each other' do 8 | it { should compile } 9 | end 10 | 11 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'puppetlabs_spec_helper/module_spec_helper' 2 | 3 | RSpec.configure do |c| 4 | c.before do 5 | # avoid "Only root can execute commands as other users" 6 | Puppet.features.stubs(:root? => true) 7 | end 8 | end -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | require 'beaker-rspec/spec_helper' 2 | require 'beaker-rspec/helpers/serverspec' 3 | require 'pry' 4 | 5 | unless ENV['BEAKER_provision'] == 'no' 6 | hosts.each do |host| 7 | # Install Puppet 8 | if host.is_pe? 9 | install_pe 10 | else 11 | install_puppet 12 | end 13 | end 14 | end 15 | 16 | RSpec.configure do |c| 17 | # Project root 18 | proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) 19 | 20 | # Readable test descriptions 21 | c.formatter = :documentation 22 | 23 | # Configure all nodes in nodeset 24 | c.before :suite do 25 | # Install module and dependencies 26 | puppet_module_install(:source => proj_root, :module_name => 'dns') 27 | hosts.each do |host| 28 | on host, puppet('module', 'install', 'puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } 29 | on host, puppet('module', 'install', 'puppetlabs-concat'), { :acceptable_exit_codes => [0,1] } 30 | end 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /templates/acl.erb: -------------------------------------------------------------------------------- 1 | acl <%= @aclname %> { 2 | <% @data.each do |ip| -%> 3 | <%= ip %>; 4 | <% end -%> 5 | }; 6 | -------------------------------------------------------------------------------- /templates/default.debian.erb: -------------------------------------------------------------------------------- 1 | # run resolvconf? 2 | <% if @resolvconf -%> 3 | RESOLVCONF=<%= @resolvconf %> 4 | <% else %> 5 | RESOLVCONF=no 6 | <% end -%> 7 | 8 | # startup options for the server 9 | <% if @options -%> 10 | OPTIONS="<%= @options %>" 11 | <% else %> 12 | OPTIONS="-u bind" 13 | <% end -%> 14 | -------------------------------------------------------------------------------- /templates/default.redhat.erb: -------------------------------------------------------------------------------- 1 | # BIND named process options 2 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | # Currently, you can use the following options: 4 | # 5 | # ROOTDIR="/some/where" -- will run named in a chroot environment. 6 | # you must set up the chroot environment 7 | # (install the bind-chroot package) before 8 | # doing this. 9 | # 10 | # OPTIONS="whatever" -- These additional options will be passed to named 11 | # at startup. Don't add -t here, use ROOTDIR instead. 12 | # 13 | # ENABLE_ZONE_WRITE=yes -- If SELinux is disabled, then allow named to write 14 | # its zone files and create files in its $ROOTDIR/var/named 15 | # directory, necessary for DDNS and slave zone transfers. 16 | # Slave zones should reside in the $ROOTDIR/var/named/slaves 17 | # directory, in which case you would not need to enable zone 18 | # writes. If SELinux is enabled, you must use only the 19 | # 'named_write_master_zones' variable to enable zone writes. 20 | # 21 | # ENABLE_SDB=yes -- This enables use of 'named_sdb', which has support 22 | # -- for the ldap, pgsql and dir zone database backends 23 | # -- compiled in, to be used instead of named. 24 | # 25 | # DISABLE_NAMED_DBUS=[1y]-- If NetworkManager is enabled in any runlevel, then 26 | # the initscript will by default enable named's D-BUS 27 | # support with the named -D option. This setting disables 28 | # this behavior. 29 | # 30 | # KEYTAB_FILE="/dir/file" -- Specify named service keytab file (for GSS-TSIG) 31 | # 32 | # DISABLE_ZONE_CHECKING -- By default, initscript calls named-checkzone 33 | # utility for every zone to ensure all zones are 34 | # valid before named starts. If you set this option 35 | # to 'yes' then initscript doesn't perform those checks. 36 | <% if @rootdir -%> 37 | ROOTDIR="<%= @rootdir %>" 38 | <% end -%> 39 | 40 | <% if @options -%> 41 | OPTIONS="<%= @options %>" 42 | <% end -%> 43 | 44 | <% if @enable_zone_write -%> 45 | ENABLE_ZONE_WRITE=<%= @enable_zone_write %> 46 | <% end -%> 47 | 48 | <% if @enable_sdb -%> 49 | ENABLE_SDB=<%= @enable_sdb %> 50 | <% end -%> 51 | 52 | <% if @disable_named_dbus -%> 53 | DISABLE_NAMED_DBUS=<%= @disable_named_dbus %> 54 | <% end -%> 55 | 56 | <% if @keytab_file -%> 57 | KEYTAB_FILE="<%= @keytab_file %>" 58 | <% end -%> 59 | 60 | <% if @disable_zone_checking -%> 61 | DISABLE_ZONE_CHECKING=<%= @disable_zone_checking %> 62 | <% end -%> 63 | -------------------------------------------------------------------------------- /templates/key.erb: -------------------------------------------------------------------------------- 1 | key "<%= @name %>" { 2 | algorithm hmac-md5; 3 | -------------------------------------------------------------------------------- /templates/named.conf.default-zones.erb: -------------------------------------------------------------------------------- 1 | // File managed with puppet 2 | // prime the server with knowledge of the root servers 3 | zone "." { 4 | type hint; 5 | file "<%= @cfg_dir %>/db.root"; 6 | }; 7 | 8 | // be authoritative for the localhost forward and reverse zones, and for 9 | // broadcast zones as per RFC 1912 10 | 11 | zone "localhost" { 12 | type master; 13 | file "<%= @cfg_dir %>/db.local"; 14 | }; 15 | 16 | zone "127.in-addr.arpa" { 17 | type master; 18 | file "<%= @cfg_dir %>/db.127"; 19 | }; 20 | 21 | zone "0.in-addr.arpa" { 22 | type master; 23 | file "<%= @cfg_dir %>/db.0"; 24 | }; 25 | 26 | zone "255.in-addr.arpa" { 27 | type master; 28 | file "<%= @cfg_dir %>/db.255"; 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /templates/named.conf.erb: -------------------------------------------------------------------------------- 1 | // File managed by puppet. 2 | // 3 | 4 | include "<%= @cfg_dir %>/named.conf.options"; 5 | <%- if @enable_default_zones -%> 6 | include "<%= @rfc1912_zones_cfg %>"; 7 | <%- end -%> 8 | include "<%= @rndc_key_file %>"; 9 | include "<%= @cfg_dir %>/named.conf.local"; 10 | -------------------------------------------------------------------------------- /templates/named.conf.options.erb: -------------------------------------------------------------------------------- 1 | options { 2 | directory "<%= @data_dir %>"; 3 | 4 | // If there is a firewall between you and nameservers you want 5 | // to talk to, you may need to fix the firewall to allow multiple 6 | // ports to talk. See http://www.kb.cert.org/vuls/id/800113 7 | 8 | // If your ISP provided one or more IP addresses for stable. 9 | // nameservers, you probably want to use them as forwarders... 10 | // Uncomment the following block, and insert the addresses replacing. 11 | // the all-0's placeholder. 12 | 13 | <% if @forwarders.size == 0 then -%> 14 | // forwarders { 15 | // 0.0.0.0; 16 | // }; 17 | <% else -%> 18 | forwarders { 19 | <%- @forwarders.each do |forwarder| -%> 20 | <%= forwarder -%>; 21 | <%- end -%> 22 | }; 23 | 24 | <% end -%> 25 | 26 | <% if @forward_policy then -%> 27 | forward <%= @forward_policy %>; 28 | <% end -%> 29 | 30 | <% if @transfers.size != 0 then -%> 31 | allow-transfer { 32 | <%- @transfers.each do |transfer| -%> 33 | <%= transfer -%>; 34 | <%- end -%> 35 | }; 36 | 37 | <% end -%> 38 | <% if @listen_on.size == 0 then -%> 39 | listen-on { any; }; 40 | 41 | <% else -%> 42 | listen-on { 43 | <%- @listen_on.each do |ipv4_addr| -%> 44 | <%= ipv4_addr -%>; 45 | <%- end -%> 46 | }; 47 | 48 | <% end -%> 49 | <% if @listen_on_ipv6.empty? then -%> 50 | listen-on-v6 { any; }; 51 | 52 | <% else -%> 53 | listen-on-v6 { 54 | <%- @listen_on_ipv6.each do |ipv6_addr| -%> 55 | <%= ipv6_addr -%>; 56 | <%- end -%> 57 | }; 58 | 59 | <% end -%> 60 | <% if @listen_on_port -%> 61 | port <%= @listen_on_port %>; 62 | 63 | <% end -%> 64 | <% if @allow_recursion.size != 0 then -%> 65 | allow-recursion { 66 | <%- @allow_recursion.each do |recursion| -%> 67 | <%= recursion -%>; 68 | <%- end -%> 69 | }; 70 | 71 | <% end -%> 72 | <% if @allow_query.size != 0 then -%> 73 | allow-query { 74 | <%- @allow_query.each do |query| -%> 75 | <%= query -%>; 76 | <%- end -%> 77 | }; 78 | 79 | <% end -%> 80 | <% if @check_names_master -%> 81 | check-names master <%= @check_names_master %>; 82 | 83 | <% end -%> 84 | <% if @check_names_slave -%> 85 | check-names slave <%= @check_names_slave %>; 86 | 87 | <% end -%> 88 | <% if @check_names_response -%> 89 | check-names response <%= @check_names_response %>; 90 | 91 | <% end -%> 92 | <% if @zone_notify -%> 93 | notify <%= @zone_notify %>; 94 | <% end -%> 95 | <% if @also_notify.size != 0 then -%> 96 | also-notify { 97 | <%- @also_notify.each do |ip| -%> 98 | <%= ip -%>; 99 | <%- end -%> 100 | }; 101 | 102 | <% end -%> 103 | <% if @no_empty_zones -%> 104 | empty-zones-enable no; 105 | 106 | <% end -%> 107 | <% if @notify_source -%> 108 | notify-source <%= @notify_source %>; 109 | 110 | <% end -%> 111 | <% if @transfer_source -%> 112 | transfer-source <%= @transfer_source %>; 113 | 114 | <% end -%> 115 | //======================================================================== 116 | // If BIND logs error messages about the root key being expired, 117 | // you will need to update your keys. See https://www.isc.org/bind-keys 118 | //======================================================================== 119 | <% if @dnssec_enable -%> 120 | dnssec-enable yes; 121 | <%- if @dnssec_validation != 'absent' -%> 122 | dnssec-validation <%= @dnssec_validation %>; 123 | <%- end -%> 124 | <% else -%> 125 | dnssec-enable no; 126 | <% end -%> 127 | auth-nxdomain no; # conform to RFC1035 128 | 129 | <%- @extra_options.each do |k, v| -%> 130 | <%- if v.is_a?(Array) -%> 131 | <%= k %> { 132 | <%- v.each do |value| -%> 133 | <%= value %>; 134 | <%- end -%> 135 | }; 136 | <%- else -%> 137 | <%= k %> <%= v %>; 138 | <%- end -%> 139 | <%- end -%> 140 | }; 141 | 142 | <% if @statistic_channel_ip and @statistic_channel_port -%> 143 | statistics-channels { 144 | inet <%= @statistic_channel_ip %> port <%= @statistic_channel_port %><% if @statistic_channel_allow -%> allow { <%= @statistic_channel_allow.join('; ') %>; }<% end -%>; 145 | }; 146 | <% end -%> 147 | 148 | <% if @control_channel_ip and @control_channel_port -%> 149 | controls { 150 | inet <%= @control_channel_ip %> port <%= @control_channel_port %><% if @control_channel_allow -%> allow { <%= @control_channel_allow.join('; ') %>; }<% end -%>; 151 | }; 152 | <% end -%> 153 | 154 | <% if @query_log_enable -%> 155 | logging { 156 | 157 | channel query_logging { 158 | file "<%= @working_dir %>/named_querylog" 159 | versions 10 size 100M; 160 | print-time yes; 161 | severity debug 1; 162 | }; 163 | category queries { 164 | query_logging; 165 | }; 166 | 167 | channel syslog_errors { 168 | syslog user; 169 | severity error; 170 | }; 171 | category default { syslog_errors; }; 172 | category lame-servers { null; }; 173 | }; 174 | <%- elsif !@log_channels.empty? or !@log_categories.empty? -%> 175 | logging { 176 | <%- @log_channels.each do |channel, options| -%> 177 | channel <%= channel %> { 178 | <%- options.each do |o| -%> 179 | <%= o %>; 180 | <%- end -%> 181 | }; 182 | <%- end -%> 183 | 184 | <%- @log_categories.each do |category, options| -%> 185 | category <%= category %> { 186 | <%- options.each do |o| -%> 187 | <%= o %>; 188 | <%- end -%> 189 | }; 190 | <%- end -%> 191 | }; 192 | <% end -%> 193 | -------------------------------------------------------------------------------- /templates/secret.erb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SECRET=`cat <%= @cfg_dir %>/bind.keys.d/K<%= @name %>.+*+*.key |tr -s " "|cut -d " " -f7` 3 | 4 | cat < <%= @cfg_dir %>/bind.keys.d/<%= @name %>.secret 5 | secret "$SECRET"; 6 | EOF 7 | -------------------------------------------------------------------------------- /templates/tsig.erb: -------------------------------------------------------------------------------- 1 | key <%= @keyname %>. { 2 | algorithm <%= @algorithm %>; 3 | secret "<%= @secret %>"; 4 | }; 5 | <% if @server.class == Array -%> 6 | <% @server.each do |host| -%> 7 | server <%= host -%> { 8 | keys { <%= @keyname %>. ; }; 9 | }; 10 | <% end -%> 11 | <% else -%> 12 | <% if @server then -%> 13 | server <%= @server %> { 14 | keys { <%= @keyname %>. ; }; 15 | }; 16 | <% end -%> 17 | <% end -%> 18 | -------------------------------------------------------------------------------- /templates/view.erb: -------------------------------------------------------------------------------- 1 | // File managed by puppet 2 | 3 | view <%= @viewname %> { 4 | <%- if !@match_clients.empty? -%> 5 | match-clients { 6 | <%= @match_clients.join(";\n ") %>; 7 | }; 8 | <%- end %> 9 | <%- if !@match_destinations.empty? -%> 10 | match-destinations { 11 | <%= @match_destinations.join(";\n ") %>; 12 | }; 13 | <%- end %> 14 | <%- if @match_recursive_only -%> 15 | match-recursive-only <%= @match_recursive_only %>; 16 | <%- end -%> 17 | <%- if !@options.empty? -%> 18 | <%- @options.each do |k, v| -%> 19 | <%- if v.respond_to?('join') -%> 20 | <%= k %> { 21 | <%= v.join(";\n ")%>; 22 | }; 23 | <%- else -%> 24 | <%= k %> <%= v %>; 25 | <%- end -%> 26 | <%- end -%> 27 | <%- end -%> 28 | 29 | <%- if @enable_default_zones -%> 30 | include "<%= @rfc1912_zones_cfg %>"; 31 | <%- end -%> 32 | -------------------------------------------------------------------------------- /templates/zone.erb: -------------------------------------------------------------------------------- 1 | zone "<%= @zone %>" { 2 | type <%= @zone_type %>; 3 | <% if @zone_notify -%> 4 | notify <%= @zone_notify %>; 5 | <% end -%> 6 | <% if @also_notify.size != 0 -%> 7 | also-notify { 8 | <%- @also_notify.each do |ip| -%> 9 | <%= ip %>; 10 | <%- end -%> 11 | }; 12 | <% end -%> 13 | <% if (@zone_type != 'forward') && (@zone_type != 'delegation-only')-%> 14 | file "<%= @zone_file %>"; 15 | <% end -%> 16 | <% if @zone_type == 'slave' or @zone_type == 'stub' -%> 17 | <%- if @slave_masters.is_a?(Array) -%> 18 | masters { <%= @slave_masters.join(";") %>;}; 19 | <%- else -%> 20 | masters { <%= @slave_masters %>;}; 21 | <%- end -%> 22 | <% elsif @zone_type == 'master' -%> 23 | <%- if @allow_transfer.is_a?(Array) and @allow_transfer.size != 0 -%> 24 | allow-transfer { 25 | <%- @allow_transfer.each do |ip| -%> 26 | <%= ip %>; 27 | <%- end -%> 28 | }; 29 | <%- end -%> 30 | <% end -%> 31 | <% if !@allow_forwarder.empty? and ( @zone_type == 'master' or @zone_type == 'forward') -%> 32 | forward <%= @forward_policy %>; 33 | forwarders { 34 | <%- @allow_forwarder.each do |ip| -%> 35 | <%= ip %>; 36 | <%- end -%> 37 | }; 38 | <% end -%> 39 | <%- if @allow_query.size != 0 %> 40 | allow-query { 41 | <%- @allow_query.each do |ip| -%> 42 | <%= ip %>; 43 | <%- end -%> 44 | }; 45 | <%- end -%> 46 | <%- if @allow_update.size != 0 %> 47 | allow-update { 48 | <%- @allow_update.each do |ip| -%> 49 | <%= ip %>; 50 | <%- end -%> 51 | }; 52 | <%- end -%> 53 | }; 54 | -------------------------------------------------------------------------------- /templates/zone_file.erb: -------------------------------------------------------------------------------- 1 | ; 2 | ; BIND data file for <%= @zone %> zone. 3 | ; File managed by puppet. 4 | ; 5 | $ORIGIN <%= @zone %>. 6 | $TTL <%= @zone_ttl %> 7 | @ IN SOA <%= @soa %>. <%= @soa_email %>. ( 8 | 00000000001 ; Serial<%# Be careful at change of number of this line. It is used in zone.pp/Exec[bump-${zone}-serial]. %> 9 | <%= @zone_refresh %> ; Refresh 10 | <%= @zone_retry %> ; Retry 11 | <%= @zone_expire %> ; Expire 12 | <%= @zone_minimum %> ) ; Negative Cache TTL 13 | ; 14 | <% @nameservers.each do |nameserver| -%> 15 | @ IN NS <%= nameserver -%>. 16 | <% end -%> 17 | ; 18 | -------------------------------------------------------------------------------- /templates/zone_record.erb: -------------------------------------------------------------------------------- 1 | <% 2 | data_a = @data.is_a?(String) ? @data.lines.to_a : @data 3 | 4 | data_a.each do |data_item| 5 | if %w[TXT SPF].include?(@record.upcase) 6 | # DNS TXT and SPF records have specific formatting requirements. 7 | # 8 | # 1. per RFC1035 section 3.3.14, they are composed of one or more 9 | # character strings. 10 | # 11 | # 2. per RFC1035 section 3.3, each character string can be up to 255 12 | # characters (section 3.3 indicates 256 octets, but includes the length 13 | # octet as part of that count). 14 | # 15 | # 3. per common usage, if a TXT record consists of multiple character 16 | # strings, the application processing that record concatenates the 17 | # character strings together with no intervening characters. 18 | # 19 | # 4. per RFC1035 section 5, if a character string includes spaces, 20 | # it must be enclosed in double-quotes ("). Within the double-quotes, 21 | # any character can occur except another double-quote; double-quotes 22 | # must be escaped by backslashes (\) (and by implication, backslashes 23 | # must also be escaped by backslashes). 24 | # 25 | # Based on these requirements, TXT and SPF records data items need 26 | # to be processed in this manner: 27 | # 28 | # 1. split the data_item into 255 (at most) character chunks. 29 | # (via `unpack` with a format string consisting of one 'a255' 30 | # for every 255 characters in the data item, rounded up) 31 | # 32 | # 2. escape all double-quotes and backslashes within each chunk by 33 | # inserting a backslash before the escaped character. 34 | # (via a `gsub` within a `collect` over the array of chunks) 35 | # 36 | # 3. surround each chunk with double-quotes. 37 | # (within the same `collect` over the array of chunks) 38 | # 39 | # 4. join the chunks back together with a space between each chunk. 40 | # (via `join(' ')`) 41 | # 42 | data_item = data_item.unpack('a255' * ((data_item.length / 255.0).ceil())).collect { |x| '"' + x.gsub(/([\\"])/, '\\\\\\1') + '"' }.join(' ') 43 | end 44 | -%> 45 | <%= @host %> <%= @ttl %> <%= @dns_class %> <%= @record.upcase %><%= @record.upcase == 'MX' ? " #{@preference}" : '' %> <%= data_item %> 46 | <% end -%> 47 | -------------------------------------------------------------------------------- /tests/init.pp: -------------------------------------------------------------------------------- 1 | # Smoketest. 2 | 3 | include dns::server 4 | 5 | dns::server::options { "${dns::server::params::cfg_dir}/named.conf.options": 6 | forwarders => [ '8.8.8.8', '8.8.4.4' ] 7 | } 8 | 9 | dns::zone { 'example.com': 10 | soa => 'ns1.example.com', 11 | soa_email => 'admin.example.com', 12 | nameservers => [ 'ns1' ], 13 | allow_transfer => [ '192.0.2.0', '2001:db8::/32' ], 14 | allow_query => [ '192.168.0.0/16' ], 15 | } 16 | 17 | dns::zone { '56.168.192.IN-ADDR.ARPA': 18 | soa => 'ns1.example.com', 19 | soa_email => 'admin.example.com', 20 | nameservers => [ 'ns1' ], 21 | } 22 | 23 | dns::record::a { 'ns1': 24 | zone => 'example.com', 25 | data => [ '192.168.56.10' ], 26 | ptr => true, 27 | } 28 | 29 | dns::record::ns { 'example.com': 30 | zone => 'example.com', 31 | data => 'ns3'; 32 | } 33 | 34 | dns::acl { 'trusted': 35 | data => [ '192.168.10.0/24', '172.16.0.0/24' ], 36 | } 37 | --------------------------------------------------------------------------------