spamassassin

This commit is contained in:
Joshua Tauberer 2013-08-23 11:59:28 -04:00
parent 5cef1bb63d
commit 97b2105a1f
7 changed files with 105 additions and 16 deletions

View File

@ -8,6 +8,8 @@ This draws heavily on Sovereign by Alex Payne (https://github.com/al3x/sovereign
Deploying to EC2 from the command line Deploying to EC2 from the command line
-------------------------------------- --------------------------------------
Amazon's EC2 isn't a great place to host a mail server. Do you still need to request permission to send email first? And you don't know if you'll get an IP address with a bad reputation from its previous owner. But it makes deployment easy, so it may at least be useful for testing.
Sign up for Amazon Web Services. Sign up for Amazon Web Services.
Create an Access Key at https://console.aws.amazon.com/iam/home?#security_credential. Download the key and save the information somewhere secure. Create an Access Key at https://console.aws.amazon.com/iam/home?#security_credential. Download the key and save the information somewhere secure.
@ -53,6 +55,8 @@ Somehow download these files.
sh scripts/index.sh sh scripts/index.sh
... ...
logout logout
You'll also want to set reverse DNS (PTR), which is something your hosting provider will probably have a control panel for.
Terminate your instance with: Terminate your instance with:

View File

@ -109,17 +109,22 @@ EOF
sed -i "s/#port = 143/port = 0/" /etc/dovecot/conf.d/10-master.conf sed -i "s/#port = 143/port = 0/" /etc/dovecot/conf.d/10-master.conf
sed -i "s/#port = 110/port = 0/" /etc/dovecot/conf.d/10-master.conf sed -i "s/#port = 110/port = 0/" /etc/dovecot/conf.d/10-master.conf
# Modify the unix socket for LMTP. # Create a Unix domain socket specific for postgres to connect via LMTP because
sed -i "s/unix_listener lmtp \(.*\)/unix_listener \/var\/spool\/postfix\/private\/dovecot-lmtp \1\n user = postfix\n group = postfix\n/" /etc/dovecot/conf.d/10-master.conf # postgres is already configured to use this location, and create a TCP socket
# for spampd to inject mail on (if it's configured later). dovecot's standard
# Add an additional auth socket for postfix. Check if it already is # lmtp unix socket is also listening.
# set to make sure this is idempotent. cat > /etc/dovecot/conf.d/99-local.conf << EOF;
if grep -q "mailinabox-postfix-private-auth" /etc/dovecot/conf.d/10-master.conf; then service lmtp {
# already done unix_listener /var/spool/postfix/private/dovecot-lmtp {
true; user = postfix
else group = postfix
sed -i "s/\(\s*unix_listener auth-userdb\)/ unix_listener \/var\/spool\/postfix\/private\/auth \{ # mailinabox-postfix-private-auth\n mode = 0666\n user = postfix\n group = postfix\n \}\n\1/" /etc/dovecot/conf.d/10-master.conf }
fi inet_listener lmtp {
address = 127.0.0.1
port = 10026
}
}
EOF
# Drew Crawford sets the auth-worker process to run as the mail user, but we don't care if it runs as root. # Drew Crawford sets the auth-worker process to run as the mail user, but we don't care if it runs as root.

48
scripts/spamassassin.sh Normal file
View File

@ -0,0 +1,48 @@
# Spam filtering with spamassassin via spampd.
apt-get -q -y install spampd dovecot-antispam
# Hook into postfix. Replace dovecot with spampd as the mail delivery agent.
tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:[127.0.0.1]:10025
# Hook into dovecot. This is actually the default but we don't want to lose track of it.
tools/editconf.py /etc/default/spampd DESTPORT=10026
# Automatically move spam into a folder called Spam. Enable the sieve plugin.
# (Note: Be careful if we want to use multiple plugins later.)
# The sieve scripts are installed by users_update.sh.
sudo sed -i "s/#mail_plugins = .*/mail_plugins = \$mail_plugins sieve/" /etc/dovecot/conf.d/20-lmtp.conf
# Enable the antispam plugin to detect when a message moves between folders so we can
# pass it to sa-learn for training. (Be careful if we use multiple plugins later.)
sudo sed -i "s/#mail_plugins = .*/mail_plugins = \$mail_plugins antispam/" /etc/dovecot/conf.d/20-imap.conf
# When mail is moved in or out of the dovecot Spam folder, re-train.
# from http://wiki2.dovecot.org/Plugins/Antispam
cat > /usr/bin/sa-learn-pipe.sh << EOF;
cat<&0 >> /tmp/sendmail-msg-\$\$.txt
/usr/bin/sa-learn \$* /tmp/sendmail-msg-\$\$.txt > /dev/null
rm -f /tmp/sendmail-msg-\$\$.txt
exit 0
EOF
chmod a+x /usr/bin/sa-learn-pipe.sh
cat > /etc/dovecot/conf.d/99-local-spampd.conf << EOF;
plugin {
antispam_backend = pipe
antispam_spam_pattern_ignorecase = SPAM
antispam_allow_append_to_spam = yes
antispam_pipe_program_spam_arg = /usr/bin/sa-learn-pipe.sh --spam
antispam_pipe_program_notspam_arg = /usr/bin/sa-learn-pipe.sh --ham
antispam_pipe_program = /bin/bash
}
EOF
# Initial training?
# sa-learn --ham storage/mail/mailboxes/*/*/cur/
# sa-learn --spam storage/mail/mailboxes/*/*/.Spam/cur/
sudo service spampd restart
sudo service dovecot restart

