1# SMTP message routing (pipeline)23# Message pipeline45A message pipeline is a set of module references and associated rules that6describe how to handle messages.78The pipeline is responsible for910- Running message filters (called "checks"), (e.g. DKIM signature verification,11 DNSBL lookup, and so on).12- Running message modifiers (e.g. DKIM signature creation).13- Associating each message recipient with one or more delivery targets.14 Delivery target is a module that does the final processing (delivery) of the15 message.1617Message handling flow is as follows:1819- Execute checks referenced in top-level `check` blocks (if any)20- Execute modifiers referenced in top-level `modify` blocks (if any)21- If there are `source` blocks - select one that matches the message sender (as22 specified in MAIL FROM). If there are no `source` blocks - the entire23 configuration is assumed to be the `default_source` block.24- Execute checks referenced in `check` blocks inside the selected `source` block25 (if any).26- Execute modifiers referenced in `modify` blocks inside selected `source`27 block (if any).2829Then, for each recipient:3031- Select the `destination` block that matches it. If there are32 no `destination` blocks - the entire used `source` block is interpreted as if it33 was a `default_destination` block.34- Execute checks referenced in the `check` block inside the selected `destination`35 block (if any).36- Execute modifiers referenced in `modify` block inside the selected `destination`37 block (if any).38- If the used block contains the `reject` directive - reject the recipient with39 the specified SMTP status code.40- If the used block contains the `deliver_to` directive - pass the message to the41 specified target module. Only recipients that are handled42 by the used block are visible to the target.4344Each recipient is handled only by a single `destination` block, in case of45overlapping `destination` - the first one takes priority.4647```48destination example.org {49 deliver_to targetA50}51destination example.org { # ambiguous and thus not allowed52 deliver_to targetB53}54```5556Same goes for `source` blocks, each message is handled only by a single block.5758Each recipient block should contain at least one `deliver_to` directive or59`reject` directive. If `destination` blocks are used, then60`default_destination` block should also be used to specify behavior for61unmatched recipients. Same goes for source blocks, `default_source` should be62used if `source` is used.6364That is, pipeline configuration should explicitly specify behavior for each65possible sender/recipient combination.6667Additionally, directives that specify final handling decision (`deliver_to`,68`reject`) can't be used at the same level as source/destination rules.69Consider example:7071```72destination example.org {73 deliver_to local_mboxes74}75reject76```7778It is not obvious whether `reject` applies to all recipients or79just for non-example.org ones, hence this is not allowed.8081Complete configuration example using all of the mentioned directives:8283```84check {85 # Run a check to make sure source SMTP server identification86 # is legit.87 spf88}8990# Messages coming from senders at example.org will be handled in91# accordance with the following configuration block.92source example.org {93 # We are example.com, so deliver all messages with recipients94 # at example.com to our local mailboxes.95 destination example.com {96 deliver_to &local_mailboxes97 }9899 # We don't do anything with recipients at different domains100 # because we are not an open relay, thus we reject them.101 default_destination {102 reject 521 5.0.0 "User not local"103 }104}105106# We do our business only with example.org, so reject all107# other senders.108default_source {109 reject110}111```112113## Directives114115116### check _block name_ { ... }117Context: pipeline configuration, source block, destination block118119List of the module references for checks that should be executed on120messages handled by block where 'check' is placed in.121122Note that message body checks placed in destination block are currently123ignored. Due to the way SMTP protocol is defined, they would cause message to124be rejected for all recipients which is not what you usually want when using125such configurations.126127Example:128129```130check {131 # Reference implicitly defined default configuration for check.132 spf133134 # Inline definition of custom config.135 spf {136 # Configuration for spf goes here.137 permerr_action reject138 }139}140```141142It is also possible to define the block of checks at the top level143as "checks" module and reference it using & syntax. Example:144145```146checks inbound_checks {147 spf148 dkim149}150151# ... somewhere else ...152{153 ...154 check &inbound_checks155}156```157158---159160### modify { ... }161Default: not specified<br>162Context: pipeline configuration, source block, destination block163164List of the module references for modifiers that should be executed on165messages handled by block where 'modify' is placed in.166167Message modifiers are similar to checks with the difference in that checks168purpose is to verify whether the message is legitimate and valid per local169policy, while modifier purpose is to post-process message and its metadata170before final delivery.171172For example, modifier can replace recipient address to make message delivered173to the different mailbox or it can cryptographically sign outgoing message174(e.g. using DKIM). Some modifier can perform multiple unrelated modifications175on the message.176177**Note**: Modifiers that affect source address can be used only globally or on178per-source basis, they will be no-op inside destination blocks. Modifiers that179affect the message header will affect it for all recipients.180181It is also possible to define the block of modifiers at the top level182as "modiifers" module and reference it using & syntax. Example:183184```185modifiers local_modifiers {186 replace_rcpt file /etc/maddy/aliases187}188189# ... somewhere else ...190{191 ...192 modify &local_modifiers193}194```195196---197198### reject _smtp-code_ _smtp-enhanced-code_ _error-description_ <br>reject _smtp-code_ _smtp-enhanced-code_ <br>reject _smtp-code_ <br>reject199Context: destination block200201Messages handled by the configuration block with this directive will be202rejected with the specified SMTP error.203204If you aren't sure which codes to use, use 541 and 5.4.0 with your message or205just leave all arguments out, the error description will say "message is206rejected due to policy reasons" which is usually what you want to mean.207208`reject` can't be used in the same block with `deliver_to` or209`destination`/`source` directives.210211Example:212213```214reject 541 5.4.0 "We don't like example.org, go away"215```216217---218219### deliver_to _target-config-block_220Context: pipeline configuration, source block, destination block221222Deliver the message to the referenced delivery target. What happens next is223defined solely by used target. If `deliver_to` is used inside `destination`224block, only matching recipients will be passed to the target.225226---227228### source_in _table-reference_ { ... }229Context: pipeline configuration230231Handle messages with envelope senders present in the specified table in232accordance with the specified configuration block.233234Takes precedence over all `sender` directives.235236Example:237238```239source_in file /etc/maddy/banned_addrs {240 reject 550 5.7.0 "You are not welcome here"241}242source example.org {243 ...244}245...246```247248See `destination_in` documentation for note about table configuration.249250---251252### source _rules..._ { ... }253Context: pipeline configuration254255Handle messages with MAIL FROM value (sender address) matching any of the rules256in accordance with the specified configuration block.257258"Rule" is either a domain or a complete address. In case of overlapping259'rules', first one takes priority. Matching is case-insensitive.260261Example:262263```264# All messages coming from example.org domain will be delivered265# to local_mailboxes.266source example.org {267 deliver_to &local_mailboxes268}269# Messages coming from different domains will be rejected.270default_source {271 reject 521 5.0.0 "You were not invited"272}273```274275---276277### reroute { ... }278Context: pipeline configuration, source block, destination block279280This directive allows to make message routing decisions based on the281result of modifiers. The block can contain all pipeline directives and they282will be handled the same with the exception that source and destination rules283will use the final recipient and sender values (e.g. after all modifiers are284applied).285286Here is the concrete example how it can be useful:287288```289destination example.org {290 modify {291 replace_rcpt file /etc/maddy/aliases292 }293 reroute {294 destination example.org {295 deliver_to &local_mailboxes296 }297 default_destination {298 deliver_to &remote_queue299 }300 }301}302```303304This configuration allows to specify alias local addresses to remote ones305without being an open relay, since remote_queue can be used only if remote306address was introduced as a result of rewrite of local address.307308**Warning**: If you have DMARC enabled (default), results generated by SPF309and DKIM checks inside a reroute block **will not** be considered in DMARC310evaluation.311312---313314### destination_in _table-reference_ { ... }315Context: pipeline configuration, source block316317Handle messages with envelope recipients present in the specified table in318accordance with the specified configuration block.319320Takes precedence over all 'destination' directives.321322Example:323324```325destination_in file /etc/maddy/remote_addrs {326 deliver_to smtp tcp://10.0.0.7:25327}328destination example.com {329 deliver_to &local_mailboxes330}331...332```333334Note that due to the syntax restrictions, it is not possible to specify335extended configuration for table module. E.g. this is not valid:336337```338destination_in sql_table {339 dsn ...340 driver ...341} {342 deliver_to whatever343}344```345346In this case, configuration should be specified separately and be referneced347using '&' syntax:348349```350table.sql_table remote_addrs {351 dsn ...352 driver ...353}354355whatever {356 destination_in &remote_addrs {357 deliver_to whatever358 }359}360```361362---363364### destination _rule..._ { ... }365Context: pipeline configuration, source block366367Handle messages with RCPT TO value (recipient address) matching any of the368rules in accordance with the specified configuration block.369370"Rule" is either a domain or a complete address. Duplicate rules are not371allowed. Matching is case-insensitive.372373Note that messages with multiple recipients are split into multiple messages if374they have recipients matched by multiple blocks. Each block will see the375message only with recipients matched by its rules.376377Example:378379```380# Messages with recipients at example.com domain will be381# delivered to local_mailboxes target.382destination example.com {383 deliver_to &local_mailboxes384}385386# Messages with other recipients will be rejected.387default_destination {388 rejected 541 5.0.0 "User not local"389}390```391392## Reusable pipeline snippets (msgpipeline module)393394The message pipeline can be used independently of the SMTP module in other395contexts that require a delivery target via `msgpipeline` module.396397Example:398399```400msgpipeline local_routing {401 destination whatever.com {402 deliver_to dummy403 }404}405406# ... somewhere else ...407deliver_to &local_routing408```