Postfix¶
Inhalt¶
- Postfix 3.10.6
- Dovecot-SASL
- postscreen
- PostgreSQL-Lookup-Maps
- Python-SPF-Engine 3.1.0
Einleitung¶
Dieses HowTo beschreibt die Installation und Konfiguration von Postfix auf FreeBSD 15+.
Postfix wird in diesem Setup als SMTP-Server mit Dovecot-SASL, postscreen und PostgreSQL-Lookup-Maps betrieben. Der aktuelle FreeBSD-Port mail/postfix steht auf 3.10.6 und bietet unter anderem den Flavor pgsql, der für dieses Setup die richtige Basis ist. postscreen ist der vorgelagerte SMTP-Schutzdienst für eingehende Verbindungen, und Postfix unterstützt Dovecot-SASL offiziell für SMTP AUTH. Für SQL-Maps gibt es mit pgsql den passenden PostgreSQL-Backend-Typ. (FreshPorts)
Zusätzlich wird in diesem HowTo die Python-SPF-Engine verwendet. Dieses Projekt stellt sowohl einen Postfix-Policy-Service als auch einen Milter für SPF-Prüfungen bereit. Auf FreeBSD wird dafür der Port mail/py-spf-engine in Version 3.1.0 verwendet. (FreshPorts)
Voraussetzungen¶
Zu den Voraussetzungen für dieses HowTo siehe bitte: Hosting System
Zusätzlich wird vorausgesetzt:
- PostgreSQL ist bereits installiert und die Datenbankzugänge für den Benutzer
postfixexistieren bereits. - Dovecot ist bereits installiert und für SASL sowie LMTP vorbereitet.
- Die Dovecot-Sockets unter
/var/spool/postfix/private/sind vorgesehen. - Die TLS-Zertifikate für
mail.example.comexistieren bereits. - Der Server soll virtuelle Domains und Mailboxen über PostgreSQL-Lookups bedienen.
Für Dovecot-SASL und Dovecot-LMTP ist genau dieser Aufbau üblich: Postfix greift auf Unix-Sockets innerhalb von /var/spool/postfix/private/ zu, weil der Zugriff von Postfix auf dieses Verzeichnis begrenzt ist. (doc.dovecot.org)
Vorbereitungen¶
DNS Records¶
Für dieses HowTo müssen zuvor folgende DNS-Records angelegt werden, sofern sie noch nicht existieren, oder entsprechend geändert werden, sofern sie bereits existieren.
example.com. IN MX 10 mail.example.com.
mail.example.com. IN A __IPADDR4__
mail.example.com. IN AAAA __IPADDR6__
_imap._tcp.example.com. IN SRV 0 0 0 .
_imaps._tcp.example.com. IN SRV 0 1 993 mail.example.com.
_pop3._tcp.example.com. IN SRV 0 0 0 .
_pop3s._tcp.example.com. IN SRV 0 0 0 .
_submission._tcp.example.com. IN SRV 0 1 587 mail.example.com.
_submissions._tcp.example.com. IN SRV 0 1 465 mail.example.com.
example.com. IN TXT "v=spf1 mx -all"
Gruppen / Benutzer / Passwörter¶
Für dieses HowTo sind keine zusätzlichen Systemgruppen oder Systembenutzer erforderlich.
Für dieses HowTo muss jedoch das Passwort für den PostgreSQL-Benutzer postfix bereits vorhanden sein, da die pgsql:-Maps in den Konfigurationsdateien darauf zugreifen.
# Passwort für PostgreSQL-Benutzer "postfix" prüfen
cat /var/db/passwords/user_postgresql_postfix
Verzeichnisse / Dateien¶
Für dieses HowTo sind keine zusätzlichen Verzeichnisse oder Dateien erforderlich.
Installation¶
Wir installieren mail/postfix@pgsql und dessen Abhängigkeiten.¶
mkdir -p /var/db/ports/mail_postfix
cat <<'EOF' > /var/db/ports/mail_postfix/options
_OPTIONS_READ=postfix-3.11.1
_FILE_COMPLETE_OPTIONS_LIST=BDB BLACKLISTD CDB DOCS EAI INST_BASE LDAP LMDB MONGO MYSQL NIS PCRE2 PGSQL SASL SQLITE TEST TLS TLSRPT SASLKMIT SASLKRB5
OPTIONS_FILE_SET+=BDB
OPTIONS_FILE_UNSET+=BLACKLISTD
OPTIONS_FILE_SET+=CDB
OPTIONS_FILE_UNSET+=DOCS
OPTIONS_FILE_SET+=EAI
OPTIONS_FILE_UNSET+=INST_BASE
OPTIONS_FILE_UNSET+=LDAP
OPTIONS_FILE_SET+=LMDB
OPTIONS_FILE_UNSET+=MONGO
OPTIONS_FILE_SET+=MYSQL
OPTIONS_FILE_UNSET+=NIS
OPTIONS_FILE_SET+=PCRE2
OPTIONS_FILE_SET+=PGSQL
OPTIONS_FILE_UNSET+=SASL
OPTIONS_FILE_UNSET+=SQLITE
OPTIONS_FILE_UNSET+=TEST
OPTIONS_FILE_SET+=TLS
OPTIONS_FILE_SET+=TLSRPT
OPTIONS_FILE_UNSET+=SASLKMIT
OPTIONS_FILE_UNSET+=SASLKRB5
EOF
portmaster -w -B -g -U --force-config mail/postfix@pgsql -n
pw groupmod mail -m postfix
Dienst in rc.conf eintragen¶
Der Dienst wird mittels sysrc in der rc.conf eingetragen und dadurch beim Systemstart automatisch gestartet.
Mailwrapper auf Postfix umstellen¶
mkdir -p /usr/local/etc/mail
install -b -m 0644 /usr/local/share/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf
Das FreeBSD-Handbook beschreibt für Postfix genau diesen Schritt mit /usr/local/etc/mail/mailer.conf, damit die Mailwrapper-Kommandos sauber auf Postfix zeigen. (docs.freebsd.org)
Konfiguration¶
Konfigurationsdatei main.cf¶
cat <<'EOF' > /usr/local/etc/postfix/main.cf
# =========================================================================
# SYSTEM & PATHS
# =========================================================================
compatibility_level = 3.11
myhostname = mail.$mydomain
mydomain = example.com
myorigin = $mydomain
mydestination = $myhostname localhost.$mydomain localhost localhost.localdomain
mail_spool_directory = /var/vmail
home_mailbox = .maildir/
openssl_path = /usr/local/bin/openssl
recipient_delimiter = +
# =========================================================================
# NETWORK & CONCURRENCY
# =========================================================================
inet_interfaces = all
inet_protocols = all
mynetworks = 127.0.0.0/8 [::1]/128 10.0.0.0/8 [fe80::]/10 __IPADDR4__/32 [__IPADDR6__]/64
default_destination_concurrency_limit = 20
local_destination_concurrency_limit = 10
amavisfeed_destination_recipient_limit = 2
dovecot_destination_recipient_limit = 1
lmtp_destination_recipient_limit = 1
anvil_rate_time_unit = 60s
smtpd_client_connection_count_limit = 50
smtpd_client_connection_rate_limit = 30
smtpd_client_message_rate_limit = 100
smtpd_client_recipient_rate_limit = 200
smtpd_proxy_options = speed_adjust
policyd-spf_time_limit = 3600
# =========================================================================
# VIRTUAL MAILBOX SETTINGS (PostgreSQL Proxied)
# =========================================================================
virtual_transport = lmtp:unix:private/dovecot-lmtp
mailbox_transport = lmtp:unix:private/dovecot-lmtp
local_transport = virtual
virtual_mailbox_base = /var/vmail
virtual_minimum_uid = 5000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
local_recipient_maps = $virtual_mailbox_maps
relay_domains = $mydestination proxy:pgsql:${config_directory}/pgsql/relay_domains.cf
transport_maps = proxy:pgsql:${config_directory}/pgsql/transport_maps.cf
virtual_alias_domains = proxy:pgsql:${config_directory}/pgsql/virtual_alias_domains_maps.cf proxy:pgsql:${config_directory}/pgsql/virtual_alias_domains_catchall_maps.cf
virtual_alias_maps = proxy:pgsql:${config_directory}/pgsql/virtual_alias_maps.cf proxy:pgsql:${config_directory}/pgsql/virtual_alias_domains_mailbox_maps.cf
virtual_mailbox_domains = proxy:pgsql:${config_directory}/pgsql/virtual_mailbox_domains.cf
virtual_mailbox_maps = proxy:pgsql:${config_directory}/pgsql/virtual_mailbox_maps.cf
# =========================================================================
# TLS / SSL (Modern Strict Settings)
# =========================================================================
smtp_tls_CAfile = /etc/ssl/cert.pem
smtpd_tls_CAfile = /etc/ssl/cert.pem
smtpd_tls_chain_files = /usr/local/etc/letsencrypt/live/$myhostname/privkey.pem /usr/local/etc/letsencrypt/live/$myhostname/fullchain.pem
smtp_tls_security_level = may
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1
smtpd_tls_received_header = yes
smtp_tls_note_starttls_offer = yes
smtp_tls_connection_reuse = yes
smtp_tls_mandatory_protocols = >=TLSv1.2
smtp_tls_protocols = >=TLSv1.2
smtpd_tls_mandatory_protocols = >=TLSv1.2
smtpd_tls_protocols = >=TLSv1.2
lmtp_tls_mandatory_protocols = >=TLSv1.2
lmtp_tls_protocols = >=TLSv1.2
tls_preempt_cipherlist = yes
tls_eecdh_auto_curves = X448 X25519 secp384r1 prime256v1 secp521r1
tls_high_cipherlist = CHACHA20 AESGCM !SSLv3 !TLSv1 !DSS !RSA !PSK !aNULL @STRENGTH
tls_medium_cipherlist = CHACHA20 AESGCM !SSLv3 !TLSv1 !DSS !RSA !PSK !aNULL @STRENGTH
tls_ssl_options = NO_RENEGOTIATION NO_SESSION_RESUMPTION_ON_RENEGOTIATION
# =========================================================================
# POSTSCREEN
# =========================================================================
postscreen_access_list = permit_mynetworks cidr:${config_directory}/postscreen_access.cidr cidr:${config_directory}/postscreen_whitelist.cidr
postscreen_cache_cleanup_interval = 12h
postscreen_cache_map = btree:${data_directory}/postscreen_cache
postscreen_greet_action = enforce
postscreen_bare_newline_action = enforce
postscreen_bare_newline_enable = yes
postscreen_non_smtp_command_enable = yes
postscreen_pipelining_enable = yes
postscreen_denylist_action = enforce
postscreen_dnsbl_action = enforce
postscreen_dnsbl_threshold = 5
postscreen_dnsbl_allowlist_threshold = -2
postscreen_dnsbl_reply_map = texthash:${config_directory}/postscreen_dnsbl_reply
postscreen_dnsbl_sites =
list.dnswl.org=127.0.[0..255].0*-2
list.dnswl.org=127.0.[0..255].1*-4
list.dnswl.org=127.0.[0..255].2*-6
list.dnswl.org=127.0.[0..255].3*-8
zen.spamhaus.org=127.0.0.20*20
zen.spamhaus.org=127.0.0.9*99
zen.spamhaus.org=127.0.0.3*10
zen.spamhaus.org=127.0.0.2*3
zen.spamhaus.org=127.0.0.[4..7]*3
zen.spamhaus.org=127.0.0.[10..11]*3
swl.spamhaus.org*-10
bl.mailspike.net=127.0.0.2*10
bl.mailspike.net=127.0.0.10*5
bl.mailspike.net=127.0.0.11*4
bl.mailspike.net=127.0.0.12*3
bl.mailspike.net=127.0.0.13*2
bl.mailspike.net=127.0.0.14*1
wl.mailspike.net=127.0.0.16*-2
wl.mailspike.net=127.0.0.17*-4
wl.mailspike.net=127.0.0.18*-6
wl.mailspike.net=127.0.0.19*-8
wl.mailspike.net=127.0.0.20*-10
backscatter.spameatingmonkey.net*2
bl.ipv6.spameatingmonkey.net*2
bl.spameatingmonkey.net*2
bl.spamcop.net*2
db.wpbl.info*2
psbl.surriel.com*2
torexit.dan.me.uk*2
tor.dan.me.uk*1
# =========================================================================
# AUTHENTICATION & RESTRICTIONS
# =========================================================================
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/dovecot-auth
smtpd_sasl_authenticated_header = yes
smtpd_client_port_logging = yes
smtpd_reject_unlisted_sender = yes
smtpd_client_restrictions = permit_mynetworks permit_sasl_authenticated
smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks permit_sasl_authenticated check_helo_access pcre:${config_directory}/helo_access.pcre reject_non_fqdn_helo_hostname reject_unknown_helo_hostname
smtpd_sender_login_maps = proxy:pgsql:${config_directory}/pgsql/sender_login_maps.cf
smtpd_sender_restrictions = permit_mynetworks permit_sasl_authenticated reject_non_fqdn_sender reject_unknown_sender_domain reject_sender_login_mismatch check_sender_access pcre:${config_directory}/sender_access.pcre reject_unlisted_sender
smtpd_recipient_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
check_recipient_access pcre:${config_directory}/recipient_checks.pcre
reject_non_fqdn_recipient
reject_unlisted_recipient
reject_unknown_recipient_domain
# check_policy_service unix:private/policyd-spf
reject_rhsbl_sender zen.spamhaus.org=127.0.1.[2..99]
reject_rhsbl_helo zen.spamhaus.org=127.0.1.[2..99]
reject_rhsbl_reverse_client zen.spamhaus.org=127.0.1.[2..99]
reject_rhsbl_sender zen.spamhaus.org=127.0.2.[2..24]
reject_rhsbl_helo zen.spamhaus.org=127.0.2.[2..24]
reject_rhsbl_reverse_client zen.spamhaus.org=127.0.2.[2..24]
reject_rbl_client zen.spamhaus.org=127.0.0.[2..255]
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination
smtpd_data_restrictions = reject_unauth_pipelining reject_multi_recipient_bounce
smtpd_etrn_restrictions = reject
receive_override_options = no_address_mappings
#recipient_bcc_maps = proxy:pgsql:${config_directory}/pgsql/recipient_bcc_maps.cf
#sender_bcc_maps = proxy:pgsql:${config_directory}/pgsql/sender_bcc_maps.cf
#sender_dependent_relayhost_maps = proxy:pgsql:${config_directory}/pgsql/sender_dependent_relayhost_maps.cf
# =========================================================================
# GENERAL & MILTERS
# =========================================================================
milter_default_action = accept
milter_protocol = 6
milter_connect_macros = j {client_name} {daemon_name} v
non_smtpd_milters = $smtpd_milters
smtpd_milters = unix:pyspf-milter/pyspf-milter.sock unix:opendkim/opendkim.sock unix:opendmarc/opendmarc.sock
alias_database = hash:${config_directory}/aliases
alias_maps = hash:${config_directory}/aliases
biff = no
append_dot_mydomain = no
allow_percent_hack = no
always_add_missing_headers = yes
disable_vrfy_command = yes
enable_long_queue_ids = yes
qmgr_message_recipient_limit = 10000
qmgr_message_active_limit = 20000
maximal_queue_lifetime = 5d
bounce_queue_lifetime = 1d
internal_mail_filter_classes = bounce
enable_original_recipient = no
mailbox_size_limit = 0
message_size_limit = 104857600
masquerade_domains = $mydomain
masquerade_exceptions = root mailer-daemon
remote_header_rewrite_domain = domain.invalid
show_user_unknown_table_name = no
smtp_dns_support_level = enabled
smtputf8_enable = no
soft_bounce = no
strict_rfc821_envelopes = yes
swap_bangpath = no
header_checks = pcre:${config_directory}/header_checks.pcre
body_checks = pcre:${config_directory}/body_checks.pcre
#mime_header_checks = pcre:${config_directory}/mime_header_checks.pcre
#nested_header_checks = pcre:${config_directory}/nested_header_checks.pcre
smtpd_command_filter = pcre:${config_directory}/command_filter.pcre
internal_mail_filter_classes = bounce
rbl_reply_maps = hash:${config_directory}/dnsbl_reply_map
local_header_rewrite_clients = permit_mynetworks permit_sasl_authenticated
EOF
# 1. Get Default Interface
DEF_IF="$(route -n get -inet default | awk '/interface:/ {print $2}')"
# 2. Get IPv4 IP
IP4="$(ifconfig "$DEF_IF" inet | awk '/inet / && $2 !~ /^127\./ {print $2}' | head -n 1)"
[ -n "$IP4" ] && sed -e "s|__IPADDR4__|$IP4|g" -i '' /usr/local/etc/postfix/main.cf
# 3. Get IPv6 IP
IP6="$(ifconfig "$DEF_IF" inet6 | awk '/inet6 / && $2 !~ /^fe80:/ && $2 !~ /^::1/ {print $2}' | head -n 1)"
[ -n "$IP6" ] && sed -e "s|__IPADDR6__|$IP6|g" -i '' /usr/local/etc/postfix/main.cf
Konfigurationsdatei master.cf¶
cat <<'EOF' > /usr/local/etc/postfix/master.cf
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - n - 1 postscreen
smtpd pass - - n - - smtpd
-o content_filter=amavisfeed:[127.0.0.1]:10024
-o milter_macro_daemon_name=VERIFYING
-o receive_override_options=no_address_mappings
-o smtp_send_xforward_command=yes
dnsblog unix - - n - 0 dnsblog
tlsproxy unix - - n - 0 tlsproxy
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o content_filter=amavisfeed:[127.0.0.1]:10026
-o local_header_rewrite_clients=static:all
-o milter_macro_daemon_name=ORIGINATING
-o receive_override_options=no_header_body_checks,no_milters
-o smtp_send_xforward_command=yes
-o smtpd_forbid_unauth_pipelining=no
-o smtpd_hide_client_session=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_tls_security_level=encrypt
submissions inet n - n - - smtpd
-o syslog_name=postfix/submissions
-o content_filter=amavisfeed:[127.0.0.1]:10026
-o local_header_rewrite_clients=static:all
-o milter_macro_daemon_name=ORIGINATING
-o receive_override_options=no_header_body_checks,no_milters
-o smtp_send_xforward_command=yes
-o smtpd_forbid_unauth_pipelining=no
-o smtpd_hide_client_session=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_wrappermode=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=${multi_instance_name?{$multi_instance_name}:{postfix}}/$service_name
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
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
# ====================================================================
# External Interfacing Services
# ====================================================================
# Dovecot LDA Deliver Transport
dovecot unix - n n - - pipe
flags=DRXhu user=vmail:vmail argv=/usr/local/libexec/dovecot/dovecot-lda
-f ${sender} -a ${recipient} -d ${user}@${nexthop} -m ${extension}
policyd-spf unix - n n - 0 spawn
user=nobody argv=/usr/local/bin/policyd-spf
# Vacation Auto-Responder
vacation unix - n n - - pipe
flags=Rq user=vacation argv=/var/db/postfixadmin/vacation.pl
-f ${sender} -- ${recipient}
# Amavis Delivery Filter
amavisfeed unix - - n - 10 smtp
-o syslog_name=postfix/amavis
-o local_header_rewrite_clients=
-o max_use=20
-o smtp_data_done_timeout=1200
-o smtp_dns_support_level=disabled
-o smtp_send_xforward_command=yes
-o smtp_tls_note_starttls_offer=no
# Amavis Re-Injection Port (After filtering)
127.0.0.1:10025 inet n - n - - smtpd
-o syslog_name=postfix/10025
-o content_filter=
-o local_header_rewrite_clients=
-o local_recipient_maps=
-o mynetworks=127.0.0.0/8
-o mynetworks_style=host
-o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_address_mappings,no_milters
-o relay_recipient_maps=
-o smtp_dns_support_level=disabled
-o smtp_tls_security_level=none
-o smtpd_client_connection_count_limit=0
-o smtpd_client_connection_rate_limit=0
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=permit_mynetworks,reject
-o smtpd_delay_reject=no
-o smtpd_end_of_data_restrictions=permit_mynetworks,reject
-o smtpd_error_sleep_time=0
-o smtpd_hard_error_limit=1000
-o smtpd_helo_restrictions=permit_mynetworks,reject
-o smtpd_milters=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_relay_restrictions=permit_mynetworks,reject
-o smtpd_restriction_classes=
-o smtpd_sender_restrictions=permit_mynetworks,reject
-o smtpd_soft_error_limit=1001
-o smtpd_tls_security_level=none
-o strict_rfc821_envelopes=yes
# OpenDKIM Interface Map (If used post-amavis)
127.0.0.1:10027 inet n - n - - smtpd
-o syslog_name=postfix/10027
-o content_filter=
-o local_header_rewrite_clients=
-o local_recipient_maps=
-o mynetworks=127.0.0.0/8
-o mynetworks_style=host
-o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
-o relay_recipient_maps=
-o smtp_dns_support_level=disabled
-o smtp_tls_security_level=none
-o smtpd_client_connection_count_limit=0
-o smtpd_client_connection_rate_limit=0
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=permit_mynetworks,reject
-o smtpd_delay_reject=no
-o smtpd_end_of_data_restrictions=permit_mynetworks,reject
-o smtpd_error_sleep_time=0
-o smtpd_hard_error_limit=1000
-o smtpd_helo_restrictions=permit_mynetworks,reject
-o smtpd_milters=unix:opendkim/opendkim.sock
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_relay_restrictions=permit_mynetworks,reject
-o smtpd_restriction_classes=
-o smtpd_sender_restrictions=permit_mynetworks,reject
-o smtpd_soft_error_limit=1001
-o smtpd_tls_security_level=none
-o strict_rfc821_envelopes=yes
EOF
PostgreSQL-Lookup-Dateien pgsql/*.cf¶
mkdir -p /usr/local/etc/postfix/pgsql
cat <<'EOF' > /usr/local/etc/postfix/pgsql/recipient_bcc_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query =
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/relay_domains.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = true AND ('%s' <> 'smtp' AND active = true)
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/sender_bcc_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query =
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/sender_dependent_relayhost_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query =
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/sender_login_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT username FROM mailbox WHERE username='%s' AND (('%s' = 'smtp' AND smtp_active = true) OR ('%s' <> 'smtp' AND active = true))
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/transport_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT REPLACE(transport, 'virtual', ':') AS transport FROM domain WHERE domain='%s' AND ('%s' <> 'smtp' AND active = true)
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_alias_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT goto FROM alias WHERE address='%s' AND ('%s' <> 'smtp' AND active = true)
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_alias_domains_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT goto FROM alias, alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address = '%u' || '@' || alias_domain.target_domain AND ('%s' <> 'smtp' AND alias.active = true) AND ('%s' <> 'smtp' AND alias_domain.active = true)
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_alias_domains_catchall_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT goto FROM alias, alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address = '%u' || '@' || alias_domain.target_domain AND ('%s' <> 'smtp' AND alias.active = true) AND ('%s' <> 'smtp' AND alias_domain.active = true)
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_alias_domains_mailbox_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT maildir FROM mailbox, alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username = '%u' || '@' || alias_domain.target_domain AND (('%s' = 'smtp' AND mailbox.smtp_active = true) OR ('%s' <> 'smtp' AND mailbox.active = true)) AND ('%s' <> 'smtp' AND alias_domain.active = true)
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_mailbox_domains.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = false AND ('%s' <> 'smtp' AND active = true) AND NOT (transport LIKE 'smtp%%' OR transport LIKE 'relay%%')
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_mailbox_limits.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT quota FROM mailbox WHERE username='%s' AND (('%s' = 'smtp' AND smtp_active = true) OR ('%s' <> 'smtp' AND active = true))
EOF
cat <<'EOF' > /usr/local/etc/postfix/pgsql/virtual_mailbox_maps.cf
hosts = localhost
user = postfix
password = __PASSWORD_POSTFIX__
dbname = postfixadmin
query = SELECT maildir FROM mailbox WHERE username='%s' AND (('%s' = 'smtp' AND smtp_active = true) OR ('%s' <> 'smtp' AND active = true))
EOF
cat /var/db/passwords/user_postgresql_postfix | xargs -I % \
sed -e "s|__PASSWORD_POSTFIX__|%|g" -i '' /usr/local/etc/postfix/pgsql/*.cf
chown -R root:postfix /usr/local/etc/postfix/pgsql
chmod 640 /usr/local/etc/postfix/pgsql/*
chmod 750 /usr/local/etc/postfix/pgsql
cat /var/db/passwords/user_postgresql_postfix
Postfix beschreibt pgsql:-Maps offiziell als passenden Weg, Postfix mit PostgreSQL-Lookup-Tabellen zu verbinden. (postfix.org)
Restriktionen und Lookup-Dateien¶
cp /etc/mail/aliases /usr/local/etc/postfix/aliases
cat <<'EOF' > /usr/local/etc/postfix/postscreen_access.cidr
127.0.0.0/8 permit
[::1]/128 permit
10.0.0.0/8 permit
[fe80::]/10 permit
__IPADDR4__/32 permit
[__IPADDR6__]/64 permit
EOF
cat <<'EOF' > /usr/local/etc/postfix/postscreen_whitelist.cidr
EOF
cat <<'EOF' > /usr/local/etc/postfix/body_checks.pcre
EOF
cat <<'EOF' > /usr/local/etc/postfix/header_checks.pcre
EOF
cat <<'EOF' > /usr/local/etc/postfix/command_filter.pcre
# Work around clients that send `RCPT TO:<'user@domain'>` (Outlook 2003/2007).
# WARNING: do not lose the parameters that follow the address.
/^(RCPT\s+TO:\s*<)'([^[:space:]]+)'(>.*)/ $1$2$3
EOF
cat <<'EOF' > /usr/local/etc/postfix/helo_access.pcre
#---------------------------------------------------------------------
# This file is part of iRedMail, which is an open source mail server
# solution for Red Hat(R) Enterprise Linux, CentOS, Debian and Ubuntu.
#
# iRedMail is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# iRedMail is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with iRedMail. If not, see <http://www.gnu.org/licenses/>.
#---------------------------------------------------------------------
#
# Sample Postfix check_helo_access rule. It should be located at:
# /etc/postfix/check_helo_access.pcre
#
# Shipped within iRedMail project:
# * http://www.iredmail.org/
# Prepend HELO hostname of sender server
#/(.*)/ PREPEND X-Original-Helo: $1 (iRedMail: http://www.iredmail.org/)
# No one will use these in helo command.
/^(localhost)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/^(localhost.localdomain)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(\.local)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
# Reject who use IP address as helo.
# Correct: [xxx.xxx.xxx.xxx]
# Incorrect: xxx.xxx.xxx.xxx
/^([0-9\.]+)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server sent non RFC compliant HELO identity (${1})
#
# This is the real HELO identify of these ISPs:
# sohu.com websmtp.sohu.com relay2nd.mail.sohu.com
# 126.com m15-78.126.com
# sina.com mail2-209.sinamail.sina.com.cn
# gmail.com xx-out-NNNN.google.com
/^(126\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server seems to be impersonating another mail server (${1})
/^(sohu\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server seems to be impersonating another mail server (${1})
/^(gmail\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server seems to be impersonating another mail server (${1})
/^(google\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server seems to be impersonating another mail server (${1})
/^(yahoo\.com\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server seems to be impersonating another mail server (${1})
/^(yahoo\.co\.jp)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server seems to be impersonating another mail server (${1})
#
# Spammers.
#
/^(728154EA470B4AA\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(taj-co\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(CF8D3DB045C1455\.net)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(dsgsfdg\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(se\.nit7-ngbo\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(mail\.goo\.ne\.jp)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(n-ong_an\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(meqail\.teamefs-ine5tl\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(zzg\.jhf-sp\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(din_glo-ng\.net)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(fda-cnc\.ie\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(yrtaj-yrco\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(m\.am\.biz\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(xr_haig\.roup\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(hjn\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(we_blf\.com\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(netvigator\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(mysam\.biz)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(mail\.teams-intl\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(seningbo\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(nblf\.com\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(kdn\.ktguide\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(zzsp\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(nblongan\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(dpu\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(nbalton\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(cncie\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(xinhaigroup\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(wz\.com)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/(\.zj\.cn)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/(\.kornet)$/ REJECT ACCESS DENIED. Your email was rejected because it appears to come from a known spamming mail server (${1})
/^(dsldevice\.lan)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/^(system\.mail)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/^(speedtouch\.lan)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
#
# Reject adsl spammers.
#
# match word `adsl` with word boundary `\b`.
/(\badsl\b)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
# bypass "[IP_ADDRESS]"
/^\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]$/ OK
# Bypass HELOs used by known big ISPs which contains IP address
/\.outbound-(email|mail)\.sendgrid\.net$/ OK
/^\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3}\.mail-.*\.facebook\.com$/ OK
/^outbound-\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3}\.pinterestmail\.com$/ OK
/\.outbound\.protection\.outlook\.com$/ OK
/^ec2-\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3}\..*\.compute\.amazonaws\.com$/ OK
/^out\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3}\.mail\.qq\.com$/ OK
# reject HELO which contains IP address
/(\d{1,3}[\.-]\d{1,3}[\.-]\d{1,3}[\.-]\d{1,3})/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(\d{1,3}\.ip\.-\d{1,3}-\d{1,3}-\d{1,3}\.eu)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(pppoe)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(dsl\.brasiltelecom\.net\.br)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(dsl\.optinet\.hr)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(dsl\.telesp\.net\.br)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(dialup)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(dhcp)/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(static-pool-[\d\.-]*\.flagman\.zp\.ua)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(speedy\.com\.ar)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(speedyterra\.com\.br)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(static\.sbb\.rs)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(static\.vsnl\.net\.in)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server appears to be on a dynamic IP address that should not be doing direct mail delivery (${1})
/(advance\.com\.ar)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(airtelbroadband\.in)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(bb\.netvision\.net\.il)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(broadband3\.iol\.cz)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(cable\.net\.co)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(catv\.broadband\.hu)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(chello\.nl)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(chello\.sk)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(client\.mchsi\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(comunitel\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(coprosys\.cz)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(dclient\.hispeed\.ch)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(dip0\.t-ipconnect\.de)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(domain\.invalid)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(dyn\.centurytel\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(embarqhsd\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(emcali\.net\.co)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(epm\.net\.co)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(fibertel\.com\.ar)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(freedom2surf\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(hgcbroadband\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(HINET-IP\.hinet\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(infonet\.by)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(is74\.ru)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(kievnet\.com\.ua)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(metrotel\.net\.co)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(nw\.nuvox\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(pldt\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(pool\.invitel\.hu)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(pool\.ukrtel\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(pools\.arcor-ip\.net)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(pppoe\.avangarddsl\.ru)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(retail\.telecomitalia\.it)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(revip2\.asianet\.co\.th)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(tim\.ro)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(tsi\.tychy\.pl)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(ttnet\.net\.tr)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(tttmaxnet\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(user\.veloxzone\.com\.br)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(utk\.ru)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(veloxzone\.com\.br)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(virtua\.com\.br)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(wanamaroc\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(wbt\.ru)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(wireless\.iaw\.on\.ca)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(business\.telecomitalia\.it)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(cotas\.com\.bo)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(marunouchi\.tokyo\.ocn\.ne\.jp)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(amedex\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/(aageneva\.com)$/ REJECT ACCESS DENIED. Your email was rejected because the sending mail server does not identify itself correctly (${1})
/^ylmf-pc/ REJECT ACCESS DENIED
/(\.*wideragents\.com)$/ REJECT ACCESS DENIED (${1})
/(\.*resumekeep\.net)$/ REJECT ACCESS DENIED (${1})
/(\.*terracedrink\.com)$/ REJECT ACCESS DENIED (${1})
/(\.*sincemessage\.com)$/ REJECT ACCESS DENIED (${1})
/(\.*ordertranquility\.com)$/ REJECT ACCESS DENIED (${1})
EOF
cat <<'EOF' > /usr/local/etc/postfix/recipient_checks.pcre
/^\@/ 550 Invalid address format.
/[!%\@].*\@/ 550 This server disallows weird address syntax.
/^postmaster\@/ OK
/^hostmaster\@/ OK
/^security\@/ OK
/^abuse\@/ OK
/^admin\@/ OK
EOF
cat <<'EOF' > /usr/local/etc/postfix/sender_access.pcre
EOF
cat <<'EOF' > /usr/local/etc/postfix/submission_header_checks.pcre
^Received:/ IGNORE
EOF
cat <<'EOF' > /usr/local/etc/postfix/postscreen_dnsbl_reply
EOF
cat <<'EOF' > /usr/local/etc/postfix/dnsbl_reply_map
EOF
cat <<'EOF' > /usr/local/etc/postfix/mx_access
0.0.0.0/8 REJECT MX in RFC 1122 Broadcast Network
10.0.0.0/8 REJECT MX in RFC 1918 Private Network
100.64.0.0/10 REJECT MX in RFC 6598 Shared Address Space
127.0.0.0/8 REJECT MX in RFC 1122 Loopback Network
169.254.0.0/16 REJECT MX in RFC 3927 Link Local Network
172.16.0.0/12 REJECT MX in RFC 1918 Private Network
192.0.0.0/24 REJECT MX in RFC 6890 IETF Protocol Assignments Network
192.0.0.0/29 REJECT MX in RFC 6333 DS-Lite Network
192.0.2.0/24 REJECT MX in RFC 5737 Documentation (TEST-NET-1) Network
192.168.0.0/16 REJECT MX in RFC 1918 Private Network
198.18.0.0/15 REJECT MX in RFC 2544 Interconnect Device Benchmark Testing Network
198.51.100.0/24 REJECT MX in RFC 5737 Documentation (TEST-NET-2) Network
203.0.113.0/24 REJECT MX in RFC 5737 Documentation (TEST-NET-3) Network
224.0.0.0/4 REJECT MX in RFC 5771 Multicast Network
240.0.0.0/4 REJECT MX in RFC 1122 Reserved Network
255.255.255.255/32 REJECT MX in RFC 919 Limited Broadcast Destination Address
::/128 REJECT MX in RFC 4291 Unspecified Address
::1/128 REJECT MX in RFC 4291 Loopback Address
::ffff:0:0/96 REJECT MX in RFC 4291 IPv4-mapped Address
100::/64 REJECT MX in RFC 6666 Discard-Only Network
2001::/23 REJECT MX in RFC 2928 IETF Protocol Assignements Network
2001::/32 REJECT MX in RFC 4380 TEREDO Network
2001:2::/48 REJECT MX in RFC 5180 Interconnect Device Benchmark Testing Network
2001:10::/28 REJECT MX in RFC 4843 ORCHID Network
2001:db8::/32 REJECT MX in RFC 3849 Documentation Network
fc00::/7 REJECT MX in RFC 4193 Unique-Local Network
fe80::/10 REJECT MX in RFC 4291 Linked-Scoped Unicast Network
ff00::/8 REJECT MX in RFC 4291 Multicast Network
EOF
# 1. Get Default Interface
DEF_IF="$(route -n get -inet default | awk '/interface:/ {print $2}')"
# 2. Get IPv4 IP
IP4="$(ifconfig "$DEF_IF" inet | awk '/inet / && $2 !~ /^127\./ {print $2}' | head -n 1)"
[ -n "$IP4" ] && sed -e "s|__IPADDR4__|$IP4|g" -i '' /usr/local/etc/postfix/postscreen_access.cidr
# 3. Get IPv6 IP
IP6="$(ifconfig "$DEF_IF" inet6 | awk '/inet6 / && $2 !~ /^fe80:/ && $2 !~ /^::1/ {print $2}' | head -n 1)"
[ -n "$IP6" ] && sed -e "s|__IPADDR6__|$IP6|g" -i '' /usr/local/etc/postfix/postscreen_access.cidr
postmap /usr/local/etc/postfix/postscreen_dnsbl_reply
postmap /usr/local/etc/postfix/dnsbl_reply_map
postmap /usr/local/etc/postfix/mx_access
/usr/local/bin/newaliases
postscreen ist genau für vorgeschaltete SMTP-Prüfungen auf eingehenden Verbindungen gedacht. Lookup-Tabellen und lokale Maps werden mit postmap gebaut; newaliases aktualisiert die Alias-Tabelle. (postfix.org)
Konfiguration prüfen¶
Vor dem ersten Start sollte die Konfiguration immer geprüft werden. Bei Postfix ist dafür postfix check der passende Weg. Zusätzlich ist postconf -n sinnvoll, um die aktiven Nicht-Default-Parameter auszugeben. Für Dovecot-SASL ist postconf -a nützlich, weil damit die unterstützten SASL-Servertypen sichtbar werden. (postfix.org)
Datenbanken¶
Für dieses HowTo sind keine neuen Datenbanken erforderlich.
Postfix nutzt in diesem Setup bestehende PostgreSQL-Tabellen über pgsql:-Lookup-Dateien. (postfix.org)
Zusatzsoftware¶
Mögliche Zusatzsoftware wird hier installiert und konfiguriert.
Postscreen-Whitelist-Helfer¶
portmaster -w -B -g -U --force-config dns/rubygem-dnsruby -n
portmaster -w -B -g -U --force-config net/rubygem-ipaddress -n
portmaster -w -B -g -U --force-config devel/rubygem-optparse -n
portmaster -w -B -g -U --force-config devel/rubygem-pp -n
cat <<'EOF' > /usr/local/etc/postfix/postscreen_whitelist.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'dnsruby'
require 'ipaddress'
require 'optparse'
require 'logger'
require 'thread'
# Initialize logger
LOGGER = Logger.new($stderr)
LOGGER.level = Logger::INFO
# Default values
DEFAULT_DOMAINS = [
# Freemail providers
"openpgp.org", "t-online.de", "telekom.de", "gmail.com", "googlemail.com", "google.com",
"gmx.net", "gmx.com", "gmx.de", "web.de", "aol.com", "microsoft.com", "outlook.com",
"live.com", "live.de", "msn.com",
# Social
"facebook.com", "instagram.com", "threads.com", "meta.com", "twitter.com", "x.com",
"pinterest.com", "reddit.com", "linkedin.com", "xing.com", "xing.de",
# Commerce
"amazon.com", "amazon.de", "paypal.com", "paypal.de", "klarna.com", "klarna.de",
"booking.com", "ebay.com", "ebay.de",
# Bulk sender / misc
"github.com", "openwall.com", "freebsd.org"
]
DEFAULT_OUTPUT = "postscreen_whitelist.cidr"
# Option parsing
options = {
domains: nil,
output: DEFAULT_OUTPUT,
force: false,
loglevel: "info",
threads: 10
}
OptionParser.new do |opts|
opts.banner = "Usage: postscreen_whitelist.rb [options]"
opts.on("-dDOMAINS", "--domains=DOMAINS", "Comma-separated list of domains or path to file") do |d|
options[:domains] = d
end
opts.on("-oFILE", "--output=FILE", "Output file (default: #{DEFAULT_OUTPUT})") do |o|
options[:output] = o
end
opts.on("-f", "--force", "Force overwrite even if result count differs >10%") do
options[:force] = true
end
opts.on("-lLEVEL", "--loglevel=LEVEL", "Logger level (debug, info, warn, error)") do |l|
options[:loglevel] = l
end
opts.on("-tN", "--threads=N", Integer, "Number of parallel DNS threads (default: 10)") do |t|
options[:threads] = t
end
opts.on("-h", "--help", "Print help") do
puts opts
exit
end
end.parse!
LOGGER.level = Logger.const_get(options[:loglevel].upcase) rescue Logger::INFO
# Load domains from file or string
def load_domains(domains_arg)
return DEFAULT_DOMAINS unless domains_arg
if File.exist?(domains_arg)
File.read(domains_arg).lines.map(&:strip).reject { |l| l.empty? || l.start_with?("#") }
else
domains_arg.split(",").map(&:strip)
end
end
domains = load_domains(options[:domains])
# DNS cache (thread-safe)
class DnsCache
def initialize
@cache = {}
@mutex = Mutex.new
end
def fetch(key)
@mutex.synchronize { @cache[key] }
end
def store(key, value)
@mutex.synchronize { @cache[key] = value }
end
end
dns_cache = DnsCache.new
# DNS helpers (with caching)
def dns_query(resolver, name, type, cache)
key = "#{name}:#{type}"
if (cached = cache.fetch(key))
return cached
end
begin
records = resolver.getresources(name, type)
cache.store(key, records)
records
rescue Dnsruby::ResolvError, Timeout::Error => e
LOGGER.debug("DNS error for #{name} #{type}: #{e}")
cache.store(key, [])
[]
end
end
def a(names, resolver, cache)
names.flat_map do |name|
dns_query(resolver, name, "AAAA", cache) + dns_query(resolver, name, "A", cache)
end.map { |r| r.address.to_s.downcase }
end
def mx(name, resolver, cache)
dns_query(resolver, name, "MX", cache).flat_map { |r| a([r.exchange], resolver, cache) }
end
def get_spf_results(domain, resolver, cache)
result = []
txt_records = dns_query(resolver, domain, "TXT", cache) + dns_query(resolver, domain, "SPF", cache)
spf_lines = txt_records.map { |r| r.strings.join }.uniq.select { |s| s =~ /^v=spf1/ }
spf_lines.each do |line|
line.split(/\s+/).each do |entry|
next if entry == "v=spf1"
case entry
when /^redirect=(.+)/ then return get_spf_results($1, resolver, cache)
when /^\??include:(.+)/ then result += get_spf_results($1, resolver, cache)
when /^\??ip4:(.+)/ then result << $1
when /^\??ip6:(.+)/ then result << $1
when /^\??mx$/ then result += mx(domain, resolver, cache)
when /^\??mx:(.+)/ then result += mx($1, resolver, cache)
when /^\??a$/ then result += a([domain], resolver, cache)
when /^\??a:(.+)/ then result += a([$1], resolver, cache)
when /\.all/ then next
else
LOGGER.debug("Unrecognized SPF entry: domain=#{domain} entry=#{entry}")
end
end
end
# Normalize netmasks
result.map! do |r|
if m = r.match(/^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/)
i = IPAddress(r)
"#{i.network.address}/#{i.network.prefix}"
else
r
end
end
result.sort.uniq
end
# Parallel SPF fetching
def fetch_all_spf(domains, resolver, cache, thread_count)
results = []
queue = Queue.new
domains.each { |d| queue << d }
threads = Array.new(thread_count) do
Thread.new do
while !queue.empty?
domain = queue.pop(true) rescue nil
next unless domain
begin
spf = get_spf_results(domain, resolver, cache)
LOGGER.info("Fetched SPF for #{domain}: #{spf.count} entries")
results.concat(spf)
rescue => e
LOGGER.error("Failed to fetch SPF for #{domain}: #{e}")
end
end
end
end
threads.each(&:join)
results.uniq.sort
end
# File diffing and writing
def count_lines(file)
File.exist?(file) ? File.read(file).lines.count : 0
end
old_lines = count_lines(options[:output])
resolver = Dnsruby::DNS.open
spf_results = fetch_all_spf(domains, resolver, dns_cache, options[:threads])
if old_lines > 0 && spf_results.count > 0
ratio = old_lines.to_f / spf_results.count
if (ratio < 0.9 || ratio > 1.1)
LOGGER.warn("More than 10% difference in line count: old: #{old_lines}, new: #{spf_results.count}")
unless options[:force]
LOGGER.warn("Run with --force to overwrite anyway.")
exit 1
end
end
end
# Backup old file
if File.exist?(options[:output])
backup_file = "#{options[:output]}.bak"
File.write(backup_file, File.read(options[:output]))
LOGGER.info("Backup of old file saved at #{backup_file}")
end
File.write(options[:output], spf_results.join(" permit\n") + " permit\n")
LOGGER.info("Whitelist written to #{options[:output]} (#{spf_results.count} entries)")
EOF
chmod 755 /usr/local/etc/postfix/postscreen_whitelist.rb
/usr/local/etc/postfix/postscreen_whitelist.rb -f
Wir installieren mail/libmilter und dessen Abhängigkeiten.¶
mail/py-pymilter, das von mail/py-spf-engine verwendet wird, hängt auf FreeBSD standardmäßig an mail/libmilter. Daher ist diese Installation in deinem Aufbau fachlich konsistent. (FreshPorts)
mkdir -p /var/db/ports/mail_libmilter
cat <<'EOF' > /var/db/ports/mail_libmilter/options
_OPTIONS_READ=libmilter-8.18.2
_FILE_COMPLETE_OPTIONS_LIST=IPV6 MILTER_SHARED MILTER_POOL DOCS
OPTIONS_FILE_SET+=IPV6
OPTIONS_FILE_SET+=MILTER_SHARED
OPTIONS_FILE_SET+=MILTER_POOL
OPTIONS_FILE_UNSET+=DOCS
EOF
portmaster -w -B -g -U --force-config mail/libmilter -n
Wir installieren mail/py-spf-engine und dessen Abhängigkeiten.¶
mail/py-spf-engine liefert zwei Betriebsarten:
den Policy-Service policyd-spf und den Milter pyspf-milter. Auf FreeBSD installiert der Port ein rc.d-Skript für pyspf-milter; der Policy-Service wird laut pkg-message typischerweise direkt aus master.cf heraus von Postfix gespawnt. (FreshPorts)
mkdir -p /var/db/ports/mail_py-pymilter
cat <<'EOF' > /var/db/ports/mail_py-pymilter/options
_OPTIONS_READ=py311-pymilter-1.0.6
_FILE_COMPLETE_OPTIONS_LIST= LIBMILTER BASE
OPTIONS_FILE_SET+=LIBMILTER
OPTIONS_FILE_UNSET+=BASE
EOF
mkdir -p /var/db/ports/dns_py-dnspython
cat <<'EOF' > /var/db/ports/dns_py-dnspython/options
_OPTIONS_READ=py311-dnspython-2.8.0
_FILE_COMPLETE_OPTIONS_LIST=DNSSEC DOH DOQ EXAMPLES IDNA TRIO
OPTIONS_FILE_SET+=DNSSEC
OPTIONS_FILE_SET+=DOH
OPTIONS_FILE_SET+=DOQ
OPTIONS_FILE_UNSET+=EXAMPLES
OPTIONS_FILE_SET+=IDNA
OPTIONS_FILE_SET+=TRIO
EOF
mkdir -p /var/db/ports/devel_py-pyasn1-modules
cat <<'EOF' > /var/db/ports/devel_py-pyasn1-modules/options
_OPTIONS_READ=py311-pyasn1-modules-0.4.1
_FILE_COMPLETE_OPTIONS_LIST=DOCS
OPTIONS_FILE_UNSET+=DOCS
EOF
mkdir -p /var/db/ports/www_py-httpcore
cat <<'EOF' > /var/db/ports/www_py-httpcore/options
_OPTIONS_READ=py311-httpcore-1.0.9
_FILE_COMPLETE_OPTIONS_LIST=ASYNCIO HTTP2 SOCKS TRIO
OPTIONS_FILE_SET+=ASYNCIO
OPTIONS_FILE_SET+=HTTP2
OPTIONS_FILE_SET+=SOCKS
OPTIONS_FILE_SET+=TRIO
EOF
mkdir -p /var/db/ports/devel_py-anyio
cat <<'EOF' > /var/db/ports/devel_py-anyio/options
_OPTIONS_READ=py311-anyio-4.12.1
_FILE_COMPLETE_OPTIONS_LIST=TRIO
OPTIONS_FILE_SET+=TRIO
EOF
mkdir -p /var/db/ports/www_py-httpx
cat <<'EOF' > /var/db/ports/www_py-httpx/options
_OPTIONS_READ=py311-httpx-0.28.1
_FILE_COMPLETE_OPTIONS_LIST=BROTLI CLI HTTP2 SOCKS ZSTD
OPTIONS_FILE_SET+=BROTLI
OPTIONS_FILE_UNSET+=CLI
OPTIONS_FILE_SET+=HTTP2
OPTIONS_FILE_SET+=SOCKS
OPTIONS_FILE_SET+=ZSTD
EOF
mkdir -p /var/db/ports/mail_py-spf-engine
cat <<'EOF' > /var/db/ports/mail_py-spf-engine/options
_OPTIONS_READ=py311-spf-engine-3.1.0
_FILE_COMPLETE_OPTIONS_LIST=DOCS
OPTIONS_FILE_UNSET+=DOCS
EOF
portmaster -w -B -g -U --force-config mail/py-spf-engine -n
pw groupmod pyspf-milter -m postfix
mkdir /var/spool/postfix/pyspf-milter
chown pyspf-milter:pyspf-milter /var/spool/postfix/pyspf-milter
chmod 770 /var/spool/postfix/pyspf-milter
Dienst in rc.conf eintragen¶
Konfigurationsdateien für SPF einrichten¶
cat <<'EOF' > /usr/local/etc/pyspf-milter/pyspf-milter.conf
debugLevel = 1
TestOnly = 1
Reason_Message = Message {rejectdefer} due to: {spf}. Please see {url}
HELO_reject = SPF_Not_Pass
Mail_From_reject = Fail
No_Mail = False
PermError_reject = False
TempError_Defer = False
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
Header_Type = AR
Hide_Receiver = Yes
Authserv_Id = mail.example.com
Socket = local:/var/spool/postfix/pyspf-milter/pyspf-milter.sock
UserID = pyspf-milter
InternalHosts = 127.0.0.1
Mock = true
MacroList = daemon_name|VERIFYING
EOF
cat <<'EOF' > /usr/local/etc/python-policyd-spf/policyd-spf.conf
debugLevel = 1
TestOnly = 1
Reason_Message = Message {rejectdefer} due to: {spf}. Please see {url}
HELO_reject = SPF_Not_Pass
Mail_From_reject = Fail
No_Mail = False
PermError_reject = False
TempError_Defer = False
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
Header_Type = AR
Hide_Receiver = Yes
Authserv_Id = mail.example.com
Socket = local:/var/spool/postfix/pyspf-milter/pyspf-milter.sock
UserID = pyspf-milter
InternalHosts = 127.0.0.1
Mock = true
MacroList = daemon_name|VERIFYING
EOF
Wichtig: Der FreeBSD-Port hat 2023 den Standardpfad für pyspf-milter geändert. Falls dein Setup einen abweichenden Pfad verwenden soll, kannst du ihn per pyspf_milter_conffile in rc.conf überschreiben. (FreeBSD Git)
Zusatzsoftware Konfiguration prüfen¶
Aufräumen¶
Überflüssige oder temporäre Verzeichnisse und Dateien entsorgen.
Zusatzsoftware Installation¶
Nicht erforderlich.
Zusatzsoftware Konfiguration¶
Nicht erforderlich.
Abschluss¶
Postfix kann nun gestartet werden.
Für spätere Änderungen:
Für Funktionstests danach:
Referenzen¶
- FreeBSD Handbook: Electronic Mail / Postfix /
mailer.conf(docs.freebsd.org) - FreshPorts:
mail/postfix(FreshPorts) - Postfix: SASL Howto (postfix.org)
- Dovecot CE: Postfix and Dovecot SASL (doc.dovecot.org)
- Dovecot CE: Postfix and Dovecot LMTP (doc.dovecot.org)
- Postfix: Postscreen Howto (postfix.org)
- Postfix: PostgreSQL Howto /
pgsql_table(5)/ Database Readme (postfix.org) - Postfix:
postfix(1),postconf(1),postmap(1)(postfix.org) - FreshPorts:
mail/py-spf-engine(FreshPorts) - PyPI:
spf-engine3.1.0 (PyPI) - FreshPorts:
mail/py-pymilter/mail/libmilter(FreshPorts) - FreeBSD Ports
UPDATING: geänderterpyspf-milter-Konfigurationspfad (FreeBSD Git)