Skip to content

Commit a7001ab

Browse files
committed
Implement auth_map and storage_map at endpoint level
This makes auth_map do what its name implies. Old auth_map in storage module is deprecated and will be removed in the next release.
1 parent 43c0325 commit a7001ab

File tree

18 files changed

+559
-122
lines changed

18 files changed

+559
-122
lines changed

docs/multiple-domains.md

Lines changed: 129 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,157 @@
11
# Multiple domains configuration
22

3-
## Separate account namespaces
3+
By default, maddy uses email addresses as account identifiers for both
4+
authentication and storage purposes. Therefore, account named `user@example.org`
5+
is completely independent from `user@example.com`. They must be created
6+
separately, may have different credentials and have separate IMAP mailboxes.
47

5-
Given two domains, example.org and example.com. foo@example.org and
6-
foo@example.com are different and completely independent accounts.
8+
This makes it extremely easy to setup maddy to manage multiple otherwise
9+
independent domains.
710

8-
All changes needed to make it work is to make sure all domains are specified in
9-
the `$(local_domains)` macro in the main configuration file. Note that you need
10-
to pick one domain as a "primary" for use in auto-generated messages.
11+
Default configuration file contains two macros - `$(primary_domain)` and
12+
`$(local_domains)`. They are used to used in several places thorough the
13+
file to configure message routing, security checks, etc.
14+
15+
In general, you should just add all domains you want maddy to manage to
16+
`$(local_domains)`, like this:
1117
```
1218
$(primary_domain) = example.org
1319
$(local_domains) = $(primary_domain) example.com
1420
```
21+
Note that you need to pick one domain as a "primary" for use in
22+
auto-generated messages.
1523

16-
The base configuration is done. You can create accounts using
17-
both domains in the name, send and receive messages and so on. Do not forget
18-
to configure corresponding SPF, DMARC and MTA-STS records as was
19-
recommended in the [introduction tutorial](tutorials/setting-up.md).
24+
With that done, you can create accounts using both domains in the name, send
25+
and receive messages and so on. Do not forget to configure corresponding SPF,
26+
DMARC and MTA-STS records as was recommended in
27+
the [introduction tutorial](tutorials/setting-up.md).
2028

21-
## Single account namespace
29+
Also note that you do not really need a separate TLS certificate for each
30+
managed domain. You can have one hostname e.g. mail.example.org set as an MX
31+
record for mulitple domains.
2232

23-
You can configure maddy to only use local part of the email
24-
as an account identifier instead of the complete email.
33+
**If you want multiple domains to share username namespace**, you should change
34+
several more options.
2535

26-
This needs two changes to default configuration:
36+
You can make "user@example.org" and "user@example.com" users share the same
37+
credentials of user "user" but have different IMAP mailboxes ("user@example.org"
38+
and "user@example.com" correspondingly). For that, it is enough to set `auth_map`
39+
globally to use `email_localpart` table:
2740
```
28-
storage.imapsql local_mailboxes {
29-
...
30-
delivery_map email_localpart
31-
auth_normalize precis_casefold
32-
}
41+
auth_map email_localpart
42+
```
43+
This way, when user logs in as "user@example.org", "user" will be passed
44+
to the authentication provider, but "user@example.org" will be passed to the
45+
storage backend. You should create accounts like this:
46+
```
47+
maddy creds create user
48+
maddy imap-acct create user@example.org
49+
maddy imap-acct create user@example.com
3350
```
3451

35-
This way, when authenticating as `foxcpp`, it will be mapped to
36-
`foxcpp` storage account. E.g. you will need to run
37-
`maddy imap-accts create foxcpp`, without the domain part.
38-
39-
If you have existing accounts, you will need to rename them.
40-
41-
Change to `auth_normalize` is necessary so that normalization function
42-
will not attempt to parse authentication identity as a email.
52+
**If you want accounts to also share the same IMAP storage of account named
53+
"user"**, you can set `storage_map` in IMAP endpoint and `delivery_map` in
54+
storage backend to use `email_locapart`:
55+
```
56+
straoge.imapsql local_mailboxes {
57+
...
58+
delivery_map email_localpart # deliver "user@*" to "user"
59+
}
60+
imap tls://0.0.0.0:993 {
61+
...
62+
storage &local_mailboxes
63+
...
64+
storage_map email_localpart # "user@*" accesses "user" mailbox
65+
}
66+
```
4367

44-
When a email is received, `delivery_map email_localpart` will strip
45-
the domain part before looking up the account. That is,
46-
`foxcpp@example.org` will be become just `foxcpp`.
68+
You also might want to make it possible to log in without
69+
specifying a domain at all. In this case, use `email_localpart_optional` for
70+
both `auth_map` and `storage_map`.
4771

