refactor: recreate profiles::postfix::gateway with parameterization

- refactor profiles::postfix::gateway as parameterized class
- move base postfix parameters, transports, and virtuals in hiera for deep merging
- convert SMTP restrictions to arrays for better readability using join()
- add postscreen enable/disable boolean with conditional master.cf configuration
- add per-domain TLS policy maps (smtp_tls_policy_maps/smtpd_tls_policy_maps)
- convert alias_maps to array parameter for flexibility
This commit is contained in:
Ben Vincent 2025-11-01 15:56:01 +11:00
parent 81f289a185
commit ed022f0de6
3 changed files with 382 additions and 231 deletions

View File

@ -158,6 +158,15 @@ lookup_options:
rke2::config_hash:
merge:
strategy: deep
postfix::configs:
merge:
strategy: deep
postfix::maps:
merge:
strategy: deep
postfix::virtuals:
merge:
strategy: deep
facts_path: '/opt/puppetlabs/facter/facts.d'

View File

@ -3,3 +3,33 @@
# additional altnames
profiles::pki::vault::alt_names:
- in-mta.main.unkin.net
# base postfix configuration (passed to postfix class)
postfix::relayhost: 'direct'
postfix::myorigin: 'main.unkin.net'
postfix::mydestination: 'blank'
postfix::mynetworks: '127.0.0.0/8 [::1]/128'
postfix::mta: true
postfix::manage_aliases: true
# profile parameters for customization
profiles::postfix::gateway::myhostname: 'in-mta.main.unkin.net'
# postfix transports
postfix::transports:
'main.unkin.net':
ensure: present
destination: 'relay'
nexthop: 'ausyd1nxvm2120.main.unkin.net:25'
# postfix virtuals
postfix::virtuals:
'root':
ensure: present
destination: 'ben@main.unkin.net'
'postmaster':
ensure: present
destination: 'ben@main.unkin.net'
'abuse':
ensure: present
destination: 'ben@main.unkin.net'

View File

