From 7d7bae1de0dccd339a03645492354dc605377f94 Mon Sep 17 00:00:00 2001 From: timdeluxe <5765175+timdeluxe@users.noreply.github.com> Date: Thu, 10 Jun 2021 15:01:51 +0200 Subject: [PATCH] (MODULES-10847) Add feature to reload apache service when content of ssl files has changed --- REFERENCE.md | 39 +++++++++++++++++++++++++++ manifests/init.pp | 4 +++ manifests/mod/ssl.pp | 40 ++++++++++++++++++++++++++++ manifests/mod/ssl/reload.pp | 17 ++++++++++++ manifests/params.pp | 6 +++++ manifests/vhost.pp | 21 ++++++++++++++- spec/acceptance/apache_ssl_spec.rb | 32 ++++++++++++++++++++++ spec/classes/mod/ssl_spec.rb | 12 +++++++++ spec/defines/vhost_spec.rb | 11 +++++++- spec/spec_helper_acceptance_local.rb | 5 ++++ 10 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 manifests/mod/ssl/reload.pp diff --git a/REFERENCE.md b/REFERENCE.md index b7f9f1fd72..5af8e13faf 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -109,6 +109,7 @@ * `apache::confd::no_accf`: Manages the `no-accf.conf` file. * `apache::default_confd_files`: Helper for setting up default conf.d files. * `apache::default_mods`: Installs and congfigures default mods for Apache +* `apache::mod::ssl::reload`: Manages the puppet_ssl folder for ssl file copies, which is needed to track changes for reloading service on changes * `apache::package`: Installs an Apache MPM. * `apache::params`: This class manages Apache parameters * `apache::php`: This class installs PHP for Apache. @@ -206,6 +207,7 @@ The following parameters are available in the `apache` class: * [`default_ssl_crl_path`](#default_ssl_crl_path) * [`default_ssl_crl_check`](#default_ssl_crl_check) * [`default_ssl_key`](#default_ssl_key) +* [`default_ssl_reload_on_change`](#default_ssl_reload_on_change) * [`default_ssl_vhost`](#default_ssl_vhost) * [`default_type`](#default_type) * [`default_vhost`](#default_vhost) @@ -433,6 +435,14 @@ environment. Default value: `$apache::params::default_ssl_key` +##### `default_ssl_reload_on_change` + +Data type: `Boolean` + +Enable reloading of apache if the content of ssl files have changed. + +Default value: ``false`` + ##### `default_ssl_vhost` Data type: `Boolean` @@ -2117,6 +2127,7 @@ The following parameters are available in the `apache::mod::disk_cache` class: * [`cache_root`](#cache_root) * [`cache_ignore_headers`](#cache_ignore_headers) +* [`default_cache_enable`](#default_cache_enable) ##### `cache_root` @@ -2139,6 +2150,16 @@ Specifies HTTP header(s) that should not be stored in the cache. Default value: ``undef`` +##### `default_cache_enable` + +Data type: `Boolean` + +Default value is true, which enables "CacheEnable disk /" in disk_cache.conf for the webserver. This would cache +every request to apache by default for every vhost. If set to false the default cache all behaviour is supressed. +You can then control this behaviour in individual vhosts by explicitly defining CacheEnable. + +Default value: ``true`` + ### `apache::mod::dumpio` Installs and configures `mod_dumpio`. @@ -6357,6 +6378,7 @@ The following parameters are available in the `apache::mod::ssl` class: * [`ssl_stapling`](#ssl_stapling) * [`ssl_stapling_return_errors`](#ssl_stapling_return_errors) * [`ssl_mutex`](#ssl_mutex) +* [`ssl_reload_on_change`](#ssl_reload_on_change) * [`apache_version`](#apache_version) * [`package_name`](#package_name) * [`ssl_sessiontickets`](#ssl_sessiontickets) @@ -6513,6 +6535,14 @@ Default based on the OS and/or Apache version: Default value: ``undef`` +##### `ssl_reload_on_change` + +Data type: `Boolean` + +Enable reloading of apache if the content of ssl files have changed. + +Default value: ``false`` + ##### `apache_version` Data type: `Any` @@ -7523,6 +7553,7 @@ The following parameters are available in the `apache::vhost` defined type: * [`ssl_crl`](#ssl_crl) * [`ssl_crl_check`](#ssl_crl_check) * [`ssl_certs_dir`](#ssl_certs_dir) +* [`ssl_reload_on_change`](#ssl_reload_on_change) * [`ssl_protocol`](#ssl_protocol) * [`ssl_cipher`](#ssl_cipher) * [`ssl_honorcipherorder`](#ssl_honorcipherorder) @@ -7918,6 +7949,14 @@ Data type: `Any` Default value: `$apache::params::ssl_certs_dir` +##### `ssl_reload_on_change` + +Data type: `Boolean` + + + +Default value: `$apache::default_ssl_reload_on_change` + ##### `ssl_protocol` Data type: `Any` diff --git a/manifests/init.pp b/manifests/init.pp index f01d101bcc..26ded53a77 100755 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -91,6 +91,9 @@ # this parameter with your SSL key's location before deploying this server in a production # environment. # +# @param default_ssl_reload_on_change +# Enable reloading of apache if the content of ssl files have changed. +# # @param default_ssl_vhost # Configures a default SSL virtual host. # If `true`, Puppet automatically configures the following virtual host using the @@ -472,6 +475,7 @@ $default_ssl_crl_path = undef, $default_ssl_crl = undef, $default_ssl_crl_check = undef, + Boolean $default_ssl_reload_on_change = false, $default_type = 'none', $dev_packages = $apache::params::dev_packages, $ip = undef, diff --git a/manifests/mod/ssl.pp b/manifests/mod/ssl.pp index 28ac3d0439..3fc0d59859 100644 --- a/manifests/mod/ssl.pp +++ b/manifests/mod/ssl.pp @@ -62,6 +62,9 @@ # - Debian/Ubuntu + Apache >= 2.4: 'default'. # - Debian/Ubuntu + Apache < 2.4: 'file:${APACHE_RUN_DIR}/ssl_mutex'. # +# @param ssl_reload_on_change +# Enable reloading of apache if the content of ssl files have changed. +# # @param apache_version # Used to verify that the Apache version you have requested is compatible with the module. # @@ -97,6 +100,7 @@ Optional[String] $stapling_cache = undef, Optional[Boolean] $ssl_stapling_return_errors = undef, $ssl_mutex = undef, + Boolean $ssl_reload_on_change = false, $apache_version = undef, $package_name = undef, ) inherits ::apache::params { @@ -174,6 +178,42 @@ include apache::mod::socache_shmcb } + if $ssl_reload_on_change { + if $ssl_cert { + include apache::mod::ssl::reload + $_ssl_cert_copy = regsubst($ssl_cert, '/', '_', 'G') + file { $_ssl_cert_copy: + path => "${apache::params::puppet_ssl_dir}/${_ssl_cert_copy}", + source => "file://${ssl_cert}", + mode => '0640', + seltype => 'cert_t', + notify => Class['apache::service'], + } + } + if $ssl_key { + include apache::mod::ssl::reload + $_ssl_key_copy = regsubst($ssl_key, '/', '_', 'G') + file { $_ssl_key_copy: + path => "${apache::params::puppet_ssl_dir}/${_ssl_key_copy}", + source => "file://${ssl_key}", + mode => '0640', + seltype => 'cert_t', + notify => Class['apache::service'], + } + } + if $ssl_ca { + include apache::mod::ssl::reload + $_ssl_ca_copy = regsubst($ssl_ca, '/', '_', 'G') + file { $_ssl_ca_copy: + path => "${apache::params::puppet_ssl_dir}/${_ssl_ca_copy}", + source => "file://${ssl_ca}", + mode => '0640', + seltype => 'cert_t', + notify => Class['apache::service'], + } + } + } + # Template uses # # $ssl_compression diff --git a/manifests/mod/ssl/reload.pp b/manifests/mod/ssl/reload.pp new file mode 100644 index 0000000000..723c6cb8df --- /dev/null +++ b/manifests/mod/ssl/reload.pp @@ -0,0 +1,17 @@ +# @summary +# Manages the puppet_ssl folder for ssl file copies, which is needed to track changes for reloading service on changes +# +# @api private +class apache::mod::ssl::reload () inherits ::apache::params { + file { $apache::params::puppet_ssl_dir: + ensure => directory, + purge => true, + recurse => true, + require => Package['httpd'], + } + file { 'README.txt': + path => "${apache::params::puppet_ssl_dir}/README.txt", + content => 'This directory contains puppet managed copies of ssl files, so it can track changes and reload apache on changes.', + seltype => 'etc_t', + } +} diff --git a/manifests/params.pp b/manifests/params.pp index ad1772bba4..10809ff18b 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -63,6 +63,7 @@ $server_root = "${httpd_root}/etc/httpd" $conf_dir = "${httpd_dir}/conf" $confd_dir = "${httpd_dir}/conf.d" + $puppet_ssl_dir = "${httpd_dir}/puppet_ssl" $mod_dir = $facts['operatingsystemmajrelease'] ? { '7' => "${httpd_dir}/conf.modules.d", default => "${httpd_dir}/conf.d", @@ -169,6 +170,7 @@ $server_root = '/etc/httpd' $conf_dir = "${httpd_dir}/conf" $confd_dir = "${httpd_dir}/conf.d" + $puppet_ssl_dir = "${httpd_dir}/puppet_ssl" $conf_enabled = undef if $::operatingsystem =~ /^[Aa]mazon$/ and $::operatingsystemmajrelease == '2' { # Amazon Linux 2 uses the /conf.modules.d/ dir @@ -343,6 +345,7 @@ $confd_dir = "${httpd_dir}/conf.d" # Overwrite conf_enabled causes errors with Shibboleth when enabled on Ubuntu 18.04 $conf_enabled = undef #"${httpd_dir}/conf-enabled.d" + $puppet_ssl_dir = "${httpd_dir}/puppet_ssl" $mod_dir = "${httpd_dir}/mods-available" $mod_enable_dir = "${httpd_dir}/mods-enabled" $vhost_dir = "${httpd_dir}/sites-available" @@ -546,6 +549,7 @@ $conf_dir = $httpd_dir $confd_dir = "${httpd_dir}/Includes" $conf_enabled = undef + $puppet_ssl_dir = "${httpd_dir}/puppet_ssl" $mod_dir = "${httpd_dir}/Modules" $mod_enable_dir = undef $vhost_dir = "${httpd_dir}/Vhosts" @@ -619,6 +623,7 @@ $conf_dir = $httpd_dir $confd_dir = "${httpd_dir}/conf.d" $conf_enabled = undef + $puppet_ssl_dir = "${httpd_dir}/puppet_ssl" $mod_dir = "${httpd_dir}/modules.d" $mod_enable_dir = undef $vhost_dir = "${httpd_dir}/vhosts.d" @@ -689,6 +694,7 @@ $conf_dir = $httpd_dir $confd_dir = "${httpd_dir}/conf.d" $conf_enabled = undef + $puppet_ssl_dir = "${httpd_dir}/puppet_ssl" $mod_dir = "${httpd_dir}/mods-available" $mod_enable_dir = "${httpd_dir}/mods-enabled" $vhost_dir = "${httpd_dir}/sites-available" diff --git a/manifests/vhost.pp b/manifests/vhost.pp index 0ae4cba07e..ebd99d2b83 100644 --- a/manifests/vhost.pp +++ b/manifests/vhost.pp @@ -1679,6 +1679,9 @@ # @param ssl_user_name # Sets the [SSLUserName](https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslusername) directive. # +# @param ssl_reload_on_change +# Enable reloading of apache if the content of ssl files have changed. +# # @param use_canonical_name # Specifies whether to use the [`UseCanonicalName directive`](https://httpd.apache.org/docs/2.4/mod/core.html#usecanonicalname), # which allows you to configure how the server determines it's own name and port. @@ -1763,6 +1766,7 @@ $ssl_crl = $apache::default_ssl_crl, $ssl_crl_check = $apache::default_ssl_crl_check, $ssl_certs_dir = $apache::params::ssl_certs_dir, + Boolean $ssl_reload_on_change = $apache::default_ssl_reload_on_change, $ssl_protocol = undef, $ssl_cipher = undef, Variant[Boolean, Enum['on', 'On', 'off', 'Off'], Undef] $ssl_honorcipherorder = undef, @@ -2707,12 +2711,27 @@ # - $ssl_openssl_conf_cmd # - $ssl_stapling # - $apache_version - if $ssl { + if $ssl and $ensure == 'present' { concat::fragment { "${name}-ssl": target => "${priority_real}${filename}.conf", order => 230, content => template('apache/vhost/_ssl.erb'), } + if $ssl_reload_on_change { + [$ssl_cert, $ssl_key, $ssl_ca, $ssl_chain, $ssl_crl].each |$ssl_file| { + if $ssl_file { + include apache::mod::ssl::reload + $_ssl_file_copy = regsubst($ssl_file, '/', '_', 'G') + file { "${filename}${_ssl_file_copy}": + path => "${apache::params::puppet_ssl_dir}/${filename}${_ssl_file_copy}", + source => "file://${ssl_file}", + mode => '0640', + seltype => 'cert_t', + notify => Class['apache::service'], + } + } + } + } } # Template uses: diff --git a/spec/acceptance/apache_ssl_spec.rb b/spec/acceptance/apache_ssl_spec.rb index a9508f1743..3d0fa0a000 100644 --- a/spec/acceptance/apache_ssl_spec.rb +++ b/spec/acceptance/apache_ssl_spec.rb @@ -48,6 +48,17 @@ class { 'apache': describe 'vhost ssl parameters' do pp = <<-MANIFEST + file { [ + '/tmp/ssl_cert', + '/tmp/ssl_key', + '/tmp/ssl_chain', + '/tmp/ssl_ca', + '/tmp/ssl_crl', + ]: + ensure => file, + before => Class['apache'] + } + class { 'apache': service_ensure => stopped, } @@ -63,6 +74,7 @@ class { 'apache': ssl_crl => '/tmp/ssl_crl', ssl_crl_check => 'chain flag', ssl_certs_dir => '/tmp', + ssl_reload_on_change => true, ssl_protocol => 'test', ssl_cipher => 'test', ssl_honorcipherorder => true, @@ -99,6 +111,26 @@ class { 'apache': it { is_expected.not_to contain 'SSLCARevocationCheck' } end end + + describe file("#{apache_hash['httpd_dir']}/puppet_ssl/test_ssl_tmp_ssl_cert") do + it { is_expected.to be_file } + end + + describe file("#{apache_hash['httpd_dir']}/puppet_ssl/test_ssl_tmp_ssl_key") do + it { is_expected.to be_file } + end + + describe file("#{apache_hash['httpd_dir']}/puppet_ssl/test_ssl_tmp_ssl_chain") do + it { is_expected.to be_file } + end + + describe file("#{apache_hash['httpd_dir']}/puppet_ssl/test_ssl_tmp_ssl_ca") do + it { is_expected.to be_file } + end + + describe file("#{apache_hash['httpd_dir']}/puppet_ssl/test_ssl_tmp_ssl_crl") do + it { is_expected.to be_file } + end end describe 'vhost ssl ssl_ca only' do diff --git a/spec/classes/mod/ssl_spec.rb b/spec/classes/mod/ssl_spec.rb index 326e45f877..52c6276248 100644 --- a/spec/classes/mod/ssl_spec.rb +++ b/spec/classes/mod/ssl_spec.rb @@ -127,6 +127,18 @@ it { is_expected.to contain_file('ssl.conf').with_content(%r{^ SSLCACertificateFile}) } end + context 'setting ssl_cert with reload' do + let :params do + { + ssl_cert: '/etc/pki/some/path/localhost.crt', + ssl_reload_on_change: true, + } + end + + it { is_expected.to contain_file('ssl.conf').with_content(%r{^ SSLCertificateFile}) } + it { is_expected.to contain_file('_etc_pki_some_path_localhost.crt') } + end + context 'with Apache version < 2.4 - ssl_compression with default value' do let :params do { diff --git a/spec/defines/vhost_spec.rb b/spec/defines/vhost_spec.rb index 5331a136ce..28291f80bc 100644 --- a/spec/defines/vhost_spec.rb +++ b/spec/defines/vhost_spec.rb @@ -71,7 +71,7 @@ 'ssl_key' => '/ssl/key', 'ssl_chain' => '/ssl/chain', 'ssl_crl_path' => '/ssl/crl', - 'ssl_crl' => 'foo.crl', + 'ssl_crl' => '/ssl/foo.crl', 'ssl_certs_dir' => '/ssl/certs', 'ssl_protocol' => 'SSLv2', 'ssl_cipher' => 'HIGH', @@ -88,6 +88,7 @@ 'ssl_proxy_cipher_suite' => 'HIGH', 'ssl_proxy_protocol' => 'TLSv1.2', 'ssl_user_name' => 'SSL_CLIENT_S_DN_CN', + 'ssl_reload_on_change' => true, 'priority' => '30', 'default_vhost' => true, 'servername' => 'example.com', @@ -516,6 +517,10 @@ content: %r{^\s+SSLSessionCacheTimeout 300$}, ) } + it { is_expected.to contain_file('rspec.example.com_ssl_cert') } + it { is_expected.to contain_file('rspec.example.com_ssl_key') } + it { is_expected.to contain_file('rspec.example.com_ssl_chain') } + it { is_expected.to contain_file('rspec.example.com_ssl_foo.crl') } it { is_expected.to contain_class('apache::mod::mime') } it { is_expected.to contain_class('apache::mod::vhost_alias') } it { is_expected.to contain_class('apache::mod::wsgi') } @@ -1859,6 +1864,10 @@ it { is_expected.not_to contain_class('apache::mod::proxy') } it { is_expected.not_to contain_class('apache::mod::proxy_http') } it { is_expected.not_to contain_class('apache::mod::headers') } + it { is_expected.not_to contain_file('rspec.example.com_ssl_cert') } + it { is_expected.not_to contain_file('rspec.example.com_ssl_key') } + it { is_expected.not_to contain_file('rspec.example.com_ssl_chain') } + it { is_expected.not_to contain_file('rspec.example.com_ssl_foo.crl') } it { is_expected.to contain_file('/var/www/foo') } it { is_expected.to contain_file('/tmp/logroot').with('ensure' => 'absent') diff --git a/spec/spec_helper_acceptance_local.rb b/spec/spec_helper_acceptance_local.rb index 2cb26b135d..6f64652915 100644 --- a/spec/spec_helper_acceptance_local.rb +++ b/spec/spec_helper_acceptance_local.rb @@ -85,6 +85,7 @@ def apache_settings_hash apache = {} case osfamily when 'redhat', 'oracle' + apache['httpd_dir'] = '/etc/httpd' apache['confd_dir'] = '/etc/httpd/conf.d' apache['conf_file'] = '/etc/httpd/conf/httpd.conf' apache['ports_file'] = '/etc/httpd/conf/ports.conf' @@ -115,6 +116,7 @@ def apache_settings_hash apache['mod_ssl_dir'] = apache['mod_dir'] end when 'debian', 'ubuntu' + apache['httpd_dir'] = '/etc/apache2' apache['confd_dir'] = '/etc/apache2/conf.d' apache['mod_dir'] = '/etc/apache2/mods-available' apache['conf_file'] = '/etc/apache2/apache2.conf' @@ -137,6 +139,7 @@ def apache_settings_hash end apache['mod_ssl_dir'] = apache['mod_dir'] when 'freebsd' + apache['httpd_dir'] = '/usr/local/etc/apache24' apache['confd_dir'] = '/usr/local/etc/apache24/Includes' apache['mod_dir'] = '/usr/local/etc/apache24/Modules' apache['conf_file'] = '/usr/local/etc/apache24/httpd.conf' @@ -151,6 +154,7 @@ def apache_settings_hash apache['version'] = '2.2' apache['mod_ssl_dir'] = apache['mod_dir'] when 'gentoo' + apache['httpd_dir'] = '/etc/apache2' apache['confd_dir'] = '/etc/apache2/conf.d' apache['mod_dir'] = '/etc/apache2/modules.d' apache['conf_file'] = '/etc/apache2/httpd.conf' @@ -165,6 +169,7 @@ def apache_settings_hash apache['version'] = '2.4' apache['mod_ssl_dir'] = apache['mod_dir'] when 'suse', 'sles' + apache['httpd_dir'] = '/etc/apache2' apache['confd_dir'] = '/etc/apache2/conf.d' apache['mod_dir'] = '/etc/apache2/mods-available' apache['conf_file'] = '/etc/apache2/httpd.conf'