4872
You also need to make `authorize_sender` check (used in `submission` endpoint)
4973
accept non-email usernames:
5074
```
5175
authorize_sender {
5276
...
53-
auth_normalize precis_casefold
54-
user_to_email regexp "(.*)" "$1@$(primary_domain)"
77+
user_to_email chain {
78+
step email_localpart_optional # remove domain from username if present
79+
step email_with_domains $(local_domains) # expand username with all allowed domains
80+
}
5581
}
5682
```
57-
Note that is would work only if clients use only one domain as sender (`$(primary_domain)`).
58-
If you want to allow sending from all domains, you need to remove `authorize_sender` check
59-
altogether since it is not currently supported.
6083

61-
After that you can create accounts without specifying the domain part:
84+
## TL;DR
85+
86+
Your options:
87+
88+
**"user@example.org" and "user@example.com" have distinct credentials and
89+
distinct mailboxes.**
90+
91+
```
92+
$(primary_domain) = example.org
93+
$(local_domains) = example.org example.com
94+
```
95+
96+
Create accounts as:
97+
98+
```shell
99+
maddy creds create user@example.org
100+
maddy imap-acct create user@example.org
101+
maddy creds create user@example.com
102+
maddy imap-acct create user@example.com
103+
```
104+
105+
**"user@example.org" and "user@example.com" have same credentials but
106+
distinct mailboxes.**
107+
108+
```
109+
$(primary_domain) = example.org
110+
$(local_domains) = example.org example.com
111+
auth_map email_localpart
112+
```
113+
114+
Create accounts as:
115+
```shell
116+
maddy creds create user
117+
maddy imap-acct create user@example.org
118+
maddy imap-acct create user@example.com
119+
```
120+
121+
**"user@example.org", "user@example.com", "user" have same credentials and same
122+
mailboxes.**
123+
62124
```
63-
maddy imap-acct create foxcpp
64-
maddy creds create foxcpp
125+
$(primary_domain) = example.org
126+
$(local_domains) = example.org example.com
127+
auth_map email_localpart_optional # authenticating as "user@*" checks credentials for "user"
128+
129+
storage.imapsql local_mailboxes {
130+
...
131+
delivery_map email_localpart_optional # deliver "user@*" to "user" mailbox
132+
}
133+
134+
imap tls://0.0.0.0:993 {
135+
...
136+
storage_map email_localpart_optional # authenticating as "user@*" accesses "user" mailboxes
137+
}
138+
139+
submission tls://0.0.0.0:465 {
140+
check {
141+
authorize_sender {
142+
...
143+
user_to_email chain {
144+
step email_localpart_optional # remove domain from username if present
145+
step email_with_domains $(local_domains) # expand username with all allowed domains
146+
}
147+
}
148+
}
149+
...
150+
}
65151
```
66-
And authenticate using "foxcpp" in email clients.
67152

68-
Messages for any foxcpp@* address with a domain in `$(local_domains)`
69-
will be delivered to that mailbox.
153+
Create accounts as:
154+
```shell
155+
maddy creds create user
156+
maddy imap-acct create user
157+
```

docs/reference/checks/authorize_sender.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ check.authorize_sender {
1616
malformed_action reject
1717
err_action reject
1818
19-
auth_normalize precis_casefold_email
20-
from_normalize precis_casefold_email
19+
auth_normalize auto
20+
from_normalize auto
2121
}
2222
```
2323
```
@@ -88,25 +88,26 @@ What to do if From or Sender header fields contain malformed values.
8888
What to do if error happens during prepare\_email or user\_to\_email lookup.
8989

9090
**Syntax:** auth\_normalize _action_ <br>
91-
**Default:** precis\_casefold\_email
91+
**Default:** auto
9292

9393
Normalization function to apply to authorization username before
9494
further processing.
9595

9696
Available options:
97-
- precis\_casefold\_email PRECIS UsernameCaseMapped profile + Unicode form for domain
98-
- precis\_casefold PRECIS UsernameCaseMapped profile for the entire string
99-
- precis\_email PRECIS UsernameCasePreserved profile + Unicode form for domain
100-
- precis PRECIS UsernameCasePreserved profile for the entire string
101-
- casefold Convert to lower case
102-
- noop Nothing
97+
- `auto` `precis_casefold_email` for valid emails, `precise_casefold` otherwise.
98+
- `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain
99+
- `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string
100+
- `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain
101+
- `precis` PRECIS UsernameCasePreserved profile for the entire string
102+
- `casefold` Convert to lower case
103+
- `noop` Nothing
103104

104105
PRECIS profiles are defined by RFC 8265. In short, they make sure
105106
that Unicode strings that look the same will be compared as if they were
106107
the same. CaseMapped profiles also convert strings to lower case.
107108

108109
**Syntax:** from\_normalize _action_ <br>
109-
**Default:** precis\_casefold\_email
110+
**Default:** auto
110111

111112
Normalization function to apply to email addresses before
112113
further processing.

docs/reference/endpoints/imap.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ imap tcp://0.0.0.0:143 tls://0.0.0.0:993 {
2020
insecure_auth no
2121
auth pam
2222
storage &local_mailboxes
23+
auth_map identity
24+
auth_map_normalize auto
25+
storage_map identity
26+
storage_map_normalize auto
2327
}
2428
```
2529

@@ -62,4 +66,46 @@ Use the specified module for authentication.
6266
**Syntax**: storage _module\_reference\_
6367

