blob: 78b070e0ef2a670cc5c67631d72fb3b6245253b5 [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001#!/bin/bash
2
3trap "postfix stop" EXIT
4
5[[ ! -d /opt/postfix/conf/sql/ ]] && mkdir -p /opt/postfix/conf/sql/
6
7# Wait for MySQL to warm-up
8while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
9 echo "Waiting for database to come up..."
10 sleep 2
11done
12
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020013until dig +short mailcow.email > /dev/null; do
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010014 echo "Waiting for DNS..."
15 sleep 1
16done
17
18cat <<EOF > /etc/aliases
19# Autogenerated by mailcow
20null: /dev/null
21watchdog: /dev/null
22ham: "|/usr/local/bin/rspamd-pipe-ham"
23spam: "|/usr/local/bin/rspamd-pipe-spam"
24EOF
25newaliases;
26
27# create sni configuration
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020028if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
29 echo -n "" > /opt/postfix/conf/sni.map
30else
31 echo -n "" > /opt/postfix/conf/sni.map;
32 for cert_dir in /etc/ssl/mail/*/ ; do
33 if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then
34 continue;
35 fi
36 IFS=" " read -r -a domains <<< "$(cat "${cert_dir}domains")"
37 for domain in "${domains[@]}"; do
38 echo -n "${domain} ${cert_dir}key.pem ${cert_dir}cert.pem" >> /opt/postfix/conf/sni.map;
39 echo "" >> /opt/postfix/conf/sni.map;
40 done
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010041 done
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020042fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010043postmap -F hash:/opt/postfix/conf/sni.map;
44
45cat <<EOF > /opt/postfix/conf/sql/mysql_relay_ne.cf
46# Autogenerated by mailcow
47user = ${DBUSER}
48password = ${DBPASS}
49hosts = unix:/var/run/mysqld/mysqld.sock
50dbname = ${DBNAME}
51query = SELECT IF(EXISTS(SELECT address, domain FROM alias
52 WHERE address = '%s'
53 AND domain IN (
54 SELECT domain FROM domain
55 WHERE backupmx = '1'
56 AND relay_all_recipients = '1'
57 AND relay_unknown_only = '1')
58
59 ), 'lmtp:inet:dovecot:24', NULL) AS 'transport'
60EOF
61
62cat <<EOF > /opt/postfix/conf/sql/mysql_relay_recipient_maps.cf
63# Autogenerated by mailcow
64user = ${DBUSER}
65password = ${DBPASS}
66hosts = unix:/var/run/mysqld/mysqld.sock
67dbname = ${DBNAME}
68query = SELECT DISTINCT
69 CASE WHEN '%d' IN (
70 SELECT domain FROM domain
71 WHERE relay_all_recipients=1
72 AND domain='%d'
73 AND backupmx=1
74 )
75 THEN '%s' ELSE (
76 SELECT goto FROM alias WHERE address='%s' AND active='1'
77 )
78 END AS result;
79EOF
80
81cat <<EOF > /opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf
82# Autogenerated by mailcow
83user = ${DBUSER}
84password = ${DBPASS}
85hosts = unix:/var/run/mysqld/mysqld.sock
86dbname = ${DBNAME}
87query = SELECT CONCAT(policy, ' ', parameters) AS tls_policy FROM tls_policy_override WHERE active = '1' AND dest = '%s'
88EOF
89
90cat <<EOF > /opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf
91# Autogenerated by mailcow
92user = ${DBUSER}
93password = ${DBPASS}
94hosts = unix:/var/run/mysqld/mysqld.sock
95dbname = ${DBNAME}
96query = SELECT IF(EXISTS(
97 SELECT 'TLS_ACTIVE' FROM alias
98 LEFT OUTER JOIN mailbox ON mailbox.username = alias.goto
99 WHERE (address='%s'
100 OR address IN (
101 SELECT CONCAT('%u', '@', target_domain) FROM alias_domain
102 WHERE alias_domain='%d'
103 )
104 ) AND JSON_UNQUOTE(JSON_VALUE(attributes, '$.tls_enforce_in')) = '1' AND mailbox.active = '1'
105 ), 'reject_plaintext_session', NULL) AS 'tls_enforce_in';
106EOF
107
108cat <<EOF > /opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf
109# Autogenerated by mailcow
110user = ${DBUSER}
111password = ${DBPASS}
112hosts = unix:/var/run/mysqld/mysqld.sock
113dbname = ${DBNAME}
114query = SELECT GROUP_CONCAT(transport SEPARATOR '') AS transport_maps
115 FROM (
116 SELECT IF(EXISTS(SELECT 'smtp_type' FROM alias
117 LEFT OUTER JOIN mailbox ON mailbox.username = alias.goto
118 WHERE (address = '%s'
119 OR address IN (
120 SELECT CONCAT('%u', '@', target_domain) FROM alias_domain
121 WHERE alias_domain = '%d'
122 )
123 )
124 AND JSON_UNQUOTE(JSON_VALUE(attributes, '$.tls_enforce_out')) = '1'
125 AND mailbox.active = '1'
126 ), 'smtp_enforced_tls:', 'smtp:') AS 'transport'
127 UNION ALL
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200128 SELECT COALESCE(
129 (SELECT hostname FROM relayhosts
130 LEFT OUTER JOIN mailbox ON JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.relayhost')) = relayhosts.id
131 WHERE relayhosts.active = '1'
132 AND (
133 mailbox.username IN (SELECT alias.goto from alias
134 JOIN mailbox ON mailbox.username = alias.goto
135 WHERE alias.active = '1'
136 AND alias.address = '%s'
137 AND alias.address NOT LIKE '@%%'
138 )
139 )
140 ),
141 (SELECT hostname FROM relayhosts
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100142 LEFT OUTER JOIN domain ON domain.relayhost = relayhosts.id
143 WHERE relayhosts.active = '1'
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200144 AND (domain.domain = '%d'
145 OR domain.domain IN (
146 SELECT target_domain FROM alias_domain
147 WHERE alias_domain = '%d'
148 )
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100149 )
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200150 )
151 )
152 ) AS transport_view;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100153EOF
154
155cat <<EOF > /opt/postfix/conf/sql/mysql_transport_maps.cf
156# Autogenerated by mailcow
157user = ${DBUSER}
158password = ${DBPASS}
159hosts = unix:/var/run/mysqld/mysqld.sock
160dbname = ${DBNAME}
161query = SELECT CONCAT('smtp_via_transport_maps:', nexthop) AS transport FROM transports
162 WHERE active = '1'
163 AND destination = '%s';
164EOF
165
166cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_resource_maps.cf
167# Autogenerated by mailcow
168user = ${DBUSER}
169password = ${DBPASS}
170hosts = unix:/var/run/mysqld/mysqld.sock
171dbname = ${DBNAME}
172query = SELECT 'null@localhost' FROM mailbox
173 WHERE kind REGEXP 'location|thing|group' AND username = '%s';
174EOF
175
176cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
177# Autogenerated by mailcow
178user = ${DBUSER}
179password = ${DBPASS}
180hosts = unix:/var/run/mysqld/mysqld.sock
181dbname = ${DBNAME}
182query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
183 WHERE id IN (
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100184 SELECT COALESCE(
185 (SELECT id FROM relayhosts
186 LEFT OUTER JOIN domain ON domain.relayhost = relayhosts.id
187 WHERE relayhosts.active = '1'
188 AND (domain.domain = '%d'
189 OR domain.domain IN (
190 SELECT target_domain FROM alias_domain
191 WHERE alias_domain = '%d'
192 )
193 )
194 ),
195 (SELECT id FROM relayhosts
196 LEFT OUTER JOIN mailbox ON JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.relayhost')) = relayhosts.id
197 WHERE relayhosts.active = '1'
198 AND (
199 mailbox.username IN (
200 SELECT alias.goto from alias
201 JOIN mailbox ON mailbox.username = alias.goto
202 WHERE alias.active = '1'
203 AND alias.address = '%s'
204 AND alias.address NOT LIKE '@%%'
205 )
206 )
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100207 )
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100208 )
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100209 )
210 AND active = '1'
211 AND username != '';
212EOF
213
214cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf
215# Autogenerated by mailcow
216user = ${DBUSER}
217password = ${DBPASS}
218hosts = unix:/var/run/mysqld/mysqld.sock
219dbname = ${DBNAME}
220query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM transports
221 WHERE nexthop = '%s'
222 AND active = '1'
223 AND username != ''
224 LIMIT 1;
225EOF
226
227cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf
228# Autogenerated by mailcow
229user = ${DBUSER}
230password = ${DBPASS}
231hosts = unix:/var/run/mysqld/mysqld.sock
232dbname = ${DBNAME}
233query = SELECT username FROM mailbox, alias_domain
234 WHERE alias_domain.alias_domain = '%d'
235 AND mailbox.username = CONCAT('%u', '@', alias_domain.target_domain)
236 AND (mailbox.active = '1' OR mailbox.active = '2')
237 AND alias_domain.active='1'
238EOF
239
240cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_maps.cf
241# Autogenerated by mailcow
242user = ${DBUSER}
243password = ${DBPASS}
244hosts = unix:/var/run/mysqld/mysqld.sock
245dbname = ${DBNAME}
246query = SELECT goto FROM alias
247 WHERE address='%s'
248 AND (active='1' OR active='2');
249EOF
250
251cat <<EOF > /opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
252# Autogenerated by mailcow
253user = ${DBUSER}
254password = ${DBPASS}
255hosts = unix:/var/run/mysqld/mysqld.sock
256dbname = ${DBNAME}
257query = SELECT bcc_dest FROM bcc_maps
258 WHERE local_dest='%s'
259 AND type='rcpt'
260 AND active='1';
261EOF
262
263cat <<EOF > /opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
264# Autogenerated by mailcow
265user = ${DBUSER}
266password = ${DBPASS}
267hosts = unix:/var/run/mysqld/mysqld.sock
268dbname = ${DBNAME}
269query = SELECT bcc_dest FROM bcc_maps
270 WHERE local_dest='%s'
271 AND type='sender'
272 AND active='1';
273EOF
274
275cat <<EOF > /opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
276# Autogenerated by mailcow
277user = ${DBUSER}
278password = ${DBPASS}
279hosts = unix:/var/run/mysqld/mysqld.sock
280dbname = ${DBNAME}
281query = SELECT new_dest FROM recipient_maps
282 WHERE old_dest='%s'
283 AND active='1';
284EOF
285
286cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
287# Autogenerated by mailcow
288user = ${DBUSER}
289password = ${DBPASS}
290hosts = unix:/var/run/mysqld/mysqld.sock
291dbname = ${DBNAME}
292query = SELECT alias_domain from alias_domain WHERE alias_domain='%s' AND active='1'
293 UNION
294 SELECT domain FROM domain
295 WHERE domain='%s'
296 AND active = '1'
297 AND backupmx = '0'
298EOF
299
300cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
301# Autogenerated by mailcow
302user = ${DBUSER}
303password = ${DBPASS}
304hosts = unix:/var/run/mysqld/mysqld.sock
305dbname = ${DBNAME}
306query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%u/') FROM mailbox WHERE username='%s' AND (active = '1' OR active = '2')
307EOF
308
309cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf
310# Autogenerated by mailcow
311user = ${DBUSER}
312password = ${DBPASS}
313hosts = unix:/var/run/mysqld/mysqld.sock
314dbname = ${DBNAME}
315query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '1' AND active = '1'
316EOF
317
318cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_sender_acl.cf
319# Autogenerated by mailcow
320user = ${DBUSER}
321password = ${DBPASS}
322hosts = unix:/var/run/mysqld/mysqld.sock
323dbname = ${DBNAME}
324# First select queries domain and alias_domain to determine if domains are active.
325query = SELECT goto FROM alias
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100326 WHERE id IN (
327 SELECT COALESCE (
328 (
329 SELECT id FROM alias
330 WHERE address='%s'
331 AND (active='1' OR active='2')
332 ), (
333 SELECT id FROM alias
334 WHERE address='@%d'
335 AND (active='1' OR active='2')
336 )
337 )
338 )
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100339 AND active='1'
340 AND (domain IN
341 (SELECT domain FROM domain
342 WHERE domain='%d'
343 AND active='1')
344 OR domain in (
345 SELECT alias_domain FROM alias_domain
346 WHERE alias_domain='%d'
347 AND active='1'
348 )
349 )
350 UNION
351 SELECT logged_in_as FROM sender_acl
352 WHERE send_as='@%d'
353 OR send_as='%s'
354 OR send_as='*'
355 OR send_as IN (
356 SELECT CONCAT('@',target_domain) FROM alias_domain
357 WHERE alias_domain = '%d')
358 OR send_as IN (
359 SELECT CONCAT('%u','@',target_domain) FROM alias_domain
360 WHERE alias_domain = '%d')
361 AND logged_in_as NOT IN (
362 SELECT goto FROM alias
363 WHERE address='%s')
364 UNION
365 SELECT username FROM mailbox, alias_domain
366 WHERE alias_domain.alias_domain = '%d'
367 AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain)
368 AND (mailbox.active = '1' OR mailbox.active ='2')
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100369 AND alias_domain.active='1';
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100370EOF
371
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200372# MX based routing
373cat <<EOF > /opt/postfix/conf/sql/mysql_mbr_access_maps.cf
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100374# Autogenerated by mailcow
375user = ${DBUSER}
376password = ${DBPASS}
377hosts = unix:/var/run/mysqld/mysqld.sock
378dbname = ${DBNAME}
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200379query = SELECT CONCAT('FILTER smtp_via_transport_maps:', nexthop) as transport FROM transports
380 WHERE '%s' REGEXP destination
381 AND active='1'
382 AND is_mx_based='1';
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100383EOF
384
385cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf
386# Autogenerated by mailcow
387user = ${DBUSER}
388password = ${DBPASS}
389hosts = unix:/var/run/mysqld/mysqld.sock
390dbname = ${DBNAME}
391query = SELECT goto FROM spamalias
392 WHERE address='%s'
393 AND validity >= UNIX_TIMESTAMP()
394EOF
395
396sed -i '/User overrides/q' /opt/postfix/conf/main.cf
397echo >> /opt/postfix/conf/main.cf
398touch /opt/postfix/conf/extra.cf
399sed -i '/myhostname/d' /opt/postfix/conf/extra.cf
400echo -e "myhostname = ${MAILCOW_HOSTNAME}\n$(cat /opt/postfix/conf/extra.cf)" > /opt/postfix/conf/extra.cf
401
402cat /opt/postfix/conf/extra.cf >> /opt/postfix/conf/main.cf
403
404if [ ! -f /opt/postfix/conf/custom_transport.pcre ]; then
405 echo "Creating dummy custom_transport.pcre"
406 touch /opt/postfix/conf/custom_transport.pcre
407fi
408
409if [[ ! -f /opt/postfix/conf/custom_postscreen_whitelist.cidr ]]; then
410 echo "Creating dummy custom_postscreen_whitelist.cidr"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200411 cat <<EOF > /opt/postfix/conf/custom_postscreen_whitelist.cidr
412# Autogenerated by mailcow
413# Rules are evaluated in the order as specified.
414# Blacklist 192.168.* except 192.168.0.1.
415# 192.168.0.1 permit
416# 192.168.0.0/16 reject
417EOF
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100418fi
419
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100420# Fix Postfix permissions
421chown -R root:postfix /opt/postfix/conf/sql/ /opt/postfix/conf/custom_transport.pcre
422chmod 640 /opt/postfix/conf/sql/*.cf /opt/postfix/conf/custom_transport.pcre
423chgrp -R postdrop /var/spool/postfix/public
424chgrp -R postdrop /var/spool/postfix/maildrop
425postfix set-permissions
426
427# Check Postfix configuration
428postconf -c /opt/postfix/conf > /dev/null
429
430if [[ $? != 0 ]]; then
431 echo "Postfix configuration error, refusing to start."
432 exit 1
433else
434 postfix -c /opt/postfix/conf start
435 sleep 126144000
436fi