Skip to content

Commit 73fe071

Browse files
committed
(maint) Add support for mod_md
Allow configuring all parameters provided by the module.
1 parent be6f385 commit 73fe071

File tree

3 files changed

+420
-0
lines changed

3 files changed

+420
-0
lines changed

manifests/mod/md.pp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# @summary
2+
# Installs and configures `mod_md`.
3+
#
4+
# @param md_activation_delay
5+
# -
6+
#
7+
# @param md_base_server
8+
# Control if base server may be managed or only virtual hosts.
9+
#
10+
# @param md_ca_challenges
11+
# Type of ACME challenge used to prove domain ownership.
12+
#
13+
# @param md_certificate_agreement
14+
# You confirm that you accepted the Terms of Service of the Certificate
15+
# Authority.
16+
#
17+
# @param md_certificate_authority
18+
# The URL of the ACME Certificate Authority service.
19+
#
20+
# @param md_certificate_check
21+
# -
22+
#
23+
# @param md_certificate_monitor
24+
# The URL of a certificate log monitor.
25+
#
26+
# @param md_certificate_protocol
27+
# The protocol to use with the Certificate Authority.
28+
#
29+
# @param md_certificate_status
30+
# Exposes public certificate information in JSON.
31+
#
32+
# @param md_challenge_dns01
33+
# Define a program to be called when the `dns-01` challenge needs to be
34+
# setup/torn down.
35+
#
36+
# @param md_contact_email
37+
# The ACME protocol requires you to give a contact url when you sign up.
38+
#
39+
# @param md_http_proxy
40+
# Define a proxy for outgoing connections.
41+
#
42+
# @param md_members
43+
# Control if the alias domain names are automatically added.
44+
#
45+
# @param md_message_cmd
46+
# Handle events for Manage Domains.
47+
#
48+
# @param md_must_staple
49+
# Control if new certificates carry the OCSP Must Staple flag.
50+
#
51+
# @param md_notify_cmd
52+
# Run a program when a Managed Domain is ready.
53+
#
54+
# @param md_port_map
55+
# Map external to internal ports for domain ownership verification.
56+
#
57+
# @param md_private_keys
58+
# Set type and size of the private keys generated.
59+
#
60+
# @param md_renew_mode
61+
# Controls if certificates shall be renewed.
62+
#
63+
# @param md_renew_window
64+
# Control when a certificate will be renewed.
65+
#
66+
# @param md_require_https
67+
# Redirects http: traffic to https: for Managed Domains.
68+
#
69+
# @param md_server_status
70+
# Control if Managed Domain information is added to server-status.
71+
#
72+
# @param md_staple_others
73+
# Enable stapling for certificates not managed by mod_md.
74+
#
75+
# @param md_stapling
76+
# Enable stapling for all or a particular MDomain.
77+
#
78+
# @param md_stapling_keep_response
79+
# Controls when old responses should be removed.
80+
#
81+
# @param md_stapling_renew_window
82+
# Control when the stapling responses will be renewed.
83+
#
84+
# @param md_store_dir
85+
# Path on the local file system to store the Managed Domains data.
86+
#
87+
# @param md_warn_window
88+
# Define the time window when you want to be warned about an expiring
89+
# certificate.
90+
#
91+
# @see https://httpd.apache.org/docs/current/mod/mod_md.html for additional documentation.
92+
class apache::mod::md (
93+
Optional[String] $md_activation_delay = undef,
94+
Optional[Enum['on', 'off']] $md_base_server = undef,
95+
Optional[Array[Enum['dns-01', 'http-01', 'tls-alpn-01']]] $md_ca_challenges = undef,
96+
Optional[Enum['accepted']] $md_certificate_agreement = undef,
97+
Optional[Stdlib::HTTPUrl] $md_certificate_authority = undef,
98+
Optional[String] $md_certificate_check = undef, # undocumented
99+
Optional[String] $md_certificate_monitor = undef,
100+
Optional[Enum['ACME']] $md_certificate_protocol = undef,
101+
Optional[Enum['on', 'off']] $md_certificate_status = undef,
102+
Optional[Stdlib::Absolutepath] $md_challenge_dns01 = undef,
103+
Optional[String] $md_contact_email = undef,
104+
Optional[Stdlib::HTTPUrl] $md_http_proxy = undef,
105+
Optional[Enum['auto', 'manual']] $md_members = undef,
106+
Optional[Stdlib::Absolutepath] $md_message_cmd = undef,
107+
Optional[Enum['on', 'off']] $md_must_staple = undef,
108+
Optional[Stdlib::Absolutepath] $md_notify_cmd = undef,
109+
Optional[String] $md_port_map = undef,
110+
Optional[String] $md_private_keys = undef,
111+
Optional[Enum['always', 'auto', 'manual']] $md_renew_mode = undef,
112+
Optional[String] $md_renew_window = undef,
113+
Optional[Enum['off', 'permanent', 'temporary']] $md_require_https = undef,
114+
Optional[Enum['on', 'off']] $md_server_status = undef,
115+
Optional[Enum['on', 'off']] $md_staple_others = undef,
116+
Optional[Enum['on', 'off']] $md_stapling = undef,
117+
Optional[String] $md_stapling_keep_response = undef,
118+
Optional[String] $md_stapling_renew_window = undef,
119+
Optional[Stdlib::Absolutepath] $md_store_dir = undef,
120+
Optional[String] $md_warn_window = undef,
121+
) {
122+
include apache
123+
include apache::mod::watchdog
124+
125+
apache::mod { 'md':
126+
}
127+
128+
file { 'md.conf':
129+
ensure => file,
130+
path => "${apache::mod_dir}/md.conf",
131+
mode => $apache::file_mode,
132+
content => epp('apache/mod/md.conf.epp'),
133+
require => Exec["mkdir ${apache::mod_dir}"],
134+
before => File[$apache::mod_dir],
135+
notify => Class['apache::service'],
136+
}
137+
}

