diff --git a/.fixtures.yml b/.fixtures.yml index 3a637f1..80616ed 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -5,4 +5,5 @@ fixtures: concat: https://github.com/puppetlabs/puppetlabs-concat.git cron_core: https://github.com/puppetlabs/puppetlabs-cron_core.git stdlib: https://github.com/puppetlabs/puppetlabs-stdlib.git + systemd: https://github.com/voxpupuli/puppet-systemd.git vcsrepo: https://github.com/puppetlabs/puppetlabs-vcsrepo.git diff --git a/REFERENCE.md b/REFERENCE.md index 24dbd0d..4f0ce4a 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -19,6 +19,7 @@ * `dehydrated::domains`: Manage the domains.txt file * `dehydrated::package`: Manage the dehydrated package * `dehydrated::repo`: Manage the dehydrated code +* `dehydrated::systemd`: Manage a system timer to refresh certificates * `dehydrated::user`: Manage the dehydrated user ### Defined types @@ -63,7 +64,8 @@ The following parameters are available in the `dehydrated` class: * [`repo_revision`](#-dehydrated--repo_revision) * [`dependencies`](#-dehydrated--dependencies) * [`apache_integration`](#-dehydrated--apache_integration) -* [`cron_integration`](#-dehydrated--cron_integration) +* [`renewal_provider`](#-dehydrated--renewal_provider) +* [`renewal_interval`](#-dehydrated--renewal_interval) * [`dehydrated_user`](#-dehydrated--dehydrated_user) * [`dehydrated_group`](#-dehydrated--dehydrated_group) * [`ip_version`](#-dehydrated--ip_version) @@ -175,13 +177,21 @@ Setup apache to serve the generated challenges. Default value: `false` -##### `cron_integration` +##### `renewal_provider` -Data type: `Boolean` +Data type: `Enum['cron','systemd','none']` -Setup cron to automatically renew certificates. +Which provider should trigger certificat renewal attempts. -Default value: `false` +Default value: `'none'` + +##### `renewal_interval` + +Data type: `Enum['never','daily','weekly']` + +How long to wait between certificate renewal attempts. + +Default value: `'daily'` ##### `dehydrated_user` diff --git a/manifests/cron.pp b/manifests/cron.pp index 3543516..890ef09 100644 --- a/manifests/cron.pp +++ b/manifests/cron.pp @@ -4,19 +4,24 @@ class dehydrated::cron { assert_private() - if $dehydrated::cron_integration { - $ensure = 'present' - } else { - $ensure = 'absent' - } + $supported_renewal_intervals = [ + 'daily', + 'weekly', + ] case $facts['os']['family'] { 'Debian', 'RedHat': { - cron { 'weekly_letsencrypt': - ensure => absent, + cron { 'daily_dehydrated': + ensure => bool2str($dehydrated::renewal_interval == 'daily', 'present', 'absent'), + command => "${dehydrated::bin} --accept-terms --cron --keep-going", + user => $dehydrated::user, + weekday => '*', + hour => 3, + minute => 30, } + cron { 'weekly_dehydrated': - ensure => $ensure, + ensure => bool2str($dehydrated::renewal_interval == 'weekly', 'present', 'absent'), command => "${dehydrated::bin} --accept-terms --cron --keep-going", user => $dehydrated::user, weekday => 0, @@ -25,17 +30,19 @@ } } 'FreeBSD': { - file_line { 'weekly_dehydrated_enable': - ensure => $ensure, - path => '/etc/periodic.conf', - line => 'weekly_dehydrated_enable="YES"', - match => '^weekly_(letsencrypt|dehydrated)_enable=', - } - file_line { 'weekly_dehydrated_user': - ensure => $ensure, - path => '/etc/periodic.conf', - line => "weekly_dehydrated_user=\"${dehydrated::user}\"", - match => '^weekly_(letsencrypt|dehydrated)_user=', + $supported_renewal_intervals.each |$interval| { + file_line { "${interval}_dehydrated_enable": + ensure => bool2str($interval == $dehydrated::renewal_interval, 'present', 'absent'), + path => '/etc/periodic.conf', + line => "${interval}_dehydrated_enable=\"YES\"", + match => "^${interval}_dehydrated_enable=", + } + file_line { "${interval}_dehydrated_user": + ensure => bool2str($interval == $dehydrated::renewal_interval, 'present', 'absent'), + path => '/etc/periodic.conf', + line => "${interval}_dehydrated_user=\"${dehydrated::user}\"", + match => "^${interval}_dehydrated_user=", + } } } default: { diff --git a/manifests/init.pp b/manifests/init.pp index b53bf4f..b2d7333 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -10,7 +10,8 @@ # @param repo_revision Revision to fetch from the repository providing dehydrated. # @param dependencies Extra dependencies needed to run dehydrated. # @param apache_integration Setup apache to serve the generated challenges. -# @param cron_integration Setup cron to automatically renew certificates. +# @param renewal_provider Which provider should trigger certificat renewal attempts. +# @param renewal_interval How long to wait between certificate renewal attempts. # @param dehydrated_user Which user should dehydrated run as? This will be implicitly enforced when running as root. # @param dehydrated_group Which group should dehydrated run as? This will be implicitly enforced when running as root. # @param ip_version Resolve names to addresses of IP version only. (curl) @@ -63,7 +64,9 @@ Array[String] $dependencies = [], Boolean $apache_integration = false, - Boolean $cron_integration = false, + + Enum['cron','systemd','none'] $renewal_provider = 'none', + Enum['never','daily','weekly'] $renewal_interval = 'daily', Optional[String[1]] $dehydrated_user = undef, Optional[String[1]] $dehydrated_group = undef, @@ -135,5 +138,10 @@ include dehydrated::apache } - include dehydrated::cron + case $renewal_provider { + 'cron': { include dehydrated::cron } + 'systemd': { include dehydrated::systemd } + 'none': {} + default: { fail("Unsupported renewal_provider ${renewal_provider}") } + } } diff --git a/manifests/systemd.pp b/manifests/systemd.pp new file mode 100644 index 0000000..73f0b9b --- /dev/null +++ b/manifests/systemd.pp @@ -0,0 +1,14 @@ +# @summary Manage a system timer to refresh certificates +# +# @api private +class dehydrated::systemd { + assert_private() + + systemd::timer { 'dehydrated.timer': + ensure => bool2str($dehydrated::renewal_interval != 'never', 'present', 'absent'), + active => true, + enable => true, + timer_content => epp('dehydrated/systemd.timer.epp'), + service_content => epp('dehydrated/systemd.service.epp'), + } +} diff --git a/spec/classes/cron_spec.rb b/spec/classes/cron_spec.rb new file mode 100644 index 0000000..14558a5 --- /dev/null +++ b/spec/classes/cron_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'dehydrated::cron' do + let(:pre_condition) do + <<~PP + class { 'dehydrated': + contact_email => 'dummy@example.com', + renewal_provider => 'cron', + } + PP + end + + on_supported_os.each do |os, facts| + context "on #{os}" do + let(:facts) { facts } + + it { is_expected.to compile.with_all_deps } + + if facts[:os]['family'] == 'Debian' + it { is_expected.to contain_cron('daily_dehydrated') } + it { is_expected.to contain_cron('weekly_dehydrated') } + end + end + end +end diff --git a/spec/classes/systemd_spec.rb b/spec/classes/systemd_spec.rb new file mode 100644 index 0000000..715f35a --- /dev/null +++ b/spec/classes/systemd_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'dehydrated::systemd' do + let(:pre_condition) do + <<~PP + class { 'dehydrated': + contact_email => 'dummy@example.com', + renewal_provider => 'systemd', + } + PP + end + + on_supported_os.each do |os, facts| + next unless facts[:os]['family'] == 'Debian' + + context "on #{os}" do + let(:facts) { facts } + + it { is_expected.to compile.with_all_deps } + + it { is_expected.to contain_systemd__timer('dehydrated.timer') } + end + end +end diff --git a/templates/systemd.service.epp b/templates/systemd.service.epp new file mode 100644 index 0000000..2762ff3 --- /dev/null +++ b/templates/systemd.service.epp @@ -0,0 +1,10 @@ +# Managed by Puppet, DO NOT EDIT + +[Unit] +Description=Renew dehydrated certificates about to expire + +[Service] +Type=oneshot +ExecStart=<%= $dehydrated::bin %> --accept-terms --cron --keep-going +User=<%= $dehydrated::user %> +Group=<%= $dehydrated::user %> diff --git a/templates/systemd.timer.epp b/templates/systemd.timer.epp new file mode 100644 index 0000000..882b7b6 --- /dev/null +++ b/templates/systemd.timer.epp @@ -0,0 +1,11 @@ +# Managed by Puppet, DO NOT EDIT + +[Unit] +Description=Trigger dehydrated certificates renewal + +[Timer] +OnCalendar=<%= $dehydrated::renewal_interval %> +Persistent=true + +[Install] +WantedBy=timers.target