diff options
author | clarkzjw <[email protected]> | 2023-02-08 00:40:09 -0800 |
---|---|---|
committer | clarkzjw <[email protected]> | 2023-02-08 00:40:09 -0800 |
commit | 1204730924436ef9e1c7c49c9557837f9a5ed0e8 (patch) | |
tree | 129d79dfd11245751cee6d4082ff5d2f6e941610 /ansible/roles/dovecot | |
parent | 9635ac4dedf69de5bff65785bcc16bef80b52d75 (diff) | |
download | mail-1204730924436ef9e1c7c49c9557837f9a5ed0e8.tar.gz |
Diffstat (limited to 'ansible/roles/dovecot')
21 files changed, 638 insertions, 0 deletions
diff --git a/ansible/roles/dovecot/files/dovecot/authdb.sqlite3.empty b/ansible/roles/dovecot/files/dovecot/authdb.sqlite3.empty new file mode 100644 index 0000000..f1e0330 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/authdb.sqlite3.empty | |||
Binary files differ | |||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/10-acl.conf b/ansible/roles/dovecot/files/dovecot/conf.d/10-acl.conf new file mode 100644 index 0000000..f9fa335 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/10-acl.conf | |||
@@ -0,0 +1,15 @@ | |||
1 | mail_plugins = $mail_plugins acl | ||
2 | |||
3 | protocol imap { | ||
4 | mail_plugins = $mail_plugins imap_acl | ||
5 | } | ||
6 | |||
7 | plugin { | ||
8 | acl_defaults_from_inbox = yes | ||
9 | } | ||
10 | |||
11 | # Should saving a mail to a nonexistent mailbox automatically create it? | ||
12 | lda_mailbox_autocreate = yes | ||
13 | |||
14 | # Should automatically created mailboxes be also automatically subscribed? | ||
15 | lda_mailbox_autosubscribe = yes | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/10-auth.conf b/ansible/roles/dovecot/files/dovecot/conf.d/10-auth.conf new file mode 100644 index 0000000..4e23fa8 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/10-auth.conf | |||
@@ -0,0 +1,46 @@ | |||
1 | # cache all authentication results for one hour | ||
2 | #auth_cache_size = 10M | ||
3 | #auth_cache_ttl = 1 hour | ||
4 | #auth_cache_negative_ttl = 1 hour | ||
5 | |||
6 | # Don't cache password details, otherwise password changes require a server HUP | ||
7 | # before the server will re-query the password data source. | ||
8 | auth_cache_size = 0 | ||
9 | |||
10 | # only use plain username/password auth - OK since everything is over TLS | ||
11 | auth_mechanisms = plain | ||
12 | |||
13 | # passdb specifies how users are authenticated - sql here, and | ||
14 | # our sql config specifies the sqlite filename with queries to use | ||
15 | passdb { | ||
16 | driver = sql | ||
17 | args = /etc/dovecot/dovecot-sql.conf.ext | ||
18 | } | ||
19 | |||
20 | # userdb specifies the location of users' "home" directories - where their | ||
21 | # mail is stored. e.g. /var/mail/vhosts/exmaple.com/user | ||
22 | # %d = domain, %n = user | ||
23 | # We can't use "prefetch" because postfix can't read users from "prefetch" db, | ||
24 | # and we can't use 'static' because the doveadm tool needs to iterate users | ||
25 | # for purging zero refcount deleted mails, so we give a userdb of sql here | ||
26 | # and specify a "get all users" SQL query in the configuration file. | ||
27 | userdb { | ||
28 | driver = sql | ||
29 | args = /etc/dovecot/dovecot-sql.conf.ext | ||
30 | } | ||
31 | |||
32 | # UNIX socket path to master authentication server to find users. | ||
33 | # This is used by imap (for shared users) and lda. | ||
34 | auth_socket_path = /var/run/dovecot/auth-userdb | ||
35 | |||
36 | # Respect /etc/hosts.deny (populated by fail2ban) | ||
37 | # You can use /etc/hosts.allow to countermand fail2ban decisions. | ||
38 | login_access_sockets = tcpwrap | ||
39 | |||
40 | service tcpwrap { | ||
41 | unix_listener login/tcpwrap { | ||
42 | group = $default_login_user | ||
43 | mode = 0600 | ||
44 | user = $default_login_user | ||
45 | } | ||
46 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/10-mail.conf b/ansible/roles/dovecot/files/dovecot/conf.d/10-mail.conf new file mode 100644 index 0000000..6c03965 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/10-mail.conf | |||
@@ -0,0 +1,28 @@ | |||
1 | # default home directory location for all users | ||
2 | mail_home = /var/mail/vhosts/%d/%n | ||
3 | |||
4 | # directory to store mail. The tilda makes it relative to the *dovecot* | ||
5 | # virtual home directory. | ||
6 | # | ||
7 | # I use mdbox - this is Dovecot's own high-performance mail store format. | ||
8 | # There are other slower, more "traditional" formats you can choose from. | ||
9 | # Read about them here: https://wiki2.dovecot.org/MailboxFormat | ||
10 | mail_location = mdbox:~/mdbox | ||
11 | |||
12 | # nothing fancy - just a standard default namespace with '/' as the | ||
13 | # hierarchy separator | ||
14 | namespace inbox { | ||
15 | separator = / | ||
16 | inbox = yes | ||
17 | } | ||
18 | |||
19 | # set this to the group that owns your vmail directory. | ||
20 | mail_privileged_group = vmail | ||
21 | |||
22 | # these lines enable attachment deduplication. Attachments must be somewhat | ||
23 | # large (64k) to store them separately from the mail store. | ||
24 | mail_attachment_dir = /var/mail/attachments | ||
25 | mail_attachment_min_size = 64k | ||
26 | |||
27 | # we'll uncomment this after we set up Solr in the following section: | ||
28 | # mail_plugins = $mail_plugins fts fts_solr | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/10-master.conf b/ansible/roles/dovecot/files/dovecot/conf.d/10-master.conf new file mode 100644 index 0000000..f99d0f4 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/10-master.conf | |||
@@ -0,0 +1,82 @@ | |||
1 | # to improve performance, disable fsync globally - we will enable it for | ||
2 | # some specific services later on | ||
3 | mail_fsync = never | ||
4 | |||
5 | service imap-login { | ||
6 | # plain-text IMAP should only be accessible from localhost | ||
7 | inet_listener imap { | ||
8 | address = 127.0.0.1, ::1 | ||
9 | } | ||
10 | |||
11 | # enable high-performance mode, described here: | ||
12 | # https://wiki.dovecot.org/LoginProcess | ||
13 | service_count = 0 | ||
14 | |||
15 | # set to the number of CPU cores on your server | ||
16 | process_min_avail = 3 | ||
17 | vsz_limit = 1G | ||
18 | } | ||
19 | |||
20 | # disable POP3 altogether | ||
21 | service pop3-login { | ||
22 | inet_listener pop3 { | ||
23 | port = 0 | ||
24 | } | ||
25 | |||
26 | inet_listener pop3s { | ||
27 | port = 0 | ||
28 | } | ||
29 | } | ||
30 | |||
31 | # enable semi-long-lived IMAP processes to improve performance | ||
32 | service imap { | ||
33 | service_count = 256 | ||
34 | # set to the number of CPU cores on your server | ||
35 | process_min_avail = 3 | ||
36 | } | ||
37 | |||
38 | # expose an LMTP socket for postfix to deliver mail | ||
39 | service lmtp { | ||
40 | unix_listener /var/spool/postfix/private/dovecot-lmtp { | ||
41 | group = postfix | ||
42 | mode = 0600 | ||
43 | user = postfix | ||
44 | } | ||
45 | } | ||
46 | |||
47 | service auth { | ||
48 | # auth_socket_path points to this userdb socket by default. It's typically | ||
49 | # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have | ||
50 | # full permissions to this socket are able to get a list of all usernames and | ||
51 | # get the results of everyone's userdb lookups. | ||
52 | # | ||
53 | # The default 0666 mode allows anyone to connect to the socket, but the | ||
54 | # userdb lookups will succeed only if the userdb returns an "uid" field that | ||
55 | # matches the caller process's UID. Also if caller's uid or gid matches the | ||
56 | # socket's uid or gid the lookup succeeds. Anything else causes a failure. | ||
57 | # | ||
58 | # To give the caller full permissions to lookup all users, set the mode to | ||
59 | # something else than 0666 and Dovecot lets the kernel enforce the | ||
60 | # permissions (e.g. 0777 allows everyone full permissions). | ||
61 | |||
62 | # auth for postfix | ||
63 | unix_listener /var/spool/postfix/private/auth { | ||
64 | mode = 0666 | ||
65 | user = postfix | ||
66 | group = postfix | ||
67 | } | ||
68 | |||
69 | # auth for doveadm tools | ||
70 | unix_listener auth-userdb { | ||
71 | mode = 0666 | ||
72 | user = vmail | ||
73 | group = vmail | ||
74 | } | ||
75 | |||
76 | client_limit = 840 | ||
77 | } | ||
78 | |||
79 | # no need to run this as root | ||
80 | service auth-worker { | ||
81 | user = vmail | ||
82 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/15-lda.conf b/ansible/roles/dovecot/files/dovecot/conf.d/15-lda.conf new file mode 100644 index 0000000..32ca50d --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/15-lda.conf | |||
@@ -0,0 +1,7 @@ | |||
1 | # configuration for mail delivered by the `dovecot-lda` command. Shouldn't | ||
2 | # be needed since we are using LMTP, but kept for backwards compatibility. | ||
3 | protocol lda { | ||
4 | # use fsync for write-safety - this deals with delivering actual mail | ||
5 | mail_fsync = optimized | ||
6 | mail_plugins = $mail_plugins sieve | ||
7 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/15-mailboxes.conf b/ansible/roles/dovecot/files/dovecot/conf.d/15-mailboxes.conf new file mode 100644 index 0000000..8674b0b --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/15-mailboxes.conf | |||
@@ -0,0 +1,24 @@ | |||
1 | # define any special IMAP folders here. You can force them to be created or | ||
2 | # created+subscribed automatically used the `auto` option. | ||
3 | namespace inbox { | ||
4 | mailbox Drafts { | ||
5 | auto = subscribe | ||
6 | special_use = \Drafts | ||
7 | } | ||
8 | mailbox Junk { | ||
9 | auto = create | ||
10 | special_use = \Junk | ||
11 | } | ||
12 | mailbox Trash { | ||
13 | auto = create | ||
14 | special_use = \Trash | ||
15 | } | ||
16 | mailbox Archive { | ||
17 | auto = subscribe | ||
18 | special_use = \Archive | ||
19 | } | ||
20 | mailbox Sent { | ||
21 | auto = subscribe | ||
22 | special_use = \Sent | ||
23 | } | ||
24 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/20-imap.conf b/ansible/roles/dovecot/files/dovecot/conf.d/20-imap.conf new file mode 100644 index 0000000..7b32396 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/20-imap.conf | |||
@@ -0,0 +1,10 @@ | |||
1 | # Use a longer IDLE interval to reduce network chatter and save battery | ||
2 | # life. Max is 30 minutes. | ||
3 | imap_idle_notify_interval = 29 mins | ||
4 | |||
5 | protocol imap { | ||
6 | # max IMAP connections per IP address | ||
7 | mail_max_userip_connections = 50 | ||
8 | # imap_sieve will be used for spam training by rspamd | ||
9 | mail_plugins = $mail_plugins imap_sieve | ||
10 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/20-lmtp.conf b/ansible/roles/dovecot/files/dovecot/conf.d/20-lmtp.conf new file mode 100644 index 0000000..a51ee42 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/20-lmtp.conf | |||
@@ -0,0 +1,5 @@ | |||
1 | protocol lmtp { | ||
2 | # use fsync for write-safety - this deals with delivering actual mail | ||
3 | mail_fsync = optimized | ||
4 | mail_plugins = $mail_plugins sieve | ||
5 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/90-imapsieve.conf b/ansible/roles/dovecot/files/dovecot/conf.d/90-imapsieve.conf new file mode 100644 index 0000000..26987b1 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/90-imapsieve.conf | |||
@@ -0,0 +1,18 @@ | |||
1 | plugin { | ||
2 | sieve_plugins = sieve_imapsieve sieve_extprograms | ||
3 | |||
4 | # From elsewhere to Junk folder | ||
5 | imapsieve_mailbox1_name = Junk | ||
6 | imapsieve_mailbox1_causes = COPY | ||
7 | imapsieve_mailbox1_before = file:/etc/dovecot/sieve/report-spam.sieve | ||
8 | |||
9 | # From Junk folder to elsewhere | ||
10 | imapsieve_mailbox2_name = * | ||
11 | imapsieve_mailbox2_from = Junk | ||
12 | imapsieve_mailbox2_causes = COPY | ||
13 | imapsieve_mailbox2_before = file:/etc/dovecot/sieve/report-ham.sieve | ||
14 | |||
15 | sieve_pipe_bin_dir = /etc/dovecot/sieve | ||
16 | |||
17 | sieve_global_extensions = +vnd.dovecot.pipe | ||
18 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/conf.d/90-sieve.conf b/ansible/roles/dovecot/files/dovecot/conf.d/90-sieve.conf new file mode 100644 index 0000000..9a753bf --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/conf.d/90-sieve.conf | |||
@@ -0,0 +1,34 @@ | |||
1 | plugin { | ||
2 | # 'active' is a symlink to one sieve source script inside directory at 'file' | ||
3 | sieve = file:~/sieve;active=~/.dovecot.sieve | ||
4 | |||
5 | # directory of global sieve scripts to run before and after processing ALL | ||
6 | # incoming mail | ||
7 | sieve_before = /etc/dovecot/sieve-before.d | ||
8 | sieve_after = /etc/dovecot/sieve-after.d | ||
9 | |||
10 | # make sieve aware of [email protected] aliases | ||
11 | recipient_delimiter = + | ||
12 | |||
13 | |||
14 | # no limits on script size or actions | ||
15 | sieve_quota_max_storage = 0 | ||
16 | sieve_max_script_size = 0 | ||
17 | sieve_max_actions = 0 | ||
18 | |||
19 | sieve_extensions = +spamtest +spamtestplus | ||
20 | |||
21 | sieve_spamtest_status_header = X-Spam-Score | ||
22 | sieve_spamtest_status_type = strlen | ||
23 | |||
24 | # X-Spamd-Bar: +++++++++ | ||
25 | sieve_spamtest_max_value = 9 | ||
26 | |||
27 | |||
28 | # X-Spamd-Result: default: False [9.19 / 15.00]; | ||
29 | # (regex not fixed to capture the above) | ||
30 | #sieve_spamtest_status_type = score | ||
31 | #sieve_spamtest_status_header = \ | ||
32 | # X-Spamd-Result: [[:alnum:]]+, score=(-?[[:digit:]]+\.[[:digit:]]) | ||
33 | #sieve_spamtest_max_value = 5.0 | ||
34 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/dovecot-sql.conf.ext b/ansible/roles/dovecot/files/dovecot/dovecot-sql.conf.ext new file mode 100644 index 0000000..f248243 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/dovecot-sql.conf.ext | |||
@@ -0,0 +1,150 @@ | |||
1 | # This file is commonly accessed via passdb {} or userdb {} section in | ||
2 | # conf.d/auth-sql.conf.ext | ||
3 | |||
4 | # This file is opened as root, so it should be owned by root and mode 0600. | ||
5 | # | ||
6 | # http://wiki2.dovecot.org/AuthDatabase/SQL | ||
7 | # | ||
8 | # For the sql passdb module, you'll need a database with a table that | ||
9 | # contains fields for at least the username and password. If you want to | ||
10 | # use the user@domain syntax, you might want to have a separate domain | ||
11 | # field as well. | ||
12 | # | ||
13 | # If your users all have the same uig/gid, and have predictable home | ||
14 | # directories, you can use the static userdb module to generate the home | ||
15 | # dir based on the username and domain. In this case, you won't need fields | ||
16 | # for home, uid, or gid in the database. | ||
17 | # | ||
18 | # If you prefer to use the sql userdb module, you'll want to add fields | ||
19 | # for home, uid, and gid. Here is an example table: | ||
20 | # | ||
21 | # CREATE TABLE users ( | ||
22 | # username VARCHAR(128) NOT NULL, | ||
23 | # domain VARCHAR(128) NOT NULL, | ||
24 | # password VARCHAR(64) NOT NULL, | ||
25 | # home VARCHAR(255) NOT NULL, | ||
26 | # uid INTEGER NOT NULL, | ||
27 | # gid INTEGER NOT NULL, | ||
28 | # active CHAR(1) DEFAULT 'Y' NOT NULL | ||
29 | # ); | ||
30 | |||
31 | # Database driver: mysql, pgsql, sqlite | ||
32 | driver = sqlite | ||
33 | |||
34 | # Database connection string. This is driver-specific setting. | ||
35 | # | ||
36 | # HA / round-robin load-balancing is supported by giving multiple host | ||
37 | # settings, like: host=sql1.host.org host=sql2.host.org | ||
38 | # | ||
39 | # pgsql: | ||
40 | # For available options, see the PostgreSQL documention for the | ||
41 | # PQconnectdb function of libpq. | ||
42 | # Use maxconns=n (default 5) to change how many connections Dovecot can | ||
43 | # create to pgsql. | ||
44 | # | ||
45 | # mysql: | ||
46 | # Basic options emulate PostgreSQL option names: | ||
47 | # host, port, user, password, dbname | ||
48 | # | ||
49 | # But also adds some new settings: | ||
50 | # client_flags - See MySQL manual | ||
51 | # connect_timeout - Connect timeout in seconds (default: 5) | ||
52 | # read_timeout - Read timeout in seconds (default: 30) | ||
53 | # write_timeout - Write timeout in seconds (default: 30) | ||
54 | # ssl_ca, ssl_ca_path - Set either one or both to enable SSL | ||
55 | # ssl_cert, ssl_key - For sending client-side certificates to server | ||
56 | # ssl_cipher - Set minimum allowed cipher security (default: HIGH) | ||
57 | # ssl_verify_server_cert - Verify that the name in the server SSL certificate | ||
58 | # matches the host (default: no) | ||
59 | # option_file - Read options from the given file instead of | ||
60 | # the default my.cnf location | ||
61 | # option_group - Read options from the given group (default: client) | ||
62 | # | ||
63 | # You can connect to UNIX sockets by using host: host=/var/run/mysql.sock | ||
64 | # Note that currently you can't use spaces in parameters. | ||
65 | # | ||
66 | # sqlite: | ||
67 | # The path to the database file. | ||
68 | # | ||
69 | # Examples: | ||
70 | # connect = host=192.168.1.1 dbname=users | ||
71 | # connect = host=sql.example.com dbname=virtual user=virtual password=blarg | ||
72 | # connect = /etc/dovecot/authdb.sqlite | ||
73 | # | ||
74 | connect = /etc/dovecot/authdb.sqlite | ||
75 | |||
76 | # Default password scheme. | ||
77 | # | ||
78 | # List of supported schemes is in | ||
79 | # http://wiki2.dovecot.org/Authentication/PasswordSchemes | ||
80 | # | ||
81 | #default_pass_scheme = SHA512-CRYPT | ||
82 | |||
83 | # passdb query to retrieve the password. It can return fields: | ||
84 | # password - The user's password. This field must be returned. | ||
85 | # user - user@domain from the database. Needed with case-insensitive lookups. | ||
86 | # username and domain - An alternative way to represent the "user" field. | ||
87 | # | ||
88 | # The "user" field is often necessary with case-insensitive lookups to avoid | ||
89 | # e.g. "name" and "nAme" logins creating two different mail directories. If | ||
90 | # your user and domain names are in separate fields, you can return "username" | ||
91 | # and "domain" fields instead of "user". | ||
92 | # | ||
93 | # The query can also return other fields which have a special meaning, see | ||
94 | # http://wiki2.dovecot.org/PasswordDatabase/ExtraFields | ||
95 | # | ||
96 | # Commonly used available substitutions (see http://wiki2.dovecot.org/Variables | ||
97 | # for full list): | ||
98 | # %u = entire user@domain | ||
99 | # %n = user part of user@domain | ||
100 | # %d = domain part of user@domain | ||
101 | # | ||
102 | # Note that these can be used only as input to SQL query. If the query outputs | ||
103 | # any of these substitutions, they're not touched. Otherwise it would be | ||
104 | # difficult to have eg. usernames containing '%' characters. | ||
105 | # | ||
106 | # Example: | ||
107 | # password_query = SELECT userid AS user, pw AS password \ | ||
108 | # FROM users WHERE userid = '%u' AND active = 'Y' | ||
109 | # | ||
110 | password_query = \ | ||
111 | SELECT '%u' AS username, domain, password \ | ||
112 | FROM users WHERE userid = '%n' AND domain = '%d' | ||
113 | |||
114 | # You can update (or modify this a bit to insert) user passwords in a shell with: | ||
115 | # sqlite3 authdb.sqlite "update users set password='$(doveadm pw -s SHA512-CRYPT -r 1856250)' where userid='USERNAME' and domain = 'DOMAIN';" | ||
116 | |||
117 | |||
118 | # userdb query to retrieve the user information. It can return fields: | ||
119 | # uid - System UID (overrides mail_uid setting) | ||
120 | # gid - System GID (overrides mail_gid setting) | ||
121 | # home - Home directory | ||
122 | # mail - Mail location (overrides mail_location setting) | ||
123 | # | ||
124 | # None of these are strictly required. If you use a single UID and GID, and | ||
125 | # home or mail directory fits to a template string, you could use userdb static | ||
126 | # instead. For a list of all fields that can be returned, see | ||
127 | # http://wiki2.dovecot.org/UserDatabase/ExtraFields | ||
128 | # | ||
129 | # Examples: | ||
130 | # user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' | ||
131 | # user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' | ||
132 | # user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' | ||
133 | # | ||
134 | user_query = \ | ||
135 | SELECT "/var/mail/vhosts/" || '%d' || '/' || '%n' AS home, 145 as uid, 145 as gid | ||
136 | |||
137 | # If you wish to avoid two SQL lookups (passdb + userdb), you can use | ||
138 | # userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll | ||
139 | # also have to return userdb fields in password_query prefixed with "userdb_" | ||
140 | # string. For example: | ||
141 | password_query = \ | ||
142 | SELECT '%u' AS user, password, \ | ||
143 | "/var/mail/vhosts/" || '%d' || '/' || '%n' AS userdb_home, 145 AS userdb_uid, 145 AS userdb_gid \ | ||
144 | FROM users WHERE userid = '%n' AND domain = '%d' | ||
145 | |||
146 | # Query to get a list of all usernames. | ||
147 | # This iteration is used for things like globally purging zero refcount emails | ||
148 | # for all users, but to get all users, we have to iterate the user storage, | ||
149 | # hence this iterator query is required. | ||
150 | iterate_query = SELECT userid AS user FROM users | ||
diff --git a/ansible/roles/dovecot/files/dovecot/dovecot.conf b/ansible/roles/dovecot/files/dovecot/dovecot.conf new file mode 100644 index 0000000..4304fa6 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/dovecot.conf | |||
@@ -0,0 +1,19 @@ | |||
1 | # IMAP for remote access, LMTP for local delivery | ||
2 | protocols = imap lmtp | ||
3 | |||
4 | # set these to the uid of your `vmail` user | ||
5 | first_valid_uid = 145 | ||
6 | last_valid_uid = 145 | ||
7 | |||
8 | #mail_debug = yes | ||
9 | ##auth_verbose = yes | ||
10 | ##auth_debug = yes | ||
11 | ##auth_debug_passwords = yes | ||
12 | ##auth_verbose_passwords = yes | ||
13 | |||
14 | |||
15 | mail_uid = vmail | ||
16 | mail_gid = vmail | ||
17 | |||
18 | !include conf.d/*.conf | ||
19 | !include_try local.conf | ||
diff --git a/ansible/roles/dovecot/files/dovecot/sieve-before.d/10-rspamd.sieve b/ansible/roles/dovecot/files/dovecot/sieve-before.d/10-rspamd.sieve new file mode 100644 index 0000000..7931a71 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/sieve-before.d/10-rspamd.sieve | |||
@@ -0,0 +1,5 @@ | |||
1 | require ["fileinto"]; | ||
2 | |||
3 | if header :is "X-Spam" "Yes" { | ||
4 | fileinto "Junk"; | ||
5 | } | ||
diff --git a/ansible/roles/dovecot/files/dovecot/sieve/report-ham.sieve b/ansible/roles/dovecot/files/dovecot/sieve/report-ham.sieve new file mode 100644 index 0000000..2ad40aa --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/sieve/report-ham.sieve | |||
@@ -0,0 +1,23 @@ | |||
1 | require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; | ||
2 | |||
3 | if environment :matches "imap.mailbox" "*" { | ||
4 | set "mailbox" "${1}"; | ||
5 | } | ||
6 | |||
7 | # This line is important because when we delete from Junk/Spam, | ||
8 | # messages get moved to Trash, which tirggers the "message moved out of | ||
9 | # spam" script (this script) which—usually!—trains the originally classified | ||
10 | # Spam as not-spam. | ||
11 | # BUT, this is just a delete! If we train our spam as not-spam on delete, that | ||
12 | # defeats our goals. | ||
13 | # In short, this always gets run on a message being moved out of Spam, but if | ||
14 | # the target mailbox is Trash, just don't run the trainer this time. | ||
15 | if string "${mailbox}" "Trash" { | ||
16 | stop; | ||
17 | } | ||
18 | |||
19 | if environment :matches "imap.email" "*" { | ||
20 | set "email" "${1}"; | ||
21 | } | ||
22 | |||
23 | pipe :copy "train-ham.sh" [ "${email}" ]; | ||
diff --git a/ansible/roles/dovecot/files/dovecot/sieve/report-spam.sieve b/ansible/roles/dovecot/files/dovecot/sieve/report-spam.sieve new file mode 100644 index 0000000..4ed95d7 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/sieve/report-spam.sieve | |||
@@ -0,0 +1,7 @@ | |||
1 | require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; | ||
2 | |||
3 | if environment :matches "imap.email" "*" { | ||
4 | set "email" "${1}"; | ||
5 | } | ||
6 | |||
7 | pipe :copy "train-spam.sh" [ "${email}" ]; | ||
diff --git a/ansible/roles/dovecot/files/dovecot/sieve/train-ham.sh b/ansible/roles/dovecot/files/dovecot/sieve/train-ham.sh new file mode 100755 index 0000000..b0f30fd --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/sieve/train-ham.sh | |||
@@ -0,0 +1 @@ | |||
exec /usr/bin/rspamc --connect localhost:11334 learn_ham | |||
diff --git a/ansible/roles/dovecot/files/dovecot/sieve/train-spam.sh b/ansible/roles/dovecot/files/dovecot/sieve/train-spam.sh new file mode 100755 index 0000000..bf1b920 --- /dev/null +++ b/ansible/roles/dovecot/files/dovecot/sieve/train-spam.sh | |||
@@ -0,0 +1 @@ | |||
exec /usr/bin/rspamc --connect localhost:11334 learn_spam | |||
diff --git a/ansible/roles/dovecot/handlers/main.yml b/ansible/roles/dovecot/handlers/main.yml new file mode 100644 index 0000000..dda7930 --- /dev/null +++ b/ansible/roles/dovecot/handlers/main.yml | |||
@@ -0,0 +1,31 @@ | |||
1 | --- | ||
2 | - name: restart dovecot | ||
3 | service: | ||
4 | name: dovecot | ||
5 | state: restarted | ||
6 | |||
7 | - name: reload dovecot | ||
8 | service: | ||
9 | name: dovecot | ||
10 | state: reloaded | ||
11 | |||
12 | # We intentionally don't have a "creates:" guard on the resieve handlers | ||
13 | # because if they get called we need to re-run them on any changes of | ||
14 | # the underlying script itself regardless whether the result .svbin | ||
15 | # already exists or not. | ||
16 | - name: resieve spam | ||
17 | command: sievec report-spam.sieve | ||
18 | args: | ||
19 | chdir: /etc/dovecot/sieve | ||
20 | |||
21 | - name: resieve ham | ||
22 | command: sievec report-ham.sieve | ||
23 | args: | ||
24 | chdir: /etc/dovecot/sieve | ||
25 | |||
26 | - name: resieve spam mover | ||
27 | command: sievec 10-rspamd.sieve | ||
28 | args: | ||
29 | chdir: /etc/dovecot/sieve-before.d | ||
30 | creates: 10-rspamd.svbin | ||
31 | |||
diff --git a/ansible/roles/dovecot/tasks/main.yml b/ansible/roles/dovecot/tasks/main.yml new file mode 100644 index 0000000..6e097c1 --- /dev/null +++ b/ansible/roles/dovecot/tasks/main.yml | |||
@@ -0,0 +1,110 @@ | |||
1 | --- | ||
2 | # dovecot install and configuration | ||
3 | - name: install dovecot | ||
4 | apt: | ||
5 | state: latest | ||
6 | pkg: | ||
7 | - dovecot-imapd | ||
8 | - dovecot-lmtpd | ||
9 | - dovecot-sieve | ||
10 | - dovecot-sqlite | ||
11 | |||
12 | # Convert existing maildir to mdbox (local on-fs dirs) with: | ||
13 | # dsync -o mail_location=mdbox:herebox mirror maildir:Maildir | ||
14 | # Or, you can pull from a remote site: | ||
15 | # Below, -R means REVERSE backup so PULL messages FROM vorash INTO mdbox, | ||
16 | # otherwise, it's a PUSH backup and mdbox PUSHES to vorash which isn't what we want | ||
17 | # doveadm -o mail_location=mdbox:herebox backup -R ssh -J [email protected] matt@vorash doveadm dsync-server | ||
18 | - name: create mail spool dirs | ||
19 | file: | ||
20 | path: /var/mail/local | ||
21 | owner: root | ||
22 | group: mail | ||
23 | mode: 0775 | ||
24 | state: directory | ||
25 | |||
26 | - name: create dovecot virtual mailbox group | ||
27 | group: | ||
28 | name: vmail | ||
29 | gid: 145 | ||
30 | state: present | ||
31 | |||
32 | - name: create dovecot virtual mailbox and virtual authentication account | ||
33 | user: | ||
34 | name: vmail | ||
35 | uid: 145 | ||
36 | group: vmail | ||
37 | shell: /sbin/nologin | ||
38 | create_home: yes | ||
39 | home: /var/mail/vhosts | ||
40 | state: present | ||
41 | |||
42 | - name: give dovecot user permission to read private keys | ||
43 | user: | ||
44 | name: dovecot | ||
45 | groups: ssl-cert | ||
46 | append: yes | ||
47 | |||
48 | # Create new passwords with: | ||
49 | # time doveadm pw -s SHA512-CRYPT -r 1856250 | ||
50 | - name: copy dovecot configs and userdb | ||
51 | copy: | ||
52 | src: dovecot/ | ||
53 | dest: /etc/dovecot/ | ||
54 | mode: preserve | ||
55 | notify: | ||
56 | - resieve spam | ||
57 | - resieve ham | ||
58 | - resieve spam mover | ||
59 | - restart dovecot | ||
60 | |||
61 | # This permission is important because dovecot has multiple users: | ||
62 | # - dovecot | ||
63 | # - dovenull | ||
64 | # - vmail | ||
65 | # but login processes are run by the 'vmail' user, so 'vmail' must have read | ||
66 | # access to the DB | ||
67 | - name: fix user permissions on authdb | ||
68 | file: | ||
69 | path: /etc/dovecot/authdb.sqlite | ||
70 | owner: vmail | ||
71 | group: vmail | ||
72 | mode: 0600 | ||
73 | |||
74 | - name: instantiate dovecot SSL template with host vars | ||
75 | template: | ||
76 | src: dovecot/conf.d/10-ssl.conf.j2 | ||
77 | dest: /etc/dovecot/conf.d/10-ssl.conf | ||
78 | notify: | ||
79 | - restart dovecot # NB this could be a reload instead | ||
80 | |||
81 | # Dovecot mdbox format requires a purge to remove storage space | ||
82 | # allocated to messages that have been fully deleted by users. | ||
83 | # (it's an append-only refcounting system, so when a refcount becomes | ||
84 | # zero on final delete, it needs some cleanup to rewrite the old | ||
85 | # pack files without the deleted emails present anymore.) | ||
86 | - cron: | ||
87 | name: setup cron so dovecot can GC mailboxes | ||
88 | minute: 0 | ||
89 | hour: 3 | ||
90 | user: vmail | ||
91 | job: "doveadm purge -A" | ||
92 | cron_file: dovecot_maint_purge | ||
93 | |||
94 | |||
95 | # verify everything is running | ||
96 | - name: verify services are running in dependency order | ||
97 | service: | ||
98 | name: "{{ item }}" | ||
99 | enabled: yes | ||
100 | state: started | ||
101 | loop: | ||
102 | - dovecot | ||
103 | |||
104 | - name: reload if certs newish | ||
105 | include_role: | ||
106 | name: certreload | ||
107 | vars: | ||
108 | certreload: | ||
109 | notifiers: | ||
110 | - reload dovecot | ||
diff --git a/ansible/roles/dovecot/templates/dovecot/conf.d/10-ssl.conf.j2 b/ansible/roles/dovecot/templates/dovecot/conf.d/10-ssl.conf.j2 new file mode 100644 index 0000000..c1654cd --- /dev/null +++ b/ansible/roles/dovecot/templates/dovecot/conf.d/10-ssl.conf.j2 | |||
@@ -0,0 +1,22 @@ | |||
1 | # require SSL for all non-localhost connections | ||
2 | ssl = required | ||
3 | |||
4 | # Config detials at https://wiki.dovecot.org/SSL/DovecotConfiguration | ||
5 | ssl_cert = </etc/ssl/{{ network.hostname.public }}-cert-combined.rsa2048.pem | ||
6 | ssl_key = </etc/ssl/private/{{ network.hostname.public }}-key.rsa2048.pem | ||
7 | |||
8 | # Since v2.2.31+ you can specify alternative ssl certificate | ||
9 | # if the algorithm differs from the primary certificate. | ||
10 | # This is useful when migrating to e.g. ECDSA certificate. | ||
11 | ssl_alt_cert = </etc/ssl/{{ network.hostname.public }}-cert-combined.prime256v1.pem | ||
12 | ssl_alt_key = </etc/ssl/private/{{ network.hostname.public }}-key.prime256v1.pem | ||
13 | |||
14 | # require modern crypto - taken from Mozilla's SSL recommendations page | ||
15 | ssl_dh_parameters_length = 4096 | ||
16 | ssl_protocols = !SSLv3 !TLSv1 !TLSv1.1 TLSv1.2 | ||
17 | ssl_cipher_list = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 | ||
18 | ssl_prefer_server_ciphers = yes | ||
19 | |||
20 | # newer dovecot 2.3+ | ||
21 | #ssl_min_protocol = TLSv1.2 | ||
22 | #ssl_dh = /etc/ssl/ffdhe4096.pem | ||