maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1# SMTP message routing (pipeline)
  2
  3# Message pipeline
  4
  5A message pipeline is a set of module references and associated rules that
  6describe how to handle messages.
  7
  8The pipeline is responsible for
  9
 10- 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 the
 15  message.
 16
 17Message handling flow is as follows:
 18
 19- 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 (as
 22  specified in MAIL FROM). If there are no `source` blocks - the entire
 23  configuration is assumed to be the `default_source` block.
 24- Execute checks referenced in `check` blocks inside the selected `source` block
 25  (if any).
 26- Execute modifiers referenced in `modify` blocks inside selected `source`
 27  block (if any).
 28
 29Then, for each recipient:
 30
 31- Select the `destination` block that matches it. If there are
 32  no `destination` blocks - the entire used `source` block is interpreted as if it
 33  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 with
 39  the specified SMTP status code.
 40- If the used block contains the `deliver_to` directive - pass the message to the
 41  specified target module. Only recipients that are handled
 42  by the used block are visible to the target.
 43
 44Each recipient is handled only by a single `destination` block, in case of
 45overlapping `destination` - the first one takes priority.
 46
 47```
 48destination example.org {
 49    deliver_to targetA
 50}
 51destination example.org { # ambiguous and thus not allowed
 52    deliver_to targetB
 53}
 54```
 55
 56Same goes for `source` blocks, each message is handled only by a single block.
 57
 58Each recipient block should contain at least one `deliver_to` directive or
 59`reject` directive. If `destination` blocks are used, then
 60`default_destination` block should also be used to specify behavior for
 61unmatched recipients.  Same goes for source blocks, `default_source` should be
 62used if `source` is used.
 63
 64That is, pipeline configuration should explicitly specify behavior for each
 65possible sender/recipient combination.
 66
 67Additionally, directives that specify final handling decision (`deliver_to`,
 68`reject`) can't be used at the same level as source/destination rules.
 69Consider example:
 70
 71```
 72destination example.org {
 73    deliver_to local_mboxes
 74}
 75reject
 76```
 77
 78It is not obvious whether `reject` applies to all recipients or
 79just for non-example.org ones, hence this is not allowed.
 80
 81Complete configuration example using all of the mentioned directives:
 82
 83```
 84check {
 85    # Run a check to make sure source SMTP server identification
 86    # is legit.
 87    spf
 88}
 89
 90# Messages coming from senders at example.org will be handled in
 91# accordance with the following configuration block.
 92source example.org {
 93    # We are example.com, so deliver all messages with recipients
 94    # at example.com to our local mailboxes.
 95    destination example.com {
 96        deliver_to &local_mailboxes
 97    }
 98
 99    # We don't do anything with recipients at different domains
100    # 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}
105
106# We do our business only with example.org, so reject all
107# other senders.
108default_source {
109    reject
110}
111```
112
113## Directives
114
115
116### check _block name_ { ... }
117Context: pipeline configuration, source block, destination block
118
119List of the module references for checks that should be executed on
120messages handled by block where 'check' is placed in.
121
122Note that message body checks placed in destination block are currently
123ignored. Due to the way SMTP protocol is defined, they would cause message to
124be rejected for all recipients which is not what you usually want when using
125such configurations.
126
127Example:
128
129```
130check {
131    # Reference implicitly defined default configuration for check.
132    spf
133
134    # Inline definition of custom config.
135    spf {
136         # Configuration for spf goes here.
137         permerr_action reject
138    }
139}
140```
141
142It is also possible to define the block of checks at the top level
143as "checks" module and reference it using & syntax. Example:
144
145```
146checks inbound_checks {
147	spf
148	dkim
149}
150
151# ... somewhere else ...
152{
153	...
154	check &inbound_checks
155}
156```
157
158---
159
160### modify { ... }
161Default: not specified<br>
162Context: pipeline configuration, source block, destination block
163
164List of the module references for modifiers that should be executed on
165messages handled by block where 'modify' is placed in.
166
167Message modifiers are similar to checks with the difference in that checks
168purpose is to verify whether the message is legitimate and valid per local
169policy, while modifier purpose is to post-process message and its metadata
170before final delivery.
171
172For example, modifier can replace recipient address to make message delivered
173to the different mailbox or it can cryptographically sign outgoing message
174(e.g. using DKIM). Some modifier can perform multiple unrelated modifications
175on the message.
176
177**Note**: Modifiers that affect source address can be used only globally or on
178per-source basis, they will be no-op inside destination blocks. Modifiers that
179affect the message header will affect it for all recipients.
180
181It is also possible to define the block of modifiers at the top level
182as "modiifers" module and reference it using & syntax. Example:
183
184```
185modifiers local_modifiers {
186	replace_rcpt file /etc/maddy/aliases
187}
188
189# ... somewhere else ...
190{
191	...
192	modify &local_modifiers
193}
194```
195
196---
197
198### reject _smtp-code_ _smtp-enhanced-code_ _error-description_ <br>reject _smtp-code_ _smtp-enhanced-code_ <br>reject _smtp-code_ <br>reject
199Context: destination block
200
201Messages handled by the configuration block with this directive will be
202rejected with the specified SMTP error.
203
204If you aren't sure which codes to use, use 541 and 5.4.0 with your message or
205just leave all arguments out, the error description will say "message is
206rejected due to policy reasons" which is usually what you want to mean.
207
208`reject` can't be used in the same block with `deliver_to` or
209`destination`/`source` directives.
210
211Example:
212
213```
214reject 541 5.4.0 "We don't like example.org, go away"
215```
216
217---
218
219### deliver_to _target-config-block_
220Context: pipeline configuration, source block, destination block
221
222Deliver the message to the referenced delivery target. What happens next is
223defined solely by used target. If `deliver_to` is used inside `destination`
224block, only matching recipients will be passed to the target.
225
226---
227
228### source_in _table-reference_ { ... }
229Context: pipeline configuration
230
231Handle messages with envelope senders present in the specified table in
232accordance with the specified configuration block.
233
234Takes precedence over all `sender` directives.
235
236Example:
237
238```
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```
247
248See `destination_in` documentation for note about table configuration.
249
250---
251
252### source _rules..._ { ... }
253Context: pipeline configuration
254
255Handle messages with MAIL FROM value (sender address) matching any of the rules
256in accordance with the specified configuration block.
257
258"Rule" is either a domain or a complete address. In case of overlapping
259'rules', first one takes priority. Matching is case-insensitive.
260
261Example:
262
263```
264# All messages coming from example.org domain will be delivered
265# to local_mailboxes.
266source example.org {
267    deliver_to &local_mailboxes
268}
269# Messages coming from different domains will be rejected.
270default_source {
271    reject 521 5.0.0 "You were not invited"
272}
273```
274
275---
276
277### reroute { ... }
278Context: pipeline configuration, source block, destination block
279
280This directive allows to make message routing decisions based on the
281result of modifiers. The block can contain all pipeline directives and they
282will be handled the same with the exception that source and destination rules
283will use the final recipient and sender values (e.g. after all modifiers are
284applied).
285
286Here is the concrete example how it can be useful:
287
288```
289destination example.org {
290    modify {
291        replace_rcpt file /etc/maddy/aliases
292    }
293    reroute {
294        destination example.org {
295            deliver_to &local_mailboxes
296        }
297        default_destination {
298            deliver_to &remote_queue
299        }
300    }
301}
302```
303
304This configuration allows to specify alias local addresses to remote ones
305without being an open relay, since remote_queue can be used only if remote
306address was introduced as a result of rewrite of local address.
307
308**Warning**: If you have DMARC enabled (default), results generated by SPF
309and DKIM checks inside a reroute block **will not** be considered in DMARC
310evaluation.
311
312---
313
314### destination_in _table-reference_ { ... }
315Context: pipeline configuration, source block
316
317Handle messages with envelope recipients present in the specified table in
318accordance with the specified configuration block.
319
320Takes precedence over all 'destination' directives.
321
322Example:
323
324```
325destination_in file /etc/maddy/remote_addrs {
326	deliver_to smtp tcp://10.0.0.7:25
327}
328destination example.com {
329	deliver_to &local_mailboxes
330}
331...
332```
333
334Note that due to the syntax restrictions, it is not possible to specify
335extended configuration for table module. E.g. this is not valid:
336
337```
338destination_in sql_table {
339	dsn ...
340	driver ...
341} {
342	deliver_to whatever
343}
344```
345
346In this case, configuration should be specified separately and be referneced
347using '&' syntax:
348
349```
350table.sql_table remote_addrs {
351	dsn ...
352	driver ...
353}
354
355whatever {
356	destination_in &remote_addrs {
357		deliver_to whatever
358	}
359}
360```
361
362---
363
364### destination _rule..._ { ... }
365Context: pipeline configuration, source block
366
367Handle messages with RCPT TO value (recipient address) matching any of the
368rules in accordance with the specified configuration block.
369
370"Rule" is either a domain or a complete address. Duplicate rules are not
371allowed. Matching is case-insensitive.
372
373Note that messages with multiple recipients are split into multiple messages if
374they have recipients matched by multiple blocks. Each block will see the
375message only with recipients matched by its rules.
376
377Example:
378
379```
380# Messages with recipients at example.com domain will be
381# delivered to local_mailboxes target.
382destination example.com {
383    deliver_to &local_mailboxes
384}
385
386# Messages with other recipients will be rejected.
387default_destination {
388    rejected 541 5.0.0 "User not local"
389}
390```
391
392## Reusable pipeline snippets (msgpipeline module)
393
394The message pipeline can be used independently of the SMTP module in other
395contexts that require a delivery target via `msgpipeline` module.
396
397Example:
398
399```
400msgpipeline local_routing {
401    destination whatever.com {
402        deliver_to dummy
403    }
404}
405
406# ... somewhere else ...
407deliver_to &local_routing
408```