diff --git a/manifests/resource/mailhost.pp b/manifests/resource/mailhost.pp index 12d044a34..0ad1d467d 100644 --- a/manifests/resource/mailhost.pp +++ b/manifests/resource/mailhost.pp @@ -3,28 +3,54 @@ # This definition creates a virtual host # # Parameters: -# [*ensure*] - Enables or disables the specified mailhost (present|absent) -# [*listen_ip*] - Default IP Address for NGINX to listen with this vHost on. Defaults to all interfaces (*) -# [*listen_port*] - Default IP Port for NGINX to listen with this vHost on. Defaults to TCP 80 -# [*listen_options*] - Extra options for listen directive like 'default' to catchall. Undef by default. -# [*ipv6_enable*] - BOOL value to enable/disable IPv6 support (false|true). Module will check to see if IPv6 -# support exists on your system before enabling. -# [*ipv6_listen_ip*] - Default IPv6 Address for NGINX to listen with this vHost on. Defaults to all interfaces (::) -# [*ipv6_listen_port*] - Default IPv6 Port for NGINX to listen with this vHost on. Defaults to TCP 80 -# [*ipv6_listen_options*] - Extra options for listen directive like 'default' to catchall. Template will allways add ipv6only=on. -# While issue jfryman/puppet-nginx#30 is discussed, default value is 'default'. -# [*index_files*] - Default index files for NGINX to read when traversing a directory -# [*ssl*] - Indicates whether to setup SSL bindings for this mailhost. -# [*ssl_cert*] - Pre-generated SSL Certificate file to reference for SSL Support. This is not generated by this module. -# [*ssl_protocols*] - SSL protocols enabled. Defaults to nginx::config::ssl_protocols -# [*ssl_ciphers*] - Override default SSL ciphers (defaults to nginx::config::ssl_ciphers) -# [*ssl_key*] - Pre-generated SSL Key file to reference for SSL Support. This is not generated by this module. -# [*ssl_port*] - Default IP Port for NGINX to listen with this SSL vHost on. Defaults to TCP 443 -# [*starttls*] - Enable STARTTLS support: (on|off|only) -# [*protocol*] - Mail protocol to use: (imap|pop3|smtp) -# [*auth_http*] - With this directive you can set the URL to the external HTTP-like server for authorization. -# [*xclient*] - Whether to use xclient for smtp (on|off) -# [*server_name*] - List of mailhostnames for which this mailhost will respond. Default [$name]. +# [*ensure*] - Enables or disables the specified mailhost (present|absent) +# [*listen_ip*] - Default IP Address for NGINX to listen with this vHost on. Defaults to all interfaces (*) +# [*listen_port*] - Default IP Port for NGINX to listen with this vHost on. Defaults to TCP 80 +# [*listen_options*] - Extra options for listen directive like 'default' to catchall. Undef by default. +# [*ipv6_enable*] - BOOL value to enable/disable IPv6 support (false|true). Module will check to see if IPv6 +# support exists on your system before enabling. +# [*ipv6_listen_ip*] - Default IPv6 Address for NGINX to listen with this vHost on. Defaults to all interfaces (::) +# [*ipv6_listen_port*] - Default IPv6 Port for NGINX to listen with this vHost on. Defaults to TCP 80 +# [*ipv6_listen_options*] - Extra options for listen directive like 'default' to catchall. Template will allways add ipv6only=on. +# While issue jfryman/puppet-nginx#30 is discussed, default value is 'default'. +# [*index_files*] - Default index files for NGINX to read when traversing a directory +# [*ssl*] - Indicates whether to setup SSL bindings for this mailhost. +# [*ssl_cert*] - Pre-generated SSL Certificate file to reference for SSL Support. This is not generated by this module. +# [*ssl_ciphers*] - Override default SSL ciphers. Defaults to nginx::ssl_ciphers +# [*ssl_client_cert*] - Pre-generated SSL Certificate file to reference for client verify SSL Support. This is not generated by this module. +# [*ssl_crl*] - String: Specifies CRL path in file system +# [*ssl_dhparam*] - This directive specifies a file containing Diffie-Hellman key agreement protocol cryptographic parameters, in PEM +# format, utilized for exchanging session keys between server and client. +# [*ssl_ecdh_curve*] - This directive specifies a curve for ECDHE ciphers. +# [*ssl_key*] - Pre-generated SSL Key file to reference for SSL Support. This is not generated by this module. +# [*ssl_password_file*] - This directive specifies a file containing passphrases for secret keys. +# [*ssl_port*] - Default IP Port for NGINX to listen with this SSL vHost on. Defaults to TCP 443 +# [*ssl_protocols*] - SSL protocols enabled. Defaults to nginx::ssl_protocols +# [*ssl_session_cache*] - Sets the type and size of the session cache. +# [*ssl_session_ticket_key*] - This directive specifies a file containing secret key used to encrypt and decrypt TLS session tickets. +# [*ssl_session_tickets*] - Whether to enable or disable session resumption through TLS session tickets. +# [*ssl_session_timeout*] - String: Specifies a time during which a client may reuse the session parameters stored in a cache. +# Defaults to 5m. +# [*ssl_trusted_cert*] - String: Specifies a file with trusted CA certificates in the PEM format used to verify client +# certificates and OCSP responses if ssl_stapling is enabled. +# [*ssl_verify_depth*] - Sets the verification depth in the client certificates chain. +# [*starttls*] - Enable STARTTLS support: (on|off|only) +# [*protocol*] - Mail protocol to use: (imap|pop3|smtp) +# [*auth_http*] - With this directive you can set the URL to the external HTTP-like server for authorization. +# [*xclient*] - Whether to use xclient for smtp (on|off) +# [*imap_auth*] - Sets permitted methods of authentication for IMAP clients. +# [*imap_capabilities*] - Sets the IMAP protocol extensions list that is passed to the client in response to the CAPABILITY command. +# [*imap_client_buffer*] - Sets the IMAP commands read buffer size. +# [*pop3_auth*] - Sets permitted methods of authentication for POP3 clients. +# [*pop3_capabilities*] - Sets the POP3 protocol extensions list that is passed to the client in response to the CAPA command. +# [*smtp_auth*] - Sets permitted methods of SASL authentication for SMTP clients. +# [*smtp_capabilities*] - Sets the SMTP protocol extensions list that is passed to the client in response to the EHLO command. +# [*proxy_pass_error_message*] - Indicates whether to pass the error message obtained during the authentication on the backend to the client. +# [*server_name*] - List of mailhostnames for which this mailhost will respond. Default [$name]. +# [*raw_prepend*] - A single string, or an array of strings to prepend to the server directive (after mailhost_cfg_prepend directive). NOTE: YOU are responsible for a semicolon on each line that requires one. +# [*raw_append*] - A single string, or an array of strings to append to the server directive (after mailhost_cfg_append directive). NOTE: YOU are responsible for a semicolon on each line that requires one. +# [*mailhost_cfg_append*] - It expects a hash with custom directives to put after everything else inside vhost +# [*mailhost_cfg_prepend*] - It expects a hash with custom directives to put before everything else inside vhost # # Actions: # @@ -45,25 +71,48 @@ # } define nginx::resource::mailhost ( $listen_port, - $ensure = 'present', - $listen_ip = '*', - $listen_options = undef, - $ipv6_enable = false, - $ipv6_listen_ip = '::', - $ipv6_listen_port = 80, - $ipv6_listen_options = 'default ipv6only=on', - $ssl = false, - $ssl_cert = undef, - $ssl_protocols = $::nginx::ssl_protocols, - $ssl_ciphers = $::nginx::ssl_ciphers, - $ssl_key = undef, - $ssl_port = undef, - $starttls = 'off', - $protocol = undef, - $auth_http = undef, - $auth_http_header = undef, - $xclient = 'on', - $server_name = [$name] + $ensure = 'present', + $listen_ip = '*', + $listen_options = undef, + $ipv6_enable = false, + $ipv6_listen_ip = '::', + $ipv6_listen_port = 80, + $ipv6_listen_options = 'default ipv6only=on', + $ssl = false, + $ssl_cert = undef, + $ssl_ciphers = $::nginx::ssl_ciphers, + $ssl_client_cert = undef, + $ssl_crl = undef, + $ssl_dhparam = undef, + $ssl_ecdh_curve = undef, + $ssl_key = undef, + $ssl_password_file = undef, + $ssl_port = undef, + $ssl_protocols = $::nginx::ssl_protocols, + $ssl_session_cache = undef, + $ssl_session_ticket_key = undef, + $ssl_session_tickets = undef, + $ssl_session_timeout = '5m', + $ssl_trusted_cert = undef, + $ssl_verify_depth = undef, + $starttls = 'off', + $protocol = undef, + $auth_http = undef, + $auth_http_header = undef, + $xclient = 'on', + $imap_auth = undef, + $imap_capabilities = undef, + $imap_client_buffer = undef, + $pop3_auth = undef, + $pop3_capabilities = undef, + $smtp_auth = undef, + $smtp_capabilities = undef, + $raw_prepend = undef, + $raw_append = undef, + $mailhost_cfg_prepend = undef, + $mailhost_cfg_append = undef, + $proxy_pass_error_message = 'off', + $server_name = [$name] ) { $root_group = $::nginx::root_group @@ -104,7 +153,6 @@ validate_string($ssl_cert) } validate_string($ssl_protocols) - validate_string($ssl_ciphers) if ($ssl_key != undef) { validate_string($ssl_key) } @@ -113,9 +161,41 @@ warning('DEPRECATION: String $ssl_port must be converted to an integer. Integer string support will be removed in a future release.') } elsif !is_integer($ssl_port) { - fail('$ssl_port must be an integer.') + fail('$ssl_port must be an integer.') } } + validate_string($ssl_ciphers) + if ($ssl_client_cert != undef) { + validate_string($ssl_client_cert) + } + if ($ssl_crl != undef) { + validate_string($ssl_crl) + } + if ($ssl_dhparam != undef) { + validate_string($ssl_dhparam) + } + if ($ssl_ecdh_curve != undef) { + validate_string($ssl_ecdh_curve) + } + if ($ssl_session_cache != undef) { + validate_string($ssl_session_cache) + } + if ($ssl_session_ticket_key != undef) { + validate_string($ssl_session_ticket_key) + } + if ($ssl_session_tickets != undef) { + validate_string($ssl_session_tickets) + } + validate_string($ssl_session_timeout) + if ($ssl_password_file != undef) { + validate_string($ssl_password_file) + } + if ($ssl_trusted_cert != undef) { + validate_string($ssl_trusted_cert) + } + if ($ssl_verify_depth != undef) and (!is_integer($ssl_verify_depth)) { + fail('$ssl_verify_depth must be an integer.') + } validate_re($starttls, '^(on|only|off)$', "${starttls} is not supported for starttls. Allowed values are 'on', 'only' and 'off'.") if ($protocol != undef) { @@ -128,6 +208,48 @@ validate_string($auth_http_header) } validate_string($xclient) + if ($imap_auth != undef) { + validate_string($imap_auth) + } + if ($imap_capabilities != undef) { + validate_array($imap_capabilities) + } + if ($imap_client_buffer != undef) { + validate_string($imap_client_buffer) + } + if ($pop3_auth != undef) { + validate_string($pop3_auth) + } + if ($pop3_capabilities != undef) { + validate_array($pop3_capabilities) + } + if ($smtp_auth != undef) { + validate_string($smtp_auth) + } + if ($smtp_capabilities != undef) { + validate_array($smtp_capabilities) + } + if ($raw_prepend != undef) { + if (is_array($raw_prepend)) { + validate_array($raw_prepend) + } else { + validate_string($raw_prepend) + } + } + if ($raw_append != undef) { + if (is_array($raw_append)) { + validate_array($raw_append) + } else { + validate_string($raw_append) + } + } + if ($mailhost_cfg_prepend != undef) { + validate_hash($mailhost_cfg_prepend) + } + if ($mailhost_cfg_append != undef) { + validate_hash($mailhost_cfg_append) + } + validate_string($proxy_pass_error_message) validate_array($server_name) $config_dir = "${::nginx::conf_dir}/conf.mail.d" diff --git a/spec/defines/resource_mailhost_spec.rb b/spec/defines/resource_mailhost_spec.rb index 0c3429dcf..be20b14d1 100644 --- a/spec/defines/resource_mailhost_spec.rb +++ b/spec/defines/resource_mailhost_spec.rb @@ -49,31 +49,31 @@ title: 'should enable IPv6', attr: 'ipv6_enable', value: true, - match: ' listen [::]:80 default ipv6only=on;' + match: ' listen [::]:80 default ipv6only=on;' }, { title: 'should not enable IPv6', attr: 'ipv6_enable', value: false, - notmatch: %r{ listen \[::\]:80 default ipv6only=on;} + notmatch: %r{ listen \[::\]:80 default ipv6only=on;} }, { title: 'should set the IPv6 listen IP', attr: 'ipv6_listen_ip', value: '2001:0db8:85a3:0000:0000:8a2e:0370:7334', - match: ' listen [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80 default ipv6only=on;' + match: ' listen [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80 default ipv6only=on;' }, { title: 'should set the IPv6 listen port', attr: 'ipv6_listen_port', value: 45, - match: ' listen [::]:45 default ipv6only=on;' + match: ' listen [::]:45 default ipv6only=on;' }, { title: 'should set the IPv6 listen options', attr: 'ipv6_listen_options', value: 'spdy', - match: ' listen [::]:80 spdy;' + match: ' listen [::]:80 spdy;' }, { title: 'should set servername(s)', @@ -122,6 +122,48 @@ attr: 'starttls', value: 'off', notmatch: %r{ ssl_session_timeout 5m;} + }, + { + title: 'should contain raw_prepend directives', + attr: 'raw_prepend', + value: [ + 'if (a) {', + ' b;', + '}' + ], + match: %r{^\s+if \(a\) \{\n\s++b;\n\s+\}} + }, + { + title: 'should contain raw_append directives', + attr: 'raw_append', + value: [ + 'if (a) {', + ' b;', + '}' + ], + match: %r{^\s+if \(a\) \{\n\s++b;\n\s+\}} + }, + { + title: 'should contain ordered prepended directives', + attr: 'mailhost_cfg_prepend', + value: { 'test1' => 'test value 1', 'test2' => ['test value 2a', 'test value 2b'], 'test3' => 'test value 3' }, + match: [ + ' test1 test value 1;', + ' test2 test value 2a;', + ' test2 test value 2b;', + ' test3 test value 3;' + ] + }, + { + title: 'should contain ordered appended directives', + attr: 'mailhost_cfg_append', + value: { 'test1' => 'test value 1', 'test2' => ['test value 2a', 'test value 2b'], 'test3' => 'test value 3' }, + match: [ + ' test1 test value 1;', + ' test2 test value 2a;', + ' test2 test value 2b;', + ' test3 test value 3;' + ] } ].each do |param| context "when #{param[:attr]} is #{param[:value]}" do @@ -137,10 +179,140 @@ it { is_expected.to contain_concat__fragment("#{title}-header") } it param[:title] do - lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") - expect(lines & Array(param[:match])).to eq(Array(param[:match])) - Array(param[:notmatch]).each do |item| - is_expected.to contain_concat__fragment("#{title}-header").without_content(item) + matches = Array(param[:match]) + + if matches.all? { |m| m.is_a? Regexp } + matches.each { |item| is_expected.to contain_concat__fragment("#{title}-header").with_content(item) } + else + lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") + expect(lines & Array(param[:match])).to eq(Array(param[:match])) + end + end + end + end + end + + describe 'mailhost template content for imap' do + [ + { + title: 'should set imap_auth', + attr: 'imap_auth', + value: 'login', + match: ' imap_auth login;' + }, + { + title: 'should set imap_capabilities', + attr: 'imap_capabilities', + value: ['"SIZE 52428800"', 'IMAP4rev1', 'UIDPLUS'], + match: ' imap_capabilities "SIZE 52428800" IMAP4rev1 UIDPLUS;' + }, + { + title: 'should set imap_client_buffer', + attr: 'imap_client_buffer', + value: '8k', + match: ' imap_client_buffer 8k;' + } + ].each do |param| + context "when #{param[:attr]} is #{param[:value]}" do + let :default_params do + { + listen_port: 25, + ipv6_enable: true, + protocol: 'imap' + } + end + + let(:params) { default_params.merge(param[:attr].to_sym => param[:value]) } + + it { is_expected.to contain_concat__fragment("#{title}-header") } + it param[:title] do + matches = Array(param[:match]) + + if matches.all? { |m| m.is_a? Regexp } + matches.each { |item| is_expected.to contain_concat__fragment("#{title}-header").with_content(item) } + else + lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") + expect(lines & Array(param[:match])).to eq(Array(param[:match])) + end + end + end + end + end + + describe 'mailhost template content for pop3' do + [ + { + title: 'should set pop3_auth', + attr: 'pop3_auth', + value: 'login', + match: ' pop3_auth login;' + }, + { + title: 'should set pop3_capabilities', + attr: 'pop3_capabilities', + value: %w(TOP USER UIDL), + match: ' pop3_capabilities TOP USER UIDL;' + } + ].each do |param| + context "when #{param[:attr]} is #{param[:value]}" do + let :default_params do + { + listen_port: 25, + ipv6_enable: true, + protocol: 'pop3' + } + end + let(:params) { default_params.merge(param[:attr].to_sym => param[:value]) } + + it { is_expected.to contain_concat__fragment("#{title}-header") } + it param[:title] do + matches = Array(param[:match]) + + if matches.all? { |m| m.is_a? Regexp } + matches.each { |item| is_expected.to contain_concat__fragment("#{title}-header").with_content(item) } + else + lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") + expect(lines & Array(param[:match])).to eq(Array(param[:match])) + end + end + end + end + end + + describe 'mailhost template content for smtp' do + [ + { + title: 'should set smtp_auth', + attr: 'smtp_auth', + value: 'login', + match: ' smtp_auth login;' + }, + { + title: 'should set smtp_capabilities', + attr: 'smtp_capabilities', + value: %w(8BITMIME PIPELINING HELP), + match: ' smtp_capabilities 8BITMIME PIPELINING HELP;' + } + ].each do |param| + context "when #{param[:attr]} is #{param[:value]}" do + let :default_params do + { + listen_port: 25, + ipv6_enable: true, + protocol: 'smtp' + } + end + let(:params) { default_params.merge(param[:attr].to_sym => param[:value]) } + + it { is_expected.to contain_concat__fragment("#{title}-header") } + it param[:title] do + matches = Array(param[:match]) + + if matches.all? { |m| m.is_a? Regexp } + matches.each { |item| is_expected.to contain_concat__fragment("#{title}-header").with_content(item) } + else + lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") + expect(lines & Array(param[:match])).to eq(Array(param[:match])) end end end @@ -150,16 +322,16 @@ describe 'mailhost template content (SSL enabled)' do [ { - title: 'should enable SSL', + title: 'should set starttls', attr: 'starttls', value: 'on', - match: ' ssl_session_timeout 5m;' + match: ' starttls on;' }, { - title: 'should enable SSL', + title: 'should set starttls', attr: 'starttls', value: 'only', - match: ' ssl_session_timeout 5m;' + match: ' starttls only;' }, { title: 'should not enable SSL', @@ -171,13 +343,97 @@ title: 'should set ssl_certificate', attr: 'ssl_cert', value: 'test-ssl-cert', - match: ' ssl_certificate test-ssl-cert;' + match: ' ssl_certificate test-ssl-cert;' }, { title: 'should set ssl_certificate_key', attr: 'ssl_key', value: 'test-ssl-cert-key', - match: ' ssl_certificate_key test-ssl-cert-key;' + match: ' ssl_certificate_key test-ssl-cert-key;' + }, + { + title: 'should set ssl_ciphers', + attr: 'ssl_ciphers', + value: 'ECDHE-ECDSA-CHACHA20-POLY1305', + match: ' ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305;' + }, + { + title: 'should set ssl_client_certificate', + attr: 'ssl_client_cert', + value: 'client-cert', + match: ' ssl_client_certificate client-cert;' + }, + { + title: 'should set ssl_crl', + attr: 'ssl_crl', + value: 'crl-file', + match: ' ssl_crl crl-file;' + }, + { + title: 'should set ssl_dhparam', + attr: 'ssl_dhparam', + value: 'dhparam-file', + match: ' ssl_dhparam dhparam-file;' + }, + { + title: 'should set ssl_ecdh_curve', + attr: 'ssl_ecdh_curve', + value: 'secp521r1', + match: ' ssl_ecdh_curve secp521r1;' + }, + { + title: 'should set ssl_client_certificate', + attr: 'ssl_client_cert', + value: 'client-cert', + match: ' ssl_client_certificate client-cert;' + }, + { + title: 'should set ssl_password_file', + attr: 'ssl_password_file', + value: 'password-file', + match: ' ssl_password_file password-file;' + }, + { + title: 'should set ssl_protocols', + attr: 'ssl_protocols', + value: 'TLSv1.2', + match: ' ssl_protocols TLSv1.2;' + }, + { + title: 'should set ssl_session_cache', + attr: 'ssl_session_cache', + value: 'none', + match: ' ssl_session_cache none;' + }, + { + title: 'should set ssl_session_ticket_key', + attr: 'ssl_session_ticket_key', + value: 'key-file', + match: ' ssl_session_ticket_key key-file;' + }, + { + title: 'should set ssl_session_tickets', + attr: 'ssl_session_tickets', + value: 'on', + match: ' ssl_session_tickets on;' + }, + { + title: 'should set ssl_session_timeout', + attr: 'ssl_session_timeout', + value: '20m', + match: ' ssl_session_timeout 20m;' + }, + { + title: 'should set ssl_trusted_certificate', + attr: 'ssl_trusted_cert', + value: 'trust-cert', + match: ' ssl_trusted_certificate trust-cert;' + }, + { + title: 'should set ssl_verify_depth', + attr: 'ssl_verify_depth', + value: 2, + match: ' ssl_verify_depth 2;' } ].each do |param| context "when #{param[:attr]} is #{param[:value]}" do @@ -193,10 +449,13 @@ it { is_expected.to contain_concat__fragment("#{title}-header") } it param[:title] do - lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") - expect(lines & Array(param[:match])).to eq(Array(param[:match])) - Array(param[:notmatch]).each do |item| - is_expected.to contain_concat__fragment("#{title}-header").without_content(item) + matches = Array(param[:match]) + + if matches.all? { |m| m.is_a? Regexp } + matches.each { |item| is_expected.to contain_concat__fragment("#{title}-header").with_content(item) } + else + lines = catalogue.resource('concat::fragment', "#{title}-header").send(:parameters)[:content].split("\n") + expect(lines & Array(param[:match])).to eq(Array(param[:match])) end end end @@ -208,38 +467,38 @@ { title: 'should set the IPv4 SSL listen port', attr: 'ssl_port', - value: '45', - match: ' listen *:45;' + value: 45, + match: ' listen *:45;' }, { title: 'should enable IPv6', attr: 'ipv6_enable', value: true, - match: ' listen [::]:587 default ipv6only=on;' + match: ' listen [::]:587 default ipv6only=on;' }, { title: 'should not enable IPv6', attr: 'ipv6_enable', value: false, - notmatch: %r{ listen \[::\]:587 default ipv6only=on;} + notmatch: %r{ listen\s+\[::\]:587 default ipv6only=on;} }, { title: 'should set the IPv6 listen IP', attr: 'ipv6_listen_ip', value: '2001:0db8:85a3:0000:0000:8a2e:0370:7334', - match: ' listen [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:587 default ipv6only=on;' + match: ' listen [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:587 default ipv6only=on;' }, { title: 'should set the IPv6 ssl port', attr: 'ssl_port', value: 45, - match: ' listen [::]:45 default ipv6only=on;' + match: ' listen [::]:45 default ipv6only=on;' }, { title: 'should set the IPv6 listen options', attr: 'ipv6_listen_options', value: 'spdy', - match: ' listen [::]:587 spdy;' + match: ' listen [::]:587 spdy;' }, { title: 'should set servername(s)', @@ -275,25 +534,25 @@ title: 'should set ssl_protocols', attr: 'ssl_protocols', value: 'test-ssl-protocol', - match: ' ssl_protocols test-ssl-protocol;' + match: ' ssl_protocols test-ssl-protocol;' }, { title: 'should set ssl_ciphers', attr: 'ssl_ciphers', value: 'test-ssl-ciphers', - match: ' ssl_ciphers test-ssl-ciphers;' + match: ' ssl_ciphers test-ssl-ciphers;' }, { title: 'should set ssl_certificate', attr: 'ssl_cert', value: 'test-ssl-cert', - match: ' ssl_certificate test-ssl-cert;' + match: ' ssl_certificate test-ssl-cert;' }, { title: 'should set ssl_certificate_key', attr: 'ssl_key', value: 'test-ssl-cert-key', - match: ' ssl_certificate_key test-ssl-cert-key;' + match: ' ssl_certificate_key test-ssl-cert-key;' } ].each do |param| context "when #{param[:attr]} is #{param[:value]}" do @@ -313,10 +572,13 @@ it { is_expected.to contain_concat__fragment("#{title}-ssl") } it param[:title] do - lines = catalogue.resource('concat::fragment', "#{title}-ssl").send(:parameters)[:content].split("\n") - expect(lines & Array(param[:match])).to eq(Array(param[:match])) - Array(param[:notmatch]).each do |item| - is_expected.to contain_concat__fragment("#{title}-ssl").without_content(item) + matches = Array(param[:match]) + + if matches.all? { |m| m.is_a? Regexp } + matches.each { |item| is_expected.to contain_concat__fragment("#{title}-ssl").with_content(item) } + else + lines = catalogue.resource('concat::fragment', "#{title}-ssl").send(:parameters)[:content].split("\n") + expect(lines & Array(param[:match])).to eq(Array(param[:match])) end end end diff --git a/templates/mailhost/mailhost.erb b/templates/mailhost/mailhost.erb index c84c1676c..116052d44 100644 --- a/templates/mailhost/mailhost.erb +++ b/templates/mailhost/mailhost.erb @@ -1,5 +1,23 @@ # MANAGED BY PUPPET server { +<% if @mailhost_cfg_prepend -%> + <%- @mailhost_cfg_prepend.sort_by{ |k,v| k}.each do |key,value| -%> + <%- if value.is_a?(Hash) -%> + <%- value.sort_by {|k,v| k}.each do |subkey,subvalue| -%> + <%- Array(subvalue).each do |asubvalue| -%> + <%= key %> <%= subkey %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- else -%> + <%- Array(value).each do |asubvalue| -%> + <%= key %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- end -%> +<% end -%> +<% Array(@raw_prepend).each do |line| -%> + <%= line %> +<% end -%> <%- if @listen_ip.is_a?(Array) then -%> <%- @listen_ip.each do |ip| -%> listen <%= ip %>:<%= @listen_port %><% if @listen_options %> <%= @listen_options %><% end %>; @@ -11,31 +29,36 @@ server { <%- if @ipv6_enable && (defined? @ipaddress6) -%> <%- if @ipv6_listen_ip.is_a?(Array) then -%> <%- @ipv6_listen_ip.each do |ipv6| -%> - listen [<%= ipv6 %>]:<%= @ipv6_listen_port %> <% if @ipv6_listen_options %><%= @ipv6_listen_options %><% end %>; + listen [<%= ipv6 %>]:<%= @ipv6_listen_port %> <% if @ipv6_listen_options %><%= @ipv6_listen_options %><% end %>; <%- end -%> <%- else -%> - listen [<%= @ipv6_listen_ip %>]:<%= @ipv6_listen_port %> <% if @ipv6_listen_options %><%= @ipv6_listen_options %><% end %>; + listen [<%= @ipv6_listen_ip %>]:<%= @ipv6_listen_port %> <% if @ipv6_listen_options %><%= @ipv6_listen_options %><% end %>; <%- end -%> <%- end -%> - server_name <%= @server_name.join(" ") %>; - protocol <%= @protocol %>; - xclient <%= @xclient %>; - auth_http <%= @auth_http %>; - <%- if @auth_http_header -%> - auth_http_header <%= @auth_http_header %>; - <%- end -%> - starttls <%= @starttls %>; - <% if @starttls == 'on' || @starttls == 'only' %> - ssl_certificate <%= @ssl_cert %>; - ssl_certificate_key <%= @ssl_key %>; - - ssl_session_timeout 5m; +<%= scope.function_template(["nginx/mailhost/mailhost_common.erb"]) -%> - ssl_protocols TLSv1; - - # Suggested from https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx_configuration_details and https://weakdh.org/sysadmin.html - ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; + ssl off; + starttls <%= @starttls %>; - ssl_prefer_server_ciphers on; +<% if @starttls == 'on' || @starttls == 'only' %> +<%= scope.function_template(["nginx/mailhost/mailhost_ssl_settings.erb"]) -%> +<%- end -%> +<% if @mailhost_cfg_append -%> + <%- @mailhost_cfg_append.sort_by{ |k,v| k}.each do |key,value| -%> + <%- if value.is_a?(Hash) -%> + <%- value.sort_by {|k,v| k}.each do |subkey,subvalue| -%> + <%- Array(subvalue).each do |asubvalue| -%> + <%= key %> <%= subkey %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- else -%> + <%- Array(value).each do |asubvalue| -%> + <%= key %> <%= asubvalue %>; + <%- end -%> + <%- end -%> <%- end -%> +<% end -%> +<% Array(@raw_append).each do |line| -%> + <%= line %> +<% end -%> } diff --git a/templates/mailhost/mailhost_common.erb b/templates/mailhost/mailhost_common.erb new file mode 100644 index 000000000..287dd6d09 --- /dev/null +++ b/templates/mailhost/mailhost_common.erb @@ -0,0 +1,41 @@ + server_name <%= @server_name.join(" ") %>; +<% if defined? @protocol -%> + protocol <%= @protocol %>; +<% end -%> + xclient <%= @xclient %>; +<% if defined? @auth_http -%> + auth_http <%= @auth_http %>; +<% end -%> +<%- if @auth_http_header -%> + auth_http_header <%= @auth_http_header %>; +<%- end -%> + + proxy_pass_error_message <%= @proxy_pass_error_message %>; + +<% if @protocol == 'imap' %> +<% if defined? @imap_auth -%> + imap_auth <%= @imap_auth %>; +<% end -%> +<% if defined? @imap_capabilities -%> + imap_capabilities <%= @imap_capabilities.join(" ") %>; +<% end -%> +<% if defined? @imap_client_buffer -%> + imap_client_buffer <%= @imap_client_buffer %>; +<% end -%> +<%- end -%> +<% if @protocol == 'pop3' %> +<% if defined? @pop3_auth -%> + pop3_auth <%= @pop3_auth %>; +<% end -%> +<% if defined? @pop3_capabilities -%> + pop3_capabilities <%= @pop3_capabilities.join(" ") %>; +<% end -%> +<%- end -%> +<% if @protocol == 'smtp' %> +<% if defined? @smtp_auth -%> + smtp_auth <%= @smtp_auth %>; +<% end -%> +<% if defined? @smtp_capabilities -%> + smtp_capabilities <%= @smtp_capabilities.join(" ") %>; +<% end -%> +<%- end -%> diff --git a/templates/mailhost/mailhost_ssl.erb b/templates/mailhost/mailhost_ssl.erb index 603ec69d2..09e066240 100644 --- a/templates/mailhost/mailhost_ssl.erb +++ b/templates/mailhost/mailhost_ssl.erb @@ -1,36 +1,62 @@ # MANAGED BY PUPPET server { +<% if @mailhost_cfg_prepend -%> + <%- @mailhost_cfg_prepend.sort_by{ |k,v| k}.each do |key,value| -%> + <%- if value.is_a?(Hash) -%> + <%- value.sort_by {|k,v| k}.each do |subkey,subvalue| -%> + <%- Array(subvalue).each do |asubvalue| -%> + <%= key %> <%= subkey %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- else -%> + <%- Array(value).each do |asubvalue| -%> + <%= key %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- end -%> +<% end -%> +<% Array(@raw_prepend).each do |line| -%> + <%= line %> +<% end -%> <%- if @listen_ip.is_a?(Array) then -%> <%- @listen_ip.each do |ip| -%> listen <%= ip %>:<%= @ssl_port %>; <%- end -%> <%- else -%> - listen <%= @listen_ip %>:<%= @ssl_port %>; + listen <%= @listen_ip %>:<%= @ssl_port %>; <%- end -%> <%# check to see if ipv6 support exists in the kernel before applying -%> <%- if @ipv6_enable && (defined? @ipaddress6) -%> <%- if @ipv6_listen_ip.is_a?(Array) then -%> <%- @ipv6_listen_ip.each do |ipv6| -%> - listen [<%= ipv6 %>]:<%= @ssl_port %><% if @ipv6_listen_options %> <%= @ipv6_listen_options %><% end %>; + listen [<%= ipv6 %>]:<%= @ssl_port %><% if @ipv6_listen_options %> <%= @ipv6_listen_options %><% end %>; <%- end -%> <%- else -%> - listen [<%= @ipv6_listen_ip %>]:<%= @ssl_port %><% if @ipv6_listen_options %> <%= @ipv6_listen_options %><% end %>; + listen [<%= @ipv6_listen_ip %>]:<%= @ssl_port %><% if @ipv6_listen_options %> <%= @ipv6_listen_options %><% end %>; <%- end -%> <%- end -%> - server_name <%= @server_name.join(" ") %>; - protocol <%= @protocol %>; - xclient <%= @xclient %>; - auth_http <%= @auth_http %>; - <%- if @auth_http_header -%> - auth_http_header <%= @auth_http_header %>; - <%- end -%> +<%= scope.function_template(["nginx/mailhost/mailhost_common.erb"]) -%> - ssl on; - ssl_certificate <%= @ssl_cert %>; - ssl_certificate_key <%= @ssl_key %>; - ssl_session_timeout 5m; + ssl on; + starttls off; - ssl_protocols <%= @ssl_protocols %>; - ssl_ciphers <%= @ssl_ciphers %>; - ssl_prefer_server_ciphers on; +<%= scope.function_template(["nginx/mailhost/mailhost_ssl_settings.erb"]) -%> +<% if @mailhost_cfg_append -%> + <%- @mailhost_cfg_append.sort_by{ |k,v| k}.each do |key,value| -%> + <%- if value.is_a?(Hash) -%> + <%- value.sort_by {|k,v| k}.each do |subkey,subvalue| -%> + <%- Array(subvalue).each do |asubvalue| -%> + <%= key %> <%= subkey %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- else -%> + <%- Array(value).each do |asubvalue| -%> + <%= key %> <%= asubvalue %>; + <%- end -%> + <%- end -%> + <%- end -%> +<% end -%> +<% Array(@raw_append).each do |line| -%> + <%= line %> +<% end -%> } diff --git a/templates/mailhost/mailhost_ssl_settings.erb b/templates/mailhost/mailhost_ssl_settings.erb new file mode 100644 index 000000000..ef8e7500d --- /dev/null +++ b/templates/mailhost/mailhost_ssl_settings.erb @@ -0,0 +1,38 @@ + + ssl_certificate <%= @ssl_cert %>; + ssl_certificate_key <%= @ssl_key %>; + ssl_ciphers <%= @ssl_ciphers %>; +<% if defined? @ssl_client_cert -%> + ssl_client_certificate <%= @ssl_client_cert %>; + ssl_verify_client on; +<% end -%> +<%- if defined? @ssl_verify_depth -%> + ssl_verify_depth <%= @ssl_verify_depth %>; +<%- end -%> +<% if @ssl_crl -%> + ssl_crl <%= @ssl_crl %>; +<% end -%> +<% if defined? @ssl_dhparam -%> + ssl_dhparam <%= @ssl_dhparam %>; +<% end -%> +<%- if defined? @ssl_ecdh_curve -%> + ssl_ecdh_curve <%= @ssl_ecdh_curve %>; +<%- end -%> +<%- if defined? @ssl_password_file -%> + ssl_password_file <%= @ssl_password_file %>; +<%- end -%> + ssl_prefer_server_ciphers on; + ssl_protocols <%= @ssl_protocols %>; +<% if defined? @ssl_session_cache -%> + ssl_session_cache <%= @ssl_session_cache %>; +<% end -%> +<%- if defined? @ssl_session_ticket_key -%> + ssl_session_ticket_key <%= @ssl_session_ticket_key %>; +<%- end -%> +<%- if defined? @ssl_session_tickets -%> + ssl_session_tickets <%= @ssl_session_tickets %>; +<%- end -%> + ssl_session_timeout <%= @ssl_session_timeout %>; +<%- if defined? @ssl_trusted_cert -%> + ssl_trusted_certificate <%= @ssl_trusted_cert %>; +<%- end -%>