diff --git a/pillar/credentials/vault.sls b/pillar/credentials/vault.sls --- a/pillar/credentials/vault.sls +++ b/pillar/credentials/vault.sls @@ -111,6 +111,7 @@ mailserver: - ops/secrets/dbserver/cluster-A/users/mailManagement - ops/secrets/dbserver/cluster-A/users/dovecot + - ops/secrets/dbserver/cluster-A/users/postfix - ops/secrets/mailserver/security opensearch: diff --git a/roles/mailserver/init.sls b/pillar/mailserver/postfix.sls copy from roles/mailserver/init.sls copy to pillar/mailserver/postfix.sls --- a/roles/mailserver/init.sls +++ b/pillar/mailserver/postfix.sls @@ -1,8 +1,12 @@ # ------------------------------------------------------------- -# Salt — Mail +# Salt — postfix Configuration # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Project: Nasqueron # License: Trivial work, not eligible to copyright # ------------------------------------------------------------- -# This state is left intentionally blank. +postfix_config: + db: + service: db-A + database: mail + credential: dbserver/cluster-A/users/postfix diff --git a/pillar/top.sls b/pillar/top.sls --- a/pillar/top.sls +++ b/pillar/top.sls @@ -53,6 +53,7 @@ hervil: - mailserver.vimbadmin - mailserver.dovecot + - mailserver.postfix ysul: - devserver.repos diff --git a/roles/mailserver/init.sls b/roles/mailserver/init.sls --- a/roles/mailserver/init.sls +++ b/roles/mailserver/init.sls @@ -5,4 +5,5 @@ # License: Trivial work, not eligible to copyright # ------------------------------------------------------------- -# This state is left intentionally blank. +include: + - .postfix diff --git a/roles/mailserver/map.jinja b/roles/mailserver/map.jinja new file mode 100644 --- /dev/null +++ b/roles/mailserver/map.jinja @@ -0,0 +1,16 @@ +{% set postfix_dirs = salt['grains.filter_by']({ + 'FreeBSD' : { + 'daemon': '/usr/local/libexec/postfix', + 'queue': '/var/spool/postfix', + 'data': '/var/db/postfix', + 'shlib': '/usr/local/lib/postfix', + 'cacerts': '/etc/ssl/certs', + }, + 'Debian' : { + 'daemon': '/usr/lib/postfix', + 'queue': '/var/spool/postfix', + 'data': '/var/lib/postfix', + 'shlib': '/usr/lib/postfix', + 'cacerts': '/etc/ssl/certs', + } +}, default='Debian') %} diff --git a/roles/mailserver/postfix/files/dynamicmaps.cf b/roles/mailserver/postfix/files/dynamicmaps.cf new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/dynamicmaps.cf @@ -0,0 +1,20 @@ +# ------------------------------------------------------------- +# Postfix main configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix/files/dynamicmaps.cf +# ------------------------------------------------------------- +# +# <auto-generated> +# This file is managed by our rOPS SaltStack repository. +# +# Changes to this file may cause incorrect behavior +# and will be lost if the state is redeployed. +# </auto-generated> + +# Postfix dynamic maps configuration file. +# +#type location of .so file open function (mkmap func) +#==== ================================ ============= ============ +pgsql {{ dirs.lib }}/postfix/postfix-pgsql.so dict_pgsql_open diff --git a/roles/mailserver/postfix/files/main.cf b/roles/mailserver/postfix/files/main.cf new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/main.cf @@ -0,0 +1,123 @@ +# ------------------------------------------------------------- +# Postfix main configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix/files/main.cf +# ------------------------------------------------------------- +# +# <auto-generated> +# This file is managed by our rOPS SaltStack repository. +# +# Changes to this file may cause incorrect behavior +# and will be lost if the state is redeployed. +# </auto-generated> + +compatibility_level = 3.8 + +# ------------------------------------------------------------- +# Postfix directories +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +command_directory = {{ dirs.sbin }} +daemon_directory = {{ postfix_dirs.daemon }} +data_directory = {{ postfix_dirs.data }} +html_directory = {{ dirs.share }}/doc/postfix +manpage_directory = {{ dirs.man }} +meta_directory = {{ dirs.etc }}/postfix +queue_directory = {{ postfix_dirs.queue }} +readme_directory = {{ dirs.share }}/doc/postfix +sample_directory = {{ dirs.etc }}/postfix +shlib_directory = {{ postfix_dirs.shlib }} + +virtual_mailbox_base = /var/mail/_virtual +virtual_uid_maps = static:6000 +virtual_gid_maps = static:6000 + +virtual_mailbox_domains=pgsql:{{ dirs.etc }}/postfix/pgsql-virtual-mailbox-domains.cf +virtual_mailbox_maps=pgsql:{{ dirs.etc }}/postfix/pgsql-virtual-mailbox-maps.cf +virtual_alias_maps=pgsql:{{ dirs.etc }}/postfix/pgsql-virtual-alias-maps.cf + +alias_maps = hash:/etc/mail/aliases +alias_database = hash:/etc/mail/aliases + +myhostname = mail.nasqueron.org + +# ------------------------------------------------------------- +# External utilities +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +mailq_path = /usr/bin/mailq +newaliases_path = /usr/bin/newaliases +sendmail_path = /usr/bin/sendmail + +# ------------------------------------------------------------- +# UNIX users and groups +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +mail_owner = postfix +setgid_group = maildrop + +# ------------------------------------------------------------- +# Debug +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +debug_peer_level = 2 + +debugger_command = + PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin + ddd $daemon_directory/$process_name $process_id & sleep 5 + +# ------------------------------------------------------------- +# Network +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +inet_protocols = all +mynetworks_style = host + +# ------------------------------------------------------------- +# Mail +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +mydestination = localhost +unknown_local_recipient_reject_code = 550 + +# ------------------------------------------------------------- +# TLS certificates +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +smtp_tls_CApath = {{ postfix_dirs.cacerts }} + +smtpd_tls_security_level=may +smtpd_tls_cert_file=/usr/local/etc/letsencrypt/live/mail.nasqueron.org/fullchain.pem +smtpd_tls_key_file=/usr/local/etc/letsencrypt/live/mail.nasqueron.org/privkey.pem + +smtpd_tls_mandatory_ciphers = high +smtpd_tls_mandatory_exclude_ciphers = aNULL,MD5 +smtpd_tls_security_level = may +smtpd_tls_mandatory_protocols = !SSLv2,!SSLv3 + +# ------------------------------------------------------------- +# Handle mail storage with dovecot +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +virtual_transport = lmtp:unix:private/dovecot-lmtp + +# ------------------------------------------------------------- +# Handle SMTP authentication using Dovecot +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +smtpd_sasl_auth_enable = yes + +smtpd_recipient_restrictions = + permit_sasl_authenticated, + permit_mynetworks, + reject_unauth_destination, + reject_rbl_client zen.spamhaus.org, + reject_rbl_client bl.spamcop.net, + reject_rbl_client cbl.abuseat.org, + check_policy_service unix:private/policy-spf + +smtpd_relay_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination reject_rbl_client zen.spamhaus.org, reject_rbl_client bl.spamcop.net, reject_rbl_client cbl.abuseat.org diff --git a/roles/mailserver/postfix/files/master.cf b/roles/mailserver/postfix/files/master.cf new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/master.cf @@ -0,0 +1,77 @@ +# ------------------------------------------------------------- +# Postfix main configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix/files/master.cf +# ------------------------------------------------------------- +# +# <auto-generated> +# This file is managed by our rOPS SaltStack repository. +# +# Changes to this file may cause incorrect behavior +# and will be lost if the state is redeployed. +# </auto-generated> + +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (yes) (never) (100) +# ========================================================================== +smtp inet n - - - - smtpd +submission inet n - - - - smtpd + -o syslog_name=postfix/submission + -o smtpd_tls_security_level=encrypt + -o smtpd_sasl_auth_enable=yes + -o smtpd_sasl_type=dovecot + -o smtpd_sasl_path=private/auth + -o smtpd_sasl_security_options=noanonymous + -o smtpd_sasl_local_domain=$myhostname + -o smtpd_client_restrictions=permit_sasl_authenticated,reject + -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject +smtps inet n - - - - smtpd +pickup fifo n - - 60 1 pickup +cleanup unix n - - - 0 cleanup +qmgr fifo n - n 300 1 qmgr +tlsmgr unix - - - 1000? 1 tlsmgr +rewrite unix - - - - - trivial-rewrite +bounce unix - - - - 0 bounce +defer unix - - - - 0 bounce +trace unix - - - - 0 bounce +verify unix - - - - 1 verify +flush unix n - - 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - - - - smtp +relay unix - - - - - smtp +showq unix n - - - - showq +error unix - - - - - error +retry unix - - - - - error +discard unix - - - - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - - - - lmtp +anvil unix - - - - 1 anvil +scache unix - - - - 1 scache +# +# ==================================================================== +# Interfaces to non-Postfix software. Be sure to examine the manual +# pages of the non-Postfix software to find out what options it wants. +# +# Many of the following services use the Postfix pipe(8) delivery +# agent. See the pipe(8) man page for information about ${recipient} +# and other message envelope options. +# ==================================================================== +# +# maildrop. See the Postfix MAILDROP_README file for details. +# Also specify in main.cf: maildrop_destination_recipient_limit=1 +# +maildrop unix - n n - - pipe + flags=DRhu user=mailbox argv=/usr/local/bin/maildrop -d ${recipient} +# +# Other external delivery methods. +# +mailman unix - n n - - pipe + flags=FR user=list argv=/usr/local/etc/postfix/postfix-to-mailman.py + ${nexthop} ${user} +policy-spf unix - n n - - spawn + user=nobody argv=/usr/local/libexec/postfix-policyd-spf-perl diff --git a/roles/mailserver/postfix/files/pgsql-virtual-alias-maps.cf b/roles/mailserver/postfix/files/pgsql-virtual-alias-maps.cf new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/pgsql-virtual-alias-maps.cf @@ -0,0 +1,19 @@ +# ------------------------------------------------------------- +# Postfix main configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix/files/pgsql-virtual-mailbox-domains.cf +# ------------------------------------------------------------- +# +# <auto-generated> +# This file is managed by our rOPS SaltStack repository. +# +# Changes to this file may cause incorrect behavior +# and will be lost if the state is redeployed. +# </auto-generated> +user = {{ db.username }} +password = {{ db.password }} +hosts = {{ db.host }} +dbname = {{ db.database }} +query = SELECT goto FROM alias WHERE address = '%s' AND active = '1' diff --git a/roles/mailserver/postfix/files/pgsql-virtual-mailbox-domains.cf b/roles/mailserver/postfix/files/pgsql-virtual-mailbox-domains.cf new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/pgsql-virtual-mailbox-domains.cf @@ -0,0 +1,19 @@ +# ------------------------------------------------------------- +# Postfix main configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix/files/pgsql-virtual-mailbox-domains.cf +# ------------------------------------------------------------- +# +# <auto-generated> +# This file is managed by our rOPS SaltStack repository. +# +# Changes to this file may cause incorrect behavior +# and will be lost if the state is redeployed. +# </auto-generated> +user = {{ db.username }} +password = {{ db.password }} +hosts = {{ db.host }} +dbname = {{ db.database }} +query = SELECT domain FROM domain WHERE domain = '%s' AND backupmx = '0' AND active = '1' diff --git a/roles/mailserver/postfix/files/pgsql-virtual-mailbox-maps.cf b/roles/mailserver/postfix/files/pgsql-virtual-mailbox-maps.cf new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/pgsql-virtual-mailbox-maps.cf @@ -0,0 +1,21 @@ +# ------------------------------------------------------------- +# Postfix main configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix/files/pgsql-virtual-mailbox-maps.cf +# ------------------------------------------------------------- +# +# <auto-generated> +# This file is managed by our rOPS SaltStack repository. +# +# Changes to this file may cause incorrect behavior +# and will be lost if the state is redeployed. +# </auto-generated> +user = {{ db.username }} +password = {{ db.password }} +hosts = {{ db.host }} +dbname = {{ db.database }} +table = mailbox +select_field = maildir +where_field = username diff --git a/roles/mailserver/postfix/files/postfix-to-mailman.py b/roles/mailserver/postfix/files/postfix-to-mailman.py new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/files/postfix-to-mailman.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------------------- +# Mailman transport for postfix +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# Source: postfix-to-mailman-2.1.py +# Description: Interface mailman to a postfix with a mailman transport. +# Does not require the creation of _any_ aliases to connect +# lists to the mail system. +# License: GPLv2 +# Authors: Original script for qmail by Bruce Perens, March 1999 +# +# Dax Kelson, dkelson@gurulabs.com, Sept 2002. +# Converted from qmail to postfix interface +# +# Jan 2003: Fixes for Mailman 2.1 +# Thanks to Simen E. Sandberg <senilix@gallerbyen.net> +# +# Feb 2003: Change the suggested postfix transport to support VERP +# Thanks to Henrique de Moraes Holschuh <henrique.holschuh@ima.sp.gov.br> +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# README +# +# It catches all mail to a virtual domain, eg "lists.example.com". +# It looks at the recipient for each mail message and decides if the mail is +# addressed to a valid list or not, and bounces the message with a helpful +# suggestion if it's not addressed to a list. It decides if it is a posting, +# a list command, or mail to the list administrator, by checking for the +# -admin, -owner, and -request addresses. It will recognize a list as soon +# as the list is created, there is no need to add _any_ aliases for any list. +# It recognizes mail to postmaster, mailman-owner, abuse, mailer-daemon, root, +# and owner, and routes those mails to MailmanOwner as defined in the +# configuration variables, above. +# ------------------------------------------------------------- + + +import os +import sys +import re +import string + + +# ------------------------------------------------------------- +# Configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +# Mailman home directory. +MailmanHome = "/usr/local/mailman" + +# Postmaster and abuse mail recipient. +MailmanOwner = "{{ mailmanAbuse }}" + +# Where mailman scripts reside +MailmanScripts = "/usr/local/mailman" + + +# ------------------------------------------------------------- +# Postfix / mailman transport methods +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def main(): + os.nice(5) # Handle mailing lists at non-interactive priority. + # delete this if you wish + + os.chdir(MailmanHome + "/lists") + + try: + local = sys.argv[2] + except IndexError: + # This might happen if we're not using Postfix + sys.stderr.write("LOCAL not set?\n") + sys.exit(1) + + local = string.lower(local) + local = re.sub("^mailman-", "", local) + + names = ("root", "postmaster", "mailer-daemon", "mailman-owner", "owner", "abuse") + for name in names: + if name == local: + os.execv("/usr/sbin/sendmail", ("/usr/sbin/sendmail", MailmanOwner)) + sys.exit(0) + + type = "post" + types = ( + ("-admin$", "admin"), + ("-owner$", "owner"), + ("-request$", "request"), + ("-bounces$", "bounces"), + ("-confirm$", "confirm"), + ("-join$", "join"), + ("-leave$", "leave"), + ("-subscribe$", "subscribe"), + ("-unsubscribe$", "unsubscribe"), + ) + + for i in types: + if re.search(i[0], local): + type = i[1] + local = re.sub(i[0], "", local) + + if os.path.exists(local): + os.execv( + MailmanScripts + "/mail/mailman", + (MailmanScripts + "/mail/mailman", type, local), + ) + else: + bounce() + sys.exit(75) + + +def bounce(): + bounce_message = """\ +TO ACCESS THE MAILING LIST SYSTEM: Start your web browser on +http://%s/ +That web page will help you subscribe or unsubscribe, and will +give you directions on how to post to each mailing list.\n""" + sys.stderr.write(bounce_message % (sys.argv[1])) + sys.exit(1) + + +try: + sys.exit(main()) +except SystemExit as argument: + sys.exit(argument) + +except Exception as argument: + info = sys.exc_info() + trace = info[2] + sys.stderr.write("%s %s\n" % (sys.exc_info()[0], argument)) + sys.stderr.write("Line %d\n" % (trace.tb_lineno)) + sys.exit(75) # Soft failure, try again later. diff --git a/roles/mailserver/postfix/init.sls b/roles/mailserver/postfix/init.sls new file mode 100644 --- /dev/null +++ b/roles/mailserver/postfix/init.sls @@ -0,0 +1,89 @@ +# ------------------------------------------------------------- +# Mail - Postfix +# ------------------------------------------------------------- +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# Source file: roles/mailserver/postfix.sls +# ------------------------------------------------------------- + +{% from "map.jinja" import dirs with context %} +{% from "roles/mailserver/map.jinja" import postfix_dirs with context %} +{% set db = pillar["postfix_config"]["db"] %} + +# ------------------------------------------------------------- +# Software +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +postfix_install: + pkg.installed: + - pkgs: + - maildrop + - mailman + - postfix-pgsql + - postfix-policyd-spf-perl + +# ------------------------------------------------------------- +# Configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +{{ dirs.etc }}/postfix/main.cf: + file.managed: + - source: salt://roles/mailserver/postfix/files/main.cf + - template: jinja + - context: + dirs: {{ dirs }} + postfix_dirs: {{ postfix_dirs }} + +{{ dirs.etc }}/postfix/postfix-to-mailman.py: + file.managed: + - source: salt://roles/mailserver/postfix/files/postfix-to-mailman.py + - template: jinja + - context: + dirs: {{ dirs }} + mailmanAbuse: postmaster@nasqueron.org + +/usr/local/etc/postfix/postfix-files: + file.symlink: + - target: /usr/local/libexec/postfix/postfix-files + +{{ dirs.etc }}/postfix/pgsql-virtual-mailbox-domains.cf: + file.managed: + - source: salt://roles/mailserver/postfix/files/pgsql-virtual-mailbox-domains.cf + - template: jinja + - context: + db: &dbcontext + database: {{ db["database"] }} + username: {{ salt["credentials.get_username"](db["credential"]) }} + password: {{ salt["credentials.get_password"](db["credential"]) }} + host: {{ pillar["nasqueron_services"][db["service"]] }} + +{{ dirs.etc }}/postfix/pgsql-virtual-mailbox-maps.cf: + file.managed: + - source: salt://roles/mailserver/postfix/files/pgsql-virtual-mailbox-maps.cf + - template: jinja + - context: + db: *dbcontext + +{{ dirs.etc }}/postfix/pgsql-virtual-alias-maps.cf: + file.managed: + - source: salt://roles/mailserver/postfix/files/pgsql-virtual-alias-maps.cf + - template: jinja + - context: + db: *dbcontext + +{{ dirs.etc }}/postfix/master.cf: + file.managed: + - source: salt://roles/mailserver/postfix/files/master.cf + - template: jinja + +{{ dirs.etc }}/postfix/dynamicmaps.cf: + file.managed: + - source: salt://roles/mailserver/postfix/files/dynamicmaps.cf + - template: jinja + - context: + dirs: {{ dirs }} + +postfix_running: + service.running: + - name: postfix + - enable: True