mirror of
https://github.com/foxcpp/maddy.git
synced 2025-04-05 05:57:39 +03:00
docs: Convert manual pages into per-module Markdown pages
This commit is contained in:
parent
2eb955bbc0
commit
a8e0a74be9
53 changed files with 3651 additions and 3469 deletions
86
.mkdocs.yml
86
.mkdocs.yml
|
@ -2,44 +2,80 @@ site_name: maddy
|
||||||
|
|
||||||
repo_url: https://github.com/foxcpp/maddy
|
repo_url: https://github.com/foxcpp/maddy
|
||||||
|
|
||||||
theme: readthedocs
|
theme: alb
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- codehilite:
|
- codehilite:
|
||||||
guess_lang: false
|
guess_lang: false
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
|
- faq.md
|
||||||
- Tutorials:
|
- Tutorials:
|
||||||
- tutorials/setting-up.md
|
- tutorials/setting-up.md
|
||||||
- tutorials/building-from-source.md
|
- tutorials/building-from-source.md
|
||||||
- tutorials/alias-to-remote.md
|
- tutorials/alias-to-remote.md
|
||||||
- tutorials/pam.md
|
- tutorials/pam.md
|
||||||
- Release builds: 'https://maddy.email/builds/'
|
- Release builds: 'https://maddy.email/builds/'
|
||||||
- Integration with software:
|
|
||||||
- third-party/dovecot.md
|
|
||||||
- third-party/smtp-servers.md
|
|
||||||
- third-party/rspamd.md
|
|
||||||
- third-party/mailman3.md
|
|
||||||
- seclevels.md
|
|
||||||
- faq.md
|
|
||||||
- multiple-domains.md
|
- multiple-domains.md
|
||||||
- unicode.md
|
|
||||||
- upgrading.md
|
- upgrading.md
|
||||||
- specifications.md
|
- seclevels.md
|
||||||
- openmetrics.md
|
- Reference manual:
|
||||||
- Manual pages:
|
- reference/modules.md
|
||||||
- man/_generated_maddy.1.md
|
- reference/global-config.md
|
||||||
- man/_generated_maddy.5.md
|
- reference/tls.md
|
||||||
- man/_generated_maddy-auth.5.md
|
- reference/tls-acme.md
|
||||||
- man/_generated_maddy-blob.5.md
|
- Endpoints configuration:
|
||||||
- man/_generated_maddy-config.5.md
|
- reference/endpoints/imap.md
|
||||||
- man/_generated_maddy-filters.5.md
|
- reference/endpoints/smtp.md
|
||||||
- man/_generated_maddy-imap.5.md
|
- reference/endpoints/openmetrics.md
|
||||||
- man/_generated_maddy-smtp.5.md
|
- IMAP storage:
|
||||||
- man/_generated_maddy-storage.5.md
|
- reference/storage/imap-filters.md
|
||||||
- man/_generated_maddy-targets.5.md
|
- reference/storage/imapsql.md
|
||||||
- man/_generated_maddy-tables.5.md
|
- Blob storage:
|
||||||
- man/_generated_maddy-tls.5.md
|
- reference/blob/fs.md
|
||||||
|
- reference/blob/s3.md
|
||||||
|
- reference/smtp-pipeline.md
|
||||||
|
- SMTP targets:
|
||||||
|
- reference/targets/queue.md
|
||||||
|
- reference/targets/remote.md
|
||||||
|
- reference/targets/smtp.md
|
||||||
|
- SMTP checks:
|
||||||
|
- reference/checks/actions.md
|
||||||
|
- reference/checks/dkim.md
|
||||||
|
- reference/checks/spf.md
|
||||||
|
- reference/checks/milter.md
|
||||||
|
- reference/checks/rspamd.md
|
||||||
|
- reference/checks/dnsbl.md
|
||||||
|
- reference/checks/command.md
|
||||||
|
- reference/checks/authorize_sender.md
|
||||||
|
- reference/checks/misc.md
|
||||||
|
- SMTP modifiers:
|
||||||
|
- reference/modifiers/dkim.md
|
||||||
|
- reference/modifiers/envelope.md
|
||||||
|
- Lookup tables (string translation):
|
||||||
|
- reference/table/static.md
|
||||||
|
- reference/table/regexp.md
|
||||||
|
- reference/table/file.md
|
||||||
|
- reference/table/sql_query.md
|
||||||
|
- reference/table/chain.md
|
||||||
|
- reference/table/email_localpart.md
|
||||||
|
- reference/table/auth.md
|
||||||
|
- Authentication providers:
|
||||||
|
- reference/auth/pass_table.md
|
||||||
|
- reference/auth/pam.md
|
||||||
|
- reference/auth/shadow.md
|
||||||
|
- reference/auth/external.md
|
||||||
|
- reference/auth/ldap.md
|
||||||
|
- reference/auth/dovecot_sasl.md
|
||||||
|
- reference/auth/plain_separate.md
|
||||||
|
- reference/config-syntax.md
|
||||||
|
- Integration with software:
|
||||||
|
- third-party/dovecot.md
|
||||||
|
- third-party/smtp-servers.md
|
||||||
|
- third-party/rspamd.md
|
||||||
|
- third-party/mailman3.md
|
||||||
- Internals:
|
- Internals:
|
||||||
|
- internals/specifications.md
|
||||||
|
- internals/unicode.md
|
||||||
- internals/quirks.md
|
- internals/quirks.md
|
||||||
- internals/sqlite.md
|
- internals/sqlite.md
|
|
@ -1,373 +0,0 @@
|
||||||
maddy-auth(5) "maddy mail server" "maddy authentication backends"
|
|
||||||
|
|
||||||
; TITLE Authentication backends
|
|
||||||
|
|
||||||
# Introduction
|
|
||||||
|
|
||||||
Modules described in this man page can be used to provide functionality to
|
|
||||||
check validity of username-password pairs in accordance with some database.
|
|
||||||
That is, they authenticate users.
|
|
||||||
|
|
||||||
Most likely, you are going to use these modules with 'auth' directive of IMAP
|
|
||||||
(*maddy-imap*(5)) or SMTP endpoint (*maddy-smtp*(5)).
|
|
||||||
|
|
||||||
Most modules listed here are also usable as a table (see *maddy-tables*(5))
|
|
||||||
that contains all usernames known to the module. Exceptions are auth.external and
|
|
||||||
pam as underlying interfaces do not define a way to check credentials
|
|
||||||
existence.
|
|
||||||
|
|
||||||
# External authentication module (auth.external)
|
|
||||||
|
|
||||||
Module for authentication using external helper binary. It looks for binary
|
|
||||||
named maddy-auth-helper in $PATH and libexecdir and uses it for authentication
|
|
||||||
using username/password pair.
|
|
||||||
|
|
||||||
The protocol is very simple:
|
|
||||||
Program is launched for each authentication. Username and password are written
|
|
||||||
to stdin, adding \\n to the end. If binary exits with 0 status code -
|
|
||||||
authentication is considered successful. If the status code is 1 -
|
|
||||||
authentication is failed. If the status code is 2 - another unrelated error has
|
|
||||||
happened. Additional information should be written to stderr.
|
|
||||||
|
|
||||||
```
|
|
||||||
auth.external {
|
|
||||||
helper /usr/bin/ldap-helper
|
|
||||||
perdomain no
|
|
||||||
domains example.org
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: helper _file_path_
|
|
||||||
|
|
||||||
Location of the helper binary. *Required.*
|
|
||||||
|
|
||||||
*Syntax*: perdomain _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Don't remove domain part of username when authenticating and require it to be
|
|
||||||
present. Can be used if you want user@domain1 and user@domain2 to be different
|
|
||||||
accounts.
|
|
||||||
|
|
||||||
*Syntax*: domains _domains..._ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
Domains that should be allowed in username during authentication.
|
|
||||||
|
|
||||||
For example, if 'domains' is set to "domain1 domain2", then
|
|
||||||
username, username@domain1 and username@domain2 will be accepted as valid login
|
|
||||||
name in addition to just username.
|
|
||||||
|
|
||||||
If used without 'perdomain', domain part will be removed from login before
|
|
||||||
check with underlying auth. mechanism. If 'perdomain' is set, then
|
|
||||||
domains must be also set and domain part WILL NOT be removed before check.
|
|
||||||
|
|
||||||
# PAM module (auth.pam)
|
|
||||||
|
|
||||||
Implements authentication using libpam. Alternatively it can be configured to
|
|
||||||
use helper binary like auth.external module does.
|
|
||||||
|
|
||||||
maddy should be built with libpam build tag to use this module without
|
|
||||||
'use_helper' directive.
|
|
||||||
```
|
|
||||||
go get -tags 'libpam' ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
auth.pam {
|
|
||||||
debug no
|
|
||||||
use_helper no
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Enable verbose logging for all modules. You don't need that unless you are
|
|
||||||
reporting a bug.
|
|
||||||
|
|
||||||
*Syntax*: use_helper _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Use LibexecDirectory/maddy-pam-helper instead of directly calling libpam.
|
|
||||||
You need to use that if:
|
|
||||||
1. maddy is not compiled with libpam, but maddy-pam-helper is built separately.
|
|
||||||
2. maddy is running as an unprivileged user and used PAM configuration requires additional
|
|
||||||
privileges (e.g. when using system accounts).
|
|
||||||
|
|
||||||
For 2, you need to make maddy-pam-helper binary setuid, see
|
|
||||||
README.md in source tree for details.
|
|
||||||
|
|
||||||
TL;DR (assuming you have the maddy group):
|
|
||||||
```
|
|
||||||
chown root:maddy /usr/lib/maddy/maddy-pam-helper
|
|
||||||
chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-pam-helper
|
|
||||||
```
|
|
||||||
|
|
||||||
# Shadow database authentication module (auth.shadow)
|
|
||||||
|
|
||||||
Implements authentication by reading /etc/shadow. Alternatively it can be
|
|
||||||
configured to use helper binary like auth.external does.
|
|
||||||
|
|
||||||
```
|
|
||||||
auth.shadow {
|
|
||||||
debug no
|
|
||||||
use_helper no
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Enable verbose logging for all modules. You don't need that unless you are
|
|
||||||
reporting a bug.
|
|
||||||
|
|
||||||
*Syntax*: use_helper _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Use LibexecDirectory/maddy-shadow-helper instead of directly reading /etc/shadow.
|
|
||||||
You need to use that if maddy is running as an unprivileged user
|
|
||||||
privileges (e.g. when using system accounts).
|
|
||||||
|
|
||||||
You need to make maddy-shadow-helper binary setuid, see
|
|
||||||
cmd/maddy-shadow-helper/README.md in source tree for details.
|
|
||||||
|
|
||||||
TL;DR (assuming you have maddy group):
|
|
||||||
```
|
|
||||||
chown root:maddy /usr/lib/maddy/maddy-shadow-helper
|
|
||||||
chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-shadow-helper
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table-based password hash lookup (auth.pass_table)
|
|
||||||
|
|
||||||
This module implements username:password authentication by looking up the
|
|
||||||
password hash using a table module (maddy-tables(5)). It can be used
|
|
||||||
to load user credentials from text file (file module) or SQL query
|
|
||||||
(sql_table module).
|
|
||||||
|
|
||||||
|
|
||||||
Definition:
|
|
||||||
```
|
|
||||||
auth.pass_table [block name] {
|
|
||||||
table <table config>
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Shortened variant for inline use:
|
|
||||||
```
|
|
||||||
pass_table <table> [table arguments] {
|
|
||||||
[additional table config]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Example, read username:password pair from the text file:
|
|
||||||
```
|
|
||||||
smtp tcp://0.0.0.0:587 {
|
|
||||||
auth pass_table file /etc/maddy/smtp_passwd
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Password hashes
|
|
||||||
|
|
||||||
pass_table expects the used table to contain certain structured values with
|
|
||||||
hash algorithm name, salt and other necessary parameters.
|
|
||||||
|
|
||||||
You should use 'maddyctl hash' command to generate suitable values.
|
|
||||||
See 'maddyctl hash --help' for details.
|
|
||||||
|
|
||||||
## maddyctl creds
|
|
||||||
|
|
||||||
If the underlying table is a "mutable" table (see maddy-tables(5)) then
|
|
||||||
the 'maddyctl creds' command can be used to modify the underlying tables
|
|
||||||
via pass_table module. It will act a "local credentials store" and will write
|
|
||||||
appropriate hash values to the table.
|
|
||||||
|
|
||||||
# Separate username and password lookup (auth.plain_separate)
|
|
||||||
|
|
||||||
This module implements authentication using username:password pairs but can
|
|
||||||
use zero or more "table modules" (maddy-tables(5)) and one or more
|
|
||||||
authentication providers to verify credentials.
|
|
||||||
|
|
||||||
```
|
|
||||||
auth.plain_separate {
|
|
||||||
user ...
|
|
||||||
user ...
|
|
||||||
...
|
|
||||||
pass ...
|
|
||||||
pass ...
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
How it works:
|
|
||||||
- Initial username input is normalized using PRECIS UsernameCaseMapped profile.
|
|
||||||
- Each table specified with the 'user' directive looked up using normalized
|
|
||||||
username. If match is not found in any table, authentication fails.
|
|
||||||
- Each authentication provider specified with the 'pass' directive is tried.
|
|
||||||
If authentication with all providers fails - an error is returned.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
**Syntax:** user _table module_
|
|
||||||
|
|
||||||
Configuration block for any module from maddy-tables(5) can be used here.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
user file /etc/maddy/allowed_users
|
|
||||||
```
|
|
||||||
|
|
||||||
**Syntax:** pass _auth provider_
|
|
||||||
|
|
||||||
Configuration block for any auth. provider module can be used here, even
|
|
||||||
'plain_split' itself.
|
|
||||||
|
|
||||||
The used auth. provider must provide username:password pair-based
|
|
||||||
authentication.
|
|
||||||
|
|
||||||
# Dovecot authentication client (auth.dovecot_sasl)
|
|
||||||
|
|
||||||
The 'dovecot_sasl' module implements the client side of the Dovecot
|
|
||||||
authentication protocol, allowing maddy to use it as a credentials source.
|
|
||||||
|
|
||||||
Currently SASL mechanisms support is limited to mechanisms supported by maddy
|
|
||||||
so you cannot get e.g. SCRAM-MD5 this way.
|
|
||||||
|
|
||||||
```
|
|
||||||
auth.dovecot_sasl {
|
|
||||||
endpoint unix://socket_path
|
|
||||||
}
|
|
||||||
|
|
||||||
dovecot_sasl unix://socket_path
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: endpoint _schema://address_ ++
|
|
||||||
*Default*: not set
|
|
||||||
|
|
||||||
Set the address to use to contact Dovecot SASL server in the standard endpoint
|
|
||||||
format.
|
|
||||||
|
|
||||||
tcp://10.0.0.1:2222 for TCP, unix:///var/lib/dovecot/auth.sock for Unix
|
|
||||||
domain sockets.
|
|
||||||
|
|
||||||
# LDAP BindDN authentication (EXPERIMENTAL) (auth.ldap)
|
|
||||||
|
|
||||||
maddy supports authentication via LDAP using DN binding. Passwords are verified
|
|
||||||
by the LDAP server.
|
|
||||||
|
|
||||||
maddy needs to know the DN to use for binding. It can be obtained either by
|
|
||||||
directory search or template .
|
|
||||||
|
|
||||||
Note that storage backends conventionally use email addresses, if you use
|
|
||||||
non-email identifiers as usernames then you should map them onto
|
|
||||||
emails on delivery by using auth_map (see *maddy-storage*(5)).
|
|
||||||
|
|
||||||
auth.ldap also can be a used as a table module. This way you can check
|
|
||||||
whether the account exists. It works only if DN template is not used.
|
|
||||||
|
|
||||||
```
|
|
||||||
auth.ldap {
|
|
||||||
urls ldap://maddy.test:389
|
|
||||||
|
|
||||||
# Specify initial bind credentials. Not required ('bind off')
|
|
||||||
# if DN template is used.
|
|
||||||
bind plain "cn=maddy,ou=people,dc=maddy,dc=test" "123456"
|
|
||||||
|
|
||||||
# Specify DN template to skip lookup.
|
|
||||||
dn_template "cn={username},ou=people,dc=maddy,dc=test"
|
|
||||||
|
|
||||||
# Specify base_dn and filter to lookup DN.
|
|
||||||
base_dn "ou=people,dc=maddy,dc=test"
|
|
||||||
filter "(&(objectClass=posixAccount)(uid={username}))"
|
|
||||||
|
|
||||||
tls_client { ... }
|
|
||||||
starttls off
|
|
||||||
debug off
|
|
||||||
connect_timeout 1m
|
|
||||||
}
|
|
||||||
```
|
|
||||||
```
|
|
||||||
auth.ldap ldap://maddy.test.389 {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax:* urls _servers..._
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
URLs of the directory servers to use. First available server
|
|
||||||
is used - no load-balancing is done.
|
|
||||||
|
|
||||||
URLs should use 'ldap://', 'ldaps://', 'ldapi://' schemes.
|
|
||||||
|
|
||||||
*Syntax:* bind off ++
|
|
||||||
bind unauth ++
|
|
||||||
bind external ++
|
|
||||||
bind plain _username_ _password_ ++
|
|
||||||
*Default:* off
|
|
||||||
|
|
||||||
Credentials to use for initial binding. Required if DN lookup is used.
|
|
||||||
|
|
||||||
'unauth' performs unauthenticated bind. 'external' performs external binding
|
|
||||||
which is useful for Unix socket connections (ldapi://) or TLS client certificate
|
|
||||||
authentication (cert. is set using tls_client directive). 'plain' performs a
|
|
||||||
simple bind using provided credentials.
|
|
||||||
|
|
||||||
*Syntax:* dn_template _template_
|
|
||||||
|
|
||||||
DN template to use for binding. '{username}' is replaced with the
|
|
||||||
username specified by the user.
|
|
||||||
|
|
||||||
*Syntax:* base_dn _dn_
|
|
||||||
|
|
||||||
Base DN to use for lookup.
|
|
||||||
|
|
||||||
*Syntax:* filter _str_
|
|
||||||
|
|
||||||
DN lookup filter. '{username}' is replaced with the username specified
|
|
||||||
by the user.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
(&(objectClass=posixAccount)(uid={username}))
|
|
||||||
```
|
|
||||||
|
|
||||||
Example (using ActiveDirectory):
|
|
||||||
```
|
|
||||||
(&(objectCategory=Person)(memberOf=CN=user-group,OU=example,DC=example,DC=org)(sAMAccountName={username})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))
|
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
(&(objectClass=Person)(mail={username}))
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax:* starttls _bool_ ++
|
|
||||||
*Default:* off
|
|
||||||
|
|
||||||
Whether to upgrade connection to TLS using STARTTLS.
|
|
||||||
|
|
||||||
*Syntax:* tls_client { ... }
|
|
||||||
|
|
||||||
Advanced TLS client configuration. See *maddy-tls*(5) for details.
|
|
||||||
|
|
||||||
*Syntax:* connect_timeout _duration_ ++
|
|
||||||
*Default:* 1m
|
|
||||||
|
|
||||||
Timeout for initial connection to the directory server.
|
|
||||||
|
|
||||||
*Syntax:* request_timeout _duration_ ++
|
|
||||||
*Default:* 1m
|
|
||||||
|
|
||||||
Timeout for each request (binding, lookup).
|
|
|
@ -1,113 +0,0 @@
|
||||||
maddy-blob(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE Message blob storage
|
|
||||||
|
|
||||||
Some IMAP storage backends support pluggable message storage that allows
|
|
||||||
message contents to be stored separately from IMAP index.
|
|
||||||
|
|
||||||
Modules described in this page are what can be used with such storage backends.
|
|
||||||
In most cases they have to be specified using the 'msg_store' directive, like
|
|
||||||
this:
|
|
||||||
```
|
|
||||||
storage.imapsql local_mailboxes {
|
|
||||||
msg_store fs /var/lib/email
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Unless explicitly configured, storage backends with pluggable storage will
|
|
||||||
store messages in state_dir/messages (e.g. /var/lib/maddy/messages) FS
|
|
||||||
directory.
|
|
||||||
|
|
||||||
# FS directory storage (storage.blob.fs)
|
|
||||||
|
|
||||||
This module stores message bodies in a file system directory.
|
|
||||||
|
|
||||||
```
|
|
||||||
storage.blob.fs {
|
|
||||||
root <directory>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
```
|
|
||||||
storage.blob.fs <directory>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax:* root _path_ ++
|
|
||||||
*Default:* not set
|
|
||||||
|
|
||||||
Path to the FS directory. Must be readable and writable by the server process.
|
|
||||||
If it does not exist - it will be created (parent directory should be writable
|
|
||||||
for this). Relative paths are interpreted relatively to server state directory.
|
|
||||||
|
|
||||||
# Amazon S3 storage (storage.blob.s3)
|
|
||||||
|
|
||||||
This modules stores messages bodies in a bucket on S3-compatible storage.
|
|
||||||
|
|
||||||
```
|
|
||||||
storage.blob.s3 {
|
|
||||||
endpoint play.min.io
|
|
||||||
secure yes
|
|
||||||
access_key "Q3AM3UQ867SPQQA43P2F"
|
|
||||||
secret_key "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
|
|
||||||
bucket maddy-test
|
|
||||||
|
|
||||||
# optional
|
|
||||||
region eu-central-1
|
|
||||||
object_prefix maddy/
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
storage.imapsql local_mailboxes {
|
|
||||||
...
|
|
||||||
msg_store s3 {
|
|
||||||
endpoint s3.amazonaws.com
|
|
||||||
access_key "..."
|
|
||||||
secret_key "..."
|
|
||||||
bucket maddy-messages
|
|
||||||
region us-west-2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax:* endpoint _address:port_
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
Root S3 endpoint. e.g. s3.amazonaws.com
|
|
||||||
|
|
||||||
*Syntax:* secure _boolean_ ++
|
|
||||||
*Default:* yes
|
|
||||||
|
|
||||||
Whether TLS should be used.
|
|
||||||
|
|
||||||
*Syntax:* access_key _string_ ++
|
|
||||||
*Syntax:* secret_key _string_
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
Static S3 credentials.
|
|
||||||
|
|
||||||
*Syntax:* bucket _name_
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
S3 bucket name. The bucket must exist and
|
|
||||||
be read-writable.
|
|
||||||
|
|
||||||
*Syntax:* region _string_ ++
|
|
||||||
*Default:* not set
|
|
||||||
|
|
||||||
S3 bucket location. May be called "endpoint"
|
|
||||||
in some manuals.
|
|
||||||
|
|
||||||
*Syntax:* object_prefix _string_ ++
|
|
||||||
*Default:* empty string
|
|
||||||
|
|
||||||
String to add to all keys stored by maddy.
|
|
||||||
|
|
||||||
Can be useful when S3 is used as a file system.
|
|
|
@ -1,953 +0,0 @@
|
||||||
maddy-filters(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE Message filtering
|
|
||||||
|
|
||||||
maddy does have two distinct types of modules that do message filtering.
|
|
||||||
"Checks" and "modifiers".
|
|
||||||
|
|
||||||
"Checks" are meant to be used to reject or quarantine
|
|
||||||
messages that are unwanted, such as potential spam or messages with spoofed
|
|
||||||
sender address. They are limited in ways they can modify the message and their
|
|
||||||
execution is heavily parallelized to improve performance.
|
|
||||||
|
|
||||||
"Modifiers" are executed serially in order they are referenced in the
|
|
||||||
configuration and are allowed to modify the message data and meta-data.
|
|
||||||
|
|
||||||
# Check actions
|
|
||||||
|
|
||||||
When a certain check module thinks the message is "bad", it takes some actions
|
|
||||||
depending on its configuration. Most checks follow the same configuration
|
|
||||||
structure and allow following actions to be taken on check failure:
|
|
||||||
|
|
||||||
- Do nothing ('action ignore')
|
|
||||||
|
|
||||||
Useful for testing deployment of new checks. Check failures are still logged
|
|
||||||
but they have no effect on message delivery.
|
|
||||||
|
|
||||||
- Reject the message ('action reject')
|
|
||||||
|
|
||||||
Reject the message at connection time. No bounce is generated locally.
|
|
||||||
|
|
||||||
- Quarantine the message ('action quarantine')
|
|
||||||
|
|
||||||
Mark message as 'quarantined'. If message is then delivered to the local
|
|
||||||
storage, the storage backend can place the message in the 'Junk' mailbox.
|
|
||||||
Another thing to keep in mind that 'remote' module (see *maddy-targets*(5))
|
|
||||||
will refuse to send quarantined messages.
|
|
||||||
|
|
||||||
# Simple checks
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
Following directives are defined for all modules listed below.
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
fail_action ignore ++
|
|
||||||
fail_action reject ++
|
|
||||||
fail_action quarantine ++
|
|
||||||
*Default*: quarantine
|
|
||||||
|
|
||||||
Action to take when check fails. See Check actions for details.
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Log both sucessfull and unsucessfull check executions instead of just
|
|
||||||
unsucessfull.
|
|
||||||
|
|
||||||
## require_mx_record
|
|
||||||
|
|
||||||
Check that domain in MAIL FROM command does have a MX record and none of them
|
|
||||||
are "null" (contain a single dot as the host).
|
|
||||||
|
|
||||||
By default, quarantines messages coming from servers missing MX records,
|
|
||||||
use 'fail_action' directive to change that.
|
|
||||||
|
|
||||||
## require_matching_rdns
|
|
||||||
|
|
||||||
Check that source server IP does have a PTR record point to the domain
|
|
||||||
specified in EHLO/HELO command.
|
|
||||||
|
|
||||||
By default, quarantines messages coming from servers with mismatched or missing
|
|
||||||
PTR record, use 'fail_action' directive to change that.
|
|
||||||
|
|
||||||
## require_tls
|
|
||||||
|
|
||||||
Check that the source server is connected via TLS; either directly, or by using
|
|
||||||
the STARTTLS command.
|
|
||||||
|
|
||||||
By default, rejects messages coming from unencrypted servers. Use the
|
|
||||||
'fail_action' directive to change that.
|
|
||||||
|
|
||||||
# DKIM authentication module (check.dkim)
|
|
||||||
|
|
||||||
This is the check module that performs verification of the DKIM signatures
|
|
||||||
present on the incoming messages.
|
|
||||||
|
|
||||||
```
|
|
||||||
check.dkim {
|
|
||||||
debug no
|
|
||||||
required_fields From Subject
|
|
||||||
allow_body_subset no
|
|
||||||
no_sig_action ignore
|
|
||||||
broken_sig_action ignore
|
|
||||||
fail_open no
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Log both sucessfull and unsucessfull check executions instead of just
|
|
||||||
unsucessfull.
|
|
||||||
|
|
||||||
*Syntax*: required_fields _string..._ ++
|
|
||||||
*Default*: From Subject
|
|
||||||
|
|
||||||
Header fields that should be included in each signature. If signature
|
|
||||||
lacks any field listed in that directive, it will be considered invalid.
|
|
||||||
|
|
||||||
Note that From is always required to be signed, even if it is not included in
|
|
||||||
this directive.
|
|
||||||
|
|
||||||
*Syntax*: no_sig_action _action_ ++
|
|
||||||
*Default*: ignore (recommended by RFC 6376)
|
|
||||||
|
|
||||||
Action to take when message without any signature is received.
|
|
||||||
|
|
||||||
Note that DMARC policy of the sender domain can request more strict handling of
|
|
||||||
missing DKIM signatures.
|
|
||||||
|
|
||||||
*Syntax*: broken_sig_action _action_ ++
|
|
||||||
*Default*: ignore (recommended by RFC 6376)
|
|
||||||
|
|
||||||
Action to take when there are not valid signatures in a message.
|
|
||||||
|
|
||||||
Note that DMARC policy of the sender domain can request more strict handling of
|
|
||||||
broken DKIM signatures.
|
|
||||||
|
|
||||||
*Syntax*: fail_open _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Whether to accept the message if a temporary error occurs during DKIM
|
|
||||||
verification. Rejecting the message with a 4xx code will require the sender
|
|
||||||
to resend it later in a hope that the problem will be resolved.
|
|
||||||
|
|
||||||
# SPF policy enforcement module (check.spf)
|
|
||||||
|
|
||||||
This is the check module that verifies whether IP address of the client is
|
|
||||||
authorized to send messages for domain in MAIL FROM address.
|
|
||||||
|
|
||||||
```
|
|
||||||
check.spf {
|
|
||||||
debug no
|
|
||||||
enforce_early no
|
|
||||||
fail_action quarantine
|
|
||||||
softfail_action ignore
|
|
||||||
permerr_action reject
|
|
||||||
temperr_action reject
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## DMARC override
|
|
||||||
|
|
||||||
It is recommended by the DMARC standard to don't fail delivery based solely on
|
|
||||||
SPF policy and always check DMARC policy and take action based on it.
|
|
||||||
|
|
||||||
If enforce_early is no, check.spf module will not take any action on SPF
|
|
||||||
policy failure if sender domain does have a DMARC record with 'quarantine' or
|
|
||||||
'reject' policy. Instead it will rely on DMARC support to take necesary
|
|
||||||
actions using SPF results as an input.
|
|
||||||
|
|
||||||
Disabling enforce_early without enabling DMARC support will make SPF policies
|
|
||||||
no-op and is considered insecure.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging for check.spf.
|
|
||||||
|
|
||||||
*Syntax*: enforce_early _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Make policy decision on MAIL FROM stage (before the message body is received).
|
|
||||||
This makes it impossible to apply DMARC override (see above).
|
|
||||||
|
|
||||||
*Syntax*: none_action reject|qurantine|ignore ++
|
|
||||||
*Default*: ignore
|
|
||||||
|
|
||||||
Action to take when SPF policy evaluates to a 'none' result.
|
|
||||||
|
|
||||||
See https://tools.ietf.org/html/rfc7208#section-2.6 for meaning of
|
|
||||||
SPF results.
|
|
||||||
|
|
||||||
*Syntax*: neutral_action reject|qurantine|ignore ++
|
|
||||||
*Default*: ignore
|
|
||||||
|
|
||||||
Action to take when SPF policy evaluates to a 'neutral' result.
|
|
||||||
|
|
||||||
See https://tools.ietf.org/html/rfc7208#section-2.6 for meaning of
|
|
||||||
SPF results.
|
|
||||||
|
|
||||||
*Syntax*: fail_action reject|qurantine|ignore ++
|
|
||||||
*Default*: quarantine
|
|
||||||
|
|
||||||
Action to take when SPF policy evaluates to a 'fail' result.
|
|
||||||
|
|
||||||
*Syntax*: softfail_action reject|qurantine|ignore ++
|
|
||||||
*Default*: ignore
|
|
||||||
|
|
||||||
Action to take when SPF policy evaluates to a 'softfail' result.
|
|
||||||
|
|
||||||
*Syntax*: permerr_action reject|qurantine|ignore ++
|
|
||||||
*Default*: reject
|
|
||||||
|
|
||||||
Action to take when SPF policy evaluates to a 'permerror' result.
|
|
||||||
|
|
||||||
*Syntax*: temperr_action reject|qurantine|ignore ++
|
|
||||||
*Default*: reject
|
|
||||||
|
|
||||||
Action to take when SPF policy evaluates to a 'temperror' result.
|
|
||||||
|
|
||||||
# DNSBL lookup module (check.dnsbl)
|
|
||||||
|
|
||||||
The dnsbl module implements checking of source IP and hostnames against a set
|
|
||||||
of DNS-based Blackhole lists (DNSBLs).
|
|
||||||
|
|
||||||
Its configuration consists of module configuration directives and a set
|
|
||||||
of blocks specifing lists to use and kind of lookups to perform on them.
|
|
||||||
|
|
||||||
```
|
|
||||||
check.dnsbl {
|
|
||||||
debug no
|
|
||||||
check_early no
|
|
||||||
|
|
||||||
quarantine_threshold 1
|
|
||||||
reject_threshold 1
|
|
||||||
|
|
||||||
# Lists configuration example.
|
|
||||||
dnsbl.example.org {
|
|
||||||
client_ipv4 yes
|
|
||||||
client_ipv6 no
|
|
||||||
ehlo no
|
|
||||||
mailfrom no
|
|
||||||
score 1
|
|
||||||
}
|
|
||||||
hsrbl.example.org {
|
|
||||||
client_ipv4 no
|
|
||||||
client_ipv6 no
|
|
||||||
ehlo yes
|
|
||||||
mailfrom yes
|
|
||||||
score 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
Arguments specify the list of IP-based BLs to use.
|
|
||||||
|
|
||||||
The following configurations are equivalent.
|
|
||||||
|
|
||||||
```
|
|
||||||
check {
|
|
||||||
dnsbl dnsbl.example.org dnsbl2.example.org
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
check {
|
|
||||||
dnsbl {
|
|
||||||
dnsbl.example.org dnsbl2.example.org {
|
|
||||||
client_ipv4 yes
|
|
||||||
client_ipv6 no
|
|
||||||
ehlo no
|
|
||||||
mailfrom no
|
|
||||||
score 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
*Syntax*: check_early _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Check BLs before mail delivery starts and silently reject blacklisted clients.
|
|
||||||
|
|
||||||
For this to work correctly, check should not be used in source/destination
|
|
||||||
pipeline block.
|
|
||||||
|
|
||||||
In particular, this means:
|
|
||||||
- No logging is done for rejected messages.
|
|
||||||
- No action is taken if quarantine_threshold is hit, only reject_threshold
|
|
||||||
applies.
|
|
||||||
- defer_sender_reject from SMTP configuration takes no effect.
|
|
||||||
- MAIL FROM is not checked, even if specified.
|
|
||||||
|
|
||||||
If you often get hit by spam attacks, this is recommended to enable this
|
|
||||||
setting to save server resources.
|
|
||||||
|
|
||||||
*Syntax*: quarantine_threshold _integer_ ++
|
|
||||||
*Default*: 1
|
|
||||||
|
|
||||||
DNSBL score needed (equals-or-higher) to quarantine the message.
|
|
||||||
|
|
||||||
*Syntax*: reject_threshold _integer_ ++
|
|
||||||
*Default*: 9999
|
|
||||||
|
|
||||||
DNSBL score needed (equals-or-higher) to reject the message.
|
|
||||||
|
|
||||||
## List configuration
|
|
||||||
|
|
||||||
```
|
|
||||||
dnsbl.example.org dnsbl.example.com {
|
|
||||||
client_ipv4 yes
|
|
||||||
client_ipv6 no
|
|
||||||
ehlo no
|
|
||||||
mailfrom no
|
|
||||||
responses 127.0.0.1/24
|
|
||||||
score 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Directive name and arguments specify the actual DNS zone to query when checking
|
|
||||||
the list. Using multiple arguments is equivalent to specifying the same
|
|
||||||
configuration separately for each list.
|
|
||||||
|
|
||||||
*Syntax*: client_ipv4 _boolean_ ++
|
|
||||||
*Default*: yes
|
|
||||||
|
|
||||||
Whether to check address of the IPv4 clients against the list.
|
|
||||||
|
|
||||||
*Syntax*: client_ipv6 _boolean_ ++
|
|
||||||
*Default*: yes
|
|
||||||
|
|
||||||
Whether to check address of the IPv6 clients against the list.
|
|
||||||
|
|
||||||
*Syntax*: ehlo _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Whether to check hostname specified n the HELO/EHLO command
|
|
||||||
against the list.
|
|
||||||
|
|
||||||
This works correctly only with domain-based DNSBLs.
|
|
||||||
|
|
||||||
*Syntax*: mailfrom _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Whether to check domain part of the MAIL FROM address against the list.
|
|
||||||
|
|
||||||
This works correctly only with domain-based DNSBLs.
|
|
||||||
|
|
||||||
*Syntax*: responses _cidr|ip..._ ++
|
|
||||||
*Default*: 127.0.0.1/24
|
|
||||||
|
|
||||||
IP networks (in CIDR notation) or addresses to permit in list lookup results.
|
|
||||||
Addresses not matching any entry in this directives will be ignored.
|
|
||||||
|
|
||||||
*Syntax*: score _integer_ ++
|
|
||||||
*Default*: 1
|
|
||||||
|
|
||||||
Score value to add for the message if it is listed.
|
|
||||||
|
|
||||||
If sum of list scores is equals or higher than quarantine_threshold, the
|
|
||||||
message will be quarantined.
|
|
||||||
|
|
||||||
If sum of list scores is equals or higher than rejected_threshold, the message
|
|
||||||
will be rejected.
|
|
||||||
|
|
||||||
It is possible to specify a negative value to make list act like a whitelist
|
|
||||||
and override results of other blocklists.
|
|
||||||
|
|
||||||
# DKIM signing module (modify.dkim)
|
|
||||||
|
|
||||||
modify.dkim module is a modifier that signs messages using DKIM
|
|
||||||
protocol (RFC 6376).
|
|
||||||
|
|
||||||
```
|
|
||||||
modify.dkim {
|
|
||||||
debug no
|
|
||||||
domains example.org example.com
|
|
||||||
selector default
|
|
||||||
key_path dkim-keys/{domain}-{selector}.key
|
|
||||||
oversign_fields ...
|
|
||||||
sign_fields ...
|
|
||||||
header_canon relaxed
|
|
||||||
body_canon relaxed
|
|
||||||
sig_expiry 120h # 5 days
|
|
||||||
hash sha256
|
|
||||||
newkey_algo rsa2048
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
domains and selector can be specified in arguments, so actual modify.dkim use can
|
|
||||||
be shortened to the following:
|
|
||||||
```
|
|
||||||
modify {
|
|
||||||
dkim example.org selector
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
*Syntax*: domains _string list_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
*REQUIRED.*
|
|
||||||
|
|
||||||
ADministrative Management Domains (ADMDs) taking responsibility for messages.
|
|
||||||
|
|
||||||
A key will be generated or read for each domain specified here, the key to use
|
|
||||||
for each message will be selected based on the SMTP envelope sender. Exception
|
|
||||||
for that is that for domain-less postmaster address and null address, the
|
|
||||||
key for the first domain will be used. If domain in envelope sender
|
|
||||||
does not match any of loaded keys, message will not be signed.
|
|
||||||
|
|
||||||
Should be specified either as a directive or as an argument.
|
|
||||||
|
|
||||||
*Syntax*: selector _string_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
*REQUIRED.*
|
|
||||||
|
|
||||||
Identifier of used key within the ADMD.
|
|
||||||
Should be specified either as a directive or as an argument.
|
|
||||||
|
|
||||||
*Syntax*: key_path _string_ ++
|
|
||||||
*Default*: dkim_keys/{domain}\_{selector}.key
|
|
||||||
|
|
||||||
Path to private key. It should be in PKCS#8 format wrapped in PAM encoding.
|
|
||||||
If key does not exist, it will be generated using algorithm specified
|
|
||||||
in newkey_algo.
|
|
||||||
|
|
||||||
Placeholders '{domain}' and '{selector}' will be replaced with corresponding
|
|
||||||
values from domain and selector directives.
|
|
||||||
|
|
||||||
Additionally, keys in PKCS#1 ("RSA PRIVATE KEY") and
|
|
||||||
RFC 5915 ("EC PRIVATE KEY") can be read by modify.dkim. Note, however that
|
|
||||||
newly generated keys are always in PKCS#8.
|
|
||||||
|
|
||||||
*Syntax*: oversign_fields _list..._ ++
|
|
||||||
*Default*: see below
|
|
||||||
|
|
||||||
Header fields that should be signed n+1 times where n is times they are
|
|
||||||
present in the message. This makes it impossible to replace field
|
|
||||||
value by prepending another field with the same name to the message.
|
|
||||||
|
|
||||||
Fields specified here don't have to be also specified in sign_fields.
|
|
||||||
|
|
||||||
Default set of oversigned fields:
|
|
||||||
- Subject
|
|
||||||
- To
|
|
||||||
- From
|
|
||||||
- Date
|
|
||||||
- MIME-Version
|
|
||||||
- Content-Type
|
|
||||||
- Content-Transfer-Encoding
|
|
||||||
- Reply-To
|
|
||||||
- Message-Id
|
|
||||||
- References
|
|
||||||
- Autocrypt
|
|
||||||
- Openpgp
|
|
||||||
|
|
||||||
*Syntax*: sign_fields _list..._ ++
|
|
||||||
*Default*: see below
|
|
||||||
|
|
||||||
Header fields that should be signed n+1 times where n is times they are
|
|
||||||
present in the message. For these fields, additional values can be prepended
|
|
||||||
by intermediate relays, but existing values can't be changed.
|
|
||||||
|
|
||||||
Default set of signed fields:
|
|
||||||
- List-Id
|
|
||||||
- List-Help
|
|
||||||
- List-Unsubscribe
|
|
||||||
- List-Post
|
|
||||||
- List-Owner
|
|
||||||
- List-Archive
|
|
||||||
- Resent-To
|
|
||||||
- Resent-Sender
|
|
||||||
- Resent-Message-Id
|
|
||||||
- Resent-Date
|
|
||||||
- Resent-From
|
|
||||||
- Resent-Cc
|
|
||||||
|
|
||||||
*Syntax*: header_canon relaxed|simple ++
|
|
||||||
*Default*: relaxed
|
|
||||||
|
|
||||||
Canonicalization algorithm to use for header fields. With 'relaxed', whitespace within
|
|
||||||
fields can be modified without breaking the signature, with 'simple' no
|
|
||||||
modifications are allowed.
|
|
||||||
|
|
||||||
*Syntax*: body_canon relaxed|simple ++
|
|
||||||
*Default*: relaxed
|
|
||||||
|
|
||||||
Canonicalization algorithm to use for message body. With 'relaxed', whitespace within
|
|
||||||
can be modified without breaking the signature, with 'simple' no
|
|
||||||
modifications are allowed.
|
|
||||||
|
|
||||||
*Syntax*: sig_expiry _duration_ ++
|
|
||||||
*Default*: 120h
|
|
||||||
|
|
||||||
Time for which signature should be considered valid. Mainly used to prevent
|
|
||||||
unauthorized resending of old messages.
|
|
||||||
|
|
||||||
*Syntax*: hash _hash_ ++
|
|
||||||
*Default*: sha256
|
|
||||||
|
|
||||||
Hash algorithm to use when computing body hash.
|
|
||||||
|
|
||||||
sha256 is the only supported algorithm now.
|
|
||||||
|
|
||||||
*Syntax*: newkey_algo rsa4096|rsa2048|ed25519 ++
|
|
||||||
*Default*: rsa2048
|
|
||||||
|
|
||||||
Algorithm to use when generating a new key.
|
|
||||||
|
|
||||||
*Syntax*: require_sender_match _ids..._ ++
|
|
||||||
*Default*: envelope auth
|
|
||||||
|
|
||||||
Require specified identifiers to match From header field and key domain,
|
|
||||||
otherwise - don't sign the message.
|
|
||||||
|
|
||||||
If From field contains multiple addresses, message will not be
|
|
||||||
signed unless allow_multiple_from is also specified. In that
|
|
||||||
case only first address will be compared.
|
|
||||||
|
|
||||||
Matching is done in a case-insensitive way.
|
|
||||||
|
|
||||||
Valid values:
|
|
||||||
- off +
|
|
||||||
Disable check, always sign.
|
|
||||||
- envelope +
|
|
||||||
Require MAIL FROM address to match From header.
|
|
||||||
- auth +
|
|
||||||
If authorization identity contains @ - then require it to
|
|
||||||
fully match From header. Otherwise, check only local-part
|
|
||||||
(username).
|
|
||||||
|
|
||||||
*Syntax*: allow_multiple_from _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Allow multiple addresses in From header field for purposes of
|
|
||||||
require_sender_match checks. Only first address will be checked, however.
|
|
||||||
|
|
||||||
*Syntax*: sign_subdomains _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Sign emails from subdomains using a top domain key.
|
|
||||||
|
|
||||||
Allows only one domain to be specified (can be workarounded using modify.dkim
|
|
||||||
multiple times).
|
|
||||||
|
|
||||||
# Envelope sender / recipient rewriting (modify.replace_sender, modify.replace_rcpt)
|
|
||||||
|
|
||||||
'replace_sender' and 'replace_rcpt' modules replace SMTP envelope addresses
|
|
||||||
based on the mapping defined by the table module (maddy-tables(5)). Currently,
|
|
||||||
only 1:1 mappings are supported (that is, it is not possible to specify
|
|
||||||
multiple replacements for a single address).
|
|
||||||
|
|
||||||
The address is normalized before lookup (Punycode in domain-part is decoded,
|
|
||||||
Unicode is normalized to NFC, the whole string is case-folded).
|
|
||||||
|
|
||||||
First, the whole address is looked up. If there is no replacement, local-part
|
|
||||||
of the address is looked up separately and is replaced in the address while
|
|
||||||
keeping the domain part intact. Replacements are not applied recursively, that
|
|
||||||
is, lookup is not repeated for the replacement.
|
|
||||||
|
|
||||||
Recipients are not deduplicated after expansion, so message may be delivered
|
|
||||||
multiple times to a single recipient. However, used delivery target can apply
|
|
||||||
such deduplication (imapsql storage does it).
|
|
||||||
|
|
||||||
Definition:
|
|
||||||
```
|
|
||||||
replace_rcpt <table> [table arguments] {
|
|
||||||
[extended table config]
|
|
||||||
}
|
|
||||||
replace_sender <table> [table arguments] {
|
|
||||||
[extended table config]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use examples:
|
|
||||||
```
|
|
||||||
modify {
|
|
||||||
replace_rcpt file /etc/maddy/aliases
|
|
||||||
replace_rcpt static {
|
|
||||||
entry a@example.org b@example.org
|
|
||||||
}
|
|
||||||
replace_rcpt regexp "(.+)@example.net" "$1@example.org"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Possible contents of /etc/maddy/aliases in the example above:
|
|
||||||
```
|
|
||||||
# Replace 'cat' with any domain to 'dog'.
|
|
||||||
# E.g. cat@example.net -> dog@example.net
|
|
||||||
cat: dog
|
|
||||||
|
|
||||||
# Replace cat@example.org with cat@example.com.
|
|
||||||
# Takes priority over the previous line.
|
|
||||||
cat@example.org: cat@example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
# System command filter (check.command)
|
|
||||||
|
|
||||||
This module executes an arbitrary system command during a specified stage of
|
|
||||||
checks execution.
|
|
||||||
|
|
||||||
```
|
|
||||||
command executable_name arg0 arg1 ... {
|
|
||||||
run_on body
|
|
||||||
|
|
||||||
code 1 reject
|
|
||||||
code 2 quarantine
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
The module arguments specify the command to run. If the first argument is not
|
|
||||||
an absolute path, it is looked up in the Libexec Directory (/usr/lib/maddy on
|
|
||||||
Linux) and in $PATH (in that ordering). Note that no additional handling
|
|
||||||
of arguments is done, especially, the command is executed directly, not via the
|
|
||||||
system shell.
|
|
||||||
|
|
||||||
There is a set of special strings that are replaced with the corresponding
|
|
||||||
message-specific values:
|
|
||||||
|
|
||||||
- {source_ip}
|
|
||||||
|
|
||||||
IPv4/IPv6 address of the sending MTA.
|
|
||||||
|
|
||||||
- {source_host}
|
|
||||||
|
|
||||||
Hostname of the sending MTA, from the HELO/EHLO command.
|
|
||||||
|
|
||||||
- {source_rdns}
|
|
||||||
|
|
||||||
PTR record of the sending MTA IP address.
|
|
||||||
|
|
||||||
- {msg_id}
|
|
||||||
|
|
||||||
Internal message identifier. Unique for each delivery.
|
|
||||||
|
|
||||||
- {auth_user}
|
|
||||||
|
|
||||||
Client username, if authenticated using SASL PLAIN
|
|
||||||
|
|
||||||
- {sender}
|
|
||||||
|
|
||||||
Message sender address, as specified in the MAIL FROM SMTP command.
|
|
||||||
|
|
||||||
- {rcpts}
|
|
||||||
|
|
||||||
List of accepted recipient addresses, including the currently handled
|
|
||||||
one.
|
|
||||||
|
|
||||||
- {address}
|
|
||||||
|
|
||||||
Currently handled address. This is a recipient address if the command
|
|
||||||
is called during RCPT TO command handling ('run_on rcpt') or a sender
|
|
||||||
address if the command is called during MAIL FROM command handling ('run_on
|
|
||||||
sender').
|
|
||||||
|
|
||||||
|
|
||||||
If value is undefined (e.g. {source_ip} for a message accepted over a Unix
|
|
||||||
socket) or unavailable (the command is executed too early), the placeholder
|
|
||||||
is replaced with an empty string. Note that it can not remove the argument.
|
|
||||||
E.g. -i {source_ip} will not become just -i, it will be -i ""
|
|
||||||
|
|
||||||
Undefined placeholders are not replaced.
|
|
||||||
|
|
||||||
## Command stdout
|
|
||||||
|
|
||||||
The command stdout must be either empty or contain a valid RFC 5322 header.
|
|
||||||
If it contains a byte stream that does not look a valid header, the message
|
|
||||||
will be rejected with a temporary error.
|
|
||||||
|
|
||||||
The header from stdout will be *prepended* to the message header.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: run_on conn|sender|rcpt|body ++
|
|
||||||
*Default*: body
|
|
||||||
|
|
||||||
When to run the command. This directive also affects the information visible
|
|
||||||
for the message.
|
|
||||||
|
|
||||||
- conn
|
|
||||||
|
|
||||||
Run before the sender address (MAIL FROM) is handled.
|
|
||||||
|
|
||||||
*Stdin*: Empty ++
|
|
||||||
*Available placeholders*: {source_ip}, {source_host}, {msg_id}, {auth_user}.
|
|
||||||
|
|
||||||
- sender
|
|
||||||
|
|
||||||
Run during sender address (MAIL FROM) handling.
|
|
||||||
|
|
||||||
*Stdin*: Empty ++
|
|
||||||
*Available placeholders*: conn placeholders + {sender}, {address}.
|
|
||||||
|
|
||||||
The {address} placeholder contains the MAIL FROM address.
|
|
||||||
|
|
||||||
- rcpt
|
|
||||||
|
|
||||||
Run during recipient address (RCPT TO) handling. The command is executed
|
|
||||||
once for each RCPT TO command, even if the same recipient is specified
|
|
||||||
multiple times.
|
|
||||||
|
|
||||||
*Stdin*: Empty ++
|
|
||||||
*Available placeholders*: sender placeholders + {rcpts}.
|
|
||||||
|
|
||||||
The {address} placeholder contains the recipient address.
|
|
||||||
|
|
||||||
- body
|
|
||||||
|
|
||||||
Run during message body handling.
|
|
||||||
|
|
||||||
*Stdin*: The message header + body ++
|
|
||||||
*Available placeholders*: all except for {address}.
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
code _integer_ ignore ++
|
|
||||||
code _integer_ quarantine ++
|
|
||||||
code _integer_ reject [SMTP code] [SMTP enhanced code] [SMTP message]
|
|
||||||
|
|
||||||
This directives specified the mapping from the command exit code _integer_ to
|
|
||||||
the message pipeline action.
|
|
||||||
|
|
||||||
Two codes are defined implicitly, exit code 1 causes the message to be rejected
|
|
||||||
with a permanent error, exit code 2 causes the message to be quarantined. Both
|
|
||||||
action can be overriden using the 'code' directive.
|
|
||||||
|
|
||||||
## Milter protocol check (check.milter)
|
|
||||||
|
|
||||||
The 'milter' implements subset of Sendmail's milter protocol that can be used
|
|
||||||
to integrate external software in maddy.
|
|
||||||
|
|
||||||
Notable limitations of protocol implementation in maddy include:
|
|
||||||
1. Changes of envelope sender address are not supported
|
|
||||||
2. Removal and addition of envelope recipients is not supported
|
|
||||||
3. Removal and replacement of header fields is not supported
|
|
||||||
4. Headers fields can be inserted only on top
|
|
||||||
5. Milter does not receive some "macros" provided by sendmail.
|
|
||||||
|
|
||||||
Restrictions 1 and 2 are inherent to the maddy checks interface and cannot be
|
|
||||||
removed without major changes to it. Restrictions 3, 4 and 5 are temporary due to
|
|
||||||
incomplete implementation.
|
|
||||||
|
|
||||||
```
|
|
||||||
check.milter {
|
|
||||||
endpoint <endpoint>
|
|
||||||
fail_open false
|
|
||||||
}
|
|
||||||
|
|
||||||
milter <endpoint>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
When defined inline, the first argument specifies endpoint to access milter
|
|
||||||
via. See below.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
**Syntax:** endpoint _scheme://path_ ++
|
|
||||||
**Default:** not set
|
|
||||||
|
|
||||||
Specifies milter protocol endpoint to use.
|
|
||||||
The endpoit is specified in standard URL-like format:
|
|
||||||
'tcp://127.0.0.1:6669' or 'unix:///var/lib/milter/filter.sock'
|
|
||||||
|
|
||||||
**Syntax:** fail_open _boolean_ ++
|
|
||||||
**Default:** false
|
|
||||||
|
|
||||||
Toggles behavior on milter I/O errors. If false ("fail closed") - message is
|
|
||||||
rejected with temporary error code. If true ("fail open") - check is skipped.
|
|
||||||
|
|
||||||
## rspamd check (check.rspamd)
|
|
||||||
|
|
||||||
The 'rspamd' module implements message filtering by contacting the rspamd
|
|
||||||
server via HTTP API.
|
|
||||||
|
|
||||||
```
|
|
||||||
check.rspamd {
|
|
||||||
tls_client { ... }
|
|
||||||
api_path http://127.0.0.1:11333
|
|
||||||
settings_id whatever
|
|
||||||
tag maddy
|
|
||||||
hostname mx.example.org
|
|
||||||
io_error_action ignore
|
|
||||||
error_resp_action ignore
|
|
||||||
add_header_action quarantine
|
|
||||||
rewrite_subj_action quarantine
|
|
||||||
flags pass_all
|
|
||||||
}
|
|
||||||
|
|
||||||
rspamd http://127.0.0.1:11333
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax:* tls_client { ... } ++
|
|
||||||
*Default:* not set
|
|
||||||
|
|
||||||
Configure TLS client if HTTPS is used, see *maddy-tls*(5) for details.
|
|
||||||
|
|
||||||
*Syntax:* api_path _url_ ++
|
|
||||||
*Default:* http://127.0.0.1:11333
|
|
||||||
|
|
||||||
URL of HTTP API endpoint. Supports both HTTP and HTTPS and can include
|
|
||||||
path element.
|
|
||||||
|
|
||||||
*Syntax:* settings_id _string_ ++
|
|
||||||
*Default:* not set
|
|
||||||
|
|
||||||
Settings ID to pass to the server.
|
|
||||||
|
|
||||||
*Syntax:* tag _string_ ++
|
|
||||||
*Default:* maddy
|
|
||||||
|
|
||||||
Value to send in MTA-Tag header field.
|
|
||||||
|
|
||||||
*Syntax:* hostname _string_ ++
|
|
||||||
*Default:* value of global directive
|
|
||||||
|
|
||||||
Value to send in MTA-Name header field.
|
|
||||||
|
|
||||||
*Syntax:* io_error_action _action_ ++
|
|
||||||
*Default:* ignore
|
|
||||||
|
|
||||||
Action to take in case of inability to contact the rspamd server.
|
|
||||||
|
|
||||||
*Syntax:* error_resp_action _action_ ++
|
|
||||||
*Default:* ignore
|
|
||||||
|
|
||||||
Action to take in case of 5xx or 4xx response received from the rspamd server.
|
|
||||||
|
|
||||||
*Syntax:* add_header_action _action_ ++
|
|
||||||
*Default:* quarantine
|
|
||||||
|
|
||||||
Action to take when rspamd requests to "add header".
|
|
||||||
|
|
||||||
X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
|
|
||||||
|
|
||||||
*Syntax:* rewrite_subj_action _action_ ++
|
|
||||||
*Default:* quarantine
|
|
||||||
|
|
||||||
Action to take when rspamd requests to "rewrite subject".
|
|
||||||
|
|
||||||
X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
|
|
||||||
|
|
||||||
*Syntax:* flags _string list..._ ++
|
|
||||||
*Default:* pass_all
|
|
||||||
|
|
||||||
Flags to pass to the rspamd server.
|
|
||||||
See https://rspamd.com/doc/architecture/protocol.html for details.
|
|
||||||
|
|
||||||
## MAIL FROM and From authorization (check.authorize_sender)
|
|
||||||
|
|
||||||
This check verifies that envelope and header sender addresses belong
|
|
||||||
to the authenticated user. Address ownership is established via table
|
|
||||||
that maps each user account to a email address it is allowed to use.
|
|
||||||
There are some special cases, see user_to_email description below.
|
|
||||||
|
|
||||||
```
|
|
||||||
check.authorize_sender {
|
|
||||||
prepare_email identity
|
|
||||||
user_to_email identity
|
|
||||||
check_header yes
|
|
||||||
|
|
||||||
unauth_action reject
|
|
||||||
no_match_action reject
|
|
||||||
malformed_action reject
|
|
||||||
err_action reject
|
|
||||||
|
|
||||||
auth_normalize precis_casefold_email
|
|
||||||
from_normalize precis_casefold_email
|
|
||||||
}
|
|
||||||
```
|
|
||||||
```
|
|
||||||
check {
|
|
||||||
authorize_sender { ... }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax:* user_to_email _table_ ++
|
|
||||||
*Default:* identity
|
|
||||||
|
|
||||||
Table to use for lookups. Result of the lookup should contain either the
|
|
||||||
domain name, the full email address or "*" string. If it is just domain - user
|
|
||||||
will be allowed to use any mailbox within a domain as a sender address.
|
|
||||||
If result contains "*" - user will be allowed to use any address.
|
|
||||||
|
|
||||||
*Syntax:* check_header _boolean_ ++
|
|
||||||
*Default:* yes
|
|
||||||
|
|
||||||
Whether to verify header sender in addition to envelope.
|
|
||||||
|
|
||||||
Either Sender or From field value should match the
|
|
||||||
authorization identity.
|
|
||||||
|
|
||||||
*Syntax:* unauth_action _action_ ++
|
|
||||||
*Default:* reject
|
|
||||||
|
|
||||||
What to do if the user is not authenticated at all.
|
|
||||||
|
|
||||||
*Syntax:* no_match_action _action_ ++
|
|
||||||
*Default:* reject
|
|
||||||
|
|
||||||
What to do if user is not allowed to use the sender address specified.
|
|
||||||
|
|
||||||
*Syntax:* malformed_action _action_ ++
|
|
||||||
*Default:* reject
|
|
||||||
|
|
||||||
What to do if From or Sender header fields contain malformed values.
|
|
||||||
|
|
||||||
*Syntax:* err_action _action_ ++
|
|
||||||
*Default:* reject
|
|
||||||
|
|
||||||
What to do if error happens during prepare_email or user_to_email lookup.
|
|
||||||
|
|
||||||
*Syntax:* auth_normalize _action_ ++
|
|
||||||
*Default:* precis_casefold_email
|
|
||||||
|
|
||||||
Normalization function to apply to authorization username before
|
|
||||||
further processing.
|
|
||||||
|
|
||||||
Available options:
|
|
||||||
- precis_casefold_email PRECIS UsernameCaseMapped profile + U-labels form for domain
|
|
||||||
- precis_casefold PRECIS UsernameCaseMapped profile for the entire string
|
|
||||||
- precis_email PRECIS UsernameCasePreserved profile + U-labels form for domain
|
|
||||||
- precis PRECIS UsernameCasePreserved profile for the entire string
|
|
||||||
- casefold Convert to lower case
|
|
||||||
- noop Nothing
|
|
||||||
|
|
||||||
*Syntax:* from_normalize _action_ ++
|
|
||||||
*Default:* precis_casefold_email
|
|
||||||
|
|
||||||
Normalization function to apply to email addresses before
|
|
||||||
further processing.
|
|
||||||
|
|
||||||
Available options are same as for auth_normalize.
|
|
|
@ -1,642 +0,0 @@
|
||||||
maddy-smtp(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE SMTP endpoint module
|
|
||||||
|
|
||||||
# SMTP endpoint module (smtp)
|
|
||||||
|
|
||||||
Module 'smtp' is a listener that implements ESMTP protocol with optional
|
|
||||||
authentication, LMTP and Submission support. Incoming messages are processed in
|
|
||||||
accordance with pipeline rules (explained in Message pipeline section below).
|
|
||||||
|
|
||||||
```
|
|
||||||
smtp tcp://0.0.0.0:25 {
|
|
||||||
hostname example.org
|
|
||||||
tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
|
|
||||||
io_debug no
|
|
||||||
debug no
|
|
||||||
insecure_auth no
|
|
||||||
read_timeout 10m
|
|
||||||
write_timeout 1m
|
|
||||||
max_message_size 32M
|
|
||||||
max_header_size 1M
|
|
||||||
auth pam
|
|
||||||
defer_sender_reject yes
|
|
||||||
dmarc yes
|
|
||||||
smtp_max_line_length 4000
|
|
||||||
limits {
|
|
||||||
endpoint rate 10
|
|
||||||
endpoint concurrency 500
|
|
||||||
}
|
|
||||||
|
|
||||||
# Example pipeline ocnfiguration.
|
|
||||||
destination example.org {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
reject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: hostname _string_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Server name to use in SMTP banner.
|
|
||||||
|
|
||||||
```
|
|
||||||
220 example.org ESMTP Service Ready
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: tls _certificate_path_ _key_path_ { ... } ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
TLS certificate & key to use. Fine-tuning of other TLS properties is possible
|
|
||||||
by specifing a configuration block and options inside it:
|
|
||||||
```
|
|
||||||
tls cert.crt key.key {
|
|
||||||
protocols tls1.2 tls1.3
|
|
||||||
}
|
|
||||||
```
|
|
||||||
See section 'TLS configuration' in *maddy*(1) for valid options.
|
|
||||||
|
|
||||||
*Syntax*: io_debug _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Write all commands and responses to stderr.
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
*Syntax*: insecure_auth _boolean_ ++
|
|
||||||
*Default*: no (yes if TLS is disabled)
|
|
||||||
|
|
||||||
Allow plain-text authentication over unencrypted connections. Not recommended!
|
|
||||||
|
|
||||||
*Syntax*: read_timeout _duration_ ++
|
|
||||||
*Default*: 10m
|
|
||||||
|
|
||||||
I/O read timeout.
|
|
||||||
|
|
||||||
*Syntax*: write_timeout _duration_ ++
|
|
||||||
*Default*: 1m
|
|
||||||
|
|
||||||
I/O write timeout.
|
|
||||||
|
|
||||||
*Syntax*: max_message_size _size_ ++
|
|
||||||
*Default*: 32M
|
|
||||||
|
|
||||||
Limit the size of incoming messages to 'size'.
|
|
||||||
|
|
||||||
*Syntax*: max_header_size _size_ ++
|
|
||||||
*Default*: 1M
|
|
||||||
|
|
||||||
Limit the size of incoming message headers to 'size'.
|
|
||||||
|
|
||||||
*Syntax*: auth _module_reference_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
Use the specified module for authentication.
|
|
||||||
|
|
||||||
*Syntax*: defer_sender_reject _boolean_ ++
|
|
||||||
*Default*: yes
|
|
||||||
|
|
||||||
Apply sender-based checks and routing logic when first RCPT TO command
|
|
||||||
is received. This allows maddy to log recipient address of the rejected
|
|
||||||
message and also improves interoperability with (improperly implemented)
|
|
||||||
clients that don't expect an error early in session.
|
|
||||||
|
|
||||||
*Syntax*: max_logged_rcpt_errors _integer_ ++
|
|
||||||
*Default*: 5
|
|
||||||
|
|
||||||
Amount of RCPT-time errors that should be logged. Further errors will be
|
|
||||||
handled silently. This is to prevent log flooding during email dictonary
|
|
||||||
attacks (address probing).
|
|
||||||
|
|
||||||
*Syntax*: max_received _integer_ ++
|
|
||||||
*Default*: 50
|
|
||||||
|
|
||||||
Max. amount of Received header fields in the message header. If the incoming
|
|
||||||
message has more fields than this number, it will be rejected with the permanent error
|
|
||||||
5.4.6 ("Routing loop detected").
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
buffer ram ++
|
|
||||||
buffer fs _[path]_ ++
|
|
||||||
buffer auto _max_size_ _[path]_ ++
|
|
||||||
*Default*: auto 1M StateDirectory/buffer
|
|
||||||
|
|
||||||
Temporary storage to use for the body of accepted messages.
|
|
||||||
|
|
||||||
- ram
|
|
||||||
|
|
||||||
Store the body in RAM.
|
|
||||||
|
|
||||||
- fs
|
|
||||||
|
|
||||||
Write out the message to the FS and read it back as needed.
|
|
||||||
_path_ can be omitted and defaults to StateDirectory/buffer.
|
|
||||||
|
|
||||||
- auto
|
|
||||||
|
|
||||||
Store message bodies smaller than _max_size_ entirely in RAM, otherwise write
|
|
||||||
them out to the FS.
|
|
||||||
_path_ can be omitted and defaults to StateDirectory/buffer.
|
|
||||||
|
|
||||||
*Syntax*: smtp_max_line_length _integer_ ++
|
|
||||||
*Default*: 4000
|
|
||||||
|
|
||||||
The maximum line length allowed in the SMTP input stream. If client sends a
|
|
||||||
longer line - connection will be closed and message (if any) will be rejected
|
|
||||||
with a permanent error.
|
|
||||||
|
|
||||||
RFC 5321 has the recommended limit of 998 bytes. Servers are not required
|
|
||||||
to handle longer lines correctly but some senders may produce them.
|
|
||||||
|
|
||||||
Unless BDAT extension is used by the sender, this limitation also applies to
|
|
||||||
the message body.
|
|
||||||
|
|
||||||
*Syntax*: dmarc _boolean_ ++
|
|
||||||
*Default*: yes
|
|
||||||
|
|
||||||
Enforce sender's DMARC policy. Due to implementation limitations, it is not a
|
|
||||||
check module.
|
|
||||||
|
|
||||||
*NOTE*: Report generation is not implemented now.
|
|
||||||
|
|
||||||
*NOTE*: DMARC needs SPF and DKIM checks to function correctly.
|
|
||||||
Without these, DMARC check will not run.
|
|
||||||
|
|
||||||
## Rate & concurrency limiting
|
|
||||||
|
|
||||||
*Syntax*: limits _config block_ ++
|
|
||||||
*Default*: no limits
|
|
||||||
|
|
||||||
This allows configuring a set of message flow restrictions including
|
|
||||||
max. concurrency and rate per-endpoint, per-source, per-destination.
|
|
||||||
|
|
||||||
Limits are specified as directives inside the block:
|
|
||||||
```
|
|
||||||
limits {
|
|
||||||
all rate 20
|
|
||||||
destination concurrency 5
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Supported limits:
|
|
||||||
|
|
||||||
- Rate limit
|
|
||||||
|
|
||||||
*Syntax*: _scope_ rate _burst_ _[period]_ ++
|
|
||||||
Restrict the amount of messages processed in _period_ to _burst_ messages.
|
|
||||||
If period is not specified, 1 second is used.
|
|
||||||
|
|
||||||
- Concurrency limit
|
|
||||||
|
|
||||||
*Syntax*: _scope_ concurrency _max_ ++
|
|
||||||
Restrict the amount of messages processed in parallel to _max_.
|
|
||||||
|
|
||||||
For each supported limitation, _scope_ determines whether it should be applied
|
|
||||||
for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or
|
|
||||||
per-recipient domain ("destination"). Having a scope other than "all" means
|
|
||||||
that the restriction will be enforced independently for each group determined
|
|
||||||
by scope. E.g. "ip rate 20" means that the same IP cannot send more than 20
|
|
||||||
messages in a scond. "destination concurrency 5" means that no more than 5
|
|
||||||
messages can be sent in parallel to a single domain.
|
|
||||||
|
|
||||||
*Note*: At the moment, SMTP endpoint on its own does not support per-recipient
|
|
||||||
limits. They will be no-op. If you want to enforce a per-recipient restriction
|
|
||||||
on outbound messages, do so using 'limits' directive for the 'remote' module
|
|
||||||
(see *maddy-targets*(5)).
|
|
||||||
|
|
||||||
It is possible to share limit counters between multiple endpoints (or any other
|
|
||||||
modules). To do so define a top-level configuration block for module "limits"
|
|
||||||
and reference it where needed using standard & syntax. E.g.
|
|
||||||
```
|
|
||||||
limits inbound_limits {
|
|
||||||
all rate 20
|
|
||||||
}
|
|
||||||
|
|
||||||
smtp smtp://0.0.0.0:25 {
|
|
||||||
limits &inbound_limits
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
submission tls://0.0.0.0:465 {
|
|
||||||
limits &inbound_limits
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Using an "all rate" restriction in such way means that no more than 20
|
|
||||||
messages can enter the server through both endpoints in one second.
|
|
||||||
|
|
||||||
# Submission module (submission)
|
|
||||||
|
|
||||||
Module 'submission' implements all functionality of the 'smtp' module and adds
|
|
||||||
certain message preprocessing on top of it, additionaly authentication is
|
|
||||||
always required.
|
|
||||||
|
|
||||||
'submission' module checks whether addresses in header fields From, Sender, To,
|
|
||||||
Cc, Bcc, Reply-To are correct and adds Message-ID and Date if it is missing.
|
|
||||||
|
|
||||||
```
|
|
||||||
submission tcp://0.0.0.0:587 tls://0.0.0.0:465 {
|
|
||||||
# ... same as smtp ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# LMTP module (lmtp)
|
|
||||||
|
|
||||||
Module 'lmtp' implements all functionality of the 'smtp' module but uses
|
|
||||||
LMTP (RFC 2033) protocol.
|
|
||||||
|
|
||||||
```
|
|
||||||
lmtp unix://lmtp.sock {
|
|
||||||
# ... same as smtp ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations of LMTP implementation
|
|
||||||
|
|
||||||
- Can't be used with TCP.
|
|
||||||
|
|
||||||
- Per-recipient status is not supported.
|
|
||||||
|
|
||||||
- Delivery to 'sql' module storage is always atomic, either all recipients will
|
|
||||||
succeed or none of them will.
|
|
||||||
|
|
||||||
# Mesage pipeline
|
|
||||||
|
|
||||||
Message pipeline is a set of module references and associated rules that
|
|
||||||
describe how to handle messages.
|
|
||||||
|
|
||||||
The pipeline is responsible for
|
|
||||||
- Running message filters (called "checks"), (e.g. DKIM signature verification,
|
|
||||||
DNSBL lookup and so on).
|
|
||||||
|
|
||||||
- Running message modifiers (e.g. DKIM signature creation).
|
|
||||||
|
|
||||||
- Assocating each message recipient with one or more delivery targets.
|
|
||||||
Delivery target is a module that does final processing (delivery) of the
|
|
||||||
message.
|
|
||||||
|
|
||||||
Message handling flow is as follows:
|
|
||||||
- Execute checks referenced in top-level 'check' blocks (if any)
|
|
||||||
|
|
||||||
- Execute modifiers referenced in top-level 'modify' blocks (if any)
|
|
||||||
|
|
||||||
- If there are 'source' blocks - select one that matches message sender (as
|
|
||||||
specified in MAIL FROM). If there are no 'source' blocks - entire
|
|
||||||
configuration is assumed to be the 'default_source' block.
|
|
||||||
|
|
||||||
- Execute checks referenced in 'check' blocks inside selected 'source' block
|
|
||||||
(if any).
|
|
||||||
|
|
||||||
- Execute modifiers referenced in 'modify' blocks inside selected 'source'
|
|
||||||
block (if any).
|
|
||||||
|
|
||||||
Then, for each recipient:
|
|
||||||
- Select 'destination' block that matches it. If there are
|
|
||||||
no 'destination' blocks - entire used 'source' block is interpreted as if it
|
|
||||||
was a 'default_destination' block.
|
|
||||||
|
|
||||||
- Execute checks referenced in 'check' block inside selected 'destination' block
|
|
||||||
(if any).
|
|
||||||
|
|
||||||
- Execute modifiers referenced in 'modify' block inside selected 'destination'
|
|
||||||
block (if any).
|
|
||||||
|
|
||||||
- If used block contains 'reject' directive - reject the recipient with
|
|
||||||
specified SMTP status code.
|
|
||||||
|
|
||||||
- If used block contains 'deliver_to' directive - pass the message to the
|
|
||||||
specified target module. Only recipients that are handled
|
|
||||||
by used block are visible to the target.
|
|
||||||
|
|
||||||
Each recipient is handled only by a single 'destination' block, in case of
|
|
||||||
overlapping 'destination' - first one takes priority.
|
|
||||||
```
|
|
||||||
destination example.org {
|
|
||||||
deliver_to targetA
|
|
||||||
}
|
|
||||||
destination example.org { # ambiguous and thus not allowed
|
|
||||||
deliver_to targetB
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Same goes for 'source' blocks, each message is handled only by a single block.
|
|
||||||
|
|
||||||
Each recipient block should contain at least one 'deliver_to' directive or
|
|
||||||
'reject' directive. If 'destination' blocks are used, then
|
|
||||||
'default_destination' block should also be used to specify behavior for
|
|
||||||
unmatched recipients. Same goes for source blocks, 'default_source' should be
|
|
||||||
used if 'source' is used.
|
|
||||||
|
|
||||||
That is, pipeline configuration should explicitly specify behavior for each
|
|
||||||
possible sender/recipient combination.
|
|
||||||
|
|
||||||
Additionally, directives that specify final handling decision ('deliver_to',
|
|
||||||
'reject') can't be used at the same level as source/destination rules.
|
|
||||||
Consider example:
|
|
||||||
```
|
|
||||||
destination example.org {
|
|
||||||
deliver_to local_mboxes
|
|
||||||
}
|
|
||||||
reject
|
|
||||||
```
|
|
||||||
It is not obvious whether 'reject' applies to all recipients or
|
|
||||||
just for non-example.org ones, hence this is not allowed.
|
|
||||||
|
|
||||||
Complete configuration example using all of the mentioned directives:
|
|
||||||
```
|
|
||||||
check {
|
|
||||||
# Run a check to make sure source SMTP server identification
|
|
||||||
# is legit.
|
|
||||||
require_matching_ehlo
|
|
||||||
}
|
|
||||||
|
|
||||||
# Messages coming from senders at example.org will be handled in
|
|
||||||
# accordance with the following configuration block.
|
|
||||||
source example.org {
|
|
||||||
# We are example.com, so deliver all messages with recipients
|
|
||||||
# at example.com to our local mailboxes.
|
|
||||||
destination example.com {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
|
|
||||||
# We don't do anything with recipients at different domains
|
|
||||||
# because we are not an open relay, thus we reject them.
|
|
||||||
default_destination {
|
|
||||||
reject 521 5.0.0 "User not local"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# We do our business only with example.org, so reject all
|
|
||||||
# other senders.
|
|
||||||
default_source {
|
|
||||||
reject
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directives
|
|
||||||
|
|
||||||
*Syntax*: check _block name_ { ... } ++
|
|
||||||
*Context*: pipeline configuration, source block, destination block
|
|
||||||
|
|
||||||
List of the module references for checks that should be executed on
|
|
||||||
messages handled by block where 'check' is placed in.
|
|
||||||
|
|
||||||
Note that message body checks placed in destination block are currently
|
|
||||||
ignored. Due to the way SMTP protocol is defined, they would cause message to
|
|
||||||
be rejected for all recipients which is not what you usually want when using
|
|
||||||
such configurations.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
check {
|
|
||||||
# Reference implicitly defined default configuration for check.
|
|
||||||
require_matching_ehlo
|
|
||||||
|
|
||||||
# Inline definition of custom config.
|
|
||||||
require_source_mx {
|
|
||||||
# Configuration for require_source_mx goes here.
|
|
||||||
fail_action reject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
It is also possible to define the block of checks at the top level
|
|
||||||
as "checks" module and reference it using & syntax. Example:
|
|
||||||
```
|
|
||||||
checks inbound_checks {
|
|
||||||
require_matching_ehlo
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... somewhere else ...
|
|
||||||
{
|
|
||||||
...
|
|
||||||
check &inbound_checks
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: modify { ... } ++
|
|
||||||
*Default*: not specified ++
|
|
||||||
*Context*: pipeline configuration, source block, destination block
|
|
||||||
|
|
||||||
List of the module references for modifiers that should be executed on
|
|
||||||
messages handled by block where 'modify' is placed in.
|
|
||||||
|
|
||||||
Message modifiers are similar to checks with the difference in that checks
|
|
||||||
purpose is to verify whether the message is legitimate and valid per local
|
|
||||||
policy, while modifier purpose is to post-process message and its metadata
|
|
||||||
before final delivery.
|
|
||||||
|
|
||||||
For example, modifier can replace recipient address to make message delivered
|
|
||||||
to the different mailbox or it can cryptographically sign outgoing message
|
|
||||||
(e.g. using DKIM). Some modifier can perform multiple unrelated modifications
|
|
||||||
on the message.
|
|
||||||
|
|
||||||
*Note*: Modifiers that affect source address can be used only globally or on
|
|
||||||
per-source basis, they will be no-op inside destination blocks. Modifiers that
|
|
||||||
affect the message header will affect it for all recipients.
|
|
||||||
|
|
||||||
It is also possible to define the block of modifiers at the top level
|
|
||||||
as "modiifers" module and reference it using & syntax. Example:
|
|
||||||
```
|
|
||||||
modifiers local_modifiers {
|
|
||||||
replace_rcpt file /etc/maddy/aliases
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... somewhere else ...
|
|
||||||
{
|
|
||||||
...
|
|
||||||
modify &local_modifiers
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
reject _smtp_code_ _smtp_enhanced_code_ _error_description_ ++
|
|
||||||
reject _smtp_code_ _smtp_enhanced_code_ ++
|
|
||||||
reject _smtp_code_ ++
|
|
||||||
reject ++
|
|
||||||
*Context*: destination block
|
|
||||||
|
|
||||||
Messages handled by the configuration block with this directive will be
|
|
||||||
rejected with the specified SMTP error.
|
|
||||||
|
|
||||||
If you aren't sure which codes to use, use 541 and 5.4.0 with your message or
|
|
||||||
just leave all arguments out, the error description will say "message is
|
|
||||||
rejected due to policy reasons" which is usually what you want to mean.
|
|
||||||
|
|
||||||
'reject' can't be used in the same block with 'deliver_to' or
|
|
||||||
'destination/source' directives.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
reject 541 5.4.0 "We don't like example.org, go away"
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: deliver_to _target-config-block_ ++
|
|
||||||
*Context*: pipeline configuration, source block, destination block
|
|
||||||
|
|
||||||
Deliver the message to the referenced delivery target. What happens next is
|
|
||||||
defined solely by used target. If deliver_to is used inside 'destination'
|
|
||||||
block, only matching recipients will be passed to the target.
|
|
||||||
|
|
||||||
*Syntax*: source_in _table reference_ { ... } ++
|
|
||||||
*Context*: pipeline configuration
|
|
||||||
|
|
||||||
Handle messages with envelope senders present in the specified table in
|
|
||||||
accordance with the specified configuration block.
|
|
||||||
|
|
||||||
Takes precedence over all 'sender' directives.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
source_in file /etc/maddy/banned_addrs {
|
|
||||||
reject 550 5.7.0 "You are not welcome here"
|
|
||||||
}
|
|
||||||
source example.org {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
See 'destination_in' documentation for note about table configuration.
|
|
||||||
|
|
||||||
*Syntax*: source _rules..._ { ... } ++
|
|
||||||
*Context*: pipeline configuration
|
|
||||||
|
|
||||||
Handle messages with MAIL FROM value (sender address) matching any of the rules
|
|
||||||
in accordance with the specified configuration block.
|
|
||||||
|
|
||||||
"Rule" is either a domain or a complete address. In case of overlapping
|
|
||||||
'rules', first one takes priority. Matching is case-insensitive.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
# All messages coming from example.org domain will be delivered
|
|
||||||
# to local_mailboxes.
|
|
||||||
source example.org {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
# Messages coming from different domains will be rejected.
|
|
||||||
default_source {
|
|
||||||
reject 521 5.0.0 "You were not invited"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: reroute { ... } ++
|
|
||||||
*Context*: pipeline configuration, source block, destination block
|
|
||||||
|
|
||||||
This directive allows to make message routing decisions based on the
|
|
||||||
result of modifiers. The block can contain all pipeline directives and they
|
|
||||||
will be handled the same with the exception that source and destination rules
|
|
||||||
will use the final recipient and sender values (e.g. after all modifiers are
|
|
||||||
applied).
|
|
||||||
|
|
||||||
Here is the concrete example how it can be useful:
|
|
||||||
```
|
|
||||||
destination example.org {
|
|
||||||
modify {
|
|
||||||
replace_rcpt file /etc/maddy/aliases
|
|
||||||
}
|
|
||||||
reroute {
|
|
||||||
destination example.org {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
deliver_to &remote_queue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This configuration allows to specify alias local addresses to remote ones
|
|
||||||
without being an open relay, since remote_queue can be used only if remote
|
|
||||||
address was introduced as a result of rewrite of local address.
|
|
||||||
|
|
||||||
*WARNING*: If you have DMARC enabled (default), results generated by SPF
|
|
||||||
and DKIM checks inside a reroute block *will not* be considered in DMARC
|
|
||||||
evaluation.
|
|
||||||
|
|
||||||
*Syntax*: destination_in _table reference_ { ... } ++
|
|
||||||
*Context*: pipeline configuration, source block
|
|
||||||
|
|
||||||
Handle messages with envelope recipients present in the specified table in
|
|
||||||
accordance with the specified configuration block.
|
|
||||||
|
|
||||||
Takes precedence over all 'destination' directives.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
destination_in file /etc/maddy/remote_addrs {
|
|
||||||
deliver_to smtp tcp://10.0.0.7:25
|
|
||||||
}
|
|
||||||
destination example.com {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that due to the syntax restrictions, it is not possible to specify
|
|
||||||
extended configuration for table module. E.g. this is not valid:
|
|
||||||
```
|
|
||||||
destination_in sql_table {
|
|
||||||
dsn ...
|
|
||||||
driver ...
|
|
||||||
} {
|
|
||||||
deliver_to whatever
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case, configuration should be specified separately and be referneced
|
|
||||||
using '&' syntax:
|
|
||||||
```
|
|
||||||
table.sql_table remote_addrs {
|
|
||||||
dsn ...
|
|
||||||
driver ...
|
|
||||||
}
|
|
||||||
|
|
||||||
whatever {
|
|
||||||
destination_in &remote_addrs {
|
|
||||||
deliver_to whatever
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: destination _rule..._ { ... } ++
|
|
||||||
*Context*: pipeline configuration, source block
|
|
||||||
|
|
||||||
Handle messages with RCPT TO value (recipient address) matching any of the
|
|
||||||
rules in accordance with the specified configuration block.
|
|
||||||
|
|
||||||
"Rule" is either a domain or a complete address. Duplicate rules are not
|
|
||||||
allowed. Matching is case-insensitive.
|
|
||||||
|
|
||||||
Note that messages with multiple recipients are split into multiple messages if
|
|
||||||
they have recipients matched by multiple blocks. Each block will see the
|
|
||||||
message only with recipients matched by its rules.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
# Messages with recipients at example.com domain will be
|
|
||||||
# delivered to local_mailboxes target.
|
|
||||||
destination example.com {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
|
|
||||||
# Messages with other recipients will be rejected.
|
|
||||||
default_destination {
|
|
||||||
rejected 541 5.0.0 "User not local"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reusable pipeline parts (msgpipeline module)
|
|
||||||
|
|
||||||
The message pipeline can be used independently of the SMTP module in other
|
|
||||||
contexts that require a delivery target.
|
|
||||||
|
|
||||||
Full pipeline functionality can be used where a delivery target is expected.
|
|
|
@ -1,202 +0,0 @@
|
||||||
maddy-targets(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE Storage backends
|
|
||||||
|
|
||||||
maddy storage interface is built with IMAP in mind and directly represents
|
|
||||||
IMAP data model. That is, maddy storage does have the concept of folders,
|
|
||||||
flags, message UIDs, etc defined as in RFC 3501.
|
|
||||||
|
|
||||||
This man page lists supported storage backends along with supported
|
|
||||||
configuration directives for each.
|
|
||||||
|
|
||||||
Most likely, you are going to use modules listed here in 'storage' directive
|
|
||||||
for IMAP endpoint module (see *maddy-imap*(5)).
|
|
||||||
|
|
||||||
In most cases, local storage modules will auto-create accounts when they are
|
|
||||||
accessed via IMAP. This relies on authentication provider used by IMAP endpoint
|
|
||||||
to provide what essentially is access control. There is a caveat, however: this
|
|
||||||
auto-creation will not happen when delivering incoming messages via SMTP as
|
|
||||||
there is no authentication to confirm that this account should indeed be
|
|
||||||
created.
|
|
||||||
|
|
||||||
# SQL-based database module (storage.imapsql)
|
|
||||||
|
|
||||||
The imapsql module implements database for IMAP index and message
|
|
||||||
metadata using SQL-based relational database.
|
|
||||||
|
|
||||||
Message contents are stored in an "external store" defined by msg_store
|
|
||||||
directive. By default this is a file system directory under /var/lib/maddy.
|
|
||||||
|
|
||||||
Supported RDBMS:
|
|
||||||
- SQLite 3.25.0
|
|
||||||
- PostgreSQL 9.6 or newer
|
|
||||||
|
|
||||||
Account names are required to have the form of a email address and are
|
|
||||||
case-insensitive. UTF-8 names are supported with restrictions defined in the
|
|
||||||
PRECIS UsernameCaseMapped profile.
|
|
||||||
|
|
||||||
```
|
|
||||||
storage.imapsql {
|
|
||||||
driver sqlite3
|
|
||||||
dsn imapsql.db
|
|
||||||
msg_store fs messages/
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
imapsql module also can be used as a lookup table (*maddy-table*(5)).
|
|
||||||
It returns empty string values for existing usernames. This might be useful
|
|
||||||
with destination_in directive (*maddy-smtp*(5)) e.g. to implement catch-all
|
|
||||||
addresses (this is a bad idea to do so, this is just an example):
|
|
||||||
```
|
|
||||||
destination_in &local_mailboxes {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
destination example.org {
|
|
||||||
modify {
|
|
||||||
replace_rcpt regexp ".*" "catchall@example.org"
|
|
||||||
}
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
Specify the driver and DSN.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: driver _string_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
Use a specified driver to communicate with the database. Supported values:
|
|
||||||
sqlite3, postgres.
|
|
||||||
|
|
||||||
Should be specified either via an argument or via this directive.
|
|
||||||
|
|
||||||
*Syntax*: dsn _string_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
Data Source Name, the driver-specific value that specifies the database to use.
|
|
||||||
|
|
||||||
For SQLite3 this is just a file path.
|
|
||||||
For PostgreSQL: https://godoc.org/github.com/lib/pq#hdr-Connection_String_Parameters
|
|
||||||
|
|
||||||
Should be specified either via an argument or via this directive.
|
|
||||||
|
|
||||||
*Syntax*: msg_store _store_ ++
|
|
||||||
*Default*: fs messages/
|
|
||||||
|
|
||||||
Module to use for message bodies storage.
|
|
||||||
|
|
||||||
See *maddy-blob*(5) for details.
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
compression off ++
|
|
||||||
compression _algorithm_ ++
|
|
||||||
compression _algorithm_ _level_ ++
|
|
||||||
*Default*: off
|
|
||||||
|
|
||||||
Apply compression to message contents.
|
|
||||||
Supported algorithms: lz4, zstd.
|
|
||||||
|
|
||||||
*Syntax*: appendlimit _size_ ++
|
|
||||||
*Default*: 32M
|
|
||||||
|
|
||||||
Don't allow users to add new messages larger than 'size'.
|
|
||||||
|
|
||||||
This does not affect messages added when using module as a delivery target.
|
|
||||||
Use 'max_message_size' directive in SMTP endpoint module to restrict it too.
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
*Syntax*: junk_mailbox _name_ ++
|
|
||||||
*Default*: Junk
|
|
||||||
|
|
||||||
The folder to put quarantined messages in. Thishis setting is not used if user
|
|
||||||
does have a folder with "Junk" special-use attribute.
|
|
||||||
|
|
||||||
*Syntax*: disable_recent _boolean_ ++
|
|
||||||
*Default: true
|
|
||||||
|
|
||||||
Disable RFC 3501-conforming handling of \Recent flag.
|
|
||||||
|
|
||||||
This significantly improves storage performance when SQLite3 or CockroackDB is
|
|
||||||
used at the cost of confusing clients that use this flag.
|
|
||||||
|
|
||||||
*Syntax*: sqlite_cache_size _integer_ ++
|
|
||||||
*Default*: defined by SQLite
|
|
||||||
|
|
||||||
SQLite page cache size. If positive - specifies amount of pages (1 page - 4
|
|
||||||
KiB) to keep in cache. If negative - specifies approximate upper bound
|
|
||||||
of cache size in KiB.
|
|
||||||
|
|
||||||
*Syntax*: sqlite_busy_timeout _integer_ ++
|
|
||||||
*Default*: 5000000
|
|
||||||
|
|
||||||
SQLite-specific performance tuning option. Amount of milliseconds to wait
|
|
||||||
before giving up on DB lock.
|
|
||||||
|
|
||||||
*Syntax*: imap_filter { ... } ++
|
|
||||||
*Default*: not set
|
|
||||||
|
|
||||||
Specifies IMAP filters to apply for messages delivered from SMTP pipeline.
|
|
||||||
|
|
||||||
See *maddy-imap*(5) for filter modules usable here.
|
|
||||||
|
|
||||||
Ex.
|
|
||||||
```
|
|
||||||
imap_filter {
|
|
||||||
command /etc/maddy/sieve.sh {account_name}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax:* delivery_map *table* ++
|
|
||||||
*Default:* identity
|
|
||||||
|
|
||||||
Use specified table module (*maddy-tables*(5)) to map recipient
|
|
||||||
addresses from incoming messages to mailbox names.
|
|
||||||
|
|
||||||
Normalization algorithm specified in delivery_normalize is appied before
|
|
||||||
delivery_map.
|
|
||||||
|
|
||||||
*Syntax:* delivery_normalize _name_ ++
|
|
||||||
*Default:* precis_casefold_email
|
|
||||||
|
|
||||||
Normalization function to apply to email addresses before mapping them
|
|
||||||
to mailboxes.
|
|
||||||
|
|
||||||
See auth_normalize.
|
|
||||||
|
|
||||||
*Syntax*: auth_map *table* ++
|
|
||||||
*Default*: identity
|
|
||||||
|
|
||||||
Use specified table module (*maddy-tables*(5)) to map authentication
|
|
||||||
usernames to mailbox names.
|
|
||||||
|
|
||||||
Normalization algorithm specified in auth_normalize is applied before
|
|
||||||
auth_map.
|
|
||||||
|
|
||||||
*Syntax*: auth_normalize _name_ ++
|
|
||||||
*Default*: precis_casefold_email
|
|
||||||
|
|
||||||
Normalization function to apply to authentication usernames before mapping
|
|
||||||
them to mailboxes.
|
|
||||||
|
|
||||||
Available options:
|
|
||||||
- precis_casefold_email PRECIS UsernameCaseMapped profile + U-labels form for domain
|
|
||||||
- precis_casefold PRECIS UsernameCaseMapped profile for the entire string
|
|
||||||
- precis_email PRECIS UsernameCasePreserved profile + U-labels form for domain
|
|
||||||
- precis PRECIS UsernameCasePreserved profile for the entire string
|
|
||||||
- casefold Convert to lower case
|
|
||||||
- noop Nothing
|
|
||||||
|
|
||||||
Note: On message delivery, recipient address is unconditionally normalized
|
|
||||||
using precis_casefold_email function.
|
|
|
@ -1,314 +0,0 @@
|
||||||
maddy-tables(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE String-string translation
|
|
||||||
|
|
||||||
Whenever you need to replace one string with another when handling anything in
|
|
||||||
maddy, you can use any of the following modules to obtain the replacement
|
|
||||||
string. They are commonly called "table modules" or just "tables".
|
|
||||||
|
|
||||||
Some table modules implement write options allowing other maddy modules to
|
|
||||||
change the source of data, effectively turning the table into a complete
|
|
||||||
interface to a key-value store for maddy. Such tables are referred to as
|
|
||||||
"mutable tables".
|
|
||||||
|
|
||||||
# File mapping (table.file)
|
|
||||||
|
|
||||||
This module builds string-string mapping from a text file.
|
|
||||||
|
|
||||||
File is reloaded every 15 seconds if there are any changes (detected using
|
|
||||||
modification time). No changes are applied if file contains syntax errors.
|
|
||||||
|
|
||||||
Definition:
|
|
||||||
```
|
|
||||||
file <file path>
|
|
||||||
```
|
|
||||||
or
|
|
||||||
```
|
|
||||||
file {
|
|
||||||
file <file path>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
```
|
|
||||||
# Resolve SMTP address aliases using text file mapping.
|
|
||||||
modify {
|
|
||||||
replace_rcpt file /etc/maddy/aliases
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Syntax
|
|
||||||
|
|
||||||
Better demonstrated by examples:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Lines starting with # are ignored.
|
|
||||||
|
|
||||||
# And so are lines only with whitespace.
|
|
||||||
|
|
||||||
# Whenever 'aaa' is looked up, return 'bbb'
|
|
||||||
aaa: bbb
|
|
||||||
|
|
||||||
# Trailing and leading whitespace is ignored.
|
|
||||||
ccc: ddd
|
|
||||||
|
|
||||||
# If there is no colon, the string is translated into ""
|
|
||||||
# That is, the following line is equivalent to
|
|
||||||
# aaa:
|
|
||||||
aaa
|
|
||||||
|
|
||||||
# If the same key is used multiple times - table.file will return
|
|
||||||
# multiple values when queries. Note that this is not used by
|
|
||||||
# most modules. E.g. replace_rcpt does not (intentionally) support
|
|
||||||
# 1-to-N alias expansion.
|
|
||||||
ddd: firstvalue
|
|
||||||
ddd: secondvalue
|
|
||||||
```
|
|
||||||
|
|
||||||
# SQL query mapping (table.sql_query)
|
|
||||||
|
|
||||||
The sql_query module implements table interface using SQL queries.
|
|
||||||
|
|
||||||
Definition:
|
|
||||||
```
|
|
||||||
table.sql_query {
|
|
||||||
driver <driver name>
|
|
||||||
dsn <data source name>
|
|
||||||
lookup <lookup query>
|
|
||||||
|
|
||||||
# Optional:
|
|
||||||
init <init query list>
|
|
||||||
list <list query>
|
|
||||||
add <add query>
|
|
||||||
del <del query>
|
|
||||||
set <set query>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
```
|
|
||||||
# Resolve SMTP address aliases using PostgreSQL DB.
|
|
||||||
modify {
|
|
||||||
replace_rcpt sql_query {
|
|
||||||
driver postgres
|
|
||||||
dsn "dbname=maddy user=maddy"
|
|
||||||
lookup "SELECT alias FROM aliases WHERE address = $1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
**Syntax**: driver _driver name_ ++
|
|
||||||
**REQUIRED**
|
|
||||||
|
|
||||||
Driver to use to access the database.
|
|
||||||
|
|
||||||
Supported drivers: postgres, sqlite3 (if compiled with C support)
|
|
||||||
|
|
||||||
**Syntax**: dsn _data source name_ ++
|
|
||||||
**REQUIRED**
|
|
||||||
|
|
||||||
Data Source Name to pass to the driver. For SQLite3 this is just a path to DB
|
|
||||||
file. For Postgres, see
|
|
||||||
https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters
|
|
||||||
|
|
||||||
**Syntax**: lookup _query_ ++
|
|
||||||
**REQUIRED**
|
|
||||||
|
|
||||||
SQL query to use to obtain the lookup result.
|
|
||||||
|
|
||||||
It will get one named argument containing the lookup key. Use :key
|
|
||||||
placeholder to access it in SQL. The result row set should contain one row, one
|
|
||||||
column with the string that will be used as a lookup result. If there are more
|
|
||||||
rows, they will be ignored. If there are more columns, lookup will fail. If
|
|
||||||
there are no rows, lookup returns "no results". If there are any error - lookup
|
|
||||||
will fail.
|
|
||||||
|
|
||||||
**Syntax**: init _queries..._ ++
|
|
||||||
**Default**: empty
|
|
||||||
|
|
||||||
List of queries to execute on initialization. Can be used to configure RDBMS.
|
|
||||||
|
|
||||||
Example, to improve SQLite3 performance:
|
|
||||||
```
|
|
||||||
table.sql_query {
|
|
||||||
driver sqlite3
|
|
||||||
dsn whatever.db
|
|
||||||
init "PRAGMA journal_mode=WAL" \
|
|
||||||
"PRAGMA synchronous=NORMAL"
|
|
||||||
lookup "SELECT alias FROM aliases WHERE address = $1"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax:* named_args _boolean_ ++
|
|
||||||
*Default:* yes
|
|
||||||
|
|
||||||
Whether to use named parameters binding when executing SQL queries
|
|
||||||
or not.
|
|
||||||
|
|
||||||
Note that maddy's PostgreSQL driver does not support named parameters and
|
|
||||||
SQLite3 driver has issues handling numbered parameters:
|
|
||||||
https://github.com/mattn/go-sqlite3/issues/472
|
|
||||||
|
|
||||||
**Syntax:** add _query_ ++
|
|
||||||
**Syntax:** list _query_ ++
|
|
||||||
**Syntax:** set _query_ ++
|
|
||||||
**Syntax:** del _query_ ++
|
|
||||||
**Default:** none
|
|
||||||
|
|
||||||
If queries are set to implement corresponding table operations - table becomes
|
|
||||||
"mutable" and can be used in contexts that require writable key-value store.
|
|
||||||
|
|
||||||
'add' query gets :key, :value named arguments - key and value strings to store.
|
|
||||||
They should be added to the store. The query *should* not add multiple values
|
|
||||||
for the same key and *should* fail if the key already exists.
|
|
||||||
|
|
||||||
'list' query gets no arguments and should return a column with all keys in
|
|
||||||
the store.
|
|
||||||
|
|
||||||
'set' query gets :key, :value named arguments - key and value and should replace the existing
|
|
||||||
entry in the database.
|
|
||||||
|
|
||||||
'del' query gets :key argument - key and should remove it from the database.
|
|
||||||
|
|
||||||
If named_args is set to "no" - key is passed as the first numbered parameter
|
|
||||||
($1), value is passed as the second numbered parameter ($2).
|
|
||||||
|
|
||||||
# Static table (table.static)
|
|
||||||
|
|
||||||
The 'static' module implements table lookups using key-value pairs in its
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
```
|
|
||||||
table.static {
|
|
||||||
entry KEY1 VALUE1
|
|
||||||
entry KEY2 VALUE2
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
**Syntax**: entry _key_ _value_
|
|
||||||
|
|
||||||
Add an entry to the table.
|
|
||||||
|
|
||||||
If the same key is used multiple times, the last one takes effect.
|
|
||||||
|
|
||||||
# Regexp rewrite table (table.regexp)
|
|
||||||
|
|
||||||
The 'regexp' module implements table lookups by applying a regular expression
|
|
||||||
to the key value. If it matches - 'replacement' value is returned with $N
|
|
||||||
placeholders being replaced with corresponding capture groups from the match.
|
|
||||||
Otherwise, no value is returned.
|
|
||||||
|
|
||||||
The regular expression syntax is the subset of PCRE. See
|
|
||||||
https://golang.org/pkg/regexp/syntax/ for details.
|
|
||||||
|
|
||||||
```
|
|
||||||
table.regexp <regexp> [replacement] {
|
|
||||||
full_match yes
|
|
||||||
case_insensitive yes
|
|
||||||
expand_placeholders yes
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that [replacement] is optional. If it is not included - table.regexp
|
|
||||||
will return the original string, therefore acting as a regexp match check.
|
|
||||||
This can be useful in combination in destination_in (*maddy-smtp*(5)) for
|
|
||||||
advanced matching:
|
|
||||||
```
|
|
||||||
destination_in regexp ".*-bounce+.*@example.com" {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
**Syntax**: full_match _boolean_ ++
|
|
||||||
**Default**: yes
|
|
||||||
|
|
||||||
Whether to implicitly add start/end anchors to the regular expression.
|
|
||||||
That is, if 'full_match' is yes, then the provided regular expression should
|
|
||||||
match the whole string. With no - partial match is enough.
|
|
||||||
|
|
||||||
**Syntax**: case_insensitive _boolean_ ++
|
|
||||||
**Default**: yes
|
|
||||||
|
|
||||||
Whether to make matching case-insensitive.
|
|
||||||
|
|
||||||
**Syntax**: expand_placeholders _boolean_ ++
|
|
||||||
**Default**: yes
|
|
||||||
|
|
||||||
Replace '$name' and '${name}' in the replacement string with contents of
|
|
||||||
corresponding capture groups from the match.
|
|
||||||
|
|
||||||
To insert a literal $ in the output, use $$ in the template.
|
|
||||||
|
|
||||||
# Identity table (table.identity)
|
|
||||||
|
|
||||||
The module 'identity' is a table module that just returns the key looked up.
|
|
||||||
|
|
||||||
```
|
|
||||||
table.identity { }
|
|
||||||
```
|
|
||||||
|
|
||||||
# No-op table (dummy)
|
|
||||||
|
|
||||||
The module 'dummy' represents an empty table.
|
|
||||||
|
|
||||||
```
|
|
||||||
dummy { }
|
|
||||||
```
|
|
||||||
|
|
||||||
# Email local part (table.email_localpart)
|
|
||||||
|
|
||||||
The module 'email_localpart' extracts and unescaped local ("username") part
|
|
||||||
of the email address.
|
|
||||||
|
|
||||||
E.g.
|
|
||||||
test@example.org => test
|
|
||||||
"test @ a"@example.org => test @ a
|
|
||||||
|
|
||||||
```
|
|
||||||
table.email_localpart { }
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table chaining module (table.chain)
|
|
||||||
|
|
||||||
The table.chain module allows chaining together multiple table modules
|
|
||||||
by using value returned by a previous table as an input for the second
|
|
||||||
table.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
table.chain {
|
|
||||||
step regexp "(.+)(\\+[^+"@]+)?@example.org" "$1@example.org"
|
|
||||||
step file /etc/maddy/emails
|
|
||||||
}
|
|
||||||
```
|
|
||||||
This will strip +prefix from mailbox before looking it up
|
|
||||||
in /etc/maddy/emails list.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: step _table_
|
|
||||||
|
|
||||||
Adds a table module to the chain. If input value is not in the table
|
|
||||||
(e.g. file) - return "not exists" error.
|
|
||||||
|
|
||||||
*Syntax*: optional_step _table_
|
|
||||||
|
|
||||||
Same as step but if input value is not in the table - it is passed to the
|
|
||||||
next step without changes.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
Something like this can be used to map emails to usernames
|
|
||||||
after translating them via aliases map:
|
|
||||||
```
|
|
||||||
table.chain {
|
|
||||||
optional_step file /etc/maddy/aliases
|
|
||||||
step regexp "(.+)@(.+)" "$1"
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,457 +0,0 @@
|
||||||
maddy-targets(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE Delivery targets
|
|
||||||
|
|
||||||
This man page describes modules that can used with 'deliver_to' directive
|
|
||||||
of SMTP endpoint module.
|
|
||||||
|
|
||||||
# SQL module (target.imapsql)
|
|
||||||
|
|
||||||
SQL module described in *maddy-storage*(5) can also be used as a delivery
|
|
||||||
target.
|
|
||||||
|
|
||||||
# Queue module (target.queue)
|
|
||||||
|
|
||||||
Queue module buffers messages on disk and retries delivery multiple times to
|
|
||||||
another target to ensure reliable delivery.
|
|
||||||
|
|
||||||
```
|
|
||||||
target.queue {
|
|
||||||
target remote
|
|
||||||
location ...
|
|
||||||
max_parallelism 16
|
|
||||||
max_tries 4
|
|
||||||
bounce {
|
|
||||||
destination example.org {
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
reject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
autogenerated_msg_domain example.org
|
|
||||||
debug no
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
First argument specifies directory to use for storage.
|
|
||||||
Relative paths are relative to the StateDirectory.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: target _block_name_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
Delivery target to use for final delivery.
|
|
||||||
|
|
||||||
*Syntax*: location _directory_ ++
|
|
||||||
*Default*: StateDirectory/configuration_block_name
|
|
||||||
|
|
||||||
File system directory to use to store queued messages.
|
|
||||||
Relative paths are relative to the StateDirectory.
|
|
||||||
|
|
||||||
*Syntax*: max_parallelism _integer_ ++
|
|
||||||
*Default*: 16
|
|
||||||
|
|
||||||
Start up to _integer_ goroutines for message processing. Basically, this option
|
|
||||||
limits amount of messages tried to be delivered concurrently.
|
|
||||||
|
|
||||||
*Syntax*: max_tries _integer_ ++
|
|
||||||
*Default*: 20
|
|
||||||
|
|
||||||
Attempt delivery up to _integer_ times. Note that no more attempts will be done
|
|
||||||
is permanent error occured during previous attempt.
|
|
||||||
|
|
||||||
Delay before the next attempt will be increased exponentally using the
|
|
||||||
following formula: 15mins \* 1.2 ^ (n - 1) where n is the attempt number.
|
|
||||||
This gives you approximately the following sequence of delays:
|
|
||||||
18mins, 21mins, 25mins, 31mins, 37mins, 44mins, 53mins, 64mins, ...
|
|
||||||
|
|
||||||
*Syntax*: bounce { ... } ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
This configuration contains pipeline configuration to be used for generated DSN
|
|
||||||
(Delivery Status Notifiaction) messages.
|
|
||||||
|
|
||||||
If this is block is not present in configuration, DSNs will not be generated.
|
|
||||||
Note, however, this is not what you want most of the time.
|
|
||||||
|
|
||||||
*Syntax*: autogenerated_msg_domain _domain_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Domain to use in sender address for DSNs. Should be specified too if 'bounce'
|
|
||||||
block is specified.
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
# Remote MX module (remote)
|
|
||||||
|
|
||||||
Module that implements message delivery to remote MTAs discovered via DNS MX
|
|
||||||
records. You probably want to use it with queue module for reliability.
|
|
||||||
|
|
||||||
```
|
|
||||||
target.remote {
|
|
||||||
hostname mx.example.org
|
|
||||||
debug no
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If a message check marks a message as 'quarantined', remote module
|
|
||||||
will refuse to deliver it.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: hostname _domain_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Hostname to use client greeting (EHLO/HELO command). Some servers require it to
|
|
||||||
be FQDN, SPF-capable servers check whether it corresponds to the server IP
|
|
||||||
address, so it is better to set it to a domain that resolves to the server IP.
|
|
||||||
|
|
||||||
*Syntax*: limits _config block_ ++
|
|
||||||
*Default*: no limits
|
|
||||||
|
|
||||||
See 'limits' directive in *maddy-smtp*(5) for SMTP endpoint.
|
|
||||||
It works the same except for address domains used for
|
|
||||||
per-source/per-destination are as observed when message exits the server.
|
|
||||||
|
|
||||||
*Syntax*: local_ip _IP address_ ++
|
|
||||||
*Default*: empty
|
|
||||||
|
|
||||||
Choose the local IP to bind for outbound SMTP connections.
|
|
||||||
|
|
||||||
*Syntax*: connect_timeout _duration_ ++
|
|
||||||
*Default*: 5m
|
|
||||||
|
|
||||||
Timeout for TCP connection establishment.
|
|
||||||
|
|
||||||
RFC 5321 recommends 5 minutes for "initial greeting" that includes TCP
|
|
||||||
handshake. maddy uses two separate timers - one for "dialing" (DNS A/AAAA
|
|
||||||
lookup + TCP handshake) and another for "initial greeting". This directive
|
|
||||||
configures the former. The latter is not configurable and is hardcoded to be
|
|
||||||
5 minutes.
|
|
||||||
|
|
||||||
*Syntax*: command_timeout _duration_ ++
|
|
||||||
*Default*: 5m
|
|
||||||
|
|
||||||
Timeout for any SMTP command (EHLO, MAIL, RCPT, DATA, etc).
|
|
||||||
|
|
||||||
If STARTTLS is used this timeout also applies to TLS handshake.
|
|
||||||
|
|
||||||
RFC 5321 recommends 5 minutes for MAIL/RCPT and 3 minutes for
|
|
||||||
DATA.
|
|
||||||
|
|
||||||
*Syntax*: submission_timeout _duration_ ++
|
|
||||||
*Default*: 12m
|
|
||||||
|
|
||||||
Time to wait after the entire message is sent (after "final dot").
|
|
||||||
|
|
||||||
RFC 5321 recommends 10 minutes.
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
*Syntax*: requiretls_override _boolean_ ++
|
|
||||||
*Default*: true
|
|
||||||
|
|
||||||
Allow local security policy to be disabled using 'TLS-Required' header field in
|
|
||||||
sent messages. Note that the field has no effect if transparent forwarding is
|
|
||||||
used, message body should be processed before outbound delivery starts for it
|
|
||||||
to take effect (e.g. message should be queued using 'queue' module).
|
|
||||||
|
|
||||||
*Syntax*: relaxed_requiretls _boolean_ ++
|
|
||||||
*Default*: true
|
|
||||||
|
|
||||||
This option disables strict conformance with REQUIRETLS specification and
|
|
||||||
allows forwarding of messages 'tagged' with REQUIRETLS to MXes that are not
|
|
||||||
advertising REQUIRETLS support. It is meant to allow REQUIRETLS use without the
|
|
||||||
need to have support from all servers. It is based on the assumption that
|
|
||||||
server referenced by MX record is likely the final destination and therefore
|
|
||||||
there is only need to secure communication towards it and not beyond.
|
|
||||||
|
|
||||||
*Syntax*: conn_reuse_limit _integer_ ++
|
|
||||||
*Default*: 10
|
|
||||||
|
|
||||||
Amount of times the same SMTP connection can be used.
|
|
||||||
Connections are never reused if the previous DATA command failed.
|
|
||||||
|
|
||||||
*Syntax*: conn_max_idle_count _integer_ ++
|
|
||||||
*Default*: 10
|
|
||||||
|
|
||||||
Max. amount of idle connections per recipient domains to keep in cache.
|
|
||||||
|
|
||||||
*Syntax*: conn_max_idle_time _integer_ ++
|
|
||||||
*Default*: 150 (2.5 min)
|
|
||||||
|
|
||||||
Amount of time the idle connection is still considered potentially usable.
|
|
||||||
|
|
||||||
## Security policies
|
|
||||||
|
|
||||||
*Syntax*: mx_auth _config block_ ++
|
|
||||||
*Default*: no policies
|
|
||||||
|
|
||||||
'remote' module implements a number of of schemes and protocols necessary to
|
|
||||||
ensure security of message delivery. Most of these schemes are concerned with
|
|
||||||
authentication of recipient server and TLS enforcement.
|
|
||||||
|
|
||||||
To enable mechanism, specify its name in the mx_auth directive block:
|
|
||||||
```
|
|
||||||
mx_auth {
|
|
||||||
dane
|
|
||||||
mtasts
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Additional configuration is possible if supported by the mechanism by
|
|
||||||
specifying additional options as a block for the corresponding mechanism.
|
|
||||||
E.g.
|
|
||||||
```
|
|
||||||
mtasts {
|
|
||||||
cache ram
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If the mx_auth directive is not specified, no mechanisms are enabled. Note
|
|
||||||
that, however, this makes outbound SMTP vulnerable to a numberous downgrade
|
|
||||||
attacks and hence not recommended.
|
|
||||||
|
|
||||||
It is possible to share the same set of policies for multiple 'remote' module
|
|
||||||
instances by defining it at the top-level using 'mx_auth' module and then
|
|
||||||
referencing it using standard & syntax:
|
|
||||||
```
|
|
||||||
mx_auth outbound_policy {
|
|
||||||
dane
|
|
||||||
mtasts {
|
|
||||||
cache ram
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... somewhere else ...
|
|
||||||
|
|
||||||
deliver_to remote {
|
|
||||||
mx_auth &outbound_policy
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... somewhere else ...
|
|
||||||
|
|
||||||
deliver_to remote {
|
|
||||||
mx_auth &outbound_policy
|
|
||||||
tls_client { ... }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security policies: MTA-STS
|
|
||||||
|
|
||||||
Checks MTA-STS policy of the recipient domain. Provides proper authentication
|
|
||||||
and TLS enforcement for delivery, but partially vulnerable to persistent active
|
|
||||||
attacks.
|
|
||||||
|
|
||||||
Sets MX level to "mtasts" if the used MX matches MTA-STS policy even if it is
|
|
||||||
not set to "enforce" mode.
|
|
||||||
|
|
||||||
```
|
|
||||||
mtasts {
|
|
||||||
cache fs
|
|
||||||
fs_dir StateDirectory/mtasts_cache
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: cache fs|ram ++
|
|
||||||
*Default*: fs
|
|
||||||
|
|
||||||
Storage to use for MTA-STS cache. 'fs' is to use a filesystem directory, 'ram'
|
|
||||||
to store the cache in memory.
|
|
||||||
|
|
||||||
It is recommended to use 'fs' since that will not discard the cache (and thus
|
|
||||||
cause MTA-STS security to disappear) on server restart. However, using the RAM
|
|
||||||
cache can make sense for high-load configurations with good uptime.
|
|
||||||
|
|
||||||
*Syntax*: fs_dir _directory_ ++
|
|
||||||
*Default*: StateDirectory/mtasts_cache
|
|
||||||
|
|
||||||
Filesystem directory to use for policies caching if 'cache' is set to 'fs'.
|
|
||||||
|
|
||||||
## Security policies: DNSSEC
|
|
||||||
|
|
||||||
Checks whether MX records are signed. Sets MX level to "dnssec" is they are.
|
|
||||||
|
|
||||||
maddy does not validate DNSSEC signatures on its own. Instead it reslies on
|
|
||||||
the upstream resolver to do so by causing lookup to fail when verification
|
|
||||||
fails and setting the AD flag for signed and verfified zones. As a safety
|
|
||||||
measure, if the resolver is not 127.0.0.1 or ::1, the AD flag is ignored.
|
|
||||||
|
|
||||||
DNSSEC is currently not supported on Windows and other platforms that do not
|
|
||||||
have the /etc/resolv.conf file in the standard format.
|
|
||||||
|
|
||||||
```
|
|
||||||
dnssec { }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security policies: DANE
|
|
||||||
|
|
||||||
Checks TLSA records for the recipient MX. Provides downgrade-resistant TLS
|
|
||||||
enforcement.
|
|
||||||
|
|
||||||
Sets TLS level to "authenticated" if a valid and matching TLSA record uses
|
|
||||||
DANE-EE or DANE-TA usage type.
|
|
||||||
|
|
||||||
See above for notes on DNSSEC. DNSSEC support is required for DANE to work.
|
|
||||||
|
|
||||||
```
|
|
||||||
dane { }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security policies: Local policy
|
|
||||||
|
|
||||||
Checks effective TLS and MX levels (as set by other policies) against local
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
```
|
|
||||||
local_policy {
|
|
||||||
min_tls_level none
|
|
||||||
min_mx_level none
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Using 'local_policy off' is equivalent to setting both directives to 'none'.
|
|
||||||
|
|
||||||
*Syntax*: min_tls_level none|encrypted|authenticated ++
|
|
||||||
*Default*: none
|
|
||||||
|
|
||||||
Set the minimal TLS security level required for all outbound messages.
|
|
||||||
|
|
||||||
See [Security levels](../../seclevels) page for details.
|
|
||||||
|
|
||||||
*Syntax*: min_mx_level: none|mtasts|dnssec ++
|
|
||||||
*Default*: none
|
|
||||||
|
|
||||||
Set the minimal MX security level required for all outbound messages.
|
|
||||||
|
|
||||||
See [Security levels](../../seclevels) page for details.
|
|
||||||
|
|
||||||
# SMTP transparent forwarding module (target.smtp)
|
|
||||||
|
|
||||||
Module that implements transparent forwarding of messages over SMTP.
|
|
||||||
|
|
||||||
Use in pipeline configuration:
|
|
||||||
```
|
|
||||||
deliver_to smtp tcp://127.0.0.1:5353
|
|
||||||
# or
|
|
||||||
deliver_to smtp tcp://127.0.0.1:5353 {
|
|
||||||
# Other settings, see below.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
target.smtp {
|
|
||||||
debug no
|
|
||||||
tls_client {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
attempt_starttls yes
|
|
||||||
require_yes no
|
|
||||||
auth off
|
|
||||||
targets tcp://127.0.0.1:2525
|
|
||||||
connect_timeout 5m
|
|
||||||
command_timeout 5m
|
|
||||||
submission_timeout 12m
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Endpoint addresses use format described in *maddy-config*(5).
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax*: debug _boolean_ ++
|
|
||||||
*Default*: global directive value
|
|
||||||
|
|
||||||
Enable verbose logging.
|
|
||||||
|
|
||||||
*Syntax*: tls_client { ... } ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
Advanced TLS client configuration options. See *maddy-tls*(5) for details.
|
|
||||||
|
|
||||||
*Syntax*: attempt_starttls _boolean_ ++
|
|
||||||
*Default*: yes (no for target.lmtp)
|
|
||||||
|
|
||||||
Attempt to use STARTTLS if it is supported by the remote server.
|
|
||||||
If TLS handshake fails, connection will be retried without STARTTLS
|
|
||||||
unless 'require_tls' is also specified.
|
|
||||||
|
|
||||||
*Syntax*: require_tls _boolean_ ++
|
|
||||||
*Default*: no
|
|
||||||
|
|
||||||
Refuse to pass messages over plain-text connections.
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
auth off ++
|
|
||||||
plain _username_ _password_ ++
|
|
||||||
forward ++
|
|
||||||
external ++
|
|
||||||
*Default*: off
|
|
||||||
|
|
||||||
Specify the way to authenticate to the remote server.
|
|
||||||
Valid values:
|
|
||||||
|
|
||||||
- off
|
|
||||||
|
|
||||||
No authentication.
|
|
||||||
|
|
||||||
- plain
|
|
||||||
|
|
||||||
Authenticate using specified username-password pair.
|
|
||||||
*Don't use* this without enforced TLS ('require_tls').
|
|
||||||
|
|
||||||
- forward
|
|
||||||
|
|
||||||
Forward credentials specified by the client.
|
|
||||||
*Don't use* this without enforced TLS ('require_tls').
|
|
||||||
|
|
||||||
- external
|
|
||||||
|
|
||||||
Request "external" SASL authentication. This is usually used for
|
|
||||||
authentication using TLS client certificates. See *maddy-tls*(5)
|
|
||||||
for how to specify the client certificate.
|
|
||||||
|
|
||||||
*Syntax*: targets _endpoints..._ ++
|
|
||||||
*Default:* not specified
|
|
||||||
|
|
||||||
REQUIRED.
|
|
||||||
|
|
||||||
List of remote server addresses to use. See Address definitions in
|
|
||||||
*maddy-config*(5) for syntax to use. Basically, it is 'tcp://ADDRESS:PORT'
|
|
||||||
for plain SMTP and 'tls://ADDRESS:PORT' for SMTPS (aka SMTP with Implicit
|
|
||||||
TLS).
|
|
||||||
|
|
||||||
Multiple addresses can be specified, they will be tried in order until connection to
|
|
||||||
one succeeds (including TLS handshake if TLS is required).
|
|
||||||
|
|
||||||
*Syntax*: connect_timeout _duration_ ++
|
|
||||||
*Default*: 5m
|
|
||||||
|
|
||||||
Same as for target.remote.
|
|
||||||
|
|
||||||
*Syntax*: command_timeout _duration_ ++
|
|
||||||
*Default*: 5m
|
|
||||||
|
|
||||||
Same as for target.remote.
|
|
||||||
|
|
||||||
*Syntax*: submission_timeout _duration_ ++
|
|
||||||
*Default*: 12m
|
|
||||||
|
|
||||||
Same as for target.remote.
|
|
||||||
|
|
||||||
# LMTP transparent forwarding module (target.lmtp)
|
|
||||||
|
|
||||||
The 'target.lmtp' module is similar to 'target.smtp' and supports all
|
|
||||||
its options and syntax but speaks LMTP instead of SMTP.
|
|
|
@ -1,379 +0,0 @@
|
||||||
maddy-tls(5) "maddy mail server" "maddy reference documentation"
|
|
||||||
|
|
||||||
; TITLE Advanced TLS configuration
|
|
||||||
|
|
||||||
# TLS server configuration
|
|
||||||
|
|
||||||
TLS certificates are obtained by modules called "certificate loaders". 'tls' directive
|
|
||||||
arguments specify name of loader to use and arguments. Due to syntax limitations
|
|
||||||
advanced configuration for loader should be specified using 'loader' directive, see
|
|
||||||
below.
|
|
||||||
|
|
||||||
```
|
|
||||||
tls file cert.pem key.pem {
|
|
||||||
protocols tls1.2 tls1.3
|
|
||||||
curve X25519
|
|
||||||
ciphers ...
|
|
||||||
}
|
|
||||||
|
|
||||||
tls {
|
|
||||||
loader file cert.pem key.pem {
|
|
||||||
# Options for loader go here.
|
|
||||||
}
|
|
||||||
protocols tls1.2 tls1.3
|
|
||||||
curve X25519
|
|
||||||
ciphers ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available certificate loaders
|
|
||||||
|
|
||||||
- file
|
|
||||||
|
|
||||||
Accepts argument pairs specifying certificate and then key.
|
|
||||||
E.g. 'tls file certA.pem keyA.pem certB.pem keyB.pem'
|
|
||||||
|
|
||||||
If multiple certificates are listed, SNI will be used.
|
|
||||||
|
|
||||||
- acme
|
|
||||||
|
|
||||||
Automatically obtains a certificate using ACME protocol (Let's Encrypt)
|
|
||||||
|
|
||||||
See below for details.
|
|
||||||
|
|
||||||
- off
|
|
||||||
|
|
||||||
Not really a loader but a special value for tls directive, explicitly disables TLS for
|
|
||||||
endpoint(s).
|
|
||||||
|
|
||||||
## Advanced TLS configuration
|
|
||||||
|
|
||||||
*Note: maddy uses secure defaults and TLS handshake is resistant to active downgrade attacks.*
|
|
||||||
*There is no need to change anything in most cases.*
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
protocols _min_version_ _max_version_ ++
|
|
||||||
protocols _version_ ++
|
|
||||||
*Default*: tls1.0 tls1.3
|
|
||||||
|
|
||||||
Minimum/maximum accepted TLS version. If only one value is specified, it will
|
|
||||||
be the only one usable version.
|
|
||||||
|
|
||||||
Valid values are: tls1.0, tls1.1, tls1.2, tls1.3
|
|
||||||
|
|
||||||
*Syntax*: ciphers _ciphers..._ ++
|
|
||||||
*Default*: Go version-defined set of 'secure ciphers', ordered by hardware
|
|
||||||
performance
|
|
||||||
|
|
||||||
List of supported cipher suites, in preference order. Not used with TLS 1.3.
|
|
||||||
|
|
||||||
Valid values:
|
|
||||||
|
|
||||||
- RSA-WITH-RC4128-SHA
|
|
||||||
- RSA-WITH-3DES-EDE-CBC-SHA
|
|
||||||
- RSA-WITH-AES128-CBC-SHA
|
|
||||||
- RSA-WITH-AES256-CBC-SHA
|
|
||||||
- RSA-WITH-AES128-CBC-SHA256
|
|
||||||
- RSA-WITH-AES128-GCM-SHA256
|
|
||||||
- RSA-WITH-AES256-GCM-SHA384
|
|
||||||
- ECDHE-ECDSA-WITH-RC4128-SHA
|
|
||||||
- ECDHE-ECDSA-WITH-AES128-CBC-SHA
|
|
||||||
- ECDHE-ECDSA-WITH-AES256-CBC-SHA
|
|
||||||
- ECDHE-RSA-WITH-RC4128-SHA
|
|
||||||
- ECDHE-RSA-WITH-3DES-EDE-CBC-SHA
|
|
||||||
- ECDHE-RSA-WITH-AES128-CBC-SHA
|
|
||||||
- ECDHE-RSA-WITH-AES256-CBC-SHA
|
|
||||||
- ECDHE-ECDSA-WITH-AES128-CBC-SHA256
|
|
||||||
- ECDHE-RSA-WITH-AES128-CBC-SHA256
|
|
||||||
- ECDHE-RSA-WITH-AES128-GCM-SHA256
|
|
||||||
- ECDHE-ECDSA-WITH-AES128-GCM-SHA256
|
|
||||||
- ECDHE-RSA-WITH-AES256-GCM-SHA384
|
|
||||||
- ECDHE-ECDSA-WITH-AES256-GCM-SHA384
|
|
||||||
- ECDHE-RSA-WITH-CHACHA20-POLY1305
|
|
||||||
- ECDHE-ECDSA-WITH-CHACHA20-POLY1305
|
|
||||||
|
|
||||||
*Syntax*: curve _curves..._ ++
|
|
||||||
*Default*: defined by Go version
|
|
||||||
|
|
||||||
The elliptic curves that will be used in an ECDHE handshake, in preference
|
|
||||||
order.
|
|
||||||
|
|
||||||
Valid values: p256, p384, p521, X25519.
|
|
||||||
|
|
||||||
# TLS client configuration
|
|
||||||
|
|
||||||
tls_client directive allows to customize behavior of TLS client implementation,
|
|
||||||
notably adjusting minimal and maximal TLS versions and allowed cipher suites,
|
|
||||||
enabling TLS client authentication.
|
|
||||||
|
|
||||||
```
|
|
||||||
tls_client {
|
|
||||||
protocols tls1.2 tls1.3
|
|
||||||
ciphers ...
|
|
||||||
curve X25519
|
|
||||||
root_ca /etc/ssl/cert.pem
|
|
||||||
|
|
||||||
cert /etc/ssl/private/maddy-client.pem
|
|
||||||
key /etc/ssl/private/maddy-client.pem
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
protocols _min_version_ _max_version_ ++
|
|
||||||
protocols _version_ ++
|
|
||||||
*Default*: tls1.0 tls1.3
|
|
||||||
|
|
||||||
Minimum/maximum accepted TLS version. If only one value is specified, it will
|
|
||||||
be the only one usable version.
|
|
||||||
|
|
||||||
Valid values are: tls1.0, tls1.1, tls1.2, tls1.3
|
|
||||||
|
|
||||||
*Syntax*: ciphers _ciphers..._ ++
|
|
||||||
*Default*: Go version-defined set of 'secure ciphers', ordered by hardware
|
|
||||||
performance
|
|
||||||
|
|
||||||
List of supported cipher suites, in preference order. Not used with TLS 1.3.
|
|
||||||
|
|
||||||
See TLS server configuration for list of supported values.
|
|
||||||
|
|
||||||
*Syntax*: curve _curves..._ ++
|
|
||||||
*Default*: defined by Go version
|
|
||||||
|
|
||||||
The elliptic curves that will be used in an ECDHE handshake, in preference
|
|
||||||
order.
|
|
||||||
|
|
||||||
Valid values: p256, p384, p521, X25519.
|
|
||||||
|
|
||||||
*Syntax*: root_ca _paths..._ ++
|
|
||||||
*Default*: system CA pool
|
|
||||||
|
|
||||||
List of files with PEM-encoded CA certificates to use when verifying
|
|
||||||
server certificates.
|
|
||||||
|
|
||||||
*Syntax*: ++
|
|
||||||
cert _cert_path_ ++
|
|
||||||
key _key_path_ ++
|
|
||||||
*Default*: not specified
|
|
||||||
|
|
||||||
Present the specified certificate when server requests a client certificate.
|
|
||||||
Files should use PEM format. Both directives should be specified.
|
|
||||||
|
|
||||||
# Automatic certificate management via ACME
|
|
||||||
|
|
||||||
```
|
|
||||||
tls.loader.acme {
|
|
||||||
debug off
|
|
||||||
hostname example.maddy.invalid
|
|
||||||
store_path /var/lib/maddy/acme
|
|
||||||
ca https://acme-v02.api.letsencrypt.org/directory
|
|
||||||
test_ca https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
email test@maddy.invalid
|
|
||||||
agreed off
|
|
||||||
challenge dns-01
|
|
||||||
dns ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Maddy supports obtaining certificates using ACME protocol.
|
|
||||||
|
|
||||||
To use it, create a configuration name for tls.loader.acme
|
|
||||||
and reference it from endpoints that should use automatically
|
|
||||||
configured certificates:
|
|
||||||
```
|
|
||||||
tls.loader.acme local_tls {
|
|
||||||
email put-your-email-here@example.org
|
|
||||||
agreed # indicate your agreement with Let's Encrypt ToS
|
|
||||||
challenge dns-01
|
|
||||||
}
|
|
||||||
|
|
||||||
smtp tcp://127.0.0.1:25 {
|
|
||||||
tls &local_tls
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Currently the only supported challenge is dns-01 one therefore
|
|
||||||
you also need to configure the DNS provider:
|
|
||||||
```
|
|
||||||
tls.loader.acme local_tls {
|
|
||||||
email maddy-acme@example.org
|
|
||||||
agreed
|
|
||||||
challenge dns-01
|
|
||||||
dns PROVIDER_NAME {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
See below for supported providers and necessary configuration
|
|
||||||
for each.
|
|
||||||
|
|
||||||
## Configuration directives
|
|
||||||
|
|
||||||
*Syntax:* debug _boolean_ ++
|
|
||||||
*Default:* global directive value
|
|
||||||
|
|
||||||
Enable debug logging.
|
|
||||||
|
|
||||||
*Syntax:* hostname _str_ ++
|
|
||||||
*Default:* global directive value
|
|
||||||
|
|
||||||
Domain name to issue certificate for. Required.
|
|
||||||
|
|
||||||
*Syntax:* store_path _path_ ++
|
|
||||||
*Default:* state_dir/acme
|
|
||||||
|
|
||||||
Where to store issued certificates and associated metadata.
|
|
||||||
Currently only filesystem-based store is supported.
|
|
||||||
|
|
||||||
*Syntax:* ca _url_ ++
|
|
||||||
*Default:* Let's Encrypt production CA
|
|
||||||
|
|
||||||
URL of ACME directory to use.
|
|
||||||
|
|
||||||
*Syntax:* test_ca _url_ ++
|
|
||||||
*Default:* Let's Encrypt staging CA
|
|
||||||
|
|
||||||
URL of ACME directory to use for retries should
|
|
||||||
primary CA fail.
|
|
||||||
|
|
||||||
maddy will keep attempting to issues certificates
|
|
||||||
using test_ca until it succeeds then it will switch
|
|
||||||
back to the one configured via 'ca' option.
|
|
||||||
|
|
||||||
This avoids rate limit issues with production CA.
|
|
||||||
|
|
||||||
*Syntax:* email _str_ ++
|
|
||||||
*Default:* not set
|
|
||||||
|
|
||||||
Email to pass while registering an ACME account.
|
|
||||||
|
|
||||||
*Syntax:* agreed _boolean_ ++
|
|
||||||
*Default:* false
|
|
||||||
|
|
||||||
Whether you agreed to ToS of the CA service you are using.
|
|
||||||
|
|
||||||
*Syntax:* challenge dns-01 ++
|
|
||||||
*Default:* not set
|
|
||||||
|
|
||||||
Challenge(s) to use while performing domain verification.
|
|
||||||
|
|
||||||
## DNS providers
|
|
||||||
|
|
||||||
Support for some providers is not provided by standard builds.
|
|
||||||
To be able to use these, you need to compile maddy
|
|
||||||
with "libdns_PROVIDER" build tag.
|
|
||||||
E.g.
|
|
||||||
```
|
|
||||||
./build.sh -tags 'libdns_googleclouddns'
|
|
||||||
```
|
|
||||||
|
|
||||||
- gandi
|
|
||||||
|
|
||||||
```
|
|
||||||
dns gandi {
|
|
||||||
api_token "token"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- digitalocean
|
|
||||||
|
|
||||||
```
|
|
||||||
dns digitalocean {
|
|
||||||
api_token "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- cloudflare
|
|
||||||
|
|
||||||
See https://github.com/libdns/cloudflare#authenticating
|
|
||||||
|
|
||||||
```
|
|
||||||
dns cloudflare {
|
|
||||||
api_token "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- vultr
|
|
||||||
|
|
||||||
```
|
|
||||||
dns vultr {
|
|
||||||
api_token "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- hetzner
|
|
||||||
|
|
||||||
```
|
|
||||||
dns hetzner {
|
|
||||||
api_token "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- namecheap
|
|
||||||
|
|
||||||
```
|
|
||||||
dns namecheap {
|
|
||||||
api_key "..."
|
|
||||||
api_username "..."
|
|
||||||
|
|
||||||
# optional: API endpoint, production one is used if not set.
|
|
||||||
endpoint "https://api.namecheap.com/xml.response"
|
|
||||||
|
|
||||||
# optional: your public IP, discovered using icanhazip.com if not set
|
|
||||||
client_ip 1.2.3.4
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- googleclouddns (non-default)
|
|
||||||
|
|
||||||
```
|
|
||||||
dns googleclouddns {
|
|
||||||
project "project_id"
|
|
||||||
service_account_json "path"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- route53 (non-default)
|
|
||||||
|
|
||||||
```
|
|
||||||
dns route53 {
|
|
||||||
secret_access_key "..."
|
|
||||||
access_key_id "..."
|
|
||||||
# or use environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- leaseweb (non-default)
|
|
||||||
|
|
||||||
```
|
|
||||||
dns leaseweb {
|
|
||||||
api_key "key"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- metaname (non-default)
|
|
||||||
|
|
||||||
```
|
|
||||||
dns metaname {
|
|
||||||
api_key "key"
|
|
||||||
account_ref "reference"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- alidns (non-default)
|
|
||||||
|
|
||||||
```
|
|
||||||
dns alidns {
|
|
||||||
key_id "..."
|
|
||||||
key_secret "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- namedotcom (non-default)
|
|
||||||
|
|
||||||
```
|
|
||||||
dns namedotcom {
|
|
||||||
user "..."
|
|
||||||
token "..."
|
|
||||||
}
|
|
||||||
```
|
|
26
docs/reference/auth/dovecot_sasl.md
Normal file
26
docs/reference/auth/dovecot_sasl.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Dovecot SASL
|
||||||
|
|
||||||
|
The 'auth.dovecot\_sasl' module implements the client side of the Dovecot
|
||||||
|
authentication protocol, allowing maddy to use it as a credentials source.
|
||||||
|
|
||||||
|
Currently SASL mechanisms support is limited to mechanisms supported by maddy
|
||||||
|
so you cannot get e.g. SCRAM-MD5 this way.
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.dovecot_sasl {
|
||||||
|
endpoint unix://socket_path
|
||||||
|
}
|
||||||
|
|
||||||
|
dovecot_sasl unix://socket_path
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: endpoint _schema://address_ <br>
|
||||||
|
**Default**: not set
|
||||||
|
|
||||||
|
Set the address to use to contact Dovecot SASL server in the standard endpoint
|
||||||
|
format.
|
||||||
|
|
||||||
|
tcp://10.0.0.1:2222 for TCP, unix:///var/lib/dovecot/auth.sock for Unix
|
||||||
|
domain sockets.
|
47
docs/reference/auth/external.md
Normal file
47
docs/reference/auth/external.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# System command
|
||||||
|
|
||||||
|
auth.external module for authentication using external helper binary. It looks for binary
|
||||||
|
named maddy-auth-helper in $PATH and libexecdir and uses it for authentication
|
||||||
|
using username/password pair.
|
||||||
|
|
||||||
|
The protocol is very simple:
|
||||||
|
Program is launched for each authentication. Username and password are written
|
||||||
|
to stdin, adding \\n to the end. If binary exits with 0 status code -
|
||||||
|
authentication is considered successful. If the status code is 1 -
|
||||||
|
authentication is failed. If the status code is 2 - another unrelated error has
|
||||||
|
happened. Additional information should be written to stderr.
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.external {
|
||||||
|
helper /usr/bin/ldap-helper
|
||||||
|
perdomain no
|
||||||
|
domains example.org
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: helper _file\_path\_
|
||||||
|
|
||||||
|
Location of the helper binary. **Required.**
|
||||||
|
|
||||||
|
**Syntax**: perdomain _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Don't remove domain part of username when authenticating and require it to be
|
||||||
|
present. Can be used if you want user@domain1 and user@domain2 to be different
|
||||||
|
accounts.
|
||||||
|
|
||||||
|
**Syntax**: domains _domains..._ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Domains that should be allowed in username during authentication.
|
||||||
|
|
||||||
|
For example, if 'domains' is set to "domain1 domain2", then
|
||||||
|
username, username@domain1 and username@domain2 will be accepted as valid login
|
||||||
|
name in addition to just username.
|
||||||
|
|
||||||
|
If used without 'perdomain', domain part will be removed from login before
|
||||||
|
check with underlying auth. mechanism. If 'perdomain' is set, then
|
||||||
|
domains must be also set and domain part WILL NOT be removed before check.
|
||||||
|
|
113
docs/reference/auth/ldap.md
Normal file
113
docs/reference/auth/ldap.md
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
# LDAP BindDN
|
||||||
|
|
||||||
|
maddy supports authentication via LDAP using DN binding. Passwords are verified
|
||||||
|
by the LDAP server.
|
||||||
|
|
||||||
|
maddy needs to know the DN to use for binding. It can be obtained either by
|
||||||
|
directory search or template .
|
||||||
|
|
||||||
|
Note that storage backends conventionally use email addresses, if you use
|
||||||
|
non-email identifiers as usernames then you should map them onto
|
||||||
|
emails on delivery by using auth\_map (see documentation page for used storage backend).
|
||||||
|
|
||||||
|
auth.ldap also can be a used as a table module. This way you can check
|
||||||
|
whether the account exists. It works only if DN template is not used.
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.ldap {
|
||||||
|
urls ldap://maddy.test:389
|
||||||
|
|
||||||
|
# Specify initial bind credentials. Not required ('bind off')
|
||||||
|
# if DN template is used.
|
||||||
|
bind plain "cn=maddy,ou=people,dc=maddy,dc=test" "123456"
|
||||||
|
|
||||||
|
# Specify DN template to skip lookup.
|
||||||
|
dn_template "cn={username},ou=people,dc=maddy,dc=test"
|
||||||
|
|
||||||
|
# Specify base_dn and filter to lookup DN.
|
||||||
|
base_dn "ou=people,dc=maddy,dc=test"
|
||||||
|
filter "(&(objectClass=posixAccount)(uid={username}))"
|
||||||
|
|
||||||
|
tls_client { ... }
|
||||||
|
starttls off
|
||||||
|
debug off
|
||||||
|
connect_timeout 1m
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
auth.ldap ldap://maddy.test.389 {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax:** urls _servers...\_
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
URLs of the directory servers to use. First available server
|
||||||
|
is used - no load-balancing is done.
|
||||||
|
|
||||||
|
URLs should use 'ldap://', 'ldaps://', 'ldapi://' schemes.
|
||||||
|
|
||||||
|
**Syntax:** bind off <br>
|
||||||
|
bind unauth <br>
|
||||||
|
bind external <br>
|
||||||
|
bind plain _username_ _password_ <br>
|
||||||
|
**Default:** off
|
||||||
|
|
||||||
|
Credentials to use for initial binding. Required if DN lookup is used.
|
||||||
|
|
||||||
|
'unauth' performs unauthenticated bind. 'external' performs external binding
|
||||||
|
which is useful for Unix socket connections (ldapi://) or TLS client certificate
|
||||||
|
authentication (cert. is set using tls\_client directive). 'plain' performs a
|
||||||
|
simple bind using provided credentials.
|
||||||
|
|
||||||
|
**Syntax:** dn\_template _template\_
|
||||||
|
|
||||||
|
DN template to use for binding. '{username}' is replaced with the
|
||||||
|
username specified by the user.
|
||||||
|
|
||||||
|
**Syntax:** base\_dn _dn\_
|
||||||
|
|
||||||
|
Base DN to use for lookup.
|
||||||
|
|
||||||
|
**Syntax:** filter _str\_
|
||||||
|
|
||||||
|
DN lookup filter. '{username}' is replaced with the username specified
|
||||||
|
by the user.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
(&(objectClass=posixAccount)(uid={username}))
|
||||||
|
```
|
||||||
|
|
||||||
|
Example (using ActiveDirectory):
|
||||||
|
```
|
||||||
|
(&(objectCategory=Person)(memberOf=CN=user-group,OU=example,DC=example,DC=org)(sAMAccountName={username})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
(&(objectClass=Person)(mail={username}))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax:** starttls _bool_ <br>
|
||||||
|
**Default:** off
|
||||||
|
|
||||||
|
Whether to upgrade connection to TLS using STARTTLS.
|
||||||
|
|
||||||
|
**Syntax:** tls\_client { ... }
|
||||||
|
|
||||||
|
Advanced TLS client configuration. See [TLS configuration / Client](/reference/tls/#client) for details.
|
||||||
|
|
||||||
|
**Syntax:** connect\_timeout _duration_ <br>
|
||||||
|
**Default:** 1m
|
||||||
|
|
||||||
|
Timeout for initial connection to the directory server.
|
||||||
|
|
||||||
|
**Syntax:** request\_timeout _duration_ <br>
|
||||||
|
**Default:** 1m
|
||||||
|
|
||||||
|
Timeout for each request (binding, lookup).
|
44
docs/reference/auth/pam.md
Normal file
44
docs/reference/auth/pam.md
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# PAM
|
||||||
|
|
||||||
|
auth.pam module implements authentication using libpam. Alternatively it can be configured to
|
||||||
|
use helper binary like auth.external module does.
|
||||||
|
|
||||||
|
maddy should be built with libpam build tag to use this module without
|
||||||
|
'use\_helper' directive.
|
||||||
|
```
|
||||||
|
go get -tags 'libpam' ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.pam {
|
||||||
|
debug no
|
||||||
|
use_helper no
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Enable verbose logging for all modules. You don't need that unless you are
|
||||||
|
reporting a bug.
|
||||||
|
|
||||||
|
**Syntax**: use\_helper _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Use LibexecDirectory/maddy-pam-helper instead of directly calling libpam.
|
||||||
|
You need to use that if:
|
||||||
|
1. maddy is not compiled with libpam, but maddy-pam-helper is built separately.
|
||||||
|
2. maddy is running as an unprivileged user and used PAM configuration requires additional
|
||||||
|
privileges (e.g. when using system accounts).
|
||||||
|
|
||||||
|
For 2, you need to make maddy-pam-helper binary setuid, see
|
||||||
|
README.md in source tree for details.
|
||||||
|
|
||||||
|
TL;DR (assuming you have the maddy group):
|
||||||
|
```
|
||||||
|
chown root:maddy /usr/lib/maddy/maddy-pam-helper
|
||||||
|
chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-pam-helper
|
||||||
|
```
|
||||||
|
|
44
docs/reference/auth/pass_table.md
Normal file
44
docs/reference/auth/pass_table.md
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Password table
|
||||||
|
|
||||||
|
auth.pass_table module implements username:password authentication by looking up the
|
||||||
|
password hash using a table module (maddy-tables(5)). It can be used
|
||||||
|
to load user credentials from text file (via table.file module) or SQL query
|
||||||
|
(via table.sql\_table module).
|
||||||
|
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
```
|
||||||
|
auth.pass_table [block name] {
|
||||||
|
table <table config>
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Shortened variant for inline use:
|
||||||
|
```
|
||||||
|
pass_table <table> [table arguments] {
|
||||||
|
[additional table config]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example, read username:password pair from the text file:
|
||||||
|
```
|
||||||
|
smtp tcp://0.0.0.0:587 {
|
||||||
|
auth pass_table file /etc/maddy/smtp_passwd
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Password hashes
|
||||||
|
|
||||||
|
pass\_table expects the used table to contain certain structured values with
|
||||||
|
hash algorithm name, salt and other necessary parameters.
|
||||||
|
|
||||||
|
You should use 'maddyctl hash' command to generate suitable values.
|
||||||
|
See 'maddyctl hash --help' for details.
|
||||||
|
|
||||||
|
## maddyctl creds
|
||||||
|
|
||||||
|
If the underlying table is a "mutable" table (see maddy-tables(5)) then
|
||||||
|
the 'maddyctl creds' command can be used to modify the underlying tables
|
||||||
|
via pass\_table module. It will act on a "local credentials store" and will write
|
||||||
|
appropriate hash values to the table.
|
42
docs/reference/auth/plain_separate.md
Normal file
42
docs/reference/auth/plain_separate.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Separate username and password lookup
|
||||||
|
|
||||||
|
auth.plain\_separate module implements authentication using username:password pairs but can
|
||||||
|
use zero or more "table modules" (maddy-tables(5)) and one or more
|
||||||
|
authentication providers to verify credentials.
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.plain_separate {
|
||||||
|
user ...
|
||||||
|
user ...
|
||||||
|
...
|
||||||
|
pass ...
|
||||||
|
pass ...
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
How it works:
|
||||||
|
- Initial username input is normalized using PRECIS UsernameCaseMapped profile.
|
||||||
|
- Each table specified with the 'user' directive looked up using normalized
|
||||||
|
username. If match is not found in any table, authentication fails.
|
||||||
|
- Each authentication provider specified with the 'pass' directive is tried.
|
||||||
|
If authentication with all providers fails - an error is returned.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
***Syntax:*** user _table module\_
|
||||||
|
|
||||||
|
Configuration block for any module from maddy-tables(5) can be used here.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
user file /etc/maddy/allowed_users
|
||||||
|
```
|
||||||
|
|
||||||
|
***Syntax:*** pass _auth provider\_
|
||||||
|
|
||||||
|
Configuration block for any auth. provider module can be used here, even
|
||||||
|
'plain\_split' itself.
|
||||||
|
|
||||||
|
The used auth. provider must provide username:password pair-based
|
||||||
|
authentication.
|
36
docs/reference/auth/shadow.md
Normal file
36
docs/reference/auth/shadow.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# /etc/shadow
|
||||||
|
|
||||||
|
auth.shadow module implements authentication by reading /etc/shadow. Alternatively it can be
|
||||||
|
configured to use helper binary like auth.external does.
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.shadow {
|
||||||
|
debug no
|
||||||
|
use_helper no
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Enable verbose logging for all modules. You don't need that unless you are
|
||||||
|
reporting a bug.
|
||||||
|
|
||||||
|
**Syntax**: use\_helper _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Use LibexecDirectory/maddy-shadow-helper instead of directly reading /etc/shadow.
|
||||||
|
You need to use that if maddy is running as an unprivileged user
|
||||||
|
privileges (e.g. when using system accounts).
|
||||||
|
|
||||||
|
You need to make maddy-shadow-helper binary setuid, see
|
||||||
|
cmd/maddy-shadow-helper/README.md in source tree for details.
|
||||||
|
|
||||||
|
TL;DR (assuming you have maddy group):
|
||||||
|
```
|
||||||
|
chown root:maddy /usr/lib/maddy/maddy-shadow-helper
|
||||||
|
chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-shadow-helper
|
||||||
|
```
|
||||||
|
|
22
docs/reference/blob/fs.md
Normal file
22
docs/reference/blob/fs.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Filesystem
|
||||||
|
|
||||||
|
This module stores message bodies in a file system directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
storage.blob.fs {
|
||||||
|
root <directory>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
storage.blob.fs <directory>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax:** root _path_ <br>
|
||||||
|
**Default:** not set
|
||||||
|
|
||||||
|
Path to the FS directory. Must be readable and writable by the server process.
|
||||||
|
If it does not exist - it will be created (parent directory should be writable
|
||||||
|
for this). Relative paths are interpreted relatively to server state directory.
|
||||||
|
|
71
docs/reference/blob/s3.md
Normal file
71
docs/reference/blob/s3.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# Amazon S3
|
||||||
|
|
||||||
|
storage.blob.s3 module stores messages bodies in a bucket on S3-compatible storage.
|
||||||
|
|
||||||
|
```
|
||||||
|
storage.blob.s3 {
|
||||||
|
endpoint play.min.io
|
||||||
|
secure yes
|
||||||
|
access_key "Q3AM3UQ867SPQQA43P2F"
|
||||||
|
secret_key "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
|
||||||
|
bucket maddy-test
|
||||||
|
|
||||||
|
# optional
|
||||||
|
region eu-central-1
|
||||||
|
object_prefix maddy/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
storage.imapsql local_mailboxes {
|
||||||
|
...
|
||||||
|
msg_store s3 {
|
||||||
|
endpoint s3.amazonaws.com
|
||||||
|
access_key "..."
|
||||||
|
secret_key "..."
|
||||||
|
bucket maddy-messages
|
||||||
|
region us-west-2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax:** endpoint _address:port\_
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
Root S3 endpoint. e.g. s3.amazonaws.com
|
||||||
|
|
||||||
|
**Syntax:** secure _boolean_ <br>
|
||||||
|
**Default:** yes
|
||||||
|
|
||||||
|
Whether TLS should be used.
|
||||||
|
|
||||||
|
**Syntax:** access\_key _string_ <br>
|
||||||
|
**Syntax:** secret\_key _string\_
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
Static S3 credentials.
|
||||||
|
|
||||||
|
**Syntax:** bucket _name\_
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
S3 bucket name. The bucket must exist and
|
||||||
|
be read-writable.
|
||||||
|
|
||||||
|
**Syntax:** region _string_ <br>
|
||||||
|
**Default:** not set
|
||||||
|
|
||||||
|
S3 bucket location. May be called "endpoint"
|
||||||
|
in some manuals.
|
||||||
|
|
||||||
|
**Syntax:** object\_prefix _string_ <br>
|
||||||
|
**Default:** empty string
|
||||||
|
|
||||||
|
String to add to all keys stored by maddy.
|
||||||
|
|
||||||
|
Can be useful when S3 is used as a file system.
|
21
docs/reference/checks/actions.md
Normal file
21
docs/reference/checks/actions.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Check actions
|
||||||
|
|
||||||
|
When a certain check module thinks the message is "bad", it takes some actions
|
||||||
|
depending on its configuration. Most checks follow the same configuration
|
||||||
|
structure and allow following actions to be taken on check failure:
|
||||||
|
|
||||||
|
- Do nothing ('action ignore')
|
||||||
|
|
||||||
|
Useful for testing deployment of new checks. Check failures are still logged
|
||||||
|
but they have no effect on message delivery.
|
||||||
|
|
||||||
|
- Reject the message ('action reject')
|
||||||
|
|
||||||
|
Reject the message at connection time. No bounce is generated locally.
|
||||||
|
|
||||||
|
- Quarantine the message ('action quarantine')
|
||||||
|
|
||||||
|
Mark message as 'quarantined'. If message is then delivered to the local
|
||||||
|
storage, the storage backend can place the message in the 'Junk' mailbox.
|
||||||
|
Another thing to keep in mind that 'target.remote' module
|
||||||
|
will refuse to send quarantined messages.
|
87
docs/reference/checks/authorize_sender.md
Normal file
87
docs/reference/checks/authorize_sender.md
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# MAIL FROM and From authorization
|
||||||
|
|
||||||
|
Module check.authorize_sender verifies that envelope and header sender addresses belong
|
||||||
|
to the authenticated user. Address ownership is established via table
|
||||||
|
that maps each user account to a email address it is allowed to use.
|
||||||
|
There are some special cases, see user\_to\_email description below.
|
||||||
|
|
||||||
|
```
|
||||||
|
check.authorize_sender {
|
||||||
|
prepare_email identity
|
||||||
|
user_to_email identity
|
||||||
|
check_header yes
|
||||||
|
|
||||||
|
unauth_action reject
|
||||||
|
no_match_action reject
|
||||||
|
malformed_action reject
|
||||||
|
err_action reject
|
||||||
|
|
||||||
|
auth_normalize precis_casefold_email
|
||||||
|
from_normalize precis_casefold_email
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
check {
|
||||||
|
authorize_sender { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax:** user\_to\_email _table_ <br>
|
||||||
|
**Default:** identity
|
||||||
|
|
||||||
|
Table to use for lookups. Result of the lookup should contain either the
|
||||||
|
domain name, the full email address or "*" string. If it is just domain - user
|
||||||
|
will be allowed to use any mailbox within a domain as a sender address.
|
||||||
|
If result contains "*" - user will be allowed to use any address.
|
||||||
|
|
||||||
|
**Syntax:** check\_header _boolean_ <br>
|
||||||
|
**Default:** yes
|
||||||
|
|
||||||
|
Whether to verify header sender in addition to envelope.
|
||||||
|
|
||||||
|
Either Sender or From field value should match the
|
||||||
|
authorization identity.
|
||||||
|
|
||||||
|
**Syntax:** unauth\_action _action_ <br>
|
||||||
|
**Default:** reject
|
||||||
|
|
||||||
|
What to do if the user is not authenticated at all.
|
||||||
|
|
||||||
|
**Syntax:** no\_match\_action _action_ <br>
|
||||||
|
**Default:** reject
|
||||||
|
|
||||||
|
What to do if user is not allowed to use the sender address specified.
|
||||||
|
|
||||||
|
**Syntax:** malformed\_action _action_ <br>
|
||||||
|
**Default:** reject
|
||||||
|
|
||||||
|
What to do if From or Sender header fields contain malformed values.
|
||||||
|
|
||||||
|
**Syntax:** err\_action _action_ <br>
|
||||||
|
**Default:** reject
|
||||||
|
|
||||||
|
What to do if error happens during prepare\_email or user\_to\_email lookup.
|
||||||
|
|
||||||
|
**Syntax:** auth\_normalize _action_ <br>
|
||||||
|
**Default:** precis\_casefold\_email
|
||||||
|
|
||||||
|
Normalization function to apply to authorization username before
|
||||||
|
further processing.
|
||||||
|
|
||||||
|
Available options:
|
||||||
|
- precis\_casefold\_email PRECIS UsernameCaseMapped profile + U-labels form for domain
|
||||||
|
- precis\_casefold PRECIS UsernameCaseMapped profile for the entire string
|
||||||
|
- precis\_email PRECIS UsernameCasePreserved profile + U-labels form for domain
|
||||||
|
- precis PRECIS UsernameCasePreserved profile for the entire string
|
||||||
|
- casefold Convert to lower case
|
||||||
|
- noop Nothing
|
||||||
|
|
||||||
|
**Syntax:** from\_normalize _action_ <br>
|
||||||
|
**Default:** precis\_casefold\_email
|
||||||
|
|
||||||
|
Normalization function to apply to email addresses before
|
||||||
|
further processing.
|
||||||
|
|
||||||
|
Available options are same as for auth\_normalize.
|
131
docs/reference/checks/command.md
Normal file
131
docs/reference/checks/command.md
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
# System command filter
|
||||||
|
|
||||||
|
This module executes an arbitrary system command during a specified stage of
|
||||||
|
checks execution.
|
||||||
|
|
||||||
|
```
|
||||||
|
command executable_name arg0 arg1 ... {
|
||||||
|
run_on body
|
||||||
|
|
||||||
|
code 1 reject
|
||||||
|
code 2 quarantine
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
The module arguments specify the command to run. If the first argument is not
|
||||||
|
an absolute path, it is looked up in the Libexec Directory (/usr/lib/maddy on
|
||||||
|
Linux) and in $PATH (in that ordering). Note that no additional handling
|
||||||
|
of arguments is done, especially, the command is executed directly, not via the
|
||||||
|
system shell.
|
||||||
|
|
||||||
|
There is a set of special strings that are replaced with the corresponding
|
||||||
|
message-specific values:
|
||||||
|
|
||||||
|
- {source\_ip}
|
||||||
|
|
||||||
|
IPv4/IPv6 address of the sending MTA.
|
||||||
|
|
||||||
|
- {source\_host}
|
||||||
|
|
||||||
|
Hostname of the sending MTA, from the HELO/EHLO command.
|
||||||
|
|
||||||
|
- {source\_rdns}
|
||||||
|
|
||||||
|
PTR record of the sending MTA IP address.
|
||||||
|
|
||||||
|
- {msg\_id}
|
||||||
|
|
||||||
|
Internal message identifier. Unique for each delivery.
|
||||||
|
|
||||||
|
- {auth\_user}
|
||||||
|
|
||||||
|
Client username, if authenticated using SASL PLAIN
|
||||||
|
|
||||||
|
- {sender}
|
||||||
|
|
||||||
|
Message sender address, as specified in the MAIL FROM SMTP command.
|
||||||
|
|
||||||
|
- {rcpts}
|
||||||
|
|
||||||
|
List of accepted recipient addresses, including the currently handled
|
||||||
|
one.
|
||||||
|
|
||||||
|
- {address}
|
||||||
|
|
||||||
|
Currently handled address. This is a recipient address if the command
|
||||||
|
is called during RCPT TO command handling ('run\_on rcpt') or a sender
|
||||||
|
address if the command is called during MAIL FROM command handling ('run\_on
|
||||||
|
sender').
|
||||||
|
|
||||||
|
|
||||||
|
If value is undefined (e.g. {source\_ip} for a message accepted over a Unix
|
||||||
|
socket) or unavailable (the command is executed too early), the placeholder
|
||||||
|
is replaced with an empty string. Note that it can not remove the argument.
|
||||||
|
E.g. -i {source\_ip} will not become just -i, it will be -i ""
|
||||||
|
|
||||||
|
Undefined placeholders are not replaced.
|
||||||
|
|
||||||
|
## Command stdout
|
||||||
|
|
||||||
|
The command stdout must be either empty or contain a valid RFC 5322 header.
|
||||||
|
If it contains a byte stream that does not look a valid header, the message
|
||||||
|
will be rejected with a temporary error.
|
||||||
|
|
||||||
|
The header from stdout will be **prepended** to the message header.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: run\_on conn|sender|rcpt|body <br>
|
||||||
|
**Default**: body
|
||||||
|
|
||||||
|
When to run the command. This directive also affects the information visible
|
||||||
|
for the message.
|
||||||
|
|
||||||
|
- conn
|
||||||
|
|
||||||
|
Run before the sender address (MAIL FROM) is handled.
|
||||||
|
|
||||||
|
**Stdin**: Empty <br>
|
||||||
|
**Available placeholders**: {source\_ip}, {source\_host}, {msg\_id}, {auth\_user}.
|
||||||
|
|
||||||
|
- sender
|
||||||
|
|
||||||
|
Run during sender address (MAIL FROM) handling.
|
||||||
|
|
||||||
|
**Stdin**: Empty <br>
|
||||||
|
**Available placeholders**: conn placeholders + {sender}, {address}.
|
||||||
|
|
||||||
|
The {address} placeholder contains the MAIL FROM address.
|
||||||
|
|
||||||
|
- rcpt
|
||||||
|
|
||||||
|
Run during recipient address (RCPT TO) handling. The command is executed
|
||||||
|
once for each RCPT TO command, even if the same recipient is specified
|
||||||
|
multiple times.
|
||||||
|
|
||||||
|
**Stdin**: Empty <br>
|
||||||
|
**Available placeholders**: sender placeholders + {rcpts}.
|
||||||
|
|
||||||
|
The {address} placeholder contains the recipient address.
|
||||||
|
|
||||||
|
- body
|
||||||
|
|
||||||
|
Run during message body handling.
|
||||||
|
|
||||||
|
**Stdin**: The message header + body <br>
|
||||||
|
**Available placeholders**: all except for {address}.
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
code _integer_ ignore <br>
|
||||||
|
code _integer_ quarantine <br>
|
||||||
|
code _integer_ reject [SMTP code] [SMTP enhanced code] [SMTP message]
|
||||||
|
|
||||||
|
This directives specified the mapping from the command exit code _integer_ to
|
||||||
|
the message pipeline action.
|
||||||
|
|
||||||
|
Two codes are defined implicitly, exit code 1 causes the message to be rejected
|
||||||
|
with a permanent error, exit code 2 causes the message to be quarantined. Both
|
||||||
|
action can be overriden using the 'code' directive.
|
||||||
|
|
55
docs/reference/checks/dkim.md
Normal file
55
docs/reference/checks/dkim.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# DKIM
|
||||||
|
|
||||||
|
This is the check module that performs verification of the DKIM signatures
|
||||||
|
present on the incoming messages.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
check.dkim {
|
||||||
|
debug no
|
||||||
|
required_fields From Subject
|
||||||
|
allow_body_subset no
|
||||||
|
no_sig_action ignore
|
||||||
|
broken_sig_action ignore
|
||||||
|
fail_open no
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Log both successfull and unsuccessful check executions instead of just
|
||||||
|
unsuccessful.
|
||||||
|
|
||||||
|
**Syntax**: required\_fields _string..._ <br>
|
||||||
|
**Default**: From Subject
|
||||||
|
|
||||||
|
Header fields that should be included in each signature. If signature
|
||||||
|
lacks any field listed in that directive, it will be considered invalid.
|
||||||
|
|
||||||
|
Note that From is always required to be signed, even if it is not included in
|
||||||
|
this directive.
|
||||||
|
|
||||||
|
**Syntax**: no\_sig\_action _action_ <br>
|
||||||
|
**Default**: ignore (recommended by RFC 6376)
|
||||||
|
|
||||||
|
Action to take when message without any signature is received.
|
||||||
|
|
||||||
|
Note that DMARC policy of the sender domain can request more strict handling of
|
||||||
|
missing DKIM signatures.
|
||||||
|
|
||||||
|
**Syntax**: broken\_sig\_action _action_ <br>
|
||||||
|
**Default**: ignore (recommended by RFC 6376)
|
||||||
|
|
||||||
|
Action to take when there are not valid signatures in a message.
|
||||||
|
|
||||||
|
Note that DMARC policy of the sender domain can request more strict handling of
|
||||||
|
broken DKIM signatures.
|
||||||
|
|
||||||
|
**Syntax**: fail\_open _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Whether to accept the message if a temporary error occurs during DKIM
|
||||||
|
verification. Rejecting the message with a 4xx code will require the sender
|
||||||
|
to resend it later in a hope that the problem will be resolved.
|
156
docs/reference/checks/dnsbl.md
Normal file
156
docs/reference/checks/dnsbl.md
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
# DNSBL lookup
|
||||||
|
|
||||||
|
The check.dnsbl module implements checking of source IP and hostnames against a set
|
||||||
|
of DNS-based Blackhole lists (DNSBLs).
|
||||||
|
|
||||||
|
Its configuration consists of module configuration directives and a set
|
||||||
|
of blocks specifing lists to use and kind of lookups to perform on them.
|
||||||
|
|
||||||
|
```
|
||||||
|
check.dnsbl {
|
||||||
|
debug no
|
||||||
|
check_early no
|
||||||
|
|
||||||
|
quarantine_threshold 1
|
||||||
|
reject_threshold 1
|
||||||
|
|
||||||
|
# Lists configuration example.
|
||||||
|
dnsbl.example.org {
|
||||||
|
client_ipv4 yes
|
||||||
|
client_ipv6 no
|
||||||
|
ehlo no
|
||||||
|
mailfrom no
|
||||||
|
score 1
|
||||||
|
}
|
||||||
|
hsrbl.example.org {
|
||||||
|
client_ipv4 no
|
||||||
|
client_ipv6 no
|
||||||
|
ehlo yes
|
||||||
|
mailfrom yes
|
||||||
|
score 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
Arguments specify the list of IP-based BLs to use.
|
||||||
|
|
||||||
|
The following configurations are equivalent.
|
||||||
|
|
||||||
|
```
|
||||||
|
check {
|
||||||
|
dnsbl dnsbl.example.org dnsbl2.example.org
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
check {
|
||||||
|
dnsbl {
|
||||||
|
dnsbl.example.org dnsbl2.example.org {
|
||||||
|
client_ipv4 yes
|
||||||
|
client_ipv6 no
|
||||||
|
ehlo no
|
||||||
|
mailfrom no
|
||||||
|
score 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: check\_early _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Check BLs before mail delivery starts and silently reject blacklisted clients.
|
||||||
|
|
||||||
|
For this to work correctly, check should not be used in source/destination
|
||||||
|
pipeline block.
|
||||||
|
|
||||||
|
In particular, this means:
|
||||||
|
- No logging is done for rejected messages.
|
||||||
|
- No action is taken if quarantine\_threshold is hit, only reject\_threshold
|
||||||
|
applies.
|
||||||
|
- defer\_sender\_reject from SMTP configuration takes no effect.
|
||||||
|
- MAIL FROM is not checked, even if specified.
|
||||||
|
|
||||||
|
If you often get hit by spam attacks, it is recommended to enable this
|
||||||
|
setting to save server resources.
|
||||||
|
|
||||||
|
**Syntax**: quarantine\_threshold _integer_ <br>
|
||||||
|
**Default**: 1
|
||||||
|
|
||||||
|
DNSBL score needed (equals-or-higher) to quarantine the message.
|
||||||
|
|
||||||
|
**Syntax**: reject\_threshold _integer_ <br>
|
||||||
|
**Default**: 9999
|
||||||
|
|
||||||
|
DNSBL score needed (equals-or-higher) to reject the message.
|
||||||
|
|
||||||
|
## List configuration
|
||||||
|
|
||||||
|
```
|
||||||
|
dnsbl.example.org dnsbl.example.com {
|
||||||
|
client_ipv4 yes
|
||||||
|
client_ipv6 no
|
||||||
|
ehlo no
|
||||||
|
mailfrom no
|
||||||
|
responses 127.0.0.1/24
|
||||||
|
score 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Directive name and arguments specify the actual DNS zone to query when checking
|
||||||
|
the list. Using multiple arguments is equivalent to specifying the same
|
||||||
|
configuration separately for each list.
|
||||||
|
|
||||||
|
**Syntax**: client\_ipv4 _boolean_ <br>
|
||||||
|
**Default**: yes
|
||||||
|
|
||||||
|
Whether to check address of the IPv4 clients against the list.
|
||||||
|
|
||||||
|
**Syntax**: client\_ipv6 _boolean_ <br>
|
||||||
|
**Default**: yes
|
||||||
|
|
||||||
|
Whether to check address of the IPv6 clients against the list.
|
||||||
|
|
||||||
|
**Syntax**: ehlo _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Whether to check hostname specified n the HELO/EHLO command
|
||||||
|
against the list.
|
||||||
|
|
||||||
|
This works correctly only with domain-based DNSBLs.
|
||||||
|
|
||||||
|
**Syntax**: mailfrom _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Whether to check domain part of the MAIL FROM address against the list.
|
||||||
|
|
||||||
|
This works correctly only with domain-based DNSBLs.
|
||||||
|
|
||||||
|
**Syntax**: responses _cidr|ip..._ <br>
|
||||||
|
**Default**: 127.0.0.1/24
|
||||||
|
|
||||||
|
IP networks (in CIDR notation) or addresses to permit in list lookup results.
|
||||||
|
Addresses not matching any entry in this directives will be ignored.
|
||||||
|
|
||||||
|
**Syntax**: score _integer_ <br>
|
||||||
|
**Default**: 1
|
||||||
|
|
||||||
|
Score value to add for the message if it is listed.
|
||||||
|
|
||||||
|
If sum of list scores is equals or higher than quarantine\_threshold, the
|
||||||
|
message will be quarantined.
|
||||||
|
|
||||||
|
If sum of list scores is equals or higher than rejected\_threshold, the message
|
||||||
|
will be rejected.
|
||||||
|
|
||||||
|
It is possible to specify a negative value to make list act like a whitelist
|
||||||
|
and override results of other blocklists.
|
47
docs/reference/checks/milter.md
Normal file
47
docs/reference/checks/milter.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Milter client
|
||||||
|
|
||||||
|
The 'milter' implements subset of Sendmail's milter protocol that can be used
|
||||||
|
to integrate external software with maddy.
|
||||||
|
maddy implements version 6 of the protocol, older versions are
|
||||||
|
not supported.
|
||||||
|
|
||||||
|
Notable limitations of protocol implementation in maddy include:
|
||||||
|
1. Changes of envelope sender address are not supported
|
||||||
|
2. Removal and addition of envelope recipients is not supported
|
||||||
|
3. Removal and replacement of header fields is not supported
|
||||||
|
4. Headers fields can be inserted only on top
|
||||||
|
5. Milter does not receive some "macros" provided by sendmail.
|
||||||
|
|
||||||
|
Restrictions 1 and 2 are inherent to the maddy checks interface and cannot be
|
||||||
|
removed without major changes to it. Restrictions 3, 4 and 5 are temporary due to
|
||||||
|
incomplete implementation.
|
||||||
|
|
||||||
|
```
|
||||||
|
check.milter {
|
||||||
|
endpoint <endpoint>
|
||||||
|
fail_open false
|
||||||
|
}
|
||||||
|
|
||||||
|
milter <endpoint>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
When defined inline, the first argument specifies endpoint to access milter
|
||||||
|
via. See below.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
***Syntax:*** endpoint _scheme://path_ <br>
|
||||||
|
***Default:*** not set
|
||||||
|
|
||||||
|
Specifies milter protocol endpoint to use.
|
||||||
|
The endpoit is specified in standard URL-like format:
|
||||||
|
'tcp://127.0.0.1:6669' or 'unix:///var/lib/milter/filter.sock'
|
||||||
|
|
||||||
|
***Syntax:*** fail\_open _boolean_ <br>
|
||||||
|
***Default:*** false
|
||||||
|
|
||||||
|
Toggles behavior on milter I/O errors. If false ("fail closed") - message is
|
||||||
|
rejected with temporary error code. If true ("fail open") - check is skipped.
|
||||||
|
|
43
docs/reference/checks/misc.md
Normal file
43
docs/reference/checks/misc.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Misc checks
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
Following directives are defined for all modules listed below.
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
fail\_action ignore <br>
|
||||||
|
fail\_action reject <br>
|
||||||
|
fail\_action quarantine <br>
|
||||||
|
**Default**: quarantine
|
||||||
|
|
||||||
|
Action to take when check fails. See Check actions for details.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Log both sucessfull and unsucessfull check executions instead of just
|
||||||
|
unsucessfull.
|
||||||
|
|
||||||
|
## require\_mx\_record
|
||||||
|
|
||||||
|
Check that domain in MAIL FROM command does have a MX record and none of them
|
||||||
|
are "null" (contain a single dot as the host).
|
||||||
|
|
||||||
|
By default, quarantines messages coming from servers missing MX records,
|
||||||
|
use 'fail\_action' directive to change that.
|
||||||
|
|
||||||
|
## require\_matching\_rdns
|
||||||
|
|
||||||
|
Check that source server IP does have a PTR record point to the domain
|
||||||
|
specified in EHLO/HELO command.
|
||||||
|
|
||||||
|
By default, quarantines messages coming from servers with mismatched or missing
|
||||||
|
PTR record, use 'fail\_action' directive to change that.
|
||||||
|
|
||||||
|
## require\_tls
|
||||||
|
|
||||||
|
Check that the source server is connected via TLS; either directly, or by using
|
||||||
|
the STARTTLS command.
|
||||||
|
|
||||||
|
By default, rejects messages coming from unencrypted servers. Use the
|
||||||
|
'fail\_action' directive to change that.
|
79
docs/reference/checks/rspamd.md
Normal file
79
docs/reference/checks/rspamd.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# rspamd
|
||||||
|
|
||||||
|
The 'rspamd' module implements message filtering by contacting the rspamd
|
||||||
|
server via HTTP API.
|
||||||
|
|
||||||
|
```
|
||||||
|
check.rspamd {
|
||||||
|
tls_client { ... }
|
||||||
|
api_path http://127.0.0.1:11333
|
||||||
|
settings_id whatever
|
||||||
|
tag maddy
|
||||||
|
hostname mx.example.org
|
||||||
|
io_error_action ignore
|
||||||
|
error_resp_action ignore
|
||||||
|
add_header_action quarantine
|
||||||
|
rewrite_subj_action quarantine
|
||||||
|
flags pass_all
|
||||||
|
}
|
||||||
|
|
||||||
|
rspamd http://127.0.0.1:11333
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax:** tls\_client { ... } <br>
|
||||||
|
**Default:** not set
|
||||||
|
|
||||||
|
Configure TLS client if HTTPS is used. See [TLS configuration / Client](/reference/tls/#client) for details.
|
||||||
|
|
||||||
|
**Syntax:** api\_path _url_ <br>
|
||||||
|
**Default:** http://127.0.0.1:11333
|
||||||
|
|
||||||
|
URL of HTTP API endpoint. Supports both HTTP and HTTPS and can include
|
||||||
|
path element.
|
||||||
|
|
||||||
|
**Syntax:** settings\_id _string_ <br>
|
||||||
|
**Default:** not set
|
||||||
|
|
||||||
|
Settings ID to pass to the server.
|
||||||
|
|
||||||
|
**Syntax:** tag _string_ <br>
|
||||||
|
**Default:** maddy
|
||||||
|
|
||||||
|
Value to send in MTA-Tag header field.
|
||||||
|
|
||||||
|
**Syntax:** hostname _string_ <br>
|
||||||
|
**Default:** value of global directive
|
||||||
|
|
||||||
|
Value to send in MTA-Name header field.
|
||||||
|
|
||||||
|
**Syntax:** io\_error\_action _action_ <br>
|
||||||
|
**Default:** ignore
|
||||||
|
|
||||||
|
Action to take in case of inability to contact the rspamd server.
|
||||||
|
|
||||||
|
**Syntax:** error\_resp\_action _action_ <br>
|
||||||
|
**Default:** ignore
|
||||||
|
|
||||||
|
Action to take in case of 5xx or 4xx response received from the rspamd server.
|
||||||
|
|
||||||
|
**Syntax:** add\_header\_action _action_ <br>
|
||||||
|
**Default:** quarantine
|
||||||
|
|
||||||
|
Action to take when rspamd requests to "add header".
|
||||||
|
|
||||||
|
X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
|
||||||
|
|
||||||
|
**Syntax:** rewrite\_subj\_action _action_ <br>
|
||||||
|
**Default:** quarantine
|
||||||
|
|
||||||
|
Action to take when rspamd requests to "rewrite subject".
|
||||||
|
|
||||||
|
X-Spam-Flag and X-Spam-Score are added to the header irregardless of value.
|
||||||
|
|
||||||
|
**Syntax:** flags _string list..._ <br>
|
||||||
|
**Default:** pass\_all
|
||||||
|
|
||||||
|
Flags to pass to the rspamd server.
|
||||||
|
See [https://rspamd.com/doc/architecture/protocol.html](https://rspamd.com/doc/architecture/protocol.html) for details.
|
83
docs/reference/checks/spf.md
Normal file
83
docs/reference/checks/spf.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# SPF
|
||||||
|
|
||||||
|
check.spf the check module that verifies whether IP address of the client is
|
||||||
|
authorized to send messages for domain in MAIL FROM address.
|
||||||
|
|
||||||
|
SPF statuses are mapped to maddy check actions in a way
|
||||||
|
specified by \*_action directives. By default, SPF failure
|
||||||
|
results in the message being quarantined and errors (both permanent and
|
||||||
|
temporary) cause message to be rejected.
|
||||||
|
Authentication-Results field is generated irregardless of status.
|
||||||
|
|
||||||
|
## DMARC override
|
||||||
|
|
||||||
|
It is recommended by the DMARC standard to don't fail delivery based solely on
|
||||||
|
SPF policy and always check DMARC policy and take action based on it.
|
||||||
|
|
||||||
|
If enforce\_early is no, check.spf module will not take any action on SPF
|
||||||
|
policy failure if sender domain does have a DMARC record with 'quarantine' or
|
||||||
|
'reject' policy. Instead it will rely on DMARC support to take necesary
|
||||||
|
actions using SPF results as an input.
|
||||||
|
|
||||||
|
Disabling enforce\_early without enabling DMARC support will make SPF policies
|
||||||
|
no-op and is considered insecure.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
check.spf {
|
||||||
|
debug no
|
||||||
|
enforce_early no
|
||||||
|
fail_action quarantine
|
||||||
|
softfail_action ignore
|
||||||
|
permerr_action reject
|
||||||
|
temperr_action reject
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging for check.spf.
|
||||||
|
|
||||||
|
**Syntax**: enforce\_early _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Make policy decision on MAIL FROM stage (before the message body is received).
|
||||||
|
This makes it impossible to apply DMARC override (see above).
|
||||||
|
|
||||||
|
**Syntax**: none\_action reject|qurantine|ignore <br>
|
||||||
|
**Default**: ignore
|
||||||
|
|
||||||
|
Action to take when SPF policy evaluates to a 'none' result.
|
||||||
|
|
||||||
|
See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of
|
||||||
|
SPF results.
|
||||||
|
|
||||||
|
**Syntax**: neutral\_action reject|qurantine|ignore <br>
|
||||||
|
**Default**: ignore
|
||||||
|
|
||||||
|
Action to take when SPF policy evaluates to a 'neutral' result.
|
||||||
|
|
||||||
|
See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of
|
||||||
|
SPF results.
|
||||||
|
|
||||||
|
**Syntax**: fail\_action reject|qurantine|ignore <br>
|
||||||
|
**Default**: quarantine
|
||||||
|
|
||||||
|
Action to take when SPF policy evaluates to a 'fail' result.
|
||||||
|
|
||||||
|
**Syntax**: softfail\_action reject|qurantine|ignore <br>
|
||||||
|
**Default**: ignore
|
||||||
|
|
||||||
|
Action to take when SPF policy evaluates to a 'softfail' result.
|
||||||
|
|
||||||
|
**Syntax**: permerr\_action reject|qurantine|ignore <br>
|
||||||
|
**Default**: reject
|
||||||
|
|
||||||
|
Action to take when SPF policy evaluates to a 'permerror' result.
|
||||||
|
|
||||||
|
**Syntax**: temperr\_action reject|qurantine|ignore <br>
|
||||||
|
**Default**: reject
|
||||||
|
|
||||||
|
Action to take when SPF policy evaluates to a 'temperror' result.
|
|
@ -1,6 +1,7 @@
|
||||||
maddy-config(5) "maddy mail server" "maddy reference documentation"
|
# Configuration files syntax
|
||||||
|
|
||||||
; TITLE Configuration files syntax
|
**Note:** This file is a technical document describing how
|
||||||
|
maddy parses configuration files.
|
||||||
|
|
||||||
Configuration consists of newline-delimited "directives". Each directive can
|
Configuration consists of newline-delimited "directives". Each directive can
|
||||||
have zero or more arguments.
|
have zero or more arguments.
|
||||||
|
@ -185,7 +186,7 @@ Also note that the following is not valid, unlike Duration values syntax:
|
||||||
|
|
||||||
Maddy configuration uses URL-like syntax to specify network addresses.
|
Maddy configuration uses URL-like syntax to specify network addresses.
|
||||||
|
|
||||||
- unix://file_path
|
- unix://file\_path
|
||||||
Unix domain socket. Relative paths are relative to runtime directory
|
Unix domain socket. Relative paths are relative to runtime directory
|
||||||
(/run/maddy).
|
(/run/maddy).
|
||||||
|
|
||||||
|
@ -202,3 +203,4 @@ using "dummy" name. It can act as a delivery target or auth.
|
||||||
provider. In the latter case, it will accept any credentials, allowing any
|
provider. In the latter case, it will accept any credentials, allowing any
|
||||||
client to authenticate using any username and password (use with care!).
|
client to authenticate using any username and password (use with care!).
|
||||||
|
|
||||||
|
|
65
docs/reference/endpoints/imap.md
Normal file
65
docs/reference/endpoints/imap.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# IMAP4rev1 endpoint
|
||||||
|
|
||||||
|
Module 'imap' is a listener that implements IMAP4rev1 protocol and provides
|
||||||
|
access to local messages storage specified by 'storage' directive.
|
||||||
|
|
||||||
|
In most cases, local storage modules will auto-create accounts when they are
|
||||||
|
accessed via IMAP. This relies on authentication provider used by IMAP endpoint
|
||||||
|
to provide what essentially is access control. There is a caveat, however: this
|
||||||
|
auto-creation will not happen when delivering incoming messages via SMTP as
|
||||||
|
there is no authentication to confirm that this account should indeed be
|
||||||
|
created.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
imap tcp://0.0.0.0:143 tls://0.0.0.0:993 {
|
||||||
|
tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
|
||||||
|
io_debug no
|
||||||
|
debug no
|
||||||
|
insecure_auth no
|
||||||
|
auth pam
|
||||||
|
storage &local_mailboxes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: tls _certificate\_path_ _key\_path_ { ... } <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
TLS certificate & key to use. Fine-tuning of other TLS properties is possible
|
||||||
|
by specifing a configuration block and options inside it:
|
||||||
|
```
|
||||||
|
tls cert.crt key.key {
|
||||||
|
protocols tls1.2 tls1.3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See [TLS configuration / Server](/reference/tls/#server-side) for details.
|
||||||
|
|
||||||
|
**Syntax**: io\_debug _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Write all commands and responses to stderr.
|
||||||
|
|
||||||
|
**Syntax**: io\_errors _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Log I/O errors.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: insecure\_auth _boolean_ <br>
|
||||||
|
**Default**: no (yes if TLS is disabled)
|
||||||
|
|
||||||
|
**Syntax**: auth _module\_reference\_
|
||||||
|
|
||||||
|
Use the specified module for authentication.
|
||||||
|
**Required.**
|
||||||
|
|
||||||
|
**Syntax**: storage _module\_reference\_
|
||||||
|
|
||||||
|
Use the specified module for message storage.
|
||||||
|
**Required.**
|
265
docs/reference/endpoints/smtp.md
Normal file
265
docs/reference/endpoints/smtp.md
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
# SMTP/LMTP/Submission endpoint
|
||||||
|
|
||||||
|
Module 'smtp' is a listener that implements ESMTP protocol with optional
|
||||||
|
authentication, LMTP and Submission support. Incoming messages are processed in
|
||||||
|
accordance with pipeline rules (explained in Message pipeline section below).
|
||||||
|
|
||||||
|
```
|
||||||
|
smtp tcp://0.0.0.0:25 {
|
||||||
|
hostname example.org
|
||||||
|
tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
|
||||||
|
io_debug no
|
||||||
|
debug no
|
||||||
|
insecure_auth no
|
||||||
|
read_timeout 10m
|
||||||
|
write_timeout 1m
|
||||||
|
max_message_size 32M
|
||||||
|
max_header_size 1M
|
||||||
|
auth pam
|
||||||
|
defer_sender_reject yes
|
||||||
|
dmarc yes
|
||||||
|
smtp_max_line_length 4000
|
||||||
|
limits {
|
||||||
|
endpoint rate 10
|
||||||
|
endpoint concurrency 500
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example pipeline ocnfiguration.
|
||||||
|
destination example.org {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
default_destination {
|
||||||
|
reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: hostname _string_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Server name to use in SMTP banner.
|
||||||
|
|
||||||
|
```
|
||||||
|
220 example.org ESMTP Service Ready
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: tls _certificate\_path_ _key\_path_ { ... } <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
TLS certificate & key to use. Fine-tuning of other TLS properties is possible
|
||||||
|
by specifing a configuration block and options inside it:
|
||||||
|
```
|
||||||
|
tls cert.crt key.key {
|
||||||
|
protocols tls1.2 tls1.3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See [TLS configuration / Server](/reference/tls/#server-side) for details.
|
||||||
|
|
||||||
|
|
||||||
|
**Syntax**: io\_debug _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Write all commands and responses to stderr.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: insecure\_auth _boolean_ <br>
|
||||||
|
**Default**: no (yes if TLS is disabled)
|
||||||
|
|
||||||
|
Allow plain-text authentication over unencrypted connections. Not recommended!
|
||||||
|
|
||||||
|
**Syntax**: read\_timeout _duration_ <br>
|
||||||
|
**Default**: 10m
|
||||||
|
|
||||||
|
I/O read timeout.
|
||||||
|
|
||||||
|
**Syntax**: write\_timeout _duration_ <br>
|
||||||
|
**Default**: 1m
|
||||||
|
|
||||||
|
I/O write timeout.
|
||||||
|
|
||||||
|
**Syntax**: max\_message\_size _size_ <br>
|
||||||
|
**Default**: 32M
|
||||||
|
|
||||||
|
Limit the size of incoming messages to 'size'.
|
||||||
|
|
||||||
|
**Syntax**: max\_header\_size _size_ <br>
|
||||||
|
**Default**: 1M
|
||||||
|
|
||||||
|
Limit the size of incoming message headers to 'size'.
|
||||||
|
|
||||||
|
**Syntax**: auth _module\_reference_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Use the specified module for authentication.
|
||||||
|
|
||||||
|
**Syntax**: defer\_sender\_reject _boolean_ <br>
|
||||||
|
**Default**: yes
|
||||||
|
|
||||||
|
Apply sender-based checks and routing logic when first RCPT TO command
|
||||||
|
is received. This allows maddy to log recipient address of the rejected
|
||||||
|
message and also improves interoperability with (improperly implemented)
|
||||||
|
clients that don't expect an error early in session.
|
||||||
|
|
||||||
|
**Syntax**: max\_logged\_rcpt\_errors _integer_ <br>
|
||||||
|
**Default**: 5
|
||||||
|
|
||||||
|
Amount of RCPT-time errors that should be logged. Further errors will be
|
||||||
|
handled silently. This is to prevent log flooding during email dictonary
|
||||||
|
attacks (address probing).
|
||||||
|
|
||||||
|
**Syntax**: max\_received _integer_ <br>
|
||||||
|
**Default**: 50
|
||||||
|
|
||||||
|
Max. amount of Received header fields in the message header. If the incoming
|
||||||
|
message has more fields than this number, it will be rejected with the permanent error
|
||||||
|
5.4.6 ("Routing loop detected").
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
buffer ram <br>
|
||||||
|
buffer fs _[path]_ <br>
|
||||||
|
buffer auto _max\_size_ _[path]_ <br>
|
||||||
|
**Default**: auto 1M StateDirectory/buffer
|
||||||
|
|
||||||
|
Temporary storage to use for the body of accepted messages.
|
||||||
|
|
||||||
|
- ram
|
||||||
|
|
||||||
|
Store the body in RAM.
|
||||||
|
|
||||||
|
- fs
|
||||||
|
|
||||||
|
Write out the message to the FS and read it back as needed.
|
||||||
|
_path_ can be omitted and defaults to StateDirectory/buffer.
|
||||||
|
|
||||||
|
- auto
|
||||||
|
|
||||||
|
Store message bodies smaller than _max\_size_ entirely in RAM, otherwise write
|
||||||
|
them out to the FS.
|
||||||
|
_path_ can be omitted and defaults to StateDirectory/buffer.
|
||||||
|
|
||||||
|
**Syntax**: smtp\_max\_line\_length _integer_ <br>
|
||||||
|
**Default**: 4000
|
||||||
|
|
||||||
|
The maximum line length allowed in the SMTP input stream. If client sends a
|
||||||
|
longer line - connection will be closed and message (if any) will be rejected
|
||||||
|
with a permanent error.
|
||||||
|
|
||||||
|
RFC 5321 has the recommended limit of 998 bytes. Servers are not required
|
||||||
|
to handle longer lines correctly but some senders may produce them.
|
||||||
|
|
||||||
|
Unless BDAT extension is used by the sender, this limitation also applies to
|
||||||
|
the message body.
|
||||||
|
|
||||||
|
**Syntax**: dmarc _boolean_ <br>
|
||||||
|
**Default**: yes
|
||||||
|
|
||||||
|
Enforce sender's DMARC policy. Due to implementation limitations, it is not a
|
||||||
|
check module.
|
||||||
|
|
||||||
|
**NOTE**: Report generation is not implemented now.
|
||||||
|
|
||||||
|
**NOTE**: DMARC needs SPF and DKIM checks to function correctly.
|
||||||
|
Without these, DMARC check will not run.
|
||||||
|
|
||||||
|
## Rate & concurrency limiting
|
||||||
|
|
||||||
|
**Syntax**: limits _config block_ <br>
|
||||||
|
**Default**: no limits
|
||||||
|
|
||||||
|
This allows configuring a set of message flow restrictions including
|
||||||
|
max. concurrency and rate per-endpoint, per-source, per-destination.
|
||||||
|
|
||||||
|
Limits are specified as directives inside the block:
|
||||||
|
```
|
||||||
|
limits {
|
||||||
|
all rate 20
|
||||||
|
destination concurrency 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported limits:
|
||||||
|
|
||||||
|
- Rate limit
|
||||||
|
|
||||||
|
**Syntax**: _scope_ rate _burst_ _[period]_ <br>
|
||||||
|
Restrict the amount of messages processed in _period_ to _burst_ messages.
|
||||||
|
If period is not specified, 1 second is used.
|
||||||
|
|
||||||
|
- Concurrency limit
|
||||||
|
|
||||||
|
**Syntax**: _scope_ concurrency _max_ <br>
|
||||||
|
Restrict the amount of messages processed in parallel to _max\_.
|
||||||
|
|
||||||
|
For each supported limitation, _scope_ determines whether it should be applied
|
||||||
|
for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or
|
||||||
|
per-recipient domain ("destination"). Having a scope other than "all" means
|
||||||
|
that the restriction will be enforced independently for each group determined
|
||||||
|
by scope. E.g. "ip rate 20" means that the same IP cannot send more than 20
|
||||||
|
messages in a scond. "destination concurrency 5" means that no more than 5
|
||||||
|
messages can be sent in parallel to a single domain.
|
||||||
|
|
||||||
|
**Note**: At the moment, SMTP endpoint on its own does not support per-recipient
|
||||||
|
limits. They will be no-op. If you want to enforce a per-recipient restriction
|
||||||
|
on outbound messages, do so using 'limits' directive for the 'table.remote' module
|
||||||
|
|
||||||
|
It is possible to share limit counters between multiple endpoints (or any other
|
||||||
|
modules). To do so define a top-level configuration block for module "limits"
|
||||||
|
and reference it where needed using standard & syntax. E.g.
|
||||||
|
```
|
||||||
|
limits inbound_limits {
|
||||||
|
all rate 20
|
||||||
|
}
|
||||||
|
|
||||||
|
smtp smtp://0.0.0.0:25 {
|
||||||
|
limits &inbound_limits
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
submission tls://0.0.0.0:465 {
|
||||||
|
limits &inbound_limits
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Using an "all rate" restriction in such way means that no more than 20
|
||||||
|
messages can enter the server through both endpoints in one second.
|
||||||
|
|
||||||
|
# Submission module (submission)
|
||||||
|
|
||||||
|
Module 'submission' implements all functionality of the 'smtp' module and adds
|
||||||
|
certain message preprocessing on top of it, additionaly authentication is
|
||||||
|
always required.
|
||||||
|
|
||||||
|
'submission' module checks whether addresses in header fields From, Sender, To,
|
||||||
|
Cc, Bcc, Reply-To are correct and adds Message-ID and Date if it is missing.
|
||||||
|
|
||||||
|
```
|
||||||
|
submission tcp://0.0.0.0:587 tls://0.0.0.0:465 {
|
||||||
|
# ... same as smtp ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# LMTP module (lmtp)
|
||||||
|
|
||||||
|
Module 'lmtp' implements all functionality of the 'smtp' module but uses
|
||||||
|
LMTP (RFC 2033) protocol.
|
||||||
|
|
||||||
|
```
|
||||||
|
lmtp unix://lmtp.sock {
|
||||||
|
# ... same as smtp ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limitations of LMTP implementation
|
||||||
|
|
||||||
|
- Can't be used with TCP.
|
||||||
|
|
||||||
|
- Delivery to 'sql' module storage is always atomic, either all recipients will
|
||||||
|
succeed or none of them will.
|
||||||
|
|
94
docs/reference/global-config.md
Normal file
94
docs/reference/global-config.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
# Global configuration directives
|
||||||
|
|
||||||
|
These directives can be specified outside of any
|
||||||
|
configuration blocks and they are applied to all modules.
|
||||||
|
|
||||||
|
Some directives can be overridden on per-module basis (e.g. hostname).
|
||||||
|
|
||||||
|
**Syntax**: state\_dir _path_ <br>
|
||||||
|
**Default**: /var/lib/maddy
|
||||||
|
|
||||||
|
The path to the state directory. This directory will be used to store all
|
||||||
|
persistent data and should be writable.
|
||||||
|
|
||||||
|
**Syntax**: runtime\_dir _path_ <br>
|
||||||
|
**Default**: /run/maddy
|
||||||
|
|
||||||
|
The path to the runtime directory. Used for Unix sockets and other temporary
|
||||||
|
objects. Should be writable.
|
||||||
|
|
||||||
|
**Syntax**: hostname _domain_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Internet hostname of this mail server. Typicall FQDN is used. It is recommended
|
||||||
|
to make sure domain specified here resolved to the public IP of the server.
|
||||||
|
|
||||||
|
**Syntax**: autogenerated\_msg\_domain _domain_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Domain that is used in From field for auto-generated messages (such as Delivery
|
||||||
|
Status Notifications).
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
tls file _cert\_file_ _pkey\_file_ <br>
|
||||||
|
tls _module reference_ <br>
|
||||||
|
tls off <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Default TLS certificate to use for all endpoints.
|
||||||
|
|
||||||
|
Must be present in either all endpoint modules configuration blocks or as
|
||||||
|
global directive.
|
||||||
|
|
||||||
|
You can also specify other configuration options such as cipher suites and TLS
|
||||||
|
version. See maddy-tls(5) for details. maddy uses reasonable
|
||||||
|
cipher suites and TLS versions by default so you generally don't have to worry
|
||||||
|
about it.
|
||||||
|
|
||||||
|
**Syntax**: tls\_client { ... } <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
This is optional block that specifies various TLS-related options to use when
|
||||||
|
making outbound connections. See TLS client configuration for details on
|
||||||
|
directives that can be used in it. maddy uses reasonable cipher suites and TLS
|
||||||
|
versions by default so you generally don't have to worry about it.
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
log _targets..._ <br>
|
||||||
|
log off <br>
|
||||||
|
**Default**: stderr
|
||||||
|
|
||||||
|
Write log to one of more "targets".
|
||||||
|
|
||||||
|
The target can be one or the following:
|
||||||
|
|
||||||
|
- stderr
|
||||||
|
|
||||||
|
Write logs to stderr.
|
||||||
|
|
||||||
|
- stderr\_ts
|
||||||
|
|
||||||
|
Write logs to stderr with timestamps.
|
||||||
|
|
||||||
|
- syslog
|
||||||
|
|
||||||
|
Send logs to the local syslog daemon.
|
||||||
|
|
||||||
|
- _file path_
|
||||||
|
|
||||||
|
Write (append) logs to file.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
log syslog /var/log/maddy.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Maddy does not perform log files rotation, this is the job of the
|
||||||
|
logrotate daemon. Send SIGUSR1 to maddy process to make it reopen log files.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Enable verbose logging for all modules. You don't need that unless you are
|
||||||
|
reporting a bug.
|
||||||
|
|
197
docs/reference/modifiers/dkim.md
Normal file
197
docs/reference/modifiers/dkim.md
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
# DKIM signing
|
||||||
|
|
||||||
|
modify.dkim module is a modifier that signs messages using DKIM
|
||||||
|
protocol (RFC 6376).
|
||||||
|
|
||||||
|
Each configuration block specifies a single selector
|
||||||
|
and one or more domains.
|
||||||
|
|
||||||
|
A key will be generated or read for each domain, the key to use
|
||||||
|
for each message will be selected based on the SMTP envelope sender. Exception
|
||||||
|
for that is that for domain-less postmaster address and null address, the
|
||||||
|
key for the first domain will be used. If domain in envelope sender
|
||||||
|
does not match any of loaded keys, message will not be signed.
|
||||||
|
Additionally, for each messages From header is checked to
|
||||||
|
match MAIL FROM and authorization identity (username sender is logged in as).
|
||||||
|
This can be controlled using require\_sender\_match directive.
|
||||||
|
|
||||||
|
Generated private keys are stored in unencrypted PKCS#8 format
|
||||||
|
in state_directory/dkim_keys (/var/lib/maddy/dkim_keys).
|
||||||
|
In the same directory .dns files are generated that contain
|
||||||
|
public key for each domain formatted in the form of a DNS record.
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
domains and selector can be specified in arguments, so actual modify.dkim use can
|
||||||
|
be shortened to the following:
|
||||||
|
```
|
||||||
|
modify {
|
||||||
|
dkim example.org selector
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
modify.dkim {
|
||||||
|
debug no
|
||||||
|
domains example.org example.com
|
||||||
|
selector default
|
||||||
|
key_path dkim-keys/{domain}-{selector}.key
|
||||||
|
oversign_fields ...
|
||||||
|
sign_fields ...
|
||||||
|
header_canon relaxed
|
||||||
|
body_canon relaxed
|
||||||
|
sig_expiry 120h # 5 days
|
||||||
|
hash sha256
|
||||||
|
newkey_algo rsa2048
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: domains _string list_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
**REQUIRED.**
|
||||||
|
|
||||||
|
ADministrative Management Domains (ADMDs) taking responsibility for messages.
|
||||||
|
|
||||||
|
Should be specified either as a directive or as an argument.
|
||||||
|
|
||||||
|
**Syntax**: selector _string_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
**REQUIRED.**
|
||||||
|
|
||||||
|
Identifier of used key within the ADMD.
|
||||||
|
Should be specified either as a directive or as an argument.
|
||||||
|
|
||||||
|
**Syntax**: key\_path _string_ <br>
|
||||||
|
**Default**: dkim\_keys/{domain}\\_{selector}.key
|
||||||
|
|
||||||
|
Path to private key. It should be in PKCS#8 format wrapped in PAM encoding.
|
||||||
|
If key does not exist, it will be generated using algorithm specified
|
||||||
|
in newkey\_algo.
|
||||||
|
|
||||||
|
Placeholders '{domain}' and '{selector}' will be replaced with corresponding
|
||||||
|
values from domain and selector directives.
|
||||||
|
|
||||||
|
Additionally, keys in PKCS#1 ("RSA PRIVATE KEY") and
|
||||||
|
RFC 5915 ("EC PRIVATE KEY") can be read by modify.dkim. Note, however that
|
||||||
|
newly generated keys are always in PKCS#8.
|
||||||
|
|
||||||
|
**Syntax**: oversign\_fields _list..._ <br>
|
||||||
|
**Default**: see below
|
||||||
|
|
||||||
|
Header fields that should be signed n+1 times where n is times they are
|
||||||
|
present in the message. This makes it impossible to replace field
|
||||||
|
value by prepending another field with the same name to the message.
|
||||||
|
|
||||||
|
Fields specified here don't have to be also specified in sign\_fields.
|
||||||
|
|
||||||
|
Default set of oversigned fields:
|
||||||
|
- Subject
|
||||||
|
- To
|
||||||
|
- From
|
||||||
|
- Date
|
||||||
|
- MIME-Version
|
||||||
|
- Content-Type
|
||||||
|
- Content-Transfer-Encoding
|
||||||
|
- Reply-To
|
||||||
|
- Message-Id
|
||||||
|
- References
|
||||||
|
- Autocrypt
|
||||||
|
- Openpgp
|
||||||
|
|
||||||
|
**Syntax**: sign\_fields _list..._ <br>
|
||||||
|
**Default**: see below
|
||||||
|
|
||||||
|
Header fields that should be signed n+1 times where n is times they are
|
||||||
|
present in the message. For these fields, additional values can be prepended
|
||||||
|
by intermediate relays, but existing values can't be changed.
|
||||||
|
|
||||||
|
Default set of signed fields:
|
||||||
|
- List-Id
|
||||||
|
- List-Help
|
||||||
|
- List-Unsubscribe
|
||||||
|
- List-Post
|
||||||
|
- List-Owner
|
||||||
|
- List-Archive
|
||||||
|
- Resent-To
|
||||||
|
- Resent-Sender
|
||||||
|
- Resent-Message-Id
|
||||||
|
- Resent-Date
|
||||||
|
- Resent-From
|
||||||
|
- Resent-Cc
|
||||||
|
|
||||||
|
**Syntax**: header\_canon relaxed|simple <br>
|
||||||
|
**Default**: relaxed
|
||||||
|
|
||||||
|
Canonicalization algorithm to use for header fields. With 'relaxed', whitespace within
|
||||||
|
fields can be modified without breaking the signature, with 'simple' no
|
||||||
|
modifications are allowed.
|
||||||
|
|
||||||
|
**Syntax**: body\_canon relaxed|simple <br>
|
||||||
|
**Default**: relaxed
|
||||||
|
|
||||||
|
Canonicalization algorithm to use for message body. With 'relaxed', whitespace within
|
||||||
|
can be modified without breaking the signature, with 'simple' no
|
||||||
|
modifications are allowed.
|
||||||
|
|
||||||
|
**Syntax**: sig\_expiry _duration_ <br>
|
||||||
|
**Default**: 120h
|
||||||
|
|
||||||
|
Time for which signature should be considered valid. Mainly used to prevent
|
||||||
|
unauthorized resending of old messages.
|
||||||
|
|
||||||
|
**Syntax**: hash _hash_ <br>
|
||||||
|
**Default**: sha256
|
||||||
|
|
||||||
|
Hash algorithm to use when computing body hash.
|
||||||
|
|
||||||
|
sha256 is the only supported algorithm now.
|
||||||
|
|
||||||
|
**Syntax**: newkey\_algo rsa4096|rsa2048|ed25519 <br>
|
||||||
|
**Default**: rsa2048
|
||||||
|
|
||||||
|
Algorithm to use when generating a new key.
|
||||||
|
|
||||||
|
**Syntax**: require\_sender\_match _ids..._ <br>
|
||||||
|
**Default**: envelope auth
|
||||||
|
|
||||||
|
Require specified identifiers to match From header field and key domain,
|
||||||
|
otherwise - don't sign the message.
|
||||||
|
|
||||||
|
If From field contains multiple addresses, message will not be
|
||||||
|
signed unless allow\_multiple\_from is also specified. In that
|
||||||
|
case only first address will be compared.
|
||||||
|
|
||||||
|
Matching is done in a case-insensitive way.
|
||||||
|
|
||||||
|
Valid values:
|
||||||
|
- off
|
||||||
|
Disable check, always sign.
|
||||||
|
- envelope
|
||||||
|
Require MAIL FROM address to match From header.
|
||||||
|
- auth
|
||||||
|
If authorization identity contains @ - then require it to
|
||||||
|
fully match From header. Otherwise, check only local-part
|
||||||
|
(username).
|
||||||
|
|
||||||
|
**Syntax**: allow\_multiple\_from _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Allow multiple addresses in From header field for purposes of
|
||||||
|
require\_sender\_match checks. Only first address will be checked, however.
|
||||||
|
|
||||||
|
**Syntax**: sign\_subdomains _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Sign emails from subdomains using a top domain key.
|
||||||
|
|
||||||
|
Allows only one domain to be specified (can be workarounded using modify.dkim
|
||||||
|
multiple times).
|
50
docs/reference/modifiers/envelope.md
Normal file
50
docs/reference/modifiers/envelope.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Envelope sender / recipient rewriting
|
||||||
|
|
||||||
|
'replace\_sender' and 'replace\_rcpt' modules replace SMTP envelope addresses
|
||||||
|
based on the mapping defined by the table module. Currently,
|
||||||
|
only 1:1 mappings are supported (that is, it is not possible to specify
|
||||||
|
multiple replacements for a single address).
|
||||||
|
|
||||||
|
The address is normalized before lookup (Punycode in domain-part is decoded,
|
||||||
|
Unicode is normalized to NFC, the whole string is case-folded).
|
||||||
|
|
||||||
|
First, the whole address is looked up. If there is no replacement, local-part
|
||||||
|
of the address is looked up separately and is replaced in the address while
|
||||||
|
keeping the domain part intact. Replacements are not applied recursively, that
|
||||||
|
is, lookup is not repeated for the replacement.
|
||||||
|
|
||||||
|
Recipients are not deduplicated after expansion, so message may be delivered
|
||||||
|
multiple times to a single recipient. However, used delivery target can apply
|
||||||
|
such deduplication (imapsql storage does it).
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
```
|
||||||
|
replace_rcpt <table> [table arguments] {
|
||||||
|
[extended table config]
|
||||||
|
}
|
||||||
|
replace_sender <table> [table arguments] {
|
||||||
|
[extended table config]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use examples:
|
||||||
|
```
|
||||||
|
modify {
|
||||||
|
replace_rcpt file /etc/maddy/aliases
|
||||||
|
replace_rcpt static {
|
||||||
|
entry a@example.org b@example.org
|
||||||
|
}
|
||||||
|
replace_rcpt regexp "(.+)@example.net" "$1@example.org"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible contents of /etc/maddy/aliases in the example above:
|
||||||
|
```
|
||||||
|
# Replace 'cat' with any domain to 'dog'.
|
||||||
|
# E.g. cat@example.net -> dog@example.net
|
||||||
|
cat: dog
|
||||||
|
|
||||||
|
# Replace cat@example.org with cat@example.com.
|
||||||
|
# Takes priority over the previous line.
|
||||||
|
cat@example.org: cat@example.com
|
||||||
|
```
|
76
docs/reference/modules.md
Normal file
76
docs/reference/modules.md
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# Modules introduction
|
||||||
|
|
||||||
|
maddy is built of many small components called "modules". Each module does one
|
||||||
|
certain well-defined task. Modules can be connected to each other in arbitrary
|
||||||
|
ways to achieve wanted functionality. Default configuration file defines
|
||||||
|
set of modules that together implement typical email server stack.
|
||||||
|
|
||||||
|
To specify the module that should be used by another module for something, look
|
||||||
|
for configuration directives with "module reference" argument. Then
|
||||||
|
put the module name as an argument for it. Optionally, if referenced module
|
||||||
|
needs that, put additional arguments after the name. You can also put a
|
||||||
|
configuration block with additional directives specifing the module
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
Here are some examples:
|
||||||
|
|
||||||
|
```
|
||||||
|
smtp ... {
|
||||||
|
# Deliver messages to the 'dummy' module with the default configuration.
|
||||||
|
deliver_to dummy
|
||||||
|
|
||||||
|
# Deliver messages to the 'target.smtp' module with
|
||||||
|
# 'tcp://127.0.0.1:1125' argument as a configuration.
|
||||||
|
deliver_to smtp tcp://127.0.0.1:1125
|
||||||
|
|
||||||
|
# Deliver messages to the 'queue' module with the specified configuration.
|
||||||
|
deliver_to queue {
|
||||||
|
target ...
|
||||||
|
max_tries 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, module configuration can be placed in a separate named block
|
||||||
|
at the top-level and referenced by its name where it is needed.
|
||||||
|
|
||||||
|
Here is the example:
|
||||||
|
```
|
||||||
|
storage.imapsql local_mailboxes {
|
||||||
|
driver sqlite3
|
||||||
|
dsn all.db
|
||||||
|
}
|
||||||
|
|
||||||
|
smtp ... {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It is recommended to use this syntax for modules that are 'expensive' to
|
||||||
|
initialize such as storage backends and authentication providers.
|
||||||
|
|
||||||
|
For top-level configuration block definition, syntax is as follows:
|
||||||
|
```
|
||||||
|
namespace.module_name config_block_name... {
|
||||||
|
module_configuration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
If config\_block\_name is omitted, it will be the same as module\_name. Multiple
|
||||||
|
names can be specified. All names must be unique.
|
||||||
|
|
||||||
|
Note the "storage." prefix. This is the actual module name and includes
|
||||||
|
"namespace". It is a little cheating to make more concise names and can
|
||||||
|
be omitted when you reference the module where it is used since it can
|
||||||
|
be implied (e.g. putting module reference in "check{}" likely means you want
|
||||||
|
something with "check." prefix)
|
||||||
|
|
||||||
|
Usual module arguments can't be specified when using this syntax, however,
|
||||||
|
modules usually provide explicit directives that allow to specify the needed
|
||||||
|
values. For example 'sql sqlite3 all.db' is equivalent to
|
||||||
|
```
|
||||||
|
storage.imapsql {
|
||||||
|
driver sqlite3
|
||||||
|
dsn all.db
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
384
docs/reference/smtp-pipeline.md
Normal file
384
docs/reference/smtp-pipeline.md
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
# SMTP message routing (pipeline)
|
||||||
|
|
||||||
|
# Message pipeline
|
||||||
|
|
||||||
|
Message pipeline is a set of module references and associated rules that
|
||||||
|
describe how to handle messages.
|
||||||
|
|
||||||
|
The pipeline is responsible for
|
||||||
|
- Running message filters (called "checks"), (e.g. DKIM signature verification,
|
||||||
|
DNSBL lookup and so on).
|
||||||
|
|
||||||
|
- Running message modifiers (e.g. DKIM signature creation).
|
||||||
|
|
||||||
|
- Assocating each message recipient with one or more delivery targets.
|
||||||
|
Delivery target is a module that does final processing (delivery) of the
|
||||||
|
message.
|
||||||
|
|
||||||
|
Message handling flow is as follows:
|
||||||
|
- Execute checks referenced in top-level 'check' blocks (if any)
|
||||||
|
|
||||||
|
- Execute modifiers referenced in top-level 'modify' blocks (if any)
|
||||||
|
|
||||||
|
- If there are 'source' blocks - select one that matches message sender (as
|
||||||
|
specified in MAIL FROM). If there are no 'source' blocks - entire
|
||||||
|
configuration is assumed to be the 'default\_source' block.
|
||||||
|
|
||||||
|
- Execute checks referenced in 'check' blocks inside selected 'source' block
|
||||||
|
(if any).
|
||||||
|
|
||||||
|
- Execute modifiers referenced in 'modify' blocks inside selected 'source'
|
||||||
|
block (if any).
|
||||||
|
|
||||||
|
Then, for each recipient:
|
||||||
|
- Select 'destination' block that matches it. If there are
|
||||||
|
no 'destination' blocks - entire used 'source' block is interpreted as if it
|
||||||
|
was a 'default\_destination' block.
|
||||||
|
|
||||||
|
- Execute checks referenced in 'check' block inside selected 'destination' block
|
||||||
|
(if any).
|
||||||
|
|
||||||
|
- Execute modifiers referenced in 'modify' block inside selected 'destination'
|
||||||
|
block (if any).
|
||||||
|
|
||||||
|
- If used block contains 'reject' directive - reject the recipient with
|
||||||
|
specified SMTP status code.
|
||||||
|
|
||||||
|
- If used block contains 'deliver\_to' directive - pass the message to the
|
||||||
|
specified target module. Only recipients that are handled
|
||||||
|
by used block are visible to the target.
|
||||||
|
|
||||||
|
Each recipient is handled only by a single 'destination' block, in case of
|
||||||
|
overlapping 'destination' - first one takes priority.
|
||||||
|
```
|
||||||
|
destination example.org {
|
||||||
|
deliver_to targetA
|
||||||
|
}
|
||||||
|
destination example.org { # ambiguous and thus not allowed
|
||||||
|
deliver_to targetB
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Same goes for 'source' blocks, each message is handled only by a single block.
|
||||||
|
|
||||||
|
Each recipient block should contain at least one 'deliver\_to' directive or
|
||||||
|
'reject' directive. If 'destination' blocks are used, then
|
||||||
|
'default\_destination' block should also be used to specify behavior for
|
||||||
|
unmatched recipients. Same goes for source blocks, 'default\_source' should be
|
||||||
|
used if 'source' is used.
|
||||||
|
|
||||||
|
That is, pipeline configuration should explicitly specify behavior for each
|
||||||
|
possible sender/recipient combination.
|
||||||
|
|
||||||
|
Additionally, directives that specify final handling decision ('deliver\_to',
|
||||||
|
'reject') can't be used at the same level as source/destination rules.
|
||||||
|
Consider example:
|
||||||
|
```
|
||||||
|
destination example.org {
|
||||||
|
deliver_to local_mboxes
|
||||||
|
}
|
||||||
|
reject
|
||||||
|
```
|
||||||
|
It is not obvious whether 'reject' applies to all recipients or
|
||||||
|
just for non-example.org ones, hence this is not allowed.
|
||||||
|
|
||||||
|
Complete configuration example using all of the mentioned directives:
|
||||||
|
```
|
||||||
|
check {
|
||||||
|
# Run a check to make sure source SMTP server identification
|
||||||
|
# is legit.
|
||||||
|
require_matching_ehlo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Messages coming from senders at example.org will be handled in
|
||||||
|
# accordance with the following configuration block.
|
||||||
|
source example.org {
|
||||||
|
# We are example.com, so deliver all messages with recipients
|
||||||
|
# at example.com to our local mailboxes.
|
||||||
|
destination example.com {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
|
||||||
|
# We don't do anything with recipients at different domains
|
||||||
|
# because we are not an open relay, thus we reject them.
|
||||||
|
default_destination {
|
||||||
|
reject 521 5.0.0 "User not local"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# We do our business only with example.org, so reject all
|
||||||
|
# other senders.
|
||||||
|
default_source {
|
||||||
|
reject
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directives
|
||||||
|
|
||||||
|
**Syntax**: check _block name_ { ... } <br>
|
||||||
|
**Context**: pipeline configuration, source block, destination block
|
||||||
|
|
||||||
|
List of the module references for checks that should be executed on
|
||||||
|
messages handled by block where 'check' is placed in.
|
||||||
|
|
||||||
|
Note that message body checks placed in destination block are currently
|
||||||
|
ignored. Due to the way SMTP protocol is defined, they would cause message to
|
||||||
|
be rejected for all recipients which is not what you usually want when using
|
||||||
|
such configurations.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
check {
|
||||||
|
# Reference implicitly defined default configuration for check.
|
||||||
|
require_matching_ehlo
|
||||||
|
|
||||||
|
# Inline definition of custom config.
|
||||||
|
require_source_mx {
|
||||||
|
# Configuration for require_source_mx goes here.
|
||||||
|
fail_action reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to define the block of checks at the top level
|
||||||
|
as "checks" module and reference it using & syntax. Example:
|
||||||
|
```
|
||||||
|
checks inbound_checks {
|
||||||
|
require_matching_ehlo
|
||||||
|
}
|
||||||
|
|
||||||
|
# ... somewhere else ...
|
||||||
|
{
|
||||||
|
...
|
||||||
|
check &inbound_checks
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: modify { ... } <br>
|
||||||
|
**Default**: not specified <br>
|
||||||
|
**Context**: pipeline configuration, source block, destination block
|
||||||
|
|
||||||
|
List of the module references for modifiers that should be executed on
|
||||||
|
messages handled by block where 'modify' is placed in.
|
||||||
|
|
||||||
|
Message modifiers are similar to checks with the difference in that checks
|
||||||
|
purpose is to verify whether the message is legitimate and valid per local
|
||||||
|
policy, while modifier purpose is to post-process message and its metadata
|
||||||
|
before final delivery.
|
||||||
|
|
||||||
|
For example, modifier can replace recipient address to make message delivered
|
||||||
|
to the different mailbox or it can cryptographically sign outgoing message
|
||||||
|
(e.g. using DKIM). Some modifier can perform multiple unrelated modifications
|
||||||
|
on the message.
|
||||||
|
|
||||||
|
**Note**: Modifiers that affect source address can be used only globally or on
|
||||||
|
per-source basis, they will be no-op inside destination blocks. Modifiers that
|
||||||
|
affect the message header will affect it for all recipients.
|
||||||
|
|
||||||
|
It is also possible to define the block of modifiers at the top level
|
||||||
|
as "modiifers" module and reference it using & syntax. Example:
|
||||||
|
```
|
||||||
|
modifiers local_modifiers {
|
||||||
|
replace_rcpt file /etc/maddy/aliases
|
||||||
|
}
|
||||||
|
|
||||||
|
# ... somewhere else ...
|
||||||
|
{
|
||||||
|
...
|
||||||
|
modify &local_modifiers
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
reject _smtp\_code_ _smtp\_enhanced\_code_ _error\_description_ <br>
|
||||||
|
reject _smtp\_code_ _smtp\_enhanced\_code_ <br>
|
||||||
|
reject _smtp\_code_ <br>
|
||||||
|
reject <br>
|
||||||
|
**Context**: destination block
|
||||||
|
|
||||||
|
Messages handled by the configuration block with this directive will be
|
||||||
|
rejected with the specified SMTP error.
|
||||||
|
|
||||||
|
If you aren't sure which codes to use, use 541 and 5.4.0 with your message or
|
||||||
|
just leave all arguments out, the error description will say "message is
|
||||||
|
rejected due to policy reasons" which is usually what you want to mean.
|
||||||
|
|
||||||
|
'reject' can't be used in the same block with 'deliver\_to' or
|
||||||
|
'destination/source' directives.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
reject 541 5.4.0 "We don't like example.org, go away"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: deliver\_to _target-config-block_ <br>
|
||||||
|
**Context**: pipeline configuration, source block, destination block
|
||||||
|
|
||||||
|
Deliver the message to the referenced delivery target. What happens next is
|
||||||
|
defined solely by used target. If deliver\_to is used inside 'destination'
|
||||||
|
block, only matching recipients will be passed to the target.
|
||||||
|
|
||||||
|
**Syntax**: source\_in _table reference_ { ... } <br>
|
||||||
|
**Context**: pipeline configuration
|
||||||
|
|
||||||
|
Handle messages with envelope senders present in the specified table in
|
||||||
|
accordance with the specified configuration block.
|
||||||
|
|
||||||
|
Takes precedence over all 'sender' directives.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
source_in file /etc/maddy/banned_addrs {
|
||||||
|
reject 550 5.7.0 "You are not welcome here"
|
||||||
|
}
|
||||||
|
source example.org {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
See 'destination\_in' documentation for note about table configuration.
|
||||||
|
|
||||||
|
**Syntax**: source _rules..._ { ... } <br>
|
||||||
|
**Context**: pipeline configuration
|
||||||
|
|
||||||
|
Handle messages with MAIL FROM value (sender address) matching any of the rules
|
||||||
|
in accordance with the specified configuration block.
|
||||||
|
|
||||||
|
"Rule" is either a domain or a complete address. In case of overlapping
|
||||||
|
'rules', first one takes priority. Matching is case-insensitive.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
# All messages coming from example.org domain will be delivered
|
||||||
|
# to local_mailboxes.
|
||||||
|
source example.org {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
# Messages coming from different domains will be rejected.
|
||||||
|
default_source {
|
||||||
|
reject 521 5.0.0 "You were not invited"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: reroute { ... } <br>
|
||||||
|
**Context**: pipeline configuration, source block, destination block
|
||||||
|
|
||||||
|
This directive allows to make message routing decisions based on the
|
||||||
|
result of modifiers. The block can contain all pipeline directives and they
|
||||||
|
will be handled the same with the exception that source and destination rules
|
||||||
|
will use the final recipient and sender values (e.g. after all modifiers are
|
||||||
|
applied).
|
||||||
|
|
||||||
|
Here is the concrete example how it can be useful:
|
||||||
|
```
|
||||||
|
destination example.org {
|
||||||
|
modify {
|
||||||
|
replace_rcpt file /etc/maddy/aliases
|
||||||
|
}
|
||||||
|
reroute {
|
||||||
|
destination example.org {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
default_destination {
|
||||||
|
deliver_to &remote_queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration allows to specify alias local addresses to remote ones
|
||||||
|
without being an open relay, since remote\_queue can be used only if remote
|
||||||
|
address was introduced as a result of rewrite of local address.
|
||||||
|
|
||||||
|
**WARNING**: If you have DMARC enabled (default), results generated by SPF
|
||||||
|
and DKIM checks inside a reroute block **will not** be considered in DMARC
|
||||||
|
evaluation.
|
||||||
|
|
||||||
|
**Syntax**: destination\_in _table reference_ { ... } <br>
|
||||||
|
**Context**: pipeline configuration, source block
|
||||||
|
|
||||||
|
Handle messages with envelope recipients present in the specified table in
|
||||||
|
accordance with the specified configuration block.
|
||||||
|
|
||||||
|
Takes precedence over all 'destination' directives.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
destination_in file /etc/maddy/remote_addrs {
|
||||||
|
deliver_to smtp tcp://10.0.0.7:25
|
||||||
|
}
|
||||||
|
destination example.com {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that due to the syntax restrictions, it is not possible to specify
|
||||||
|
extended configuration for table module. E.g. this is not valid:
|
||||||
|
```
|
||||||
|
destination_in sql_table {
|
||||||
|
dsn ...
|
||||||
|
driver ...
|
||||||
|
} {
|
||||||
|
deliver_to whatever
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, configuration should be specified separately and be referneced
|
||||||
|
using '&' syntax:
|
||||||
|
```
|
||||||
|
table.sql_table remote_addrs {
|
||||||
|
dsn ...
|
||||||
|
driver ...
|
||||||
|
}
|
||||||
|
|
||||||
|
whatever {
|
||||||
|
destination_in &remote_addrs {
|
||||||
|
deliver_to whatever
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: destination _rule..._ { ... } <br>
|
||||||
|
**Context**: pipeline configuration, source block
|
||||||
|
|
||||||
|
Handle messages with RCPT TO value (recipient address) matching any of the
|
||||||
|
rules in accordance with the specified configuration block.
|
||||||
|
|
||||||
|
"Rule" is either a domain or a complete address. Duplicate rules are not
|
||||||
|
allowed. Matching is case-insensitive.
|
||||||
|
|
||||||
|
Note that messages with multiple recipients are split into multiple messages if
|
||||||
|
they have recipients matched by multiple blocks. Each block will see the
|
||||||
|
message only with recipients matched by its rules.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
# Messages with recipients at example.com domain will be
|
||||||
|
# delivered to local_mailboxes target.
|
||||||
|
destination example.com {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
|
||||||
|
# Messages with other recipients will be rejected.
|
||||||
|
default_destination {
|
||||||
|
rejected 541 5.0.0 "User not local"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reusable pipeline snippets (msgpipeline module)
|
||||||
|
|
||||||
|
The message pipeline can be used independently of the SMTP module in other
|
||||||
|
contexts that require a delivery target via "msgpipeline" module.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
msgpipeline local_routing {
|
||||||
|
destination whatever.com {
|
||||||
|
deliver_to dummy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ... somewhere else ...
|
||||||
|
deliver_to &local_routing
|
||||||
|
```
|
181
docs/reference/storage/imapsql.md
Normal file
181
docs/reference/storage/imapsql.md
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
# SQL-indexed storage
|
||||||
|
|
||||||
|
The imapsql module implements database for IMAP index and message
|
||||||
|
metadata using SQL-based relational database.
|
||||||
|
|
||||||
|
Message contents are stored in an "blob store" defined by msg\_store
|
||||||
|
directive. By default this is a file system directory under /var/lib/maddy.
|
||||||
|
|
||||||
|
Supported RDBMS:
|
||||||
|
- SQLite 3.25.0
|
||||||
|
- PostgreSQL 9.6 or newer
|
||||||
|
- CockroachDB 20.1.5 or newer
|
||||||
|
|
||||||
|
Account names are required to have the form of a email address (unless configured otherwise)
|
||||||
|
and are case-insensitive. UTF-8 names are supported with restrictions defined in the
|
||||||
|
PRECIS UsernameCaseMapped profile.
|
||||||
|
|
||||||
|
```
|
||||||
|
storage.imapsql {
|
||||||
|
driver sqlite3
|
||||||
|
dsn imapsql.db
|
||||||
|
msg_store fs messages/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
imapsql module also can be used as a lookup table.
|
||||||
|
It returns empty string values for existing usernames. This might be useful
|
||||||
|
with destination\_in directive e.g. to implement catch-all
|
||||||
|
addresses (this is a bad idea to do so, this is just an example):
|
||||||
|
```
|
||||||
|
destination_in &local_mailboxes {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
destination example.org {
|
||||||
|
modify {
|
||||||
|
replace_rcpt regexp ".*" "catchall@example.org"
|
||||||
|
}
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
Specify the driver and DSN.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: driver _string_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
Use a specified driver to communicate with the database. Supported values:
|
||||||
|
sqlite3, postgres.
|
||||||
|
|
||||||
|
Should be specified either via an argument or via this directive.
|
||||||
|
|
||||||
|
**Syntax**: dsn _string_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
Data Source Name, the driver-specific value that specifies the database to use.
|
||||||
|
|
||||||
|
For SQLite3 this is just a file path.
|
||||||
|
For PostgreSQL: [https://godoc.org/github.com/lib/pq#hdr-Connection\_String\_Parameters](https://godoc.org/github.com/lib/pq#hdr-Connection\_String\_Parameters)
|
||||||
|
|
||||||
|
Should be specified either via an argument or via this directive.
|
||||||
|
|
||||||
|
**Syntax**: msg\_store _store_ <br>
|
||||||
|
**Default**: fs messages/
|
||||||
|
|
||||||
|
Module to use for message bodies storage.
|
||||||
|
|
||||||
|
See "Blob storage" section for what you can use here.
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
compression off <br>
|
||||||
|
compression _algorithm_ <br>
|
||||||
|
compression _algorithm_ _level_ <br>
|
||||||
|
**Default**: off
|
||||||
|
|
||||||
|
Apply compression to message contents.
|
||||||
|
Supported algorithms: lz4, zstd.
|
||||||
|
|
||||||
|
**Syntax**: appendlimit _size_ <br>
|
||||||
|
**Default**: 32M
|
||||||
|
|
||||||
|
Don't allow users to add new messages larger than 'size'.
|
||||||
|
|
||||||
|
This does not affect messages added when using module as a delivery target.
|
||||||
|
Use 'max\_message\_size' directive in SMTP endpoint module to restrict it too.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: junk\_mailbox _name_ <br>
|
||||||
|
**Default**: Junk
|
||||||
|
|
||||||
|
The folder to put quarantined messages in. Thishis setting is not used if user
|
||||||
|
does have a folder with "Junk" special-use attribute.
|
||||||
|
|
||||||
|
**Syntax**: disable\_recent _boolean_ <br>
|
||||||
|
*Default: true
|
||||||
|
|
||||||
|
Disable RFC 3501-conforming handling of \Recent flag.
|
||||||
|
|
||||||
|
This significantly improves storage performance when SQLite3 or CockroackDB is
|
||||||
|
used at the cost of confusing clients that use this flag.
|
||||||
|
|
||||||
|
**Syntax**: sqlite\_cache\_size _integer_ <br>
|
||||||
|
**Default**: defined by SQLite
|
||||||
|
|
||||||
|
SQLite page cache size. If positive - specifies amount of pages (1 page - 4
|
||||||
|
KiB) to keep in cache. If negative - specifies approximate upper bound
|
||||||
|
of cache size in KiB.
|
||||||
|
|
||||||
|
**Syntax**: sqlite\_busy\_timeout _integer_ <br>
|
||||||
|
**Default**: 5000000
|
||||||
|
|
||||||
|
SQLite-specific performance tuning option. Amount of milliseconds to wait
|
||||||
|
before giving up on DB lock.
|
||||||
|
|
||||||
|
**Syntax**: imap\_filter { ... } <br>
|
||||||
|
**Default**: not set
|
||||||
|
|
||||||
|
Specifies IMAP filters to apply for messages delivered from SMTP pipeline.
|
||||||
|
|
||||||
|
Ex.
|
||||||
|
```
|
||||||
|
imap_filter {
|
||||||
|
command /etc/maddy/sieve.sh {account_name}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax:** delivery\_map **table** <br>
|
||||||
|
**Default:** identity
|
||||||
|
|
||||||
|
Use specified table module to map recipient
|
||||||
|
addresses from incoming messages to mailbox names.
|
||||||
|
|
||||||
|
Normalization algorithm specified in delivery\_normalize is appied before
|
||||||
|
delivery\_map.
|
||||||
|
|
||||||
|
**Syntax:** delivery\_normalize _name_ <br>
|
||||||
|
**Default:** precis\_casefold\_email
|
||||||
|
|
||||||
|
Normalization function to apply to email addresses before mapping them
|
||||||
|
to mailboxes.
|
||||||
|
|
||||||
|
See auth\_normalize.
|
||||||
|
|
||||||
|
**Syntax**: auth\_map **table** <br>
|
||||||
|
**Default**: identity
|
||||||
|
|
||||||
|
Use specified table module to map authentication
|
||||||
|
usernames to mailbox names.
|
||||||
|
|
||||||
|
Normalization algorithm specified in auth\_normalize is applied before
|
||||||
|
auth\_map.
|
||||||
|
|
||||||
|
**Syntax**: auth\_normalize _name_ <br>
|
||||||
|
**Default**: precis\_casefold\_email
|
||||||
|
|
||||||
|
Normalization function to apply to authentication usernames before mapping
|
||||||
|
them to mailboxes.
|
||||||
|
|
||||||
|
Available options:
|
||||||
|
- precis\_casefold\_email PRECIS UsernameCaseMapped profile + U-labels form for domain
|
||||||
|
- precis\_casefold PRECIS UsernameCaseMapped profile for the entire string
|
||||||
|
- precis\_email PRECIS UsernameCasePreserved profile + U-labels form for domain
|
||||||
|
- precis PRECIS UsernameCasePreserved profile for the entire string
|
||||||
|
- casefold Convert to lower case
|
||||||
|
- noop Nothing
|
||||||
|
|
||||||
|
Note: On message delivery, recipient address is unconditionally normalized
|
||||||
|
using precis\_casefold\_email function.
|
||||||
|
|
6
docs/reference/table/auth.md
Normal file
6
docs/reference/table/auth.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Authentication providers
|
||||||
|
|
||||||
|
Most authentication providers are also usable as a table
|
||||||
|
that contains all usernames known to the module. Exceptions are auth.external and
|
||||||
|
pam as underlying interfaces do not define a way to check credentials
|
||||||
|
existence.
|
38
docs/reference/table/chain.md
Normal file
38
docs/reference/table/chain.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Table chaining
|
||||||
|
|
||||||
|
The table.chain module allows chaining together multiple table modules
|
||||||
|
by using value returned by a previous table as an input for the second
|
||||||
|
table.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
table.chain {
|
||||||
|
step regexp "(.+)(\\+[^+"@]+)?@example.org" "$1@example.org"
|
||||||
|
step file /etc/maddy/emails
|
||||||
|
}
|
||||||
|
```
|
||||||
|
This will strip +prefix from mailbox before looking it up
|
||||||
|
in /etc/maddy/emails list.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
**Syntax**: step _table\_
|
||||||
|
|
||||||
|
Adds a table module to the chain. If input value is not in the table
|
||||||
|
(e.g. file) - return "not exists" error.
|
||||||
|
|
||||||
|
**Syntax**: optional\_step _table\_
|
||||||
|
|
||||||
|
Same as step but if input value is not in the table - it is passed to the
|
||||||
|
next step without changes.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Something like this can be used to map emails to usernames
|
||||||
|
after translating them via aliases map:
|
||||||
|
```
|
||||||
|
table.chain {
|
||||||
|
optional_step file /etc/maddy/aliases
|
||||||
|
step regexp "(.+)@(.+)" "$1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
12
docs/reference/table/email_localpart.md
Normal file
12
docs/reference/table/email_localpart.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Email local part
|
||||||
|
|
||||||
|
The module 'table.email\_localpart' extracts and unescaped local ("username") part
|
||||||
|
of the email address.
|
||||||
|
|
||||||
|
E.g.
|
||||||
|
test@example.org => test
|
||||||
|
"test @ a"@example.org => test @ a
|
||||||
|
|
||||||
|
```
|
||||||
|
table.email_localpart { }
|
||||||
|
```
|
54
docs/reference/table/file.md
Normal file
54
docs/reference/table/file.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# File
|
||||||
|
|
||||||
|
table.file module builds string-string mapping from a text file.
|
||||||
|
|
||||||
|
File is reloaded every 15 seconds if there are any changes (detected using
|
||||||
|
modification time). No changes are applied if file contains syntax errors.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
```
|
||||||
|
file <file path>
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```
|
||||||
|
file {
|
||||||
|
file <file path>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
```
|
||||||
|
# Resolve SMTP address aliases using text file mapping.
|
||||||
|
modify {
|
||||||
|
replace_rcpt file /etc/maddy/aliases
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
Better demonstrated by examples:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Lines starting with # are ignored.
|
||||||
|
|
||||||
|
# And so are lines only with whitespace.
|
||||||
|
|
||||||
|
# Whenever 'aaa' is looked up, return 'bbb'
|
||||||
|
aaa: bbb
|
||||||
|
|
||||||
|
# Trailing and leading whitespace is ignored.
|
||||||
|
ccc: ddd
|
||||||
|
|
||||||
|
# If there is no colon, the string is translated into ""
|
||||||
|
# That is, the following line is equivalent to
|
||||||
|
# aaa:
|
||||||
|
aaa
|
||||||
|
|
||||||
|
# If the same key is used multiple times - table.file will return
|
||||||
|
# multiple values when queries. Note that this is not used by
|
||||||
|
# most modules. E.g. replace_rcpt does not (intentionally) support
|
||||||
|
# 1-to-N alias expansion.
|
||||||
|
ddd: firstvalue
|
||||||
|
ddd: secondvalue
|
||||||
|
```
|
||||||
|
|
58
docs/reference/table/regexp.md
Normal file
58
docs/reference/table/regexp.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# Regexp rewrite table
|
||||||
|
|
||||||
|
The 'regexp' module implements table lookups by applying a regular expression
|
||||||
|
to the key value. If it matches - 'replacement' value is returned with $N
|
||||||
|
placeholders being replaced with corresponding capture groups from the match.
|
||||||
|
Otherwise, no value is returned.
|
||||||
|
|
||||||
|
The regular expression syntax is the subset of PCRE. See
|
||||||
|
[https://golang.org/pkg/regexp/syntax](https://golang.org/pkg/regexp/syntax)/ for details.
|
||||||
|
|
||||||
|
```
|
||||||
|
table.regexp <regexp> [replacement] {
|
||||||
|
full_match yes
|
||||||
|
case_insensitive yes
|
||||||
|
expand_placeholders yes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that [replacement] is optional. If it is not included - table.regexp
|
||||||
|
will return the original string, therefore acting as a regexp match check.
|
||||||
|
This can be useful in combination in destination\_in for
|
||||||
|
advanced matching:
|
||||||
|
```
|
||||||
|
destination_in regexp ".*-bounce+.*@example.com" {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
***Syntax***: full\_match _boolean_ <br>
|
||||||
|
***Default***: yes
|
||||||
|
|
||||||
|
Whether to implicitly add start/end anchors to the regular expression.
|
||||||
|
That is, if 'full\_match' is yes, then the provided regular expression should
|
||||||
|
match the whole string. With no - partial match is enough.
|
||||||
|
|
||||||
|
***Syntax***: case\_insensitive _boolean_ <br>
|
||||||
|
***Default***: yes
|
||||||
|
|
||||||
|
Whether to make matching case-insensitive.
|
||||||
|
|
||||||
|
***Syntax***: expand\_placeholders _boolean_ <br>
|
||||||
|
***Default***: yes
|
||||||
|
|
||||||
|
Replace '$name' and '${name}' in the replacement string with contents of
|
||||||
|
corresponding capture groups from the match.
|
||||||
|
|
||||||
|
To insert a literal $ in the output, use $$ in the template.
|
||||||
|
|
||||||
|
# Identity table (table.identity)
|
||||||
|
|
||||||
|
The module 'identity' is a table module that just returns the key looked up.
|
||||||
|
|
||||||
|
```
|
||||||
|
table.identity { }
|
||||||
|
```
|
||||||
|
|
110
docs/reference/table/sql_query.md
Normal file
110
docs/reference/table/sql_query.md
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# SQL query mapping
|
||||||
|
|
||||||
|
The table.sql\_query module implements table interface using SQL queries.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
```
|
||||||
|
table.sql_query {
|
||||||
|
driver <driver name>
|
||||||
|
dsn <data source name>
|
||||||
|
lookup <lookup query>
|
||||||
|
|
||||||
|
# Optional:
|
||||||
|
init <init query list>
|
||||||
|
list <list query>
|
||||||
|
add <add query>
|
||||||
|
del <del query>
|
||||||
|
set <set query>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
```
|
||||||
|
# Resolve SMTP address aliases using PostgreSQL DB.
|
||||||
|
modify {
|
||||||
|
replace_rcpt sql_query {
|
||||||
|
driver postgres
|
||||||
|
dsn "dbname=maddy user=maddy"
|
||||||
|
lookup "SELECT alias FROM aliases WHERE address = $1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
***Syntax***: driver _driver name_ <br>
|
||||||
|
***REQUIRED***
|
||||||
|
|
||||||
|
Driver to use to access the database.
|
||||||
|
|
||||||
|
Supported drivers: postgres, sqlite3 (if compiled with C support)
|
||||||
|
|
||||||
|
***Syntax***: dsn _data source name_ <br>
|
||||||
|
***REQUIRED***
|
||||||
|
|
||||||
|
Data Source Name to pass to the driver. For SQLite3 this is just a path to DB
|
||||||
|
file. For Postgres, see
|
||||||
|
[https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection\_String\_Parameters](https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection\_String\_Parameters)
|
||||||
|
|
||||||
|
***Syntax***: lookup _query_ <br>
|
||||||
|
***REQUIRED***
|
||||||
|
|
||||||
|
SQL query to use to obtain the lookup result.
|
||||||
|
|
||||||
|
It will get one named argument containing the lookup key. Use :key
|
||||||
|
placeholder to access it in SQL. The result row set should contain one row, one
|
||||||
|
column with the string that will be used as a lookup result. If there are more
|
||||||
|
rows, they will be ignored. If there are more columns, lookup will fail. If
|
||||||
|
there are no rows, lookup returns "no results". If there are any error - lookup
|
||||||
|
will fail.
|
||||||
|
|
||||||
|
***Syntax***: init _queries..._ <br>
|
||||||
|
***Default***: empty
|
||||||
|
|
||||||
|
List of queries to execute on initialization. Can be used to configure RDBMS.
|
||||||
|
|
||||||
|
Example, to improve SQLite3 performance:
|
||||||
|
```
|
||||||
|
table.sql_query {
|
||||||
|
driver sqlite3
|
||||||
|
dsn whatever.db
|
||||||
|
init "PRAGMA journal_mode=WAL" \
|
||||||
|
"PRAGMA synchronous=NORMAL"
|
||||||
|
lookup "SELECT alias FROM aliases WHERE address = $1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax:** named\_args _boolean_ <br>
|
||||||
|
**Default:** yes
|
||||||
|
|
||||||
|
Whether to use named parameters binding when executing SQL queries
|
||||||
|
or not.
|
||||||
|
|
||||||
|
Note that maddy's PostgreSQL driver does not support named parameters and
|
||||||
|
SQLite3 driver has issues handling numbered parameters:
|
||||||
|
[https://github.com/mattn/go-sqlite3/issues/472](https://github.com/mattn/go-sqlite3/issues/472)
|
||||||
|
|
||||||
|
***Syntax:*** add _query_ <br>
|
||||||
|
***Syntax:*** list _query_ <br>
|
||||||
|
***Syntax:*** set _query_ <br>
|
||||||
|
***Syntax:*** del _query_ <br>
|
||||||
|
***Default:*** none
|
||||||
|
|
||||||
|
If queries are set to implement corresponding table operations - table becomes
|
||||||
|
"mutable" and can be used in contexts that require writable key-value store.
|
||||||
|
|
||||||
|
'add' query gets :key, :value named arguments - key and value strings to store.
|
||||||
|
They should be added to the store. The query **should** not add multiple values
|
||||||
|
for the same key and **should** fail if the key already exists.
|
||||||
|
|
||||||
|
'list' query gets no arguments and should return a column with all keys in
|
||||||
|
the store.
|
||||||
|
|
||||||
|
'set' query gets :key, :value named arguments - key and value and should replace the existing
|
||||||
|
entry in the database.
|
||||||
|
|
||||||
|
'del' query gets :key argument - key and should remove it from the database.
|
||||||
|
|
||||||
|
If named\_args is set to "no" - key is passed as the first numbered parameter
|
||||||
|
($1), value is passed as the second numbered parameter ($2).
|
||||||
|
|
21
docs/reference/table/static.md
Normal file
21
docs/reference/table/static.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Static table
|
||||||
|
|
||||||
|
The 'static' module implements table lookups using key-value pairs in its
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
```
|
||||||
|
table.static {
|
||||||
|
entry KEY1 VALUE1
|
||||||
|
entry KEY2 VALUE2
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
***Syntax***: entry _key_ _value\_
|
||||||
|
|
||||||
|
Add an entry to the table.
|
||||||
|
|
||||||
|
If the same key is used multiple times, the last one takes effect.
|
||||||
|
|
84
docs/reference/targets/queue.md
Normal file
84
docs/reference/targets/queue.md
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# Local queue
|
||||||
|
|
||||||
|
Queue module buffers messages on disk and retries delivery multiple times to
|
||||||
|
another target to ensure reliable delivery.
|
||||||
|
|
||||||
|
It is also responsible for generation of DSN messages
|
||||||
|
in case of delivery failures.
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
First argument specifies directory to use for storage.
|
||||||
|
Relative paths are relative to the StateDirectory.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
target.queue {
|
||||||
|
target remote
|
||||||
|
location ...
|
||||||
|
max_parallelism 16
|
||||||
|
max_tries 4
|
||||||
|
bounce {
|
||||||
|
destination example.org {
|
||||||
|
deliver_to &local_mailboxes
|
||||||
|
}
|
||||||
|
default_destination {
|
||||||
|
reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
autogenerated_msg_domain example.org
|
||||||
|
debug no
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: target _block\_name_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
Delivery target to use for final delivery.
|
||||||
|
|
||||||
|
**Syntax**: location _directory_ <br>
|
||||||
|
**Default**: StateDirectory/configuration\_block\_name
|
||||||
|
|
||||||
|
File system directory to use to store queued messages.
|
||||||
|
Relative paths are relative to the StateDirectory.
|
||||||
|
|
||||||
|
**Syntax**: max\_parallelism _integer_ <br>
|
||||||
|
**Default**: 16
|
||||||
|
|
||||||
|
Start up to _integer_ goroutines for message processing. Basically, this option
|
||||||
|
limits amount of messages tried to be delivered concurrently.
|
||||||
|
|
||||||
|
**Syntax**: max\_tries _integer_ <br>
|
||||||
|
**Default**: 20
|
||||||
|
|
||||||
|
Attempt delivery up to _integer_ times. Note that no more attempts will be done
|
||||||
|
is permanent error occured during previous attempt.
|
||||||
|
|
||||||
|
Delay before the next attempt will be increased exponentally using the
|
||||||
|
following formula: 15mins \* 1.2 ^ (n - 1) where n is the attempt number.
|
||||||
|
This gives you approximately the following sequence of delays:
|
||||||
|
18mins, 21mins, 25mins, 31mins, 37mins, 44mins, 53mins, 64mins, ...
|
||||||
|
|
||||||
|
**Syntax**: bounce { ... } <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
This configuration contains pipeline configuration to be used for generated DSN
|
||||||
|
(Delivery Status Notifiaction) messages.
|
||||||
|
|
||||||
|
If this is block is not present in configuration, DSNs will not be generated.
|
||||||
|
Note, however, this is not what you want most of the time.
|
||||||
|
|
||||||
|
**Syntax**: autogenerated\_msg\_domain _domain_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Domain to use in sender address for DSNs. Should be specified too if 'bounce'
|
||||||
|
block is specified.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Enable verbose logging.
|
246
docs/reference/targets/remote.md
Normal file
246
docs/reference/targets/remote.md
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
# Remote MX delivery
|
||||||
|
|
||||||
|
Module that implements message delivery to remote MTAs discovered via DNS MX
|
||||||
|
records. You probably want to use it with queue module for reliability.
|
||||||
|
|
||||||
|
If a message check marks a message as 'quarantined', remote module
|
||||||
|
will refuse to deliver it.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
target.remote {
|
||||||
|
hostname mx.example.org
|
||||||
|
debug no
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: hostname _domain_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Hostname to use client greeting (EHLO/HELO command). Some servers require it to
|
||||||
|
be FQDN, SPF-capable servers check whether it corresponds to the server IP
|
||||||
|
address, so it is better to set it to a domain that resolves to the server IP.
|
||||||
|
|
||||||
|
**Syntax**: limits _config block_ <br>
|
||||||
|
**Default**: no limits
|
||||||
|
|
||||||
|
See ['limits' directive for SMTP endpoint](/reference/endpoints/smtp/#rate-concurrency-limiting).
|
||||||
|
It works the same except for address domains used for
|
||||||
|
per-source/per-destination are as observed when message exits the server.
|
||||||
|
|
||||||
|
**Syntax**: local\_ip _IP address_ <br>
|
||||||
|
**Default**: empty
|
||||||
|
|
||||||
|
Choose the local IP to bind for outbound SMTP connections.
|
||||||
|
|
||||||
|
**Syntax**: connect\_timeout _duration_ <br>
|
||||||
|
**Default**: 5m
|
||||||
|
|
||||||
|
Timeout for TCP connection establishment.
|
||||||
|
|
||||||
|
RFC 5321 recommends 5 minutes for "initial greeting" that includes TCP
|
||||||
|
handshake. maddy uses two separate timers - one for "dialing" (DNS A/AAAA
|
||||||
|
lookup + TCP handshake) and another for "initial greeting". This directive
|
||||||
|
configures the former. The latter is not configurable and is hardcoded to be
|
||||||
|
5 minutes.
|
||||||
|
|
||||||
|
**Syntax**: command\_timeout _duration_ <br>
|
||||||
|
**Default**: 5m
|
||||||
|
|
||||||
|
Timeout for any SMTP command (EHLO, MAIL, RCPT, DATA, etc).
|
||||||
|
|
||||||
|
If STARTTLS is used this timeout also applies to TLS handshake.
|
||||||
|
|
||||||
|
RFC 5321 recommends 5 minutes for MAIL/RCPT and 3 minutes for
|
||||||
|
DATA.
|
||||||
|
|
||||||
|
**Syntax**: submission\_timeout _duration_ <br>
|
||||||
|
**Default**: 12m
|
||||||
|
|
||||||
|
Time to wait after the entire message is sent (after "final dot").
|
||||||
|
|
||||||
|
RFC 5321 recommends 10 minutes.
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: requiretls\_override _boolean_ <br>
|
||||||
|
**Default**: true
|
||||||
|
|
||||||
|
Allow local security policy to be disabled using 'TLS-Required' header field in
|
||||||
|
sent messages. Note that the field has no effect if transparent forwarding is
|
||||||
|
used, message body should be processed before outbound delivery starts for it
|
||||||
|
to take effect (e.g. message should be queued using 'queue' module).
|
||||||
|
|
||||||
|
**Syntax**: relaxed\_requiretls _boolean_ <br>
|
||||||
|
**Default**: true
|
||||||
|
|
||||||
|
This option disables strict conformance with REQUIRETLS specification and
|
||||||
|
allows forwarding of messages 'tagged' with REQUIRETLS to MXes that are not
|
||||||
|
advertising REQUIRETLS support. It is meant to allow REQUIRETLS use without the
|
||||||
|
need to have support from all servers. It is based on the assumption that
|
||||||
|
server referenced by MX record is likely the final destination and therefore
|
||||||
|
there is only need to secure communication towards it and not beyond.
|
||||||
|
|
||||||
|
**Syntax**: conn\_reuse\_limit _integer_ <br>
|
||||||
|
**Default**: 10
|
||||||
|
|
||||||
|
Amount of times the same SMTP connection can be used.
|
||||||
|
Connections are never reused if the previous DATA command failed.
|
||||||
|
|
||||||
|
**Syntax**: conn\_max\_idle\_count _integer_ <br>
|
||||||
|
**Default**: 10
|
||||||
|
|
||||||
|
Max. amount of idle connections per recipient domains to keep in cache.
|
||||||
|
|
||||||
|
**Syntax**: conn\_max\_idle\_time _integer_ <br>
|
||||||
|
**Default**: 150 (2.5 min)
|
||||||
|
|
||||||
|
Amount of time the idle connection is still considered potentially usable.
|
||||||
|
|
||||||
|
## Security policies
|
||||||
|
|
||||||
|
**Syntax**: mx\_auth _config block_ <br>
|
||||||
|
**Default**: no policies
|
||||||
|
|
||||||
|
'remote' module implements a number of of schemes and protocols necessary to
|
||||||
|
ensure security of message delivery. Most of these schemes are concerned with
|
||||||
|
authentication of recipient server and TLS enforcement.
|
||||||
|
|
||||||
|
To enable mechanism, specify its name in the mx\_auth directive block:
|
||||||
|
```
|
||||||
|
mx_auth {
|
||||||
|
dane
|
||||||
|
mtasts
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Additional configuration is possible if supported by the mechanism by
|
||||||
|
specifying additional options as a block for the corresponding mechanism.
|
||||||
|
E.g.
|
||||||
|
```
|
||||||
|
mtasts {
|
||||||
|
cache ram
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the mx\_auth directive is not specified, no mechanisms are enabled. Note
|
||||||
|
that, however, this makes outbound SMTP vulnerable to a numberous downgrade
|
||||||
|
attacks and hence not recommended.
|
||||||
|
|
||||||
|
It is possible to share the same set of policies for multiple 'remote' module
|
||||||
|
instances by defining it at the top-level using 'mx\_auth' module and then
|
||||||
|
referencing it using standard & syntax:
|
||||||
|
```
|
||||||
|
mx_auth outbound_policy {
|
||||||
|
dane
|
||||||
|
mtasts {
|
||||||
|
cache ram
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ... somewhere else ...
|
||||||
|
|
||||||
|
deliver_to remote {
|
||||||
|
mx_auth &outbound_policy
|
||||||
|
}
|
||||||
|
|
||||||
|
# ... somewhere else ...
|
||||||
|
|
||||||
|
deliver_to remote {
|
||||||
|
mx_auth &outbound_policy
|
||||||
|
tls_client { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### MTA-STS
|
||||||
|
|
||||||
|
Checks MTA-STS policy of the recipient domain. Provides proper authentication
|
||||||
|
and TLS enforcement for delivery, but partially vulnerable to persistent active
|
||||||
|
attacks.
|
||||||
|
|
||||||
|
Sets MX level to "mtasts" if the used MX matches MTA-STS policy even if it is
|
||||||
|
not set to "enforce" mode.
|
||||||
|
|
||||||
|
```
|
||||||
|
mtasts {
|
||||||
|
cache fs
|
||||||
|
fs_dir StateDirectory/mtasts_cache
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: cache fs|ram <br>
|
||||||
|
**Default**: fs
|
||||||
|
|
||||||
|
Storage to use for MTA-STS cache. 'fs' is to use a filesystem directory, 'ram'
|
||||||
|
to store the cache in memory.
|
||||||
|
|
||||||
|
It is recommended to use 'fs' since that will not discard the cache (and thus
|
||||||
|
cause MTA-STS security to disappear) on server restart. However, using the RAM
|
||||||
|
cache can make sense for high-load configurations with good uptime.
|
||||||
|
|
||||||
|
**Syntax**: fs\_dir _directory_ <br>
|
||||||
|
**Default**: StateDirectory/mtasts\_cache
|
||||||
|
|
||||||
|
Filesystem directory to use for policies caching if 'cache' is set to 'fs'.
|
||||||
|
|
||||||
|
### DNSSEC
|
||||||
|
|
||||||
|
Checks whether MX records are signed. Sets MX level to "dnssec" is they are.
|
||||||
|
|
||||||
|
maddy does not validate DNSSEC signatures on its own. Instead it reslies on
|
||||||
|
the upstream resolver to do so by causing lookup to fail when verification
|
||||||
|
fails and setting the AD flag for signed and verfified zones. As a safety
|
||||||
|
measure, if the resolver is not 127.0.0.1 or ::1, the AD flag is ignored.
|
||||||
|
|
||||||
|
DNSSEC is currently not supported on Windows and other platforms that do not
|
||||||
|
have the /etc/resolv.conf file in the standard format.
|
||||||
|
|
||||||
|
```
|
||||||
|
dnssec { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### DANE
|
||||||
|
|
||||||
|
Checks TLSA records for the recipient MX. Provides downgrade-resistant TLS
|
||||||
|
enforcement.
|
||||||
|
|
||||||
|
Sets TLS level to "authenticated" if a valid and matching TLSA record uses
|
||||||
|
DANE-EE or DANE-TA usage type.
|
||||||
|
|
||||||
|
See above for notes on DNSSEC. DNSSEC support is required for DANE to work.
|
||||||
|
|
||||||
|
```
|
||||||
|
dane { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local policy
|
||||||
|
|
||||||
|
Checks effective TLS and MX levels (as set by other policies) against local
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
```
|
||||||
|
local_policy {
|
||||||
|
min_tls_level none
|
||||||
|
min_mx_level none
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using 'local\_policy off' is equivalent to setting both directives to 'none'.
|
||||||
|
|
||||||
|
**Syntax**: min\_tls\_level none|encrypted|authenticated <br>
|
||||||
|
**Default**: none
|
||||||
|
|
||||||
|
Set the minimal TLS security level required for all outbound messages.
|
||||||
|
|
||||||
|
See [Security levels](../../seclevels) page for details.
|
||||||
|
|
||||||
|
**Syntax**: min\_mx\_level: none|mtasts|dnssec <br>
|
||||||
|
**Default**: none
|
||||||
|
|
||||||
|
Set the minimal MX security level required for all outbound messages.
|
||||||
|
|
||||||
|
See [Security levels](../../seclevels) page for details.
|
||||||
|
|
114
docs/reference/targets/smtp.md
Normal file
114
docs/reference/targets/smtp.md
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
# SMTP & LMTP transparent forwarding
|
||||||
|
|
||||||
|
Module that implements transparent forwarding of messages over SMTP.
|
||||||
|
|
||||||
|
Use in pipeline configuration:
|
||||||
|
```
|
||||||
|
deliver_to smtp tcp://127.0.0.1:5353
|
||||||
|
# or
|
||||||
|
deliver_to smtp tcp://127.0.0.1:5353 {
|
||||||
|
# Other settings, see below.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
target.lmtp can be used instead of target.smtp to
|
||||||
|
use LMTP protocol.
|
||||||
|
|
||||||
|
Endpoint addresses use format described in [Configuration files syntax / Address definitions](/reference/config-syntax/#address-definitions).
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
target.smtp {
|
||||||
|
debug no
|
||||||
|
tls_client {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
attempt_starttls yes
|
||||||
|
require_yes no
|
||||||
|
auth off
|
||||||
|
targets tcp://127.0.0.1:2525
|
||||||
|
connect_timeout 5m
|
||||||
|
command_timeout 5m
|
||||||
|
submission_timeout 12m
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: debug _boolean_ <br>
|
||||||
|
**Default**: global directive value
|
||||||
|
|
||||||
|
Enable verbose logging.
|
||||||
|
|
||||||
|
**Syntax**: tls\_client { ... } <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Advanced TLS client configuration options. See [TLS configuration / Client](/reference/tls/#client) for details.
|
||||||
|
|
||||||
|
**Syntax**: attempt\_starttls _boolean_ <br>
|
||||||
|
**Default**: yes (no for target.lmtp)
|
||||||
|
|
||||||
|
Attempt to use STARTTLS if it is supported by the remote server.
|
||||||
|
If TLS handshake fails, connection will be retried without STARTTLS
|
||||||
|
unless 'require\_tls' is also specified.
|
||||||
|
|
||||||
|
**Syntax**: require\_tls _boolean_ <br>
|
||||||
|
**Default**: no
|
||||||
|
|
||||||
|
Refuse to pass messages over plain-text connections.
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
auth off <br>
|
||||||
|
plain _username_ _password_ <br>
|
||||||
|
forward <br>
|
||||||
|
external <br>
|
||||||
|
**Default**: off
|
||||||
|
|
||||||
|
Specify the way to authenticate to the remote server.
|
||||||
|
Valid values:
|
||||||
|
|
||||||
|
- off
|
||||||
|
|
||||||
|
No authentication.
|
||||||
|
|
||||||
|
- plain
|
||||||
|
|
||||||
|
Authenticate using specified username-password pair.
|
||||||
|
**Don't use** this without enforced TLS ('require\_tls').
|
||||||
|
|
||||||
|
- forward
|
||||||
|
|
||||||
|
Forward credentials specified by the client.
|
||||||
|
**Don't use** this without enforced TLS ('require\_tls').
|
||||||
|
|
||||||
|
- external
|
||||||
|
|
||||||
|
Request "external" SASL authentication. This is usually used for
|
||||||
|
authentication using TLS client certificates. See [TLS configuration / Client](/reference/tls/#client) for details.
|
||||||
|
|
||||||
|
**Syntax**: targets _endpoints..._ <br>
|
||||||
|
**Default:** not specified
|
||||||
|
|
||||||
|
REQUIRED.
|
||||||
|
|
||||||
|
List of remote server addresses to use. See [Address definitions](/reference/config-syntax/#address-definitions)
|
||||||
|
for syntax to use. Basically, it is 'tcp://ADDRESS:PORT'
|
||||||
|
for plain SMTP and 'tls://ADDRESS:PORT' for SMTPS (aka SMTP with Implicit
|
||||||
|
TLS).
|
||||||
|
|
||||||
|
Multiple addresses can be specified, they will be tried in order until connection to
|
||||||
|
one succeeds (including TLS handshake if TLS is required).
|
||||||
|
|
||||||
|
**Syntax**: connect\_timeout _duration_ <br>
|
||||||
|
**Default**: 5m
|
||||||
|
|
||||||
|
Same as for target.remote.
|
||||||
|
|
||||||
|
**Syntax**: command\_timeout _duration_ <br>
|
||||||
|
**Default**: 5m
|
||||||
|
|
||||||
|
Same as for target.remote.
|
||||||
|
|
||||||
|
**Syntax**: submission\_timeout _duration_ <br>
|
||||||
|
**Default**: 12m
|
||||||
|
|
||||||
|
Same as for target.remote.
|
225
docs/reference/tls-acme.md
Normal file
225
docs/reference/tls-acme.md
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
# Automatic certificate management via ACME
|
||||||
|
|
||||||
|
Maddy supports obtaining certificates using ACME protocol.
|
||||||
|
|
||||||
|
To use it, create a configuration name for tls.loader.acme
|
||||||
|
and reference it from endpoints that should use automatically
|
||||||
|
configured certificates:
|
||||||
|
```
|
||||||
|
tls.loader.acme local_tls {
|
||||||
|
email put-your-email-here@example.org
|
||||||
|
agreed # indicate your agreement with Let's Encrypt ToS
|
||||||
|
challenge dns-01
|
||||||
|
}
|
||||||
|
|
||||||
|
smtp tcp://127.0.0.1:25 {
|
||||||
|
tls &local_tls
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
You can also use a global `tls` directive to use automatically
|
||||||
|
obtained certificates for all endpoints:
|
||||||
|
```
|
||||||
|
tls &local_tls
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently the only supported challenge is dns-01 one therefore
|
||||||
|
you also need to configure the DNS provider:
|
||||||
|
```
|
||||||
|
tls.loader.acme local_tls {
|
||||||
|
email maddy-acme@example.org
|
||||||
|
agreed
|
||||||
|
challenge dns-01
|
||||||
|
dns PROVIDER_NAME {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
See below for supported providers and necessary configuration
|
||||||
|
for each.
|
||||||
|
|
||||||
|
## Configuration directives
|
||||||
|
|
||||||
|
```
|
||||||
|
tls.loader.acme {
|
||||||
|
debug off
|
||||||
|
hostname example.maddy.invalid
|
||||||
|
store_path /var/lib/maddy/acme
|
||||||
|
ca https://acme-v02.api.letsencrypt.org/directory
|
||||||
|
test_ca https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
email test@maddy.invalid
|
||||||
|
agreed off
|
||||||
|
challenge dns-01
|
||||||
|
dns ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax:** debug _boolean_ <br>
|
||||||
|
**Default:** global directive value
|
||||||
|
|
||||||
|
Enable debug logging.
|
||||||
|
|
||||||
|
**Syntax:** hostname _str_ <br>
|
||||||
|
**Default:** global directive value
|
||||||
|
|
||||||
|
Domain name to issue certificate for. Required.
|
||||||
|
|
||||||
|
**Syntax:** store\_path _path_ <br>
|
||||||
|
**Default:** state\_dir/acme
|
||||||
|
|
||||||
|
Where to store issued certificates and associated metadata.
|
||||||
|
Currently only filesystem-based store is supported.
|
||||||
|
|
||||||
|
**Syntax:** ca _url_ <br>
|
||||||
|
**Default:** Let's Encrypt production CA
|
||||||
|
|
||||||
|
URL of ACME directory to use.
|
||||||
|
|
||||||
|
**Syntax:** test\_ca _url_ <br>
|
||||||
|
**Default:** Let's Encrypt staging CA
|
||||||
|
|
||||||
|
URL of ACME directory to use for retries should
|
||||||
|
primary CA fail.
|
||||||
|
|
||||||
|
maddy will keep attempting to issues certificates
|
||||||
|
using test\_ca until it succeeds then it will switch
|
||||||
|
back to the one configured via 'ca' option.
|
||||||
|
|
||||||
|
This avoids rate limit issues with production CA.
|
||||||
|
|
||||||
|
**Syntax:** email _str_ <br>
|
||||||
|
**Default:** not set
|
||||||
|
|
||||||
|
Email to pass while registering an ACME account.
|
||||||
|
|
||||||
|
**Syntax:** agreed _boolean_ <br>
|
||||||
|
**Default:** false
|
||||||
|
|
||||||
|
Whether you agreed to ToS of the CA service you are using.
|
||||||
|
|
||||||
|
**Syntax:** challenge dns-01 <br>
|
||||||
|
**Default:** not set
|
||||||
|
|
||||||
|
Challenge(s) to use while performing domain verification.
|
||||||
|
|
||||||
|
## DNS providers
|
||||||
|
|
||||||
|
Support for some providers is not provided by standard builds.
|
||||||
|
To be able to use these, you need to compile maddy
|
||||||
|
with "libdns\_PROVIDER" build tag.
|
||||||
|
E.g.
|
||||||
|
```
|
||||||
|
./build.sh -tags 'libdns_googleclouddns'
|
||||||
|
```
|
||||||
|
|
||||||
|
- gandi
|
||||||
|
|
||||||
|
```
|
||||||
|
dns gandi {
|
||||||
|
api_token "token"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- digitalocean
|
||||||
|
|
||||||
|
```
|
||||||
|
dns digitalocean {
|
||||||
|
api_token "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- cloudflare
|
||||||
|
|
||||||
|
See [https://github.com/libdns/cloudflare#authenticating](https://github.com/libdns/cloudflare#authenticating)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns cloudflare {
|
||||||
|
api_token "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- vultr
|
||||||
|
|
||||||
|
```
|
||||||
|
dns vultr {
|
||||||
|
api_token "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- hetzner
|
||||||
|
|
||||||
|
```
|
||||||
|
dns hetzner {
|
||||||
|
api_token "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- namecheap
|
||||||
|
|
||||||
|
```
|
||||||
|
dns namecheap {
|
||||||
|
api_key "..."
|
||||||
|
api_username "..."
|
||||||
|
|
||||||
|
# optional: API endpoint, production one is used if not set.
|
||||||
|
endpoint "https://api.namecheap.com/xml.response"
|
||||||
|
|
||||||
|
# optional: your public IP, discovered using icanhazip.com if not set
|
||||||
|
client_ip 1.2.3.4
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- googleclouddns (non-default)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns googleclouddns {
|
||||||
|
project "project_id"
|
||||||
|
service_account_json "path"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- route53 (non-default)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns route53 {
|
||||||
|
secret_access_key "..."
|
||||||
|
access_key_id "..."
|
||||||
|
# or use environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- leaseweb (non-default)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns leaseweb {
|
||||||
|
api_key "key"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- metaname (non-default)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns metaname {
|
||||||
|
api_key "key"
|
||||||
|
account_ref "reference"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- alidns (non-default)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns alidns {
|
||||||
|
key_id "..."
|
||||||
|
key_secret "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- namedotcom (non-default)
|
||||||
|
|
||||||
|
```
|
||||||
|
dns namedotcom {
|
||||||
|
user "..."
|
||||||
|
token "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
155
docs/reference/tls.md
Normal file
155
docs/reference/tls.md
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
# TLS configuration
|
||||||
|
|
||||||
|
## Server-side
|
||||||
|
|
||||||
|
TLS certificates are obtained by modules called "certificate loaders". 'tls' directive
|
||||||
|
arguments specify name of loader to use and arguments. Due to syntax limitations
|
||||||
|
advanced configuration for loader should be specified using 'loader' directive, see
|
||||||
|
below.
|
||||||
|
|
||||||
|
```
|
||||||
|
tls file cert.pem key.pem {
|
||||||
|
protocols tls1.2 tls1.3
|
||||||
|
curve X25519
|
||||||
|
ciphers ...
|
||||||
|
}
|
||||||
|
|
||||||
|
tls {
|
||||||
|
loader file cert.pem key.pem {
|
||||||
|
# Options for loader go here.
|
||||||
|
}
|
||||||
|
protocols tls1.2 tls1.3
|
||||||
|
curve X25519
|
||||||
|
ciphers ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available certificate loaders
|
||||||
|
|
||||||
|
- file
|
||||||
|
|
||||||
|
Accepts argument pairs specifying certificate and then key.
|
||||||
|
E.g. 'tls file certA.pem keyA.pem certB.pem keyB.pem'
|
||||||
|
|
||||||
|
If multiple certificates are listed, SNI will be used.
|
||||||
|
|
||||||
|
- acme
|
||||||
|
|
||||||
|
Automatically obtains a certificate using ACME protocol (Let's Encrypt)
|
||||||
|
|
||||||
|
- off
|
||||||
|
|
||||||
|
Not really a loader but a special value for tls directive, explicitly disables TLS for
|
||||||
|
endpoint(s).
|
||||||
|
|
||||||
|
## Advanced TLS configuration
|
||||||
|
|
||||||
|
**Note: maddy uses secure defaults and TLS handshake is resistant to active downgrade attacks.**
|
||||||
|
**There is no need to change anything in most cases.**
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
protocols _min\_version_ _max\_version_ <br>
|
||||||
|
protocols _version_ <br>
|
||||||
|
**Default**: tls1.0 tls1.3
|
||||||
|
|
||||||
|
Minimum/maximum accepted TLS version. If only one value is specified, it will
|
||||||
|
be the only one usable version.
|
||||||
|
|
||||||
|
Valid values are: tls1.0, tls1.1, tls1.2, tls1.3
|
||||||
|
|
||||||
|
**Syntax**: ciphers _ciphers..._ <br>
|
||||||
|
**Default**: Go version-defined set of 'secure ciphers', ordered by hardware
|
||||||
|
performance
|
||||||
|
|
||||||
|
List of supported cipher suites, in preference order. Not used with TLS 1.3.
|
||||||
|
|
||||||
|
Valid values:
|
||||||
|
|
||||||
|
- RSA-WITH-RC4128-SHA
|
||||||
|
- RSA-WITH-3DES-EDE-CBC-SHA
|
||||||
|
- RSA-WITH-AES128-CBC-SHA
|
||||||
|
- RSA-WITH-AES256-CBC-SHA
|
||||||
|
- RSA-WITH-AES128-CBC-SHA256
|
||||||
|
- RSA-WITH-AES128-GCM-SHA256
|
||||||
|
- RSA-WITH-AES256-GCM-SHA384
|
||||||
|
- ECDHE-ECDSA-WITH-RC4128-SHA
|
||||||
|
- ECDHE-ECDSA-WITH-AES128-CBC-SHA
|
||||||
|
- ECDHE-ECDSA-WITH-AES256-CBC-SHA
|
||||||
|
- ECDHE-RSA-WITH-RC4128-SHA
|
||||||
|
- ECDHE-RSA-WITH-3DES-EDE-CBC-SHA
|
||||||
|
- ECDHE-RSA-WITH-AES128-CBC-SHA
|
||||||
|
- ECDHE-RSA-WITH-AES256-CBC-SHA
|
||||||
|
- ECDHE-ECDSA-WITH-AES128-CBC-SHA256
|
||||||
|
- ECDHE-RSA-WITH-AES128-CBC-SHA256
|
||||||
|
- ECDHE-RSA-WITH-AES128-GCM-SHA256
|
||||||
|
- ECDHE-ECDSA-WITH-AES128-GCM-SHA256
|
||||||
|
- ECDHE-RSA-WITH-AES256-GCM-SHA384
|
||||||
|
- ECDHE-ECDSA-WITH-AES256-GCM-SHA384
|
||||||
|
- ECDHE-RSA-WITH-CHACHA20-POLY1305
|
||||||
|
- ECDHE-ECDSA-WITH-CHACHA20-POLY1305
|
||||||
|
|
||||||
|
**Syntax**: curve _curves..._ <br>
|
||||||
|
**Default**: defined by Go version
|
||||||
|
|
||||||
|
The elliptic curves that will be used in an ECDHE handshake, in preference
|
||||||
|
order.
|
||||||
|
|
||||||
|
Valid values: p256, p384, p521, X25519.
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
tls\_client directive allows to customize behavior of TLS client implementation,
|
||||||
|
notably adjusting minimal and maximal TLS versions and allowed cipher suites,
|
||||||
|
enabling TLS client authentication.
|
||||||
|
|
||||||
|
```
|
||||||
|
tls_client {
|
||||||
|
protocols tls1.2 tls1.3
|
||||||
|
ciphers ...
|
||||||
|
curve X25519
|
||||||
|
root_ca /etc/ssl/cert.pem
|
||||||
|
|
||||||
|
cert /etc/ssl/private/maddy-client.pem
|
||||||
|
key /etc/ssl/private/maddy-client.pem
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
protocols _min\_version_ _max\_version_ <br>
|
||||||
|
protocols _version_ <br>
|
||||||
|
**Default**: tls1.0 tls1.3
|
||||||
|
|
||||||
|
Minimum/maximum accepted TLS version. If only one value is specified, it will
|
||||||
|
be the only one usable version.
|
||||||
|
|
||||||
|
Valid values are: tls1.0, tls1.1, tls1.2, tls1.3
|
||||||
|
|
||||||
|
**Syntax**: ciphers _ciphers..._ <br>
|
||||||
|
**Default**: Go version-defined set of 'secure ciphers', ordered by hardware
|
||||||
|
performance
|
||||||
|
|
||||||
|
List of supported cipher suites, in preference order. Not used with TLS 1.3.
|
||||||
|
|
||||||
|
See TLS server configuration for list of supported values.
|
||||||
|
|
||||||
|
**Syntax**: curve _curves..._ <br>
|
||||||
|
**Default**: defined by Go version
|
||||||
|
|
||||||
|
The elliptic curves that will be used in an ECDHE handshake, in preference
|
||||||
|
order.
|
||||||
|
|
||||||
|
Valid values: p256, p384, p521, X25519.
|
||||||
|
|
||||||
|
**Syntax**: root\_ca _paths..._ <br>
|
||||||
|
**Default**: system CA pool
|
||||||
|
|
||||||
|
List of files with PEM-encoded CA certificates to use when verifying
|
||||||
|
server certificates.
|
||||||
|
|
||||||
|
**Syntax**: <br>
|
||||||
|
cert _cert\_path_ <br>
|
||||||
|
key _key\_path_ <br>
|
||||||
|
**Default**: not specified
|
||||||
|
|
||||||
|
Present the specified certificate when server requests a client certificate.
|
||||||
|
Files should use PEM format. Both directives should be specified.
|
|
@ -1,4 +1,4 @@
|
||||||
# Security levels
|
# Outbound delivery security
|
||||||
|
|
||||||
maddy implements a number of schemes and protocols for discovery and
|
maddy implements a number of schemes and protocols for discovery and
|
||||||
enforcement of security features supported by the recipient MTA.
|
enforcement of security features supported by the recipient MTA.
|
||||||
|
@ -83,8 +83,7 @@ passive attacks.
|
||||||
|
|
||||||
## maddy security policies
|
## maddy security policies
|
||||||
|
|
||||||
See [**maddy-targets(5)**](../man/\_generated\_maddy-targets.5) page for
|
See [Remote MX delivery](/reference/targets/remote/) for description of configuration options available for each policy mechanism
|
||||||
description of configuration options available for each policy mechanism
|
|
||||||
supported by maddy.
|
supported by maddy.
|
||||||
|
|
||||||
[RFC 8461 Section 10.2]: https://www.rfc-editor.org/rfc/rfc8461.html#section-10.2 (SMTP MTA Strict Transport Security - 10.2. Preventing Policy Discovery)
|
[RFC 8461 Section 10.2]: https://www.rfc-editor.org/rfc/rfc8461.html#section-10.2 (SMTP MTA Strict Transport Security - 10.2. Preventing Policy Discovery)
|
||||||
|
|
6
docs/third-party/rspamd.md
vendored
6
docs/third-party/rspamd.md
vendored
|
@ -35,8 +35,4 @@ Default mapping of rspamd action -> maddy action is as follows:
|
||||||
- "rewrite subject" => Quarantine
|
- "rewrite subject" => Quarantine
|
||||||
- "soft reject" => Reject with temporary error
|
- "soft reject" => Reject with temporary error
|
||||||
- "reject" => Reject with permanent error
|
- "reject" => Reject with permanent error
|
||||||
- "greylist" => Ignored
|
- "greylist" => Ignored
|
||||||
|
|
||||||
This and additional data to pass to rspamd (MTA name, settings ID, etc)
|
|
||||||
can be configured as described in
|
|
||||||
[**maddy-checks**(5)](/man/_generated_maddy-filters.5/#rspamd-check-checkrspamd).
|
|
Loading…
Add table
Add a link
Reference in a new issue