-
Notifications
You must be signed in to change notification settings - Fork 3
/
make-mta.sh
executable file
·410 lines (355 loc) · 11.2 KB
/
make-mta.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
#!/bin/bash
set -e
if [ $(whoami) != "root" ]; then
echo "You have to be root to run this script"
exit
fi
log=/tmp/dns.txt
if [ -d /etc/exim4 ]; then
echo "You already seem to have an exim installation. Running this script"
echo -n "may be dangerous. Continue anyway? (y/n) "
read answer
if [ "$answer" != "y" ]; then
exit
fi
fi
# Do preliminary pre-MTA stuff.
function preinstall() {
echo -n "Do basic system updates? (y/n) "
read answer
if [ "$answer" != "y" ]; then
return
fi
apt update
apt -y upgrade
apt -y autoremove
}
function firewall() {
echo -n "Install firewall? (y/n) "
read firewall
if [ "$firewall" != "y" ]; then
return
fi
apt -y install ufw
ufw allow ssh
ufw enable
ufw allow smtp
ufw allow imaps
ufw allow http
ufw allow https
# SMTP submit ports (with SSL and STARTTLS).
ufw allow smtps
ufw allow 587
# Make syslog slightly less noisy.
if [ -f /etc/rsyslog.d/20-ufw.conf ]; then
sed -isave 's/^#& stop/\& stop/' /etc/rsyslog.d/20-ufw.conf
service rsyslog restart
fi
# Make brute forcing less likely.
apt -y install fail2ban
cat <<EOF > /etc/fail2ban/jail.d/exim.conf
[exim]
enabled=true
EOF
cat <<EOF > /etc/fail2ban/jail.d/dovecot.conf
[dovecot]
enabled=true
EOF
systemctl restart fail2ban.service
}
function sethost() {
ip=$(hostname -I | awk '{ print $1; }')
host=$(getent hosts $ip | awk '{ print $2; }')
echo -n "What is the host name? (default $host) "
read prompthost
if [ "$prompthost" != "" ]; then
host="$prompthost"
fi
}
function check-service() {
service $1 status >/dev/null
}
function check-web() {
lsof -i :80 -sTCP:LISTEN
}
function check-package() {
return apt show $1 | grep ^Version: > /dev/null
}
function get-certbot() {
echo -n "Get certificates from Let's Encypt? (y/n) "
read answer
if [ "$answer" != "y" ]; then
return
fi
apt -y install certbot lsof
# If we're using MTA-STS, then get the certificate for that domain, too.
local hosts="-d $host"
if [ "$mta_sts" != "" ]; then
hosts="-d $host -d mta-sts.$domain"
fi
# If we have a web server, then don't use standalone certbot,
# because it'll fail.
if check-web > /dev/null; then
if check-web | grep apache > /dev/null; then
if check-package python-certbot-apache; then
apt -y install python-certbot-apache
else
apt -y install python3-certbot-apache
fi
elif check-web | grep nginx > /dev/null; then
if check-package python-certbot-nginx; then
apt -y install python-certbot-nginx
else
apt -y install python3-certbot-nginx
fi
fi
# Try to use the web server to get certificates.
certbot certonly $hosts
# If we're using Apache, then restart it to start using the
# new certificates -- especially when adding an MTA-STS domain.
if check-web | grep apache > /dev/null; then
systemctl restart apache2
fi
else
# This will start a standalone http server and get the certificate.
certbot certonly --standalone $hosts
fi
# Renew certificates.
echo "10 3 * * 1 certbot renew" >> /var/spool/cron/crontabs/root
}
function exim() {
apt -y install exim4-daemon-heavy clamav spamassassin clamav-daemon\
sasl2-bin bind9
# Allow authentication of submitted mail via the SASL daemon.
adduser Debian-exim sasl
sed -i 's/^START=.*/START=yes/' /etc/default/saslauthd
systemctl restart saslauthd.service
# Use the Let's Encrypt certificates in exim.
cat <<EOF > /etc/exim4/conf.d/main/00_tls_macros
MAIN_TLS_ENABLE=yes
MAIN_TLS_PRIVATEKEY=/etc/letsencrypt/live/$host/privkey.pem
MAIN_TLS_CERTIFICATE=/etc/letsencrypt/live/$host/fullchain.pem
EOF
# Make exim use the split config and be an internet MTA.
sed -i "s/dc_use_split_config='false'/dc_use_split_config='true'/" \
/etc/exim4/update-exim4.conf.conf
sed -i "s/dc_other_hostnames=.*/dc_other_hostnames='$host:$domain'/" \
/etc/exim4/update-exim4.conf.conf
sed -i "s/dc_eximconfig_configtype='local'/dc_eximconfig_configtype='internet'/" \
/etc/exim4/update-exim4.conf.conf
sed -i "s/dc_local_interfaces='127.0.0.1 ; ::1'/dc_local_interfaces=''/" \
/etc/exim4/update-exim4.conf.conf
# Add the SMTP submit ports (TLS and STARTTLS).
cat <<EOF > /etc/exim4/conf.d/main/00_ports
daemon_smtp_ports = smtp : smtps : 587
tls_on_connect_ports = 465
EOF
# Allow authentication via SASL.
cat <<"EOF" > /etc/exim4/conf.d/auth/10_plain_server
plain_server:
driver = plaintext
public_name = PLAIN
server_condition = ${if saslauthd{{$auth2}{$auth3}}{1}{0}}
server_set_id = $auth2
server_prompts = :
server_advertise_condition = ${if eq{$received_port}{587}{${if eq{$tls_in_cipher}{}{no}{yes}}}{${if eq{$received_port}{465}{yes}{no}}}}
EOF
# Make exim do virus and spam scanning.
sed -i 's/# av_scanner/av_scanner/' \
/etc/exim4/conf.d/main/02_exim4-config_options
sed -i 's/# spamd_address/spamd_address/' \
/etc/exim4/conf.d/main/02_exim4-config_options
# clamav needs about 1GB of memory to run. If we don't have that,
# then don't make exim try to talk to clamav.
memory=$(grep MemTotal /proc/meminfo | awk '{print $2}')
pref=""
if [ "$memory" -gt 1500000 ]; then
pref="#"
echo "# Commented out because clamav may not be running due to low memory"\
> /etc/exim4/conf.d/acl/45_stop_spam
fi
cat <<EOF >> /etc/exim4/conf.d/acl/45_stop_spam
${pref}deny
${pref} malware = *
${pref} message = This message was detected as possible malware (\$malware_name).
EOF
cat <<"EOF" >> /etc/exim4/conf.d/acl/45_stop_spam
deny message = This message scored too many spam points
spam = Debian-exim:true
condition = ${if >{$spam_score_int}{49}{yes}{no}}
EOF
# The previous elements have to be in the check_data ACL, but that
# file ends with "accept", which lets everything through. So
# remove that from that file...
sed -i 's/^ *accept/ #accept/' \
/etc/exim4/conf.d/acl/40_exim4-config_check_data
# ... and put it in a separate file.
cat <<"EOF" > /etc/exim4/conf.d/acl/55_exim4_check_data_end
# accept otherwise
accept
EOF
# For URIBL (etc.) to work, the requests should come from this
# host since the free accounts are accounted per IP.
echo "dns_server 127.0.0.1" >> /etc/spamassassin/local.cf
systemctl enable spamassassin.service
service spamassassin restart
# Ensure that we're not being an open DNS server.
sed -i \
's/listen-on-v6.*/listen-on-v6 { ::1; }; listen-on { 127.0.0.1; };/' \
/etc/bind/named.conf.options
if check-service named; then
service named restart
else
service bind9 restart
fi
# ClamAV needs to be able to access /var/spool/exim4/scan.
adduser clamav Debian-exim
update-exim4.conf
service exim4 restart
# Allow exim to read the Let's Encrypt certificates.
adduser Debian-exim mail
chgrp -R mail /etc/letsencrypt
chmod -R g+rx /etc/letsencrypt
}
function dkim() {
cd /etc/exim4
# Generate private and public keys.
openssl genrsa -out "$domain-dkim-private.pem" 2048
openssl rsa -in "$domain-dkim-private.pem" -out \
"$domain-dkim-public.pem" -pubout
# Allow exim to read the file.
chmod g+r "$domain-dkim-private.pem"
chgrp Debian-exim "$domain-dkim-private.pem"
local selector=$(date +%Y%m%d)
cat <<EOF > conf.d/main/00_dkim_macros
DKIM_CANON = relaxed
DKIM_SELECTOR = $selector
DKIM_DOMAIN = $domain
DKIM_PRIVATE_KEY = /etc/exim4/$domain-dkim-private.pem
EOF
update-exim4.conf
service exim4 reload
echo
echo "Make the following TXT DNS record for $selector._domainkey.$domain"
echo -n " v=DKIM1; k=rsa; p="
grep -v '^-' < "$domain-dkim-public.pem" | tr -d '\n'
echo
echo "# This DNS BIND-formatted file can be imported into some DNS providers" > $log
echo -n "$selector._domainkey.$domain. 1 IN TXT \"v=DKIM1; k=rsa; p=" >> $log
grep -v '^-' < "$domain-dkim-public.pem" | tr -d '\n' >> $log
echo "\"" >> $log
}
function dns() {
echo
echo "Make the following TXT DNS record for $domain"
echo " v=spf1 a mx ~all"
echo
echo "Make the following TXT DNS record for _dmarc.$domain"
echo " v=DMARC1; p=none"
echo
echo "Make the following MX DNS record for $domain"
echo " $host"
echo "$domain. 1 IN TXT \"v=spf1 a mx ~all\"" >> $log
echo "_dmarc.$domain. 1 IN TXT \"v=DMARC1; p=none\"" >> $log
echo "$domain. 1 IN MX 9 $host." >> $log
if [ "$mta_sts" != "" ]; then
echo
echo "Make the following TXT DNS record for _mta-sts.$domain"
local stamp=$(date -u +"%Y%m%d%H%M%SZ")
echo " v=STSv1; id=$stamp;"
echo "_mta-sts.$domain. 1 IN TXT \"v=STSv1; id=$stamp;\"" >> $log
fi
echo
echo "Domain file for import can be found at $log"
}
function dovecot() {
echo -n "Install Dovecot IMAP? (y/n) "
read answer
if [ "$answer" != "y" ]; then
return
fi
apt -y install dovecot-imapd
# Use the Let's Encrypt certificates.
sed -i "s#/etc/dovecot/private/dovecot.pem#/etc/letsencrypt/live/$host/fullchain.pem#" /etc/dovecot/conf.d/10-ssl.conf
sed -i "s#/etc/dovecot/private/dovecot.key#/etc/letsencrypt/live/$host/privkey.pem#" /etc/dovecot/conf.d/10-ssl.conf
service dovecot restart
# The certificate will change, and dovecot has to reload so that
# it doesn't expire.
echo "20 3 * * 1 systemctl reload dovecot.service" >> \
/var/spool/cron/crontabs/root
}
function mta-sts() {
echo -n "Use MTA-STS to declare that all traffic should use TLS? (y/n) "
read answer
if [ "$answer" != "y" ]; then
return
fi
mta_sts=true
if ! getent hosts mta-sts.$domain; then
echo "Make the following CNAME DNS record for mta-sts.$domain"
echo " $host"
echo
echo "Press Enter to continue"
read answer
fi
local web=""
if check-web > /dev/null; then
if check-web | grep apache > /dev/null; then
web=apache
elif check-web | grep nginx > /dev/null; then
web=nginx
fi
else
apt -y install apache2
a2enmod ssl
systemctl restart apache2
web=apache
fi
if [ "$web" = apache ]; then
cat <<EOF > /etc/apache2/sites-available/001-mta-sts.conf
<VirtualHost *:443>
ServerName mta-sts.$domain
DocumentRoot /var/www/mta-sts
SSLCertificateFile /etc/letsencrypt/live/$host/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/$host/privkey.pem
</VirtualHost>
EOF
else
echo "Unable to determine what web server to use for MTA-STS"
fi
# Create the STS file.
if [ ! -d /var/www/mta-sts/.well-known ]; then
mkdir -p /var/www/mta-sts/.well-known
fi
cat <<EOF > /var/www/mta-sts/.well-known/mta-sts.txt
version: STSv1
mode: enforce
mx: $host
max_age: 1209600
EOF
}
function continue-mta-sts() {
if [ "$mta_sts" = "" ]; then
return
fi
# We can't enable the mta-sts site before we have the certificates.
if check-web | grep apache > /dev/null; then
cd /etc/apache2/sites-enabled
if [ ! -e 001-mta-sts.conf ]; then
ln -s ../sites-available/001-mta-sts.conf 001-mta-sts.conf
fi
systemctl restart apache2
fi
}
preinstall
firewall
sethost
echo "Configuring for $host..."
domain=$(echo $host | sed 's/^[^.]*[.]//')
mta-sts
get-certbot
continue-mta-sts
exim
dovecot
dkim
dns