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.
This commit is contained in:
Andreas Thienemann 2018-03-11 19:22:12 +01:00
parent e9ef0aa547
commit b6b7ab4cca
4 changed files with 373 additions and 69 deletions

View file

@ -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``
------------------

View file

@ -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:

View file

@ -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

261
postfix/services.jinja Normal file
View file

@ -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'
] %}