@ -1,250 +1,362 @@
class profiles::postfix::gateway (
$tls_cert_file = '/etc/pki/tls/vault/certificate.pem',
$tls_key_file = '/etc/pki/tls/vault/certificate.pem',
$tls_ca_file = '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem',
Stdlib::Absolutepath $tls_cert_file = '/etc/pki/tls/vault/certificate.pem',
Stdlib::Absolutepath $tls_key_file = '/etc/pki/tls/vault/certificate.pem',
Stdlib::Absolutepath $tls_ca_file = '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem',
String $myhostname = $trusted['certname'],
String $message_size_limit = '133169152',
String $mailbox_size_limit = '133169152',
String $local_transport = 'error:No local mail delivery',
Boolean $enable_postscreen = true,
Array[String] $alias_maps = [
'hash:/etc/aliases',
'hash:/etc/postfix/aliases',
],
Array[String] $postscreen_dnsbl_sites = [
'zen.spamhaus.org*3',
'b.barracudacentral.org=127.0.0.[2..11]*2',
'bl.spameatingmonkey.net*2',
'bl.spamcop.net',
'dnsbl.sorbs.net',
'swl.spamhaus.org*-4',
'list.dnswl.org=127.[0..255].[0..255].0*-2',
'list.dnswl.org=127.[0..255].[0..255].1*-4',
'list.dnswl.org=127.[0..255].[0..255].[2..3]*-6',
],
Array[String] $smtpd_client_restrictions = [
'permit_sasl_authenticated',
'permit_mynetworks',
'reject_rbl_client zen.spamhaus.org',
],
Array[String] $smtpd_sender_restrictions = [
'permit_sasl_authenticated',
'check_sender_access hash:/etc/postfix/sender_access',
'reject_non_fqdn_sender',
'reject_unknown_sender_domain',
],
Array[String] $smtpd_recipient_restrictions = [
'permit_sasl_authenticated',
'permit_mynetworks',
'reject_unauth_destination',
'reject_non_fqdn_recipient',
'reject_unknown_recipient_domain',
'check_recipient_access hash:/etc/postfix/recipient_access',
'reject_unverified_recipient',
],
Array[String] $smtpd_relay_restrictions = [
'permit_sasl_authenticated',
'permit_mynetworks',
'reject_unauth_destination',
],
Hash[String, String] $smtp_tls_policy_maps = {},
Hash[String, String] $smtpd_tls_policy_maps = {},
) {
$alias_maps = 'hash:/etc/aliases, hash:/etc/postfix/aliases'
$alias_maps_string = join($alias_maps, ', ')
class { 'postfix':
relayhost => 'direct',
myorigin => 'main.unkin.net',
mydestination => 'blank',
mynetworks => '127.0.0.0/8 [::1]/128',
alias_maps => $alias_maps,
mta => true,
manage_aliases => true,
master_smtp => 'smtp inet n - n - 1 postscreen',
master_entries => [
# Postscreen backend services
# Set master.cf configuration based on postscreen setting
if $enable_postscreen {
$master_smtp = 'smtp inet n - n - 1 postscreen'
$master_entries = [
'smtpd pass - - n - - smtpd',
'dnsblog unix - - n - 0 dnsblog',
'tlsproxy unix - - n - 0 tlsproxy',
],
]
$postscreen_configs = {
'postscreen_access_list' => {
'value' => 'permit_mynetworks, cidr:/etc/postfix/postscreen_access'
},
'postscreen_blacklist_action' => {
'value' => 'enforce'
},
'postscreen_cache_map' => {
'value' => 'btree:$data_directory/postscreen_cache'
},
'postscreen_dnsbl_action' => {
'value' => 'enforce'
},
'postscreen_dnsbl_sites' => {
'value' => join($postscreen_dnsbl_sites, ', ')
},
'postscreen_dnsbl_threshold' => {
'value' => '2'
},
'postscreen_greet_action' => {
'value' => 'enforce'
},
'postscreen_greet_banner' => {
'value' => '$smtpd_banner'
},
'postscreen_greet_wait' => {
'value' => "\${stress?2}\${stress:6}s"
},
}
} else {
$master_smtp = undef
$master_entries = []
$postscreen_configs = {}
}
postfix::config {
'alias_database':
value => $alias_maps;
'default_destination_recipient_limit':
value => '1';
'disable_vrfy_command':
value => 'yes';
'enable_long_queue_ids':
value => 'yes';
'error_notice_recipient':
value => 'root';
'header_checks':
value => 'regexp:/etc/postfix/header_checks';
'local_recipient_maps':
ensure => 'blank'; # no local mailboxes
'local_transport':
value => 'error:No local mail delivery';
'mailbox_size_limit':
value => '133169152'; # ~127MB
'message_size_limit':
value => '133169152'; # ~127MB
'myhostname':
value => 'in-mta.main.unkin.net';
'non_smtpd_milters':
ensure => 'blank';
'postscreen_access_list':
value => 'permit_mynetworks, cidr:/etc/postfix/postscreen_access';
'postscreen_blacklist_action':
value => 'enforce';
'postscreen_cache_map':
value => 'btree:$data_directory/postscreen_cache';
'postscreen_dnsbl_action':
value => 'enforce';
'postscreen_dnsbl_sites':
value => join([
'zen.spamhaus.org*3',
'b.barracudacentral.org=127.0.0.[2..11]*2',
'bl.spameatingmonkey.net*2',
'bl.spamcop.net',
'dnsbl.sorbs.net',
'swl.spamhaus.org*-4',
'list.dnswl.org=127.[0..255].[0..255].0*-2',
'list.dnswl.org=127.[0..255].[0..255].1*-4',
'list.dnswl.org=127.[0..255].[0..255].[2..3]*-6'
], ', ');
'postscreen_dnsbl_threshold':
value => '2';
'postscreen_greet_action':
value => 'enforce';
'postscreen_greet_banner':
value => '$smtpd_banner';
'postscreen_greet_wait':
value => "\${stress?2}\${stress:6}s";
'qmqpd_authorized_clients':
value => '127.0.0.1 [::1]';
'recipient_canonical_maps':
value => 'hash:/etc/postfix/recipient_canonical';
'recipient_delimiter':
value => '+';
'relay_domains':
value => 'hash:/etc/postfix/relay_domains';
'relay_recipient_maps':
value => 'hash:/etc/postfix/relay_recipients';
'sender_canonical_maps':
value => 'hash:/etc/postfix/sender_canonical';
'smtp_tls_CAfile':
value => $tls_ca_file;
'smtp_tls_mandatory_protocols':
value => '!SSLv2,!SSLv3';
'smtp_tls_note_starttls_offer':
value => 'yes';
'smtp_tls_protocols':
value => '!SSLv2,!SSLv3';
'smtp_tls_security_level':
value => 'may';
'smtp_tls_session_cache_database':
value => 'btree:/var/lib/postfix/smtp_tls_session_cache';
'smtp_use_tls':
value => 'yes';
'smtpd_banner':
value => '$myhostname ESMTP $mail_name';
'smtpd_client_restrictions':
value => 'permit_sasl_authenticated, permit_mynetworks, reject_rbl_client zen.spamhaus.org';
'smtpd_data_restrictions':
value => 'reject_unauth_pipelining';
'smtpd_delay_reject':
value => 'yes';
'smtpd_discard_ehlo_keywords':
value => 'chunking, silent-discard';
'smtpd_forbid_bare_newline':
value => 'yes';
'smtpd_forbid_bare_newline_exclusions':
value => '$mynetworks';
'smtpd_forbid_unauth_pipelining':
value => 'yes';
'smtpd_helo_required':
value => 'yes';
'smtpd_helo_restrictions':
value => 'check_helo_access hash:/etc/postfix/helo_access, reject_invalid_hostname';
'smtpd_milters':
value => 'inet:127.0.0.1:33333';
'smtpd_recipient_restrictions':
value => join([
'permit_sasl_authenticated',
'permit_mynetworks',
'reject_unauth_destination',
'reject_non_fqdn_recipient',
'reject_unknown_recipient_domain',
'check_recipient_access hash:/etc/postfix/recipient_access',
'check_policy_service inet:127.0.0.1:2501',
'reject_unverified_recipient'
], ', ');
'smtpd_relay_restrictions':
value => 'permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination';
'smtpd_sender_restrictions':
value => join([
'permit_sasl_authenticated',
'check_sender_access hash:/etc/postfix/sender_access',
'reject_non_fqdn_sender',
'reject_unknown_sender_domain'
], ', ');
'smtpd_tls_CAfile':
value => $tls_ca_file;
'smtpd_tls_cert_file':
value => $tls_cert_file;
'smtpd_tls_ciphers':
value => 'medium';
'smtpd_tls_key_file':
value => $tls_key_file;
'smtpd_tls_loglevel':
value => '1';
'smtpd_tls_mandatory_protocols':
value => '!SSLv2,!SSLv3';
'smtpd_tls_protocols':
value => '!SSLv2,!SSLv3';
'smtpd_tls_received_header':
value => 'yes';
'smtpd_tls_security_level':
value => 'may';
'smtpd_tls_session_cache_database':
value => 'btree:/var/lib/postfix/smtpd_tls_session_cache';
'smtpd_tls_session_cache_timeout':
value => '3600s';
'smtpd_use_tls':
value => 'yes';
'tls_medium_cipherlist':
value => join([
# Base postfix configuration
$base_configs = {
'alias_database' => {
'value' => $alias_maps_string
},
'default_destination_recipient_limit' => {
'value' => '1'
},
'disable_vrfy_command' => {
'value' => 'yes'
},
'enable_long_queue_ids' => {
'value' => 'yes'
},
'error_notice_recipient' => {
'value' => 'root'
},
'header_checks' => {
'value' => 'regexp:/etc/postfix/header_checks'
},
'local_recipient_maps' => {
'ensure' => 'blank'
},
'local_transport' => {
'value' => $local_transport
},
'mailbox_size_limit' => {
'value' => $mailbox_size_limit
},
'message_size_limit' => {
'value' => $message_size_limit
},
'myhostname' => {
'value' => $myhostname
},
'non_smtpd_milters' => {
'ensure' => 'blank'
},
'qmqpd_authorized_clients' => {
'value' => '127.0.0.1 [::1]'
},
'recipient_canonical_maps' => {
'value' => 'hash:/etc/postfix/recipient_canonical'
},
'recipient_delimiter' => {
'value' => '+'
},
'relay_domains' => {
'value' => 'hash:/etc/postfix/relay_domains'
},
'relay_recipient_maps' => {
'value' => 'hash:/etc/postfix/relay_recipients'
},
'sender_canonical_maps' => {
'value' => 'hash:/etc/postfix/sender_canonical'
},
'smtp_tls_CAfile' => {
'value' => $tls_ca_file
},
'smtp_tls_mandatory_protocols' => {
'value' => '!SSLv2,!SSLv3'
},
'smtp_tls_note_starttls_offer' => {
'value' => 'yes'
},
'smtp_tls_protocols' => {
'value' => '!SSLv2,!SSLv3'
},
'smtp_tls_security_level' => {
'value' => 'may'
},
'smtp_tls_session_cache_database' => {
'value' => 'btree:/var/lib/postfix/smtp_tls_session_cache'
},
'smtp_use_tls' => {
'value' => 'yes'
},
'smtpd_banner' => {
'value' => '$myhostname ESMTP $mail_name'
},
'smtpd_client_restrictions' => {
'value' => join($smtpd_client_restrictions, ', ')
},
'smtpd_data_restrictions' => {
'value' => 'reject_unauth_pipelining'
},
'smtpd_delay_reject' => {
'value' => 'yes'
},
'smtpd_discard_ehlo_keywords' => {
'value' => 'chunking, silent-discard'
},
'smtpd_forbid_bare_newline' => {
'value' => 'yes'
},
'smtpd_forbid_bare_newline_exclusions' => {
'value' => '$mynetworks'
},
'smtpd_forbid_unauth_pipelining' => {
'value' => 'yes'
},
'smtpd_helo_required' => {
'value' => 'yes'
},
'smtpd_helo_restrictions' => {
'value' => 'check_helo_access hash:/etc/postfix/helo_access, reject_invalid_hostname'
},
'smtpd_milters' => {
'value' => 'inet:127.0.0.1:33333'
},
'smtpd_recipient_restrictions' => {
'value' => join($smtpd_recipient_restrictions, ', ')
},
'smtpd_relay_restrictions' => {
'value' => join($smtpd_relay_restrictions, ', ')
},
'smtpd_sender_restrictions' => {
'value' => join($smtpd_sender_restrictions, ', ')
},
'smtpd_tls_CAfile' => {
'value' => $tls_ca_file
},
'smtpd_tls_cert_file' => {
'value' => $tls_cert_file
},
'smtpd_tls_ciphers' => {
'value' => 'medium'
},
'smtpd_tls_key_file' => {
'value' => $tls_key_file
},
'smtpd_tls_loglevel' => {
'value' => '1'
},
'smtpd_tls_mandatory_protocols' => {
'value' => '!SSLv2,!SSLv3'
},
'smtpd_tls_protocols' => {
'value' => '!SSLv2,!SSLv3'
},
'smtpd_tls_received_header' => {
'value' => 'yes'
},
'smtpd_tls_security_level' => {
'value' => 'may'
},
'smtpd_tls_session_cache_database' => {
'value' => 'btree:/var/lib/postfix/smtpd_tls_session_cache'
},
'smtpd_tls_session_cache_timeout' => {
'value' => '3600s'
},
'smtpd_use_tls' => {
'value' => 'yes'
},
'tls_medium_cipherlist' => {
'value' => join([
'ECDSA+AESGCM:ECDH+AESGCM:DH+AESGCM:ECDSA+AES:ECDH+AES:DH+AES',
'ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS'
], ':');
'tls_preempt_cipherlist':
value => 'yes';
'tls_random_source':
value => 'dev:/dev/urandom';
'unverified_recipient_reject_code':
value => '550';
'unverified_recipient_reject_reason':
value => 'No user at this address';
], ':')
},
'tls_preempt_cipherlist' => {
'value' => 'yes'
},
'tls_random_source' => {
'value' => 'dev:/dev/urandom'
},
'unverified_recipient_reject_code' => {
'value' => '550'
},
'unverified_recipient_reject_reason' => {
'value' => 'No user at this address'
},
'smtp_tls_policy_maps' => {
'value' => 'hash:/etc/postfix/smtp_tls_policy_maps'
},
'smtpd_tls_policy_maps' => {
'value' => 'hash:/etc/postfix/smtpd_tls_policy_maps'
},
}
postfix::map { 'postscreen_access':
ensure => present,
type => 'cidr',
source => 'puppet:///modules/profiles/postfix/gateway/postscreen_access'
}
postfix::map { 'relay_recipients':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/relay_recipients'
}
postfix::map { 'relay_domains':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/relay_domains'
}
postfix::map { 'aliases':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/aliases'
}
postfix::map { 'helo_access':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/helo_access'
}
postfix::map { 'sender_access':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/sender_access'
}
postfix::map { 'recipient_access':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/recipient_access'
}
postfix::map { 'recipient_canonical':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/recipient_canonical'
}
postfix::map { 'sender_canonical':
ensure => present,
type => 'hash',
source => 'puppet:///modules/profiles/postfix/gateway/sender_canonical'
# Generate TLS policy content
$smtp_tls_content = !empty($smtp_tls_policy_maps) ? {
true => $smtp_tls_policy_maps.map |$domain, $policy| { "${domain} ${policy}" }.join("\n"),
false => "# SMTP TLS policy map\n# Format: destination policy\n# Example: gmail.com encrypt"
}
postfix::transport {
'main.unkin.net':
ensure => present,
destination => 'relay',
nexthop => 'ausyd1nxvm2120.main.unkin.net:25';
$smtpd_tls_content = !empty($smtpd_tls_policy_maps) ? {
true => $smtpd_tls_policy_maps.map |$client, $policy| { "${client} ${policy}" }.join("\n"),
false => "# SMTPD TLS policy map\n# Format: client policy\n# Example: 192.168.1.0/24 encrypt"
}
postfix::virtual {
'root':
ensure => present,
destination => 'ben@main.unkin.net';
'postmaster':
ensure => present,
destination => 'ben@main.unkin.net';
'abuse':
ensure => present,
destination => 'ben@main.unkin.net';
# Postfix maps (includes TLS policy maps)
$postfix_maps = {
'postscreen_access' => {
'ensure' => 'present',
'type' => 'cidr',
'source' => 'puppet:///modules/profiles/postfix/gateway/postscreen_access'
},
'relay_recipients' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/relay_recipients'
},
'relay_domains' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/relay_domains'
},
'aliases' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/aliases'
},
'helo_access' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/helo_access'
},
'sender_access' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/sender_access'
},
'recipient_access' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/recipient_access'
},
'recipient_canonical' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/recipient_canonical'
},
'sender_canonical' => {
'ensure' => 'present',
'type' => 'hash',
'source' => 'puppet:///modules/profiles/postfix/gateway/sender_canonical'
},
'smtp_tls_policy_maps' => {
'ensure' => 'present',
'type' => 'hash',
'content' => $smtp_tls_content,
},
'smtpd_tls_policy_maps' => {
'ensure' => 'present',
'type' => 'hash',
'content' => $smtpd_tls_content,
},
}
# Merge base configs with postscreen configs
$all_configs = $base_configs + $postscreen_configs
class { 'postfix':
master_smtp => $master_smtp,
master_entries => $master_entries,
alias_maps => $alias_maps_string,
configs => $all_configs,
maps => $postfix_maps,
}
}