22
scripts/users_update.sh Normal file
View File

@ -0,0 +1,22 @@
# Install dovecot sieve scripts to automatically move spam into the Spam folder.
db_path=$STORAGE_ROOT/mail/users.sqlite
for user in `echo "SELECT email FROM users;" | sqlite3 $db_path`; do
maildir=`echo $user | sed "s/\(.*\)@\(.*\)/\2\/\1/"`
# Write the sieve file to move mail classified as spam into the spam folder.
mkdir -p $STORAGE_ROOT/mail/mailboxes/$maildir; # in case user has not received any mail
cat > $STORAGE_ROOT/mail/mailboxes/$maildir/.dovecot.sieve << EOF;
require ["regex", "fileinto", "imap4flags"];
if allof (header :regex "X-Spam-Status" "^Yes") {
setflag "\\\\Seen";
fileinto "Spam";
stop;
}
EOF
done

View File

@ -1,7 +1,9 @@
import imaplib, os import imaplib, os
username = "testuser@" + os.environ.get("DOMAIN", "testdomain.com")
M = imaplib.IMAP4_SSL(os.environ["INSTANCE_IP"]) M = imaplib.IMAP4_SSL(os.environ["INSTANCE_IP"])
M.login("testuser@testdomain.com", "testpw") M.login(username, "testpw")
M.select() M.select()
print("Login successful.") print("Login successful.")
typ, data = M.search(None, 'ALL') typ, data = M.search(None, 'ALL')

View File

@ -4,20 +4,27 @@ import sys, re
# sanity check # sanity check
if len(sys.argv) < 3: if len(sys.argv) < 3:
print("usage: python3 editconf.py /etc/file.conf NAME=VAL [NAME=VAL ...]") print("usage: python3 editconf.py /etc/file.conf [-s] NAME=VAL [NAME=VAL ...]")
sys.exit(1) sys.exit(1)
# parse command line arguments # parse command line arguments
filename = sys.argv[1] filename = sys.argv[1]
settings = sys.argv[2:] settings = sys.argv[2:]
delimiter = "="
delimiter_re = r"\s*=\s*"
if settings[0] == "-s":
settings.pop(0)
delimiter = " "
delimiter_re = r"\s+"
# create the new config file in memory # create the new config file in memory
found = set() found = set()
buf = "" buf = ""
for line in open(filename): for line in open(filename):
for i in range(len(settings)): for i in range(len(settings)):
name, val = settings[i].split("=", 1) name, val = settings[i].split("=", 1)
m = re.match("\s*" + re.escape(name) + "\s*=\s*(.*?)\s*$", line) m = re.match("\s*" + re.escape(name) + delimiter_re + "(.*?)\s*$", line)
if m: if m:
# If this is already the setting, do nothing. # If this is already the setting, do nothing.
if m.group(1) == val: if m.group(1) == val:
@ -33,7 +40,7 @@ for line in open(filename):
break break
# add the new setting # add the new setting
buf += name + "=" + val + "\n" buf += name + delimiter + val + "\n"
# note that we've applied this option # note that we've applied this option
found.add(i) found.add(i)
@ -46,7 +53,8 @@ for line in open(filename):
# Put any settings we didn't see at the end of the file. # Put any settings we didn't see at the end of the file.
for i in range(len(settings)): for i in range(len(settings)):
if i not in found: if i not in found:
buf += settings[i] + "\n" name, val = settings[i].split("=", 1)
buf += name + delimiter + val + "\n"
# Write out the new file. # Write out the new file.
with open(filename, "w") as f: with open(filename, "w") as f: