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..e4d656a --- /dev/null +++ b/hieradata/roles/infra/mail.yaml @@ -0,0 +1,20 @@ +--- + +# Common mail server configuration + +# base postfix configuration (passed to postfix class) +postfix::relayhost: 'direct' +postfix::myorigin: 'main.unkin.net' +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..8be4a30 --- /dev/null +++ b/hieradata/roles/infra/mail/backend.yaml @@ -0,0 +1,87 @@ +--- +# additional altnames +profiles::pki::vault::alt_names: + - mail.main.unkin.net + +# manage dovecot +dovecot::install::packages: + - dovecot + - dovecot-pgsql +profiles::dovecot::server::maildir_path: "%{hiera('profiles::postfix::gateway::virtual_mailbox_base')}" + +#dovecot::config: +# auth.conf: +# values: +# auth_mechanisms: 'plain login' +# auth_username_format: '%Lu' +# auth_default_realm: 'main.unkin.net' +# auth-vmail.conf: +# values: +# passdb: | +# { +# driver = pam +# } +# userdb: | +# { +# driver = passwd +# override_fields = uid=vmail gid=vmail home=/shared/apps/maildata/%u +# } +# mail.conf: +# 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 +# } +# } +# sections: +# - name: 'namespace inbox' +# values: +# 'inbox': 'yes' +# 'seperator': '.' +# 'prefix': 'INBOX' + +# 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', '::'] +postfix::use_dovecot_lda: true # use built-in dovecot LDA support +postfix::mail_user: 'vmail:vmail' +profiles::postfix::gateway::enable_postscreen: false # disable postscreen (backend doesn't need it) +profiles::postfix::gateway::myhostname: 'mail.main.unkin.net' +profiles::postfix::gateway::enable_dovecot: true # enable dovecot integration +profiles::postfix::gateway::virtual_mailbox_domains: + - 'main.unkin.net' +profiles::postfix::gateway::virtual_mailbox_base: '/shared/apps/maildata' + +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::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..3b71498 100644 --- a/hieradata/roles/infra/mail/gateway.yaml +++ b/hieradata/roles/infra/mail/gateway.yaml @@ -1,21 +1,15 @@ --- - # 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::smtp_listen: '0.0.0.0' postfix::mta: true -postfix::manage_aliases: true - -# profile parameters for customization profiles::postfix::gateway::myhostname: 'in-mta.main.unkin.net' -# postfix map content (templates) profiles::postfix::gateway::relay_recipients_maps: '@main.unkin.net': 'OK' @@ -38,15 +32,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/server.pp b/site/profiles/manifests/dovecot/server.pp new file mode 100644 index 0000000..c4c0a8e --- /dev/null +++ b/site/profiles/manifests/dovecot/server.pp @@ -0,0 +1,99 @@ +class profiles::dovecot::server ( + 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 $maildir_path = '/var/vmail', + String $maildir_var = '%d/%n', + String $hostname = $trusted['certname'], + Array[String] $listen = ['*', '::'], + Array[String] $protocols = ['imap'], +) { + + # Ensure the maildata directory exists + file { $maildir_path: + ensure => directory, + owner => 'vmail', + group => 'vmail', + mode => '0755', + } + + # Create vmail user for dovecot + user { 'vmail': + ensure => present, + uid => 5000, + gid => 5000, + home => $maildir_path, + 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:${maildir_path}/${maildir_var}/Maildir", + 'login_trusted_networks' => '10.0.0.0/8 127.0.0.0/8 [::1]/128', + 'disable_plaintext_auth' => 'no', + 'auth_mechanisms' => 'cram-md5 plain login', + 'ssl' => 'required', + 'ssl_cert' => $tls_cert_file, + 'ssl_key' => $tls_key_file, + 'ssl_ca' => $tls_ca_file, + 'ssl_min_protocol' => 'TLSv1.2', + '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', + }, + sections => [ + { + name => 'passdb', + values => { + 'driver' => 'passwd-file', + 'args' => 'scheme=CRAM-MD5 username_format=%u /etc/dovecot/users', + }, + }, + { + name => 'userdb', + values => { + 'driver' => 'static', + 'args' => "uid=vmail gid=vmail home=${maildir_path}/${maildir_var}", + }, + }, + ], + } + + + # # Postfix smtp-auth + # unix_listener /var/spool/postfix/private/auth { + # mode = 0666 + # user = postfix + # group = postfix + # } + + + # Configure dovecot + class { 'dovecot': + main_config => $main_config, + include_sysdefault => false, + require => [User['vmail'], Group['vmail'], File[$maildir_path]], + } + +} diff --git a/site/profiles/manifests/postfix/gateway.pp b/site/profiles/manifests/postfix/gateway.pp index d86cbff..f98dfa1 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,39 @@ 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; + 'home_mailbox': value => "${virtual_mailbox_base}/%d/%n/Maildir"; + } + } else { + postfix::config { + 'virtual_mailbox_domains': ensure => 'absent'; + 'virtual_mailbox_maps': ensure => 'absent'; + 'virtual_uid_maps': ensure => 'absent'; + 'virtual_gid_maps': ensure => 'absent'; + 'virtual_mailbox_base': ensure => 'absent'; + 'virtual_transport': ensure => 'absent'; + 'home_mailbox': 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..860ad65 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::server + include profiles::postfix::gateway } }