diff --git a/.fixtures.yml b/.fixtures.yml
index 84d77b2..76c7fdb 100644
--- a/.fixtures.yml
+++ b/.fixtures.yml
@@ -2,3 +2,4 @@ fixtures:
repositories:
concat: "https://github.com/puppetlabs/puppetlabs-concat.git"
stdlib: "https://github.com/puppetlabs/puppetlabs-stdlib.git"
+ systemd: "https://github.com/voxpupuli/puppet-systemd.git"
diff --git a/REFERENCE.md b/REFERENCE.md
index 3381041..2b17ec9 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -6,7 +6,7 @@
### Classes
-* [`unbound`](#unbound): Class: unbound Installs and configures Unbound, the caching DNS resolver from NLnet Labs
+* [`unbound`](#unbound): Installs and configures Unbound, the caching DNS resolver from NLnet Labs
* [`unbound::remote`](#unbound--remote): Class: unbound::remote Configure remote control of the unbound daemon process === Parameters: [*enable*] (optional) The option is used t
### Defined types
@@ -36,8 +36,6 @@
### `unbound`
-Class: unbound
-
Installs and configures Unbound, the caching DNS resolver from NLnet Labs
#### Parameters
@@ -47,6 +45,7 @@ The following parameters are available in the `unbound` class:
* [`hints_file`](#-unbound--hints_file)
* [`hints_file_content`](#-unbound--hints_file_content)
* [`unbound_version`](#-unbound--unbound_version)
+* [`update_root_hints`](#-unbound--update_root_hints)
* [`manage_service`](#-unbound--manage_service)
* [`verbosity`](#-unbound--verbosity)
* [`statistics_interval`](#-unbound--statistics_interval)
@@ -274,6 +273,14 @@ the version of the installed unbound instance. defaults to the fact, but you can
Default value: `$facts['unbound_version']`
+##### `update_root_hints`
+
+Data type: `Enum['absent','present','unmanaged']`
+
+If set to true (and hints_file isn't set to 'builtin') a systemd timer will be configured to update the root hints file every month
+
+Default value: `fact('systemd') ? { true => 'present', default => 'unmanaged'`
+
##### `manage_service`
Data type: `Boolean`
diff --git a/files/roothints.timer b/files/roothints.timer
new file mode 100644
index 0000000..5ea68f6
--- /dev/null
+++ b/files/roothints.timer
@@ -0,0 +1,11 @@
+# THIS FILE IS MANAGED BY PUPPET
+# BASED ON https://wiki.archlinux.org/title/Unbound#Roothints_systemd_timer
+[Unit]
+Description=Run root.hints monthly
+
+[Timer]
+OnCalendar=monthly
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/manifests/init.pp b/manifests/init.pp
index edce29e..5280ae6 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -1,6 +1,5 @@
-# Class: unbound
#
-# Installs and configures Unbound, the caching DNS resolver from NLnet Labs
+# @summary Installs and configures Unbound, the caching DNS resolver from NLnet Labs
#
# @param hints_file
# File path to the root-hints. Set to 'builtin' to remove root-hint option from unbound.conf and use built-in hints.
@@ -8,6 +7,9 @@
# Contents of the root hints file, if it's not remotely fetched.
# @param unbound_version
# the version of the installed unbound instance. defaults to the fact, but you can overwrite it. this reduces the initial puppet runs from two to one
+# @param update_root_hints
+# If set to true (and hints_file isn't set to 'builtin') a systemd timer will be configured to update the root hints file every month
+#
class unbound (
Boolean $manage_service = true,
Integer[0,5] $verbosity = 1,
@@ -135,7 +137,7 @@
Optional[Integer] $key_cache_slabs = undef,
Optional[Unbound::Size] $neg_cache_size = undef,
Boolean $unblock_lan_zones = false,
- Boolean $insecure_lan_zones = false, # version 1.5.8
+ Boolean $insecure_lan_zones = false, # version 1.5.8
Unbound::Local_zone $local_zone = {},
Array[String[1]] $local_data = [],
Array[String[1]] $local_data_ptr = [],
@@ -212,6 +214,7 @@
Integer[1] $redis_timeout = 100,
Stdlib::Absolutepath $unbound_conf_d = "${confdir}/unbound.conf.d",
Unbound::Hints_file $hints_file = "${confdir}/root.hints",
+ Enum['absent','present','unmanaged'] $update_root_hints = fact('systemd') ? { true => 'present', default => 'unmanaged' },
Optional[String[1]] $hints_file_content = undef,
Hash[String[1], Unbound::Rpz] $rpzs = {},
Optional[String[1]] $unbound_version = $facts['unbound_version'],
@@ -316,6 +319,19 @@
mode => '0444',
content => $hints_file_content,
}
+ if $update_root_hints == 'present' {
+ systemd::timer { 'roothints.timer':
+ timer_content => file("${module_name}/roothints.timer"),
+ service_content => epp("${module_name}/roothints.service.epp", { 'hints_file' => $hints_file, 'root_hints_url' => $root_hints_url, 'fetch_client' => $fetch_client }),
+ active => true,
+ enable => true,
+ }
+ }
+ }
+ if $update_root_hints == 'absent' {
+ systemd::timer { 'roothints.timer':
+ ensure => 'absent',
+ }
}
# purge unmanaged files in configuration directory
diff --git a/metadata.json b/metadata.json
index a58fde6..ab4383b 100644
--- a/metadata.json
+++ b/metadata.json
@@ -113,6 +113,10 @@
{
"name": "puppetlabs/stdlib",
"version_requirement": ">= 4.25.0 < 10.0.0"
+ },
+ {
+ "name": "puppet/systemd",
+ "version_requirement": ">= 6.3.0 < 7.0.0"
}
]
}
diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb
index d6eea4d..ef169ec 100644
--- a/spec/classes/init_spec.rb
+++ b/spec/classes/init_spec.rb
@@ -18,15 +18,6 @@
pidfile = nil
- if facts.dig(:os, 'family').nil?
- if facts[:osfamily]
- puts "Skipping tests on on platform #{facts[:osfamily]} due to missing facts[:os][:family]"
- else
- puts "Skipping tests on on platform #{facts[:kernel]} due to missing facts[:os][:family]"
- end
- next
- end
-
case facts[:os]['family']
when 'Debian'
pidfile = '/run/unbound.pid'
@@ -67,6 +58,10 @@
it { is_expected.to contain_file(keys_d_dir) }
it { is_expected.to contain_file(hints_file) }
+ context 'on Linux', if: facts[:kernel] == 'Linux' do
+ it { is_expected.to contain_systemd__timer('roothints.timer') }
+ end
+
it do
expect(subject).to contain_file(unbound_conf_d).with(
'ensure' => 'directory',
@@ -1038,41 +1033,89 @@
end
end
- context 'no root hints in config' do
- let(:params) do
- {
- hints_file: 'builtin'
- }
+ context 'roothints' do
+ context 'no root hints in config' do
+ let(:params) do
+ {
+ hints_file: 'builtin'
+ }
+ end
+
+ it do
+ expect(subject).to contain_concat__fragment(
+ 'unbound-header'
+ ).without_content(%r{root-hints})
+ end
+
+ it { is_expected.not_to contain_systemd__timer('roothints.timer') }
end
- it do
- expect(subject).to contain_concat__fragment(
- 'unbound-header'
- ).without_content(%r{root-hints})
+ context 'no root hints in config and update_root_hints=unmanaged' do
+ let(:params) do
+ {
+ hints_file: 'builtin',
+ update_root_hints: 'unmanaged'
+ }
+ end
+
+ it do
+ expect(subject).to contain_concat__fragment(
+ 'unbound-header'
+ ).without_content(%r{root-hints})
+ end
+
+ it { is_expected.not_to contain_systemd__timer('roothints.timer') }
end
- end
- context 'hieradata root hints' do
- let(:params) do
- {
- skip_roothints_download: true,
- hints_file_content: File.read('spec/classes/expected/hieradata-root-hint.conf'),
- }
+ context 'no root hints in config and update_root_hints=absent' do
+ let(:params) do
+ {
+ hints_file: 'builtin',
+ update_root_hints: 'absent'
+ }
+ end
+
+ it do
+ expect(subject).to contain_concat__fragment(
+ 'unbound-header'
+ ).without_content(%r{root-hints})
+ end
+
+ it { is_expected.to contain_systemd__timer('roothints.timer').with_ensure('absent') }
end
- it do
- expect(subject).to contain_file(hints_file).with(
- 'ensure' => 'file',
- 'mode' => '0444',
- 'content' => File.read('spec/classes/expected/hieradata-root-hint.conf')
- )
+ context 'update_root_hints=absent' do
+ let(:params) do
+ {
+ update_root_hints: 'absent'
+ }
+ end
+
+ it { is_expected.to contain_systemd__timer('roothints.timer').with_ensure('absent') }
end
- end
- context 'with File defaults' do
- let(:pre_condition) { "File { mode => '0644', owner => 'root', group => 'root' }" }
+ context 'hieradata root hints' do
+ let(:params) do
+ {
+ skip_roothints_download: true,
+ hints_file_content: File.read('spec/classes/expected/hieradata-root-hint.conf'),
+ }
+ end
- it { is_expected.to compile.with_all_deps }
+ it do
+ expect(subject).to contain_file(hints_file).with(
+ 'ensure' => 'file',
+ 'mode' => '0444',
+ 'content' => File.read('spec/classes/expected/hieradata-root-hint.conf')
+ )
+ end
+ end
+
+ context 'with File defaults' do
+ let(:pre_condition) { "File { mode => '0644', owner => 'root', group => 'root' }" }
+
+ it { is_expected.to compile.with_all_deps }
+ end
end
context 'RPZs config' do
diff --git a/templates/roothints.service.epp b/templates/roothints.service.epp
new file mode 100644
index 0000000..293d587
--- /dev/null
+++ b/templates/roothints.service.epp
@@ -0,0 +1,9 @@
+<%- | Stdlib::Absolutepath $hints_file, Stdlib::HTTPSUrl $root_hints_url, String[1] $fetch_client | -%>
+# THIS FILE IS MANAGED BY PUPPET
+# BASED ON https://wiki.archlinux.org/title/Unbound#Roothints_systemd_timer
+[Unit]
+Description=Update root hints for unbound
+After=network.target
+
+[Service]
+ExecStart=<%= $fetch_client %> <%= $hints_file %> <%= $root_hints_url %>