From b6b7ab4cca5e46870ad3ed22967acd9ca538ed7f Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Sun, 11 Mar 2018 19:22:12 +0100 Subject: [PATCH] Allow for full managed master.cf services Currently master.cf only allows for _very_ limited configuration options mainly focussed on SMTP submission settings. This is rather limited and does not scale very well for managing the other services defined in master.cf. This patch has moved all the service definitions into a jinja file and generates the master.cf service definition on the fly based on these defaults. Defaults can be overridden in a pillar to customize the rendered master.cf file accordingly to local needs. Undefined values will be filled with the postfix defaults. Care has been taken that the previous ways of managing the submission configuration options are still supported for backwards compatibility to prevent breakage for existing users of the formula. --- README.rst | 2 +- pillar.example | 19 +++ postfix/files/master.cf | 160 +++++++++++++----------- postfix/services.jinja | 261 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 373 insertions(+), 69 deletions(-) create mode 100644 postfix/services.jinja diff --git a/README.rst b/README.rst index b1f8e41..0410cc9 100644 --- a/README.rst +++ b/README.rst @@ -23,7 +23,7 @@ Installs and starts postfix SMTP server ``postfix.config`` ------------------ -Manages postfix main.cf configuration file +Manages postfix main.cf and optionally the master.cf configuration file ``postfix.policyd-spf`` ------------------ diff --git a/pillar.example b/pillar.example index a3bfdee..e00a93f 100644 --- a/pillar.example +++ b/pillar.example @@ -16,6 +16,25 @@ postfix: smtpd_sasl_auth_enable: yes smtpd_client_restrictions: permit_sasl_authenticated,reject + # Alternative way of managing services allowing for much more finegrained + # control over each service. See postfix/services.jinja for details. + services: + smtp: + # Limit to no more than 10 smtp processes + maxproc: 10 + # Enable oldstyle TLS wrapped SMTP + smtps: + enable: True + submission: + enable: True + args: + - "-o smtpd_tls_security_level=encrypt" + - "-o smtpd_sasl_auth_enable=yes" + - "-o smtpd_client_restrictions: permit_sasl_authenticated,reject" + tlsproxy: + enable: True + chroot: True + enable_service: True postgrey: diff --git a/postfix/files/master.cf b/postfix/files/master.cf index 5b69be5..0520ed1 100644 --- a/postfix/files/master.cf +++ b/postfix/files/master.cf @@ -1,15 +1,66 @@ {%- from "postfix/map.jinja" import postfix with context -%} +{%- set master_config = salt['pillar.get']('postfix:master_config', {}) -%} +{%- from "postfix/services.jinja" import postfix_master_services_defaults, postfix_master_services_order -%} -{%- macro set_option(parameter, value) -%} - {%- if value is number or value is string -%} --o {{ parameter }}={{ value }} - {%- elif value is iterable -%} --o {{ parameter }}={{ value | join(', ')}} +{#- + # Handle the case that the pillar data does not provide any service + # configuration but submission parameters are provided in the pillar.. + # This is important for backwards compatibility with sites that are using + # the previous enable_submission pillar settings. + -#} +{%- set additional_services = {} -%} +{%- if master_config.get('enable_submission', False) and not salt[ + 'pillar.get']('postfix:master_config:services:submission', False) -%} +{%- do additional_services.update({'submission': {'chroot': False, + 'command': 'smtpd', + 'enable': True, + 'type': 'inet', + 'args': [], + 'private': False}}) -%} +{%- if master_config.get('submission', False) -%} +{%- for parameter, value in master_config.get('submission', {}).items() -%} +{%- if value is number or value is string -%} +{%- do additional_services['submission']['args'].append('-o %s=%s' % ( + parameter, value)) -%} +{%- elif value is iterable -%} +{%- do additional_services['submission']['args'].append('-o %s=%s' % ( + parameter, ', '.join(value))) -%} +{%- endif -%} +{%- endfor -%} +{%- else -%} +{%- do additional_services[ + 'submission']['args'].extend(['# -o syslog_name=postfix/submission', + '-o smtpd_tls_security_level=encrypt', + '-o smtpd_sasl_auth_enable=yes', + '# -o smtpd_reject_unlisted_recipient=no', + '# -o smtpd_client_restrictions=$mua_client_restrictions', + '# -o smtpd_helo_restrictions=$mua_helo_restrictions', + '# -o smtpd_sender_restrictions=$mua_sender_restrictions', + '# -o smtpd_recipient_restrictions=', + '# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject', + '# -o milter_macro_daemon_name=ORIGINATING' + ]) -%} +{%- endif -%} +{%- endif -%} + +{#- Format the postfix service parameters correctly -#} +{%- macro service_param(service, service_name, parameter_name, default='-') -%} + {#- Fetch the value from the passed service dictionary or fall back to the + # service defaults by chaining .get() commands. #} + {%- set value = service.get(parameter_name, + postfix_master_services_defaults[service_name].get( + parameter_name, default)) -%} + {%- if value is sameas false -%} +n + {%- elif value is sameas true -%} +y + {%- elif value is number or value is string -%} +{{ value }} + {%- else -%} +- {%- endif -%} {%- endmacro -%} -{% set master_config = salt['pillar.get']('postfix:master_config', {}) -%} - # # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master" or @@ -21,67 +72,40 @@ # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (no) (never) (100) # ========================================================================== -smtp inet n - n - - smtpd -#smtp inet n - n - 1 postscreen -#smtpd pass - - n - - smtpd -#dnsblog unix - - n - 0 dnsblog -#tlsproxy unix - - n - 0 tlsproxy -{%- if master_config.get('enable_submission', False) %} -submission inet n - n - - smtpd -{%- if master_config.get('submission', False) -%} -{% for parameter, value in master_config.get('submission', {}).items() %} - {{ set_option(parameter, value) }} -{%- endfor -%} -{% else %} -# -o syslog_name=postfix/submission - -o smtpd_tls_security_level=encrypt - -o smtpd_sasl_auth_enable=yes -{% endif %} -# -o smtpd_reject_unlisted_recipient=no -# -o smtpd_client_restrictions=$mua_client_restrictions -# -o smtpd_helo_restrictions=$mua_helo_restrictions -# -o smtpd_sender_restrictions=$mua_sender_restrictions -# -o smtpd_recipient_restrictions= -# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -# -o milter_macro_daemon_name=ORIGINATING -{% endif -%} -#smtps inet n - n - - smtpd -# -o syslog_name=postfix/smtps -# -o smtpd_tls_wrappermode=yes -# -o smtpd_sasl_auth_enable=yes -# -o smtpd_reject_unlisted_recipient=no -# -o smtpd_client_restrictions=$mua_client_restrictions -# -o smtpd_helo_restrictions=$mua_helo_restrictions -# -o smtpd_sender_restrictions=$mua_sender_restrictions -# -o smtpd_recipient_restrictions= -# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -# -o milter_macro_daemon_name=ORIGINATING -#628 inet n - n - - qmqpd -pickup unix n - n 60 1 pickup -cleanup unix n - n - 0 cleanup -qmgr unix n - n 300 1 qmgr -#qmgr unix n - n 300 1 oqmgr -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 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 +{%- for service_name in postfix_master_services_order %} +{#- Try to get the service configuration from the pillar if present. + # Next try if the service has been dynamically configured and is present in + # the additional_services dictionary. + # If absent, fall back to the defaults provided in services.jinja -#} +{%- set service = salt['pillar.get']('postfix:master_config:services:%s' % ( + service_name,), + additional_services.get(service_name, + postfix_master_services_defaults[service_name])) -%} +{%- if service.get('enable', True) -%} +{%- set comment = '' -%} +{%- else -%} +{%- set comment = '#' -%} +{%- endif %} +{{ "%s%-9s %-5s %-7s %-7s %-7s %-7s %-7s %s" | format(comment, + service_param(service, service_name, 'service', service_name), + service_param(service, service_name, 'type'), + service_param(service, service_name, 'private'), + service_param(service, service_name, 'unpriv'), + service_param(service, service_name, 'chroot'), + service_param(service, service_name, 'wakeup'), + service_param(service, service_name, 'maxproc'), + service_param(service, service_name, 'command', service_name)) -}} +{%- if service.args is not none -%} +{%- for option in service.get('args', postfix_master_services_defaults[ + service_name].get('args', [])) -%} +{%- if option.startswith('#') %} +{{ option }} +{%- else %} +{{ comment }} {{ option }} +{%- endif %} +{%- endfor %} +{%- endif %} +{%- endfor %} # # ==================================================================== # Interfaces to non-Postfix software. Be sure to examine the manual diff --git a/postfix/services.jinja b/postfix/services.jinja new file mode 100644 index 0000000..8bb1689 --- /dev/null +++ b/postfix/services.jinja @@ -0,0 +1,261 @@ +{#- + # Default Postfix master processes as defined by postfix + # + # The dictionary is keyed off the service name ("smtp", "smtpd", etc.) except + # for the few cases the service name is repeated to illustrate alternative + # options in the file. + # In such a case the second entry has a unique identifier appended, e.g. + # "smtp-unix". The 'service' attribute is used to provide the service name + # that will be rendered by jinja thus overriding the usual key to prevent + # clashes. +-#} + +{% set postfix_master_services_defaults = { + 'smtp': { + 'type': 'inet', + 'private': False, + 'chroot': False, + 'command': 'smtpd' + }, + 'smtp-postscreen': { + 'service': 'smtp', + 'enable': False, + 'type': 'inet', + 'maxproc': 1, + 'private': False, + 'chroot': False, + 'command': 'postscreen' + }, + 'smtpd': { + 'enable': False, + 'type': 'pass', + 'chroot': False + }, + 'dnsblog': { + 'maxproc': 0, + 'enable': False, + 'type': 'unix', + 'chroot': False + }, + 'tlsproxy': { + 'maxproc': 0, + 'enable': False, + 'type': 'unix', + 'chroot': False + }, + 'submission': { + 'chroot': False, + 'command': 'smtpd', + 'enable': False, + 'args': [ + '-o syslog_name=postfix/submission', + '-o smtpd_tls_security_level=encrypt', + '-o smtpd_sasl_auth_enable=yes', + '-o smtpd_reject_unlisted_recipient=no', + '-o smtpd_client_restrictions=$mua_client_restrictions', + '-o smtpd_helo_restrictions=$mua_helo_restrictions', + '-o smtpd_sender_restrictions=$mua_sender_restrictions', + '-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject', + '-o milter_macro_daemon_name=ORIGINATING' + ], + 'type': 'inet', + 'private': False + }, + 'smtps': { + 'chroot': False, + 'command': 'smtpd', + 'enable': False, + 'args': [ + '-o syslog_name=postfix/smtps', + '-o smtpd_tls_wrappermode=yes', + '-o smtpd_sasl_auth_enable=yes', + '-o smtpd_reject_unlisted_recipient=no', + '-o smtpd_client_restrictions=$mua_client_restrictions', + '-o smtpd_helo_restrictions=$mua_helo_restrictions', + '-o smtpd_sender_restrictions=$mua_sender_restrictions', + '-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject', + '-o milter_macro_daemon_name=ORIGINATING' + ], + 'type': 'inet', + 'private': False + }, + '628': { + 'enable': False, + 'command': 'qmqpd', + 'type': 'inet', + 'chroot': False, + 'private': False + }, + 'pickup': { + 'maxproc': 1, + 'chroot': False, + 'wakeup': 60, + 'type': 'unix', + 'private': False, + }, + 'cleanup': { + 'maxproc': 0, + 'chroot': False, + 'type': 'unix', + 'private': False + }, + 'qmgr': { + 'chroot': False, + 'private': False, + 'maxproc': 1, + 'wakeup': 300, + 'type': 'unix' + }, + 'qmgr-oqmgr': { + 'service': 'qmgr', + 'chroot': False, + 'private': False, + 'maxproc': 1, + 'enable': False, + 'command': 'oqmgr', + 'wakeup': 300, + 'type': 'unix' + }, + 'tlsmgr': { + 'wakeup': '1000?', + 'chroot': False, + 'type': 'unix', + 'maxproc': 1 + }, + 'rewrite': { + 'type': 'unix', + 'chroot': False, + 'command': 'trivial-rewrite' + }, + 'bounce': { + 'maxproc': 0, + 'chroot': False, + 'type': 'unix' + }, + 'defer': { + 'maxproc': 0, + 'chroot': False, + 'type': 'unix', + 'command': 'bounce' + }, + 'trace': { + 'maxproc': 0, + 'chroot': False, + 'type': 'unix', + 'command': 'bounce' + }, + 'smtp-unix': { + 'service': 'smtp', + 'chroot': False, + 'type': 'unix', + 'command': 'smtp' + }, + 'verify': { + 'maxproc': 1, + 'chroot': False, + 'type': 'unix' + }, + 'flush': { + 'maxproc': 0, + 'chroot': False, + 'wakeup': '1000?', + 'type': 'unix', + 'private': False + }, + 'proxymap': { + 'chroot': False, + 'type': 'unix' + }, + 'proxywrite': { + 'maxproc': 1, + 'chroot': False, + 'type': 'unix', + 'command': 'proxymap' + }, + 'relay': { + 'type': 'unix', + 'chroot': False, + 'args': [ + '# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5' + ], + 'command': 'smtp' + }, + 'showq': { + 'chroot': False, + 'type': 'unix', + 'private': False + }, + 'error': { + 'chroot': False, + 'type': 'unix' + }, + 'retry': { + 'type': 'unix', + 'chroot': False, + 'command': 'error' + }, + 'discard': { + 'chroot': False, + 'type': 'unix' + }, + 'local': { + 'unpriv': False, + 'chroot': False, + 'type': 'unix' + }, + 'virtual': { + 'unpriv': False, + 'chroot': False, + 'type': 'unix' + }, + 'lmtp': { + 'chroot': False, + 'type': 'unix' + }, + 'anvil': { + 'maxproc': 1, + 'chroot': False, + 'type': 'unix' + }, + 'scache': { + 'maxproc': 1, + 'chroot': False, + 'type': 'unix' + } +} %} + +{# Service order inside the master.cf file #} +{% set postfix_master_services_order = [ + 'smtp', + 'smtp-postscreen', + 'smtpd', + 'dnsblog', + 'tlsproxy', + 'submission', + 'smtps', + '628', + 'pickup', + 'cleanup', + 'qmgr', + 'qmgr-oqmgr', + 'tlsmgr', + 'rewrite', + 'bounce', + 'defer', + 'trace', + 'verify', + 'flush', + 'proxymap', + 'proxywrite', + 'smtp-unix', + 'relay', + 'showq', + 'error', + 'retry', + 'discard', + 'local', + 'virtual', + 'lmtp', + 'anvil', + 'scache' +] %}