aboutsummaryrefslogtreecommitdiffstats
path: root/roles/postfix_server
diff options
context:
space:
mode:
authorStonewall Jackson <stonewall@sacredheartsc.com>2023-02-04 01:23:43 -0500
committerStonewall Jackson <stonewall@sacredheartsc.com>2023-02-04 01:52:13 -0500
commit0261e875679f1bf63c8d689da7fc7e014597885d (patch)
tree3f19cd74a0c1070944f75437f30b098d6ef2ffcb /roles/postfix_server
downloadselfhosted-0261e875679f1bf63c8d689da7fc7e014597885d.tar.gz
selfhosted-0261e875679f1bf63c8d689da7fc7e014597885d.zip
initial commit
Diffstat (limited to 'roles/postfix_server')
-rw-r--r--roles/postfix_server/defaults/main.yml13
-rw-r--r--roles/postfix_server/files/etc/sasl2/smtpd.conf2
-rw-r--r--roles/postfix_server/files/etc/systemd/system/postfix.service.d/override.conf6
-rw-r--r--roles/postfix_server/handlers/main.yml9
-rw-r--r--roles/postfix_server/tasks/freeipa.yml95
-rw-r--r--roles/postfix_server/tasks/main.yml61
-rw-r--r--roles/postfix_server/templates/etc/postfix/main.cf.j2109
-rw-r--r--roles/postfix_server/templates/etc/postfix/master.cf.j234
-rw-r--r--roles/postfix_server/templates/etc/postfix/virtual_aliases.cf.j28
-rw-r--r--roles/postfix_server/templates/etc/postfix/virtual_mailboxes.cf.j27
-rw-r--r--roles/postfix_server/vars/main.yml64
11 files changed, 408 insertions, 0 deletions
diff --git a/roles/postfix_server/defaults/main.yml b/roles/postfix_server/defaults/main.yml
new file mode 100644
index 0000000..3052a76
--- /dev/null
+++ b/roles/postfix_server/defaults/main.yml
@@ -0,0 +1,13 @@
+postfix_message_size_limit: 67108864 # 64 MB
+postfix_recipient_delimiter: '+'
+postfix_lmtp_require_tls: yes
+postfix_virtual_domains: ['{{ email_domain }}']
+postfix_myorigin: '{{ email_domain }}'
+postfix_mynetworks: "{{ vlans.values() | map(attribute='cidr') }}"
+postfix_myhostname: '{{ ansible_fqdn }}'
+
+postfix_virtual_transport: lmtp:inet:{{ imap_host }}:24
+postfix_mailbox_quota_service: inet:{{ imap_host }}:10993
+postfix_milter: inet:{{ rspamd_host }}:11332
+
+postfix_recipient_group: role-imap-access
diff --git a/roles/postfix_server/files/etc/sasl2/smtpd.conf b/roles/postfix_server/files/etc/sasl2/smtpd.conf
new file mode 100644
index 0000000..cc61713
--- /dev/null
+++ b/roles/postfix_server/files/etc/sasl2/smtpd.conf
@@ -0,0 +1,2 @@
+pwcheck_method: saslauthd
+mech_list: gssapi plain login
diff --git a/roles/postfix_server/files/etc/systemd/system/postfix.service.d/override.conf b/roles/postfix_server/files/etc/systemd/system/postfix.service.d/override.conf
new file mode 100644
index 0000000..d7d8e76
--- /dev/null
+++ b/roles/postfix_server/files/etc/systemd/system/postfix.service.d/override.conf
@@ -0,0 +1,6 @@
+[Unit]
+Wants=gssproxy.service
+After=syslog.target network-online.target gssproxy.service
+
+[Service]
+Environment=GSS_USE_PROXY=yes
diff --git a/roles/postfix_server/handlers/main.yml b/roles/postfix_server/handlers/main.yml
new file mode 100644
index 0000000..286b942
--- /dev/null
+++ b/roles/postfix_server/handlers/main.yml
@@ -0,0 +1,9 @@
+- name: restart postfix
+ systemd:
+ name: postfix
+ state: restarted
+
+- name: restart saslauthd
+ systemd:
+ name: saslauthd
+ state: restarted
diff --git a/roles/postfix_server/tasks/freeipa.yml b/roles/postfix_server/tasks/freeipa.yml
new file mode 100644
index 0000000..84d7818
--- /dev/null
+++ b/roles/postfix_server/tasks/freeipa.yml
@@ -0,0 +1,95 @@
+- name: create smtp service principal
+ ipaservice:
+ ipaadmin_principal: '{{ ipa_user }}'
+ ipaadmin_password: '{{ ipa_pass }}'
+ name: 'smtp/{{ ansible_fqdn }}'
+ pac_type: NONE
+ state: present
+
+- name: retrieve smtp service keytab
+ include_role:
+ name: freeipa_keytab
+ vars:
+ keytab_principal: 'smtp/{{ ansible_fqdn }}'
+ keytab_path: '{{ postfix_keytab }}'
+
+- name: configure gssproxy
+ include_role:
+ name: gssproxy_client
+ vars:
+ gssproxy_name: postfix
+ gssproxy_section: service/postfix
+ gssproxy_keytab: '{{ postfix_keytab }}'
+ gssproxy_client_keytab: '{{ postfix_keytab }}'
+ gssproxy_cred_usage: both
+ gssproxy_euid: postfix
+
+- name: create SELinux policy for smtpd to access gssproxy
+ include_role:
+ name: selinux_policy
+ apply:
+ tags: selinux
+ vars:
+ selinux_policy_name: smtpd_gssproxy
+ selinux_policy_te: '{{ postfix_selinux_policy_te }}'
+ tags: selinux
+
+- name: generate PAM configuration for smtp
+ copy:
+ content: |
+ auth required pam_sss.so
+ account required pam_sss.so
+ dest: /etc/pam.d/smtp
+
+- name: create smtp HBAC service
+ ipahbacsvc:
+ ipaadmin_principal: '{{ ipa_user }}'
+ ipaadmin_password: '{{ ipa_pass }}'
+ name: '{{ postfix_hbac_service }}'
+ description: Postfix SMTP server
+ state: present
+ run_once: True
+
+- name: create mail-servers hostgroup
+ ipahostgroup:
+ ipaadmin_principal: '{{ ipa_user }}'
+ ipaadmin_password: '{{ ipa_pass }}'
+ name: '{{ postfix_hbac_hostgroup }}'
+ description: Mail Servers
+ host: "{{ groups[postfix_hbac_hostgroup] | map('regex_replace', '$', '.' ~ ansible_domain) }}"
+ state: present
+ run_once: True
+
+# Note: we explicitly allow all here. SSSD will only be consulted when a user
+# performs a PLAIN login, falling back to saslauthd/PAM authentication.
+# Users with a valid Kerberos ticket bypass the PAM stack entirely, so a
+# restrictive HBAC rule is pointless.
+- name: create HBAC rule for smtp
+ ipahbacrule:
+ ipaadmin_principal: '{{ ipa_user }}'
+ ipaadmin_password: '{{ ipa_pass }}'
+ name: allow_smtp_on_mail_servers
+ description: Allow SMTP on mail servers
+ hostgroup:
+ - '{{ postfix_hbac_hostgroup }}'
+ usercategory: all
+ hbacsvc:
+ - '{{ postfix_hbac_service }}'
+ run_once: True
+
+- name: create systemd override directory
+ file:
+ path: /etc/systemd/system/postfix.service.d
+ state: directory
+
+- name: create systemd override file
+ copy:
+ src: etc/systemd/system/postfix.service.d/override.conf
+ dest: /etc/systemd/system/postfix.service.d/override.conf
+ notify: restart postfix
+ register: postfix_systemd_unit
+
+- name: reload systemd daemons
+ systemd:
+ daemon_reload: yes
+ when: postfix_systemd_unit.changed
diff --git a/roles/postfix_server/tasks/main.yml b/roles/postfix_server/tasks/main.yml
new file mode 100644
index 0000000..4f22d49
--- /dev/null
+++ b/roles/postfix_server/tasks/main.yml
@@ -0,0 +1,61 @@
+- name: install postfix
+ dnf:
+ name: '{{ postfix_packages }}'
+ state: present
+
+- name: request TLS certificate
+ include_role:
+ name: certbot
+ vars:
+ certificate_sans: ['{{ postfix_myhostname }}']
+ certificate_path: '{{ postfix_certificate_path }}'
+ certificate_key_path: '{{ postfix_certificate_key_path }}'
+ certificate_owner: postfix
+ certificate_hook: systemctl reload postfix
+
+- import_tasks: freeipa.yml
+ tags: freeipa
+
+- name: generate dhparams
+ openssl_dhparam:
+ path: '{{ postfix_dhparams_path }}'
+ size: 2048
+
+- name: generate postifx configuration
+ template:
+ src: etc/postfix/{{ item }}.j2
+ dest: /etc/postfix/{{ item }}
+ loop:
+ - main.cf
+ - master.cf
+ - virtual_mailboxes.cf
+ - virtual_aliases.cf
+ notify: restart postfix
+
+- name: configure saslauthd for smtpd
+ copy:
+ src: etc/sasl2/smtpd.conf
+ dest: /etc/sasl2/smtpd.conf
+ notify: restart saslauthd
+
+- name: enable saslauthd
+ systemd:
+ name: saslauthd
+ enabled: yes
+ state: started
+
+- name: enable postfix
+ systemd:
+ name: postfix
+ enabled: yes
+ state: started
+
+- name: open firewall ports
+ firewalld:
+ service: '{{ item }}'
+ permanent: yes
+ immediate: yes
+ state: enabled
+ loop:
+ - smtp
+ - smtp-submission
diff --git a/roles/postfix_server/templates/etc/postfix/main.cf.j2 b/roles/postfix_server/templates/etc/postfix/main.cf.j2
new file mode 100644
index 0000000..9132dff
--- /dev/null
+++ b/roles/postfix_server/templates/etc/postfix/main.cf.j2
@@ -0,0 +1,109 @@
+compatibility_level = 2
+
+### path definitions
+queue_directory = /var/spool/postfix
+command_directory = /usr/sbin
+daemon_directory = /usr/libexec/postfix
+data_directory = /var/lib/postfix
+mail_owner = postfix
+
+sendmail_path = /usr/sbin/sendmail.postfix
+newaliases_path = /usr/bin/newaliases.postfix
+mailq_path = /usr/bin/mailq.postfix
+setgid_group = postdrop
+html_directory = no
+manpage_directory = /usr/share/man
+sample_directory = /usr/share/doc/postfix/samples
+readme_directory = /usr/share/doc/postfix/README_FILES
+meta_directory = /etc/postfix
+shlib_directory = /usr/lib64/postfix
+
+import_environment = MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG TZ XAUTHORITY DISPLAY LANG=C POSTLOG_SERVICE POSTLOG_HOSTNAME GSS_USE_PROXY=yes
+
+myorigin = {{ postfix_myorigin }}
+myhostname = {{ postfix_myhostname }}
+
+mynetworks = 127.0.0.0/8 {{ postfix_mynetworks | join(' ') }}
+
+# disable local delivery
+mydestination =
+
+inet_interfaces = all
+inet_protocols = all
+
+# disable open relay
+mynetworks_style = host
+
+alias_database = hash:/etc/aliases
+
+smtputf8_enable = yes
+recipient_delimiter = {{ postfix_recipient_delimiter }}
+message_size_limit = {{ postfix_message_size_limit }}
+
+strict_rfc821_envelopes = yes
+allow_percent_hack = no
+swap_bangpath = no
+disable_vrfy_command = yes
+show_user_unknown_table_name = no
+
+tls_medium_cipherlist = {{ postfix_cipherlist }}
+tls_preempt_cipherlist = no
+
+smtpd_tls_security_level = may
+smtpd_tls_auth_only = yes
+smtpd_tls_cert_file = {{ postfix_certificate_path }}
+smtpd_tls_key_file = {{ postfix_certificate_key_path }}
+smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
+smtpd_tls_mandatory_ciphers = medium
+smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
+smtpd_tls_dh1024_param_file = {{ postfix_dhparams_path }}
+
+smtpd_sasl_security_options = noanonymous, noplaintext
+smtpd_sasl_tls_security_options = noanonymous
+
+smtpd_helo_required = yes
+
+smtp_tls_security_level = may
+smtp_tls_CAfile = {{ postfix_smtp_ca_file }}
+
+lmtp_tls_CAfile = {{ postfix_smtp_ca_file }}
+lmtp_tls_security_level = {{ 'secure' if postfix_lmtp_require_tls else 'may' }}
+
+# public mailserver - restrictive policy
+smtpd_helo_required = yes
+smtpd_client_restrictions =
+ permit_mynetworks,
+ reject_unauth_pipelining,
+ reject_unknown_reverse_client_hostname
+smtpd_helo_restrictions =
+ permit_mynetworks,
+ reject_invalid_helo_hostname,
+ reject_non_fqdn_helo_hostname,
+ reject_unauth_pipelining
+smtpd_sender_restrictions =
+ permit_mynetworks,
+ reject_non_fqdn_sender,
+ reject_unknown_sender_domain,
+ reject_unauth_pipelining
+smtpd_relay_restrictions =
+ permit_mynetworks,
+ reject_unauth_destination
+smtpd_recipient_restrictions =
+ permit_mynetworks,
+ reject_non_fqdn_recipient,
+ reject_unknown_recipient_domain,
+ reject_unauth_pipelining,
+ reject_unlisted_recipient,
+ reject_unauth_destination,
+ check_policy_service {{ postfix_mailbox_quota_service }}
+smtpd_data_restrictions =
+ permit_mynetworks,
+ reject_unauth_pipelining
+
+virtual_transport = {{ postfix_virtual_transport }}
+virtual_mailbox_domains = {{ freeipa_realm }} {{ postfix_virtual_domains | join(' ') }}
+virtual_mailbox_maps = ldap:$config_directory/virtual_mailboxes.cf
+virtual_alias_maps = ldap:$config_directory/virtual_aliases.cf
+
+milter_default_action = accept
+smtpd_milters = {{ postfix_milter }}
diff --git a/roles/postfix_server/templates/etc/postfix/master.cf.j2 b/roles/postfix_server/templates/etc/postfix/master.cf.j2
new file mode 100644
index 0000000..1742b7a
--- /dev/null
+++ b/roles/postfix_server/templates/etc/postfix/master.cf.j2
@@ -0,0 +1,34 @@
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (no) (never) (100)
+# ==========================================================================
+smtp inet n - n - - smtpd
+submission inet n - n - - smtpd
+ -o syslog_name=postfix/submission
+ -o smtpd_tls_security_level=encrypt
+ -o smtpd_sasl_auth_enable=yes
+pickup unix n - n 60 1 pickup
+cleanup unix n - n - 0 cleanup
+qmgr unix n - n 300 1 qmgr
+tlsmgr unix - - n 1000? 1 tlsmgr
+rewrite unix - - n - - trivial-rewrite
+bounce unix - - n - 0 bounce
+defer unix - - n - 0 bounce
+trace unix - - n - 0 bounce
+verify unix - - n - 1 verify
+flush unix n - n 1000? 0 flush
+proxymap unix - - n - - proxymap
+proxywrite unix - - n - 1 proxymap
+smtp unix - - n - - smtp
+relay unix - - n - - smtp
+ -o syslog_name=postfix/$service_name
+showq unix n - n - - showq
+error unix - - n - - error
+retry unix - - n - - error
+discard unix - - n - - discard
+local unix - n n - - local
+virtual unix - n n - - virtual
+lmtp unix - - n - - lmtp
+anvil unix - - n - 1 anvil
+scache unix - - n - 1 scache
+postlog unix-dgram n - n - 1 postlogd
diff --git a/roles/postfix_server/templates/etc/postfix/virtual_aliases.cf.j2 b/roles/postfix_server/templates/etc/postfix/virtual_aliases.cf.j2
new file mode 100644
index 0000000..9ba32e8
--- /dev/null
+++ b/roles/postfix_server/templates/etc/postfix/virtual_aliases.cf.j2
@@ -0,0 +1,8 @@
+version = 3
+bind = sasl
+sasl_mechs = gssapi
+server_host = {{ freeipa_hosts | join(" ") }}
+search_base = {{ freeipa_accounts_basedn }}
+query_filter = (|(mail=%s)(mailAlternateAddress=%s))
+special_result_attribute = member
+result_attribute = krbprincipalname
diff --git a/roles/postfix_server/templates/etc/postfix/virtual_mailboxes.cf.j2 b/roles/postfix_server/templates/etc/postfix/virtual_mailboxes.cf.j2
new file mode 100644
index 0000000..a6fae98
--- /dev/null
+++ b/roles/postfix_server/templates/etc/postfix/virtual_mailboxes.cf.j2
@@ -0,0 +1,7 @@
+version = 3
+bind = sasl
+sasl_mechs = gssapi
+server_host = {{ freeipa_hosts | join(" ") }}
+search_base = {{ freeipa_user_basedn }}
+query_filter = (&(krbprincipalname=%s)(memberof=cn={{ postfix_recipient_group }},{{ freeipa_group_basedn }}))
+result_attribute = krbprincipalname
diff --git a/roles/postfix_server/vars/main.yml b/roles/postfix_server/vars/main.yml
new file mode 100644
index 0000000..050c880
--- /dev/null
+++ b/roles/postfix_server/vars/main.yml
@@ -0,0 +1,64 @@
+postfix_packages:
+ - postfix
+ - postfix-ldap
+ - cyrus-sasl
+ - cyrus-sasl-gssapi
+ - cyrus-sasl-plain
+ - s-nail
+
+postfix_certificate_path: /etc/pki/tls/certs/postfix2.pem
+postfix_certificate_key_path: /etc/pki/tls/private/postfix2.key
+postfix_dhparams_path: /etc/pki/tls/misc/dhparams-postfix.pem
+
+postfix_hbac_service: smtp
+postfix_hbac_hostgroup: mail_servers
+
+postfix_smtp_ca_file: /etc/pki/tls/certs/ca-bundle.crt
+postfix_cipherlist: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
+
+postfix_keytab: /var/lib/gssproxy/clients/postfix.keytab
+
+postfix_selinux_policy_te: |
+ require {
+ type postfix_exec_t;
+ type postfix_smtpd_exec_t;
+ type postfix_cleanup_t;
+ type postfix_cleanup_exec_t;
+ type postfix_master_t;
+ type postfix_cleanup_t;
+ type postfix_smtpd_t;
+ type gssproxy_t;
+ type gssproxy_var_lib_t;
+ class file getattr;
+ class dir search;
+ class sock_file write;
+ class unix_stream_socket connectto;
+ class process noatsecure;
+ class key { read view write };
+ }
+
+ #============= postfix_smtpd_t ==============
+ allow postfix_smtpd_t gssproxy_t:unix_stream_socket connectto;
+ allow postfix_smtpd_t gssproxy_var_lib_t:dir search;
+ allow postfix_smtpd_t gssproxy_var_lib_t:sock_file write;
+ allow postfix_smtpd_t postfix_master_t:key { read view write };
+
+ #============= postfix_master_t ==============
+ allow postfix_master_t postfix_smtpd_t:process noatsecure;
+ allow postfix_master_t postfix_smtpd_t:key { read write };
+ allow postfix_master_t postfix_cleanup_t:process noatsecure;
+ allow postfix_master_t gssproxy_t:unix_stream_socket connectto;
+ allow postfix_master_t gssproxy_var_lib_t:dir search;
+ allow postfix_master_t gssproxy_var_lib_t:sock_file write;
+
+ #============= postfix_cleanup_t ==============
+ allow postfix_cleanup_t gssproxy_var_lib_t:dir search;
+ allow postfix_cleanup_t gssproxy_var_lib_t:sock_file write;
+ allow postfix_cleanup_t gssproxy_t:unix_stream_socket connectto;
+ allow postfix_cleanup_t postfix_master_t:key read;
+ allow postfix_cleanup_t postfix_smtpd_t:key read;
+
+ #============= gssproxy_t ==============
+ allow gssproxy_t postfix_cleanup_exec_t:file getattr;
+ allow gssproxy_t postfix_smtpd_exec_t:file getattr;
+ allow gssproxy_t postfix_exec_t:file getattr;