spec/classes/mod/md_spec.rb

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
require 'spec_helper'
2+
3+
describe 'apache::mod::md', type: :class do
4+
on_supported_os.each do |os, facts|
5+
context "on #{os}" do
6+
let :facts do
7+
facts
8+
end
9+
10+
if facts[:os]['family'] == 'Debian'
11+
context 'validating all md params - using Debian' do
12+
md_options = {
13+
'md_activation_delay' => { type: 'Duration', pass_opt: 'MDActivationDelay' },
14+
'md_base_server' => { type: 'OnOff', pass_opt: 'MDBaseServer' },
15+
'md_ca_challenges' => { type: 'CAChallenges', pass_opt: 'MDCAChallenges' },
16+
'md_certificate_agreement' => { type: 'MDCertificateAgreement', pass_opt: 'MDCertificateAgreement' },
17+
'md_certificate_authority' => { type: 'URL', pass_opt: 'MDCertificateAuthority' },
18+
'md_certificate_check' => { type: 'String', pass_opt: 'MDCertificateCheck' },
19+
'md_certificate_monitor' => { type: 'URL', pass_opt: 'MDCertificateMonitor' },
20+
'md_certificate_protocol' => { type: 'MDCertificateProtocol', pass_opt: 'MDCertificateProtocol' },
21+
'md_certificate_status' => { type: 'OnOff', pass_opt: 'MDCertificateStatus' },
22+
'md_challenge_dns01' => { type: 'Path', pass_opt: 'MDChallengeDns01' },
23+
'md_contact_email' => { type: 'EMail', pass_opt: 'MDContactEmail' },
24+
'md_http_proxy' => { type: 'URL', pass_opt: 'MDHttpProxy' },
25+
'md_members' => { type: 'MDMembers', pass_opt: 'MDMembers' },
26+
'md_message_cmd' => { type: 'Path', pass_opt: 'MDMessageCmd' },
27+
'md_must_staple' => { type: 'OnOff', pass_opt: 'MDMustStaple' },
28+
'md_notify_cmd' => { type: 'Path', pass_opt: 'MDNotifyCmd' },
29+
'md_port_map' => { type: 'String', pass_opt: 'MDPortMap' },
30+
'md_private_keys' => { type: 'String', pass_opt: 'MDPrivateKeys' },
31+
'md_renew_mode' => { type: 'MDRenewMode', pass_opt: 'MDRenewMode' },
32+
'md_renew_window' => { type: 'Duration', pass_opt: 'MDRenewWindow' },
33+
'md_require_https' => { type: 'MDRequireHttps', pass_opt: 'MDRequireHttps' },
34+
'md_server_status' => { type: 'OnOff', pass_opt: 'MDServerStatus' },
35+
'md_staple_others' => { type: 'OnOff', pass_opt: 'MDStapleOthers' },
36+
'md_stapling' => { type: 'OnOff', pass_opt: 'MDStapling' },
37+
'md_stapling_keep_response' => { type: 'Duration', pass_opt: 'MDStaplingKeepResponse' },
38+
'md_stapling_renew_window' => { type: 'Duration', pass_opt: 'MDStaplingRenewWindow' },
39+
'md_store_dir' => { type: 'Path', pass_opt: 'MDStoreDir' },
40+
'md_warn_window' => { type: 'Duration', pass_opt: 'MDWarnWindow' },
41+
}
42+
43+
md_options.each do |config_option, config_hash|
44+
puppetized_config_option = config_option
45+
case config_hash[:type]
46+
when 'CAChallenges'
47+
valid_config_values = [['dns-01'], ['tls-alpn-01', 'http-01']]
48+
valid_config_values.each do |valid_value|
49+
describe "with #{puppetized_config_option} => #{valid_value}" do
50+
let :params do
51+
{ puppetized_config_option.to_sym => valid_value }
52+
end
53+
54+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value.join(' ')}}) }
55+
end
56+
end
57+
when 'EMail'
58+
valid_config_values = ['root@example.com']
59+
valid_config_values.each do |valid_value|
60+
describe "with #{puppetized_config_option} => #{valid_value}" do
61+
let :params do
62+
{ puppetized_config_option.to_sym => valid_value }
63+
end
64+
65+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
66+
end
67+
end
68+
when 'Duration'
69+
valid_config_values = ['7d', '33%']
70+
valid_config_values.each do |valid_value|
71+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
72+
let :params do
73+
{ puppetized_config_option.to_sym => valid_value }
74+
end
75+
76+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
77+
end
78+
end
79+
when 'MDCertificateAgreement'
80+
valid_config_values = ['accepted']
81+
valid_config_values.each do |valid_value|
82+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
83+
let :params do
84+
{ puppetized_config_option.to_sym => valid_value }
85+
end
86+
87+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
88+
end
89+
end
90+
when 'MDCertificateProtocol'
91+
valid_config_values = ['ACME']
92+
valid_config_values.each do |valid_value|
93+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
94+
let :params do
95+
{ puppetized_config_option.to_sym => valid_value }
96+
end
97+
98+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
99+
end
100+
end
101+
when 'MDMembers'
102+
valid_config_values = ['auto', 'manual']
103+
valid_config_values.each do |valid_value|
104+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
105+
let :params do
106+
{ puppetized_config_option.to_sym => valid_value }
107+
end
108+
109+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
110+
end
111+
end
112+
when 'MDRenewMode'
113+
valid_config_values = ['always', 'auto', 'manual']
114+
valid_config_values.each do |valid_value|
115+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
116+
let :params do
117+
{ puppetized_config_option.to_sym => valid_value }
118+
end
119+
120+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
121+
end
122+
end
123+
when 'MDRequireHttps'
124+
valid_config_values = ['off', 'temporary', 'permanent']
125+
valid_config_values.each do |valid_value|
126+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
127+
let :params do
128+
{ puppetized_config_option.to_sym => valid_value }
129+
end
130+
131+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
132+
end
133+
end
134+
when 'OnOff'
135+
valid_config_values = ['on', 'off']
136+
valid_config_values.each do |valid_value|
137+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
138+
let :params do
139+
{ puppetized_config_option.to_sym => valid_value }
140+
end
141+
142+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
143+
end
144+
end
145+
when 'Path'
146+
valid_config_values = ['/some/path/to/somewhere']
147+
valid_config_values.each do |valid_value|
148+
describe "with #{puppetized_config_option} => #{valid_value}" do
149+
let :params do
150+
{ puppetized_config_option.to_sym => valid_value }
151+
end
152+
153+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} "#{valid_value}"$}) }
154+
end
155+
end
156+
when 'String'
157+
valid_config_values = ['a random string']
158+
valid_config_values.each do |valid_value|
159+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
160+
let :params do
161+
{ puppetized_config_option.to_sym => valid_value }
162+
end
163+
164+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
165+
end
166+
end
167+
when 'URL'
168+
valid_config_values = ['https://example.com/example']
169+
valid_config_values.each do |valid_value|
170+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
171+
let :params do
172+
{ puppetized_config_option.to_sym => valid_value }
173+
end
174+
175+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
176+
end
177+
end
178+
else
179+
valid_config_values = config_hash[:type]
180+
valid_config_values.each do |valid_value|
181+
describe "with #{puppetized_config_option} => '#{valid_value}'" do
182+
let :params do
183+
{ puppetized_config_option.to_sym => valid_value }
184+
end
185+
186+
it { is_expected.to contain_file('md.conf').with_content(%r{^#{config_hash[:pass_opt]} #{valid_value}$}) }
187+
end
188+
end
189+
end
190+
end
191+
end
192+
end
193+
194+
it { is_expected.to contain_class('apache::mod::watchdog') }
195+
it { is_expected.to contain_apache__mod('md') }
196+
it { is_expected.to contain_file('md.conf') }
197+
end
198+
end
199+
end

0 commit comments

Comments
 (0)