diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..306b999 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +sources/ \ No newline at end of file diff --git a/hieradata/roles/infra/mail.yaml b/hieradata/roles/infra/mail.yaml new file mode 100644 index 0000000..40e19b4 --- /dev/null +++ b/hieradata/roles/infra/mail.yaml @@ -0,0 +1,21 @@ +--- + +# Common mail server configuration + +# base postfix configuration (passed to postfix class) +postfix::relayhost: 'direct' +postfix::myorigin: 'main.unkin.net' +postfix::mta: true +postfix::manage_aliases: true + +# Common postfix virtuals for all mail servers +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' diff --git a/hieradata/roles/infra/mail/backend.yaml b/hieradata/roles/infra/mail/backend.yaml new file mode 100644 index 0000000..ac58e5c --- /dev/null +++ b/hieradata/roles/infra/mail/backend.yaml @@ -0,0 +1,49 @@ +--- + +# Backend-specific configuration + +# additional altnames +profiles::pki::vault::alt_names: + - mail.main.unkin.net + +# backend-specific postfix configuration +postfix::mydestination: 'localhost' +postfix::mynetworks: '127.0.0.0/8 [::1]/128 10.10.12.0/24' +postfix::smtp_listen: ['0.0.0.0', '::'] + +# disable postscreen (backend doesn't need it) +profiles::postfix::gateway::enable_postscreen: false +profiles::postfix::gateway::myhostname: 'mail.main.unkin.net' + +# enable dovecot integration +profiles::postfix::gateway::enable_dovecot: true +profiles::postfix::gateway::virtual_mailbox_domains: + - 'main.unkin.net' +profiles::postfix::gateway::virtual_mailbox_base: '/shared/apps/maildata' + +# use built-in dovecot LDA support +postfix::use_dovecot_lda: true +postfix::mail_user: 'vmail:vmail' + +# virtual maps using gateway profile parameters +profiles::postfix::gateway::virtual_mailbox_maps: + 'ben@main.unkin.net': 'main.unkin.net/ben/' + 'root@main.unkin.net': 'main.unkin.net/ben/' + 'postmaster@main.unkin.net': 'main.unkin.net/ben/' + 'abuse@main.unkin.net': 'main.unkin.net/ben/' + +profiles::postfix::gateway::virtual_alias_maps: {} + +# simplified restrictions for backend (no RBL checks) +profiles::postfix::gateway::smtpd_client_restrictions: + - 'permit_mynetworks' + - 'reject_unauth_destination' + +profiles::postfix::gateway::smtpd_sender_restrictions: + - 'permit_mynetworks' + - 'reject_non_fqdn_sender' + +profiles::postfix::gateway::smtpd_recipient_restrictions: + - 'permit_mynetworks' + - 'reject_non_fqdn_recipient' + - 'reject_unauth_destination' diff --git a/hieradata/roles/infra/mail/gateway.yaml b/hieradata/roles/infra/mail/gateway.yaml index 458b528..2983f83 100644 --- a/hieradata/roles/infra/mail/gateway.yaml +++ b/hieradata/roles/infra/mail/gateway.yaml @@ -1,16 +1,15 @@ --- +# Gateway-specific configuration + # 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' +# gateway-specific postfix configuration postfix::mydestination: 'blank' postfix::mynetworks: '127.0.0.0/8 [::1]/128' -postfix::mta: true -postfix::manage_aliases: true +postfix::smtp_listen: '0.0.0.0' # profile parameters for customization profiles::postfix::gateway::myhostname: 'in-mta.main.unkin.net' @@ -38,15 +37,3 @@ postfix::transports: 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' diff --git a/site/profiles/manifests/dovecot/backend.pp b/site/profiles/manifests/dovecot/backend.pp new file mode 100644 index 0000000..6d2ae1a --- /dev/null +++ b/site/profiles/manifests/dovecot/backend.pp @@ -0,0 +1,155 @@ +class profiles::dovecot::backend ( + 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', + Stdlib::Absolutepath $mail_location = '/shared/apps/maildata/%u', + String $hostname = $trusted['certname'], + Array[String] $listen = ['*', '::'], + Array[String] $protocols = ['imap', 'imaps'], + Hash[String, Any] $auth_config = {}, + Hash[String, Any] $mail_config = {}, + Hash[String, Any] $ssl_config = {}, + Hash[String, Any] $logging_config = {}, +) { + + # Ensure the maildata directory exists + file { '/shared/apps/maildata': + ensure => directory, + owner => 'vmail', + group => 'vmail', + mode => '0755', + } + + # Create vmail user for dovecot + user { 'vmail': + ensure => present, + uid => 5000, + gid => 5000, + home => '/shared/apps/maildata', + shell => '/usr/sbin/nologin', + managehome => false, + system => true, + } + + group { 'vmail': + ensure => present, + gid => 5000, + system => true, + } + + # Main dovecot configuration + $main_config = { + 'values' => { + 'listen' => join($listen, ', '), + 'protocols' => join($protocols, ' '), + 'default_login_user' => 'vmail', + 'default_internal_user' => 'vmail', + 'first_valid_uid' => '5000', + 'last_valid_uid' => '5000', + 'first_valid_gid' => '5000', + 'last_valid_gid' => '5000', + 'mail_uid' => 'vmail', + 'mail_gid' => 'vmail', + 'mail_location' => "maildir:${mail_location}", + 'login_trusted_networks' => '10.0.0.0/8 127.0.0.0/8 [::1]/128', + } + } + + # SSL configuration + $default_ssl_config = { + 'ssl' => { + 'values' => { + 'ssl' => 'required', + 'ssl_cert' => "<${tls_cert_file}", + 'ssl_key' => "<${tls_key_file}", + 'ssl_ca' => "<${tls_ca_file}", + 'ssl_protocols' => '!SSLv2 !SSLv3', + 'ssl_cipher_list' => join([ + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES', + 'ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS' + ], ':'), + 'ssl_prefer_server_ciphers' => 'yes', + 'ssl_dh_parameters_length' => '2048', + } + } + } + + # Authentication configuration + $default_auth_config = { + 'auth' => { + 'values' => { + 'auth_mechanisms' => 'plain login', + 'auth_username_format' => '%Lu', + 'auth_default_realm' => 'main.unkin.net', + } + }, + 'auth-system' => { + 'values' => { + 'passdb' => '{ + driver = pam +}', + 'userdb' => '{ + driver = passwd + override_fields = uid=vmail gid=vmail home=/shared/apps/maildata/%u +}', + } + } + } + + # Mail configuration + $default_mail_config = { + 'mail' => { + 'values' => { + 'mail_plugins' => '$mail_plugins', + 'namespace inbox' => '{ + inbox = yes + location = + mailbox Drafts { + special_use = \\Drafts + } + mailbox Junk { + special_use = \\Junk + } + mailbox Sent { + special_use = \\Sent + } + mailbox "Sent Messages" { + special_use = \\Sent + } + mailbox Trash { + special_use = \\Trash + } +}', + } + } + } + + # Logging configuration + $default_logging_config = { + 'logging' => { + 'values' => { + 'log_path' => 'syslog', + 'syslog_facility' => 'mail', + 'auth_verbose' => 'yes', + 'auth_debug' => 'no', + 'mail_debug' => 'no', + } + } + } + + # Merge configurations + $final_ssl_config = deep_merge($default_ssl_config, $ssl_config) + $final_auth_config = deep_merge($default_auth_config, $auth_config) + $final_mail_config = deep_merge($default_mail_config, $mail_config) + $final_logging_config = deep_merge($default_logging_config, $logging_config) + + $all_configs = $final_ssl_config + $final_auth_config + $final_mail_config + $final_logging_config + + # Configure dovecot + class { 'dovecot': + main_config => $main_config, + configs => $all_configs, + require => [User['vmail'], Group['vmail'], File['/shared/apps/maildata']], + } + +} \ No newline at end of file diff --git a/site/profiles/manifests/postfix/gateway.pp b/site/profiles/manifests/postfix/gateway.pp index d86cbff..3038b65 100644 --- a/site/profiles/manifests/postfix/gateway.pp +++ b/site/profiles/manifests/postfix/gateway.pp @@ -47,15 +47,24 @@ class profiles::postfix::gateway ( 'permit_mynetworks', 'reject_unauth_destination', ], - Hash[String, String] $smtp_tls_policy_maps = {}, + Hash[Stdlib::Fqdn, String] $smtp_tls_policy_maps = {}, Hash[String, String] $sender_canonical_maps = {}, - Hash[String, String] $sender_access_maps = {}, + Hash[Stdlib::Email, String] $sender_access_maps = {}, Hash[String, String] $relay_recipients_maps = {}, - Hash[String, String] $relay_domains_maps = {}, + Hash[Stdlib::Fqdn, String] $relay_domains_maps = {}, Hash[String, String] $recipient_canonical_maps = {}, - Hash[String, String] $recipient_access_maps = {}, - Hash[String, String] $postscreen_access_maps = {}, + Hash[Stdlib::Email, String] $recipient_access_maps = {}, + Hash[Variant[Stdlib::IP::Address, Stdlib::IP::Address::CIDR], String] $postscreen_access_maps = {}, Hash[String, String] $helo_access_maps = {}, + Hash[Stdlib::Email, String] $virtual_mailbox_maps = {}, + Hash[Variant[Stdlib::Email, Pattern[/^@.+$/]], Stdlib::Email] $virtual_alias_maps = {}, + # Dovecot integration + Boolean $enable_dovecot = false, + Array[Stdlib::Fqdn] $virtual_mailbox_domains = [], + String $virtual_uid_maps = 'static:5000', + String $virtual_gid_maps = 'static:5000', + Stdlib::Absolutepath $virtual_mailbox_base = '/var/vmail', + String $virtual_transport = 'dovecot', ) { $alias_maps_string = join($alias_maps, ', ') @@ -281,6 +290,7 @@ class profiles::postfix::gateway ( }, } + # Postfix maps (all using templates now) $postfix_maps = { 'postscreen_access' => { @@ -333,6 +343,38 @@ class profiles::postfix::gateway ( 'type' => 'hash', 'content' => template('profiles/postfix/gateway/smtp_tls_policy_maps.erb') }, + 'virtual_mailbox_maps' => { + 'ensure' => 'present', + 'type' => 'hash', + 'content' => template('profiles/postfix/gateway/virtual_mailbox_maps.erb') + }, + 'virtual_alias_maps' => { + 'ensure' => 'present', + 'type' => 'hash', + 'content' => template('profiles/postfix/gateway/virtual_alias_maps.erb') + }, + } + + if $enable_dovecot { + postfix::config { + 'virtual_mailbox_domains': value => join($virtual_mailbox_domains, ', '); + 'virtual_mailbox_maps': value => 'hash:/etc/postfix/virtual_mailbox_maps'; + 'virtual_alias_maps': value => 'hash:/etc/postfix/virtual_alias_maps'; + 'virtual_uid_maps': value => $virtual_uid_maps; + 'virtual_gid_maps': value => $virtual_gid_maps; + 'virtual_mailbox_base': value => $virtual_mailbox_base; + 'virtual_transport': value => $virtual_transport; + } + } else { + postfix::config { + 'virtual_mailbox_domains': ensure => 'absent'; + 'virtual_mailbox_maps': ensure => 'absent'; + 'virtual_alias_maps': ensure => 'absent'; + 'virtual_uid_maps': ensure => 'absent'; + 'virtual_gid_maps': ensure => 'absent'; + 'virtual_mailbox_base': ensure => 'absent'; + 'virtual_transport': ensure => 'absent'; + } } # Merge base configs with postscreen configs diff --git a/site/profiles/templates/postfix/gateway/virtual_alias_maps.erb b/site/profiles/templates/postfix/gateway/virtual_alias_maps.erb new file mode 100644 index 0000000..a49726e --- /dev/null +++ b/site/profiles/templates/postfix/gateway/virtual_alias_maps.erb @@ -0,0 +1,9 @@ +# FILE MANAGED BY PUPPET, CHANGES WILL BE REPLACED +# +# Defines virtual alias mappings +# Maps email addresses or patterns to target addresses +# Format: alias@foo.net real@corp.foo.net + +<% @virtual_alias_maps.each do |source, target| -%> +<%= source %> <%= target %> +<% end -%> diff --git a/site/profiles/templates/postfix/gateway/virtual_mailbox_maps.erb b/site/profiles/templates/postfix/gateway/virtual_mailbox_maps.erb new file mode 100644 index 0000000..58d175f --- /dev/null +++ b/site/profiles/templates/postfix/gateway/virtual_mailbox_maps.erb @@ -0,0 +1,9 @@ +# FILE MANAGED BY PUPPET, CHANGES WILL BE REPLACED +# +# Defines virtual mailbox mappings for dovecot delivery +# Maps email addresses to maildir paths +# Format: user@domain maildir/path/ + +<% @virtual_mailbox_maps.each do |email, path| -%> +<%= email %> <%= path %> +<% end -%> \ No newline at end of file diff --git a/site/roles/manifests/infra/mail/backend.pp b/site/roles/manifests/infra/mail/backend.pp index c13b141..50385b3 100644 --- a/site/roles/manifests/infra/mail/backend.pp +++ b/site/roles/manifests/infra/mail/backend.pp @@ -6,5 +6,7 @@ class roles::infra::mail::backend { }else{ include profiles::defaults include profiles::base + include profiles::dovecot::backend + include profiles::postfix::gateway } }