6468
Use the specified module for message storage.
65-
**Required.**
69+
**Required.**
70+
71+
**Syntax**: storage\_map _module\_reference_ <br>
72+
**Default**: identity
73+
74+
Use the specified table to map SASL usernames to storage account names.
75+
76+
Before username is looked up, it is normalized using function defined by
77+
`storage_map_normalize`.
78+
79+
This directive is useful if you want users user@example.org and user@example.com
80+
to share the same storage account named "user". In this case, use
81+
```
82+
storage_map email_localpart
83+
```
84+
85+
Note that `storage_map` does not affect the username passed to the
86+
authentication provider.
87+
88+
It also does not affect how message delivery is handled, you should specify
89+
`delivery_map` in storage module to define how to map email addresses
90+
to storage accounts. E.g.
91+
```
92+
storage.imapsql local_mailboxes {
93+
...
94+
delivery_map email_localpart # deliver "user@*" to mailbox for "user"
95+
}
96+
```
97+
98+
**Syntax**: storage\_map_normalize _function_ <br>
99+
**Default**: auto
100+
101+
Same as `auth_map_normalize` but for `storage_map`.
102+
103+
**Syntax**: auth\_map_normalize _function_ <br>
104+
**Default**: auto
105+
106+
Overrides global `auth_map_normalize` value for this endpoint.
107+
108+
See [Global configuration](/reference/global-config) for details.
109+
110+
111+

docs/reference/global-config.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Global configuration directives
22

3-
These directives can be specified outside of any
3+
These directives can be specified outside of any
44
configuration blocks and they are applied to all modules.
55

66
Some directives can be overridden on per-module basis (e.g. hostname).
@@ -23,6 +23,56 @@ objects. Should be writable.
2323
Internet hostname of this mail server. Typicall FQDN is used. It is recommended
2424
to make sure domain specified here resolved to the public IP of the server.
2525

26+
**Syntax**: auth\_map _module\_reference_ <br>
27+
**Default**: identity
28+
29+
Use the specified table to translate SASL usernames before passing it to the
30+
authentication provider.
31+
32+
Before username is looked up, it is normalized using function defined by
33+
`auth_map_normalize`.
34+
35+
Note that `auth_map` does not affect the storage account name used. You probably
36+
should also use `storage_map` in IMAP config block to handle this.
37+
38+
This directive is useful if used authentication provider does not support
39+
using emails as usernames but you still want users to have separate mailboxes
40+
on separate domains. In this case, use it with `email_localpart` table:
41+
```
42+
auth_map email_localpart
43+
```
44+
With this configuration, `user@example.org` and `user@example.com` will use
45+
`user` credentials when authenticating, but will access `user@example.org` and
46+
`user@example.com` mailboxes correspondingly. If you want to also accept
47+
`user` as a username, use `auth_map email_localpart_optional`.
48+
49+
If you want `user@example.org` and `user@example.com` to have the same mailbox,
50+
also set `storage_map` in IMAP config block to use `email_localpart`
51+
(or `email_localpart_optional` if you want to also accept just "user"):
52+
```
53+
storage_map email_localpart
54+
```
55+
In this case you will need to create storage accounts without domain part in
56+
the name:
57+
```
58+
maddy imap-acct create user # instead of user@example.org
59+
```
60+
61+
**Syntax**: auth\_map_normalize _function_ <br>
62+
**Default**: auto
63+
64+
Normalization function to apply to SASL usernames before mapping
65+
them to storage accounts.
66+
67+
Available options:
68+
- `auto` `precis_casefold_email` for valid emails, `precise_casefold` otherwise.
69+
- `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain
70+
- `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string
71+
- `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain
72+
- `precis` PRECIS UsernameCasePreserved profile for the entire string
73+
- `casefold` Convert to lower case
74+
- `noop` Nothing
75+
2676
**Syntax**: autogenerated\_msg\_domain _domain_ <br>
2777
**Default**: not specified
2878

docs/reference/storage/imapsql.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ See auth\_normalize.
156156
**Syntax**: auth\_map **table** <br>
157157
**Default**: identity
158158

159+
**DEPRECATED:** Use `storage_map` in imap config instead.
160+
159161
Use specified table module to map authentication
160162
usernames to mailbox names.
161163

@@ -165,6 +167,8 @@ auth\_map.
165167
**Syntax**: auth\_normalize _name_ <br>
166168
**Default**: precis\_casefold\_email
167169

170+
**DEPRECATED:** Use `storage_map_normalize` in imap config instead.
171+
168172
Normalization function to apply to authentication usernames before mapping
169173
them to mailboxes.
170174

docs/reference/table/email_with_domain.md renamed to docs/reference/table/email_with_domains.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ with `table.chain`. Example:
1212
```
1313
modify {
1414
replace_rcpt chain {
15-
email_local_part
16-
email_with_domains example.org example.com
15+
step email_local_part
16+
step email_with_domains example.org example.com
1717
}
1818
}
1919
```

0 commit comments

Comments
 (0)