Raspberry Pi Email Server

raspberry-pi-email-server.png

The RasPi’s small size and low power consumption make it an ideal choice for use as a home email server. After trying a couple of different pieces of software, I finally found an excellent combination: Postfix with Dovecot and Squirrelmail, plus Spamasssassin and Sieve for spam filtering.

There are many, many tutorials out there for the first trilogy of programs, but since the configuration is slightly different for each distribution I kept coming unstuck when setting mine up on the Pi. Having finally got mine configured properly, I’ve put together a set of 5 tutorials, which will take you from a vanilla Raspbian image to a fully functioning email server in no time.

When writing the tutorial I made an effort to explain what each setting does instead of just dumping commands. With a bit of luck at the end of the process you’ll not only have a working server, you’ll understand how it works… without having to wade through reams of documentation like I did!

If you follow the tutorials from start to finish, here’s what you’ll end up with:

  1. An email server that you can run 24/7/365 for under £5 of electricity per year
  2. Personalised email address like you@yourdomain.com (requires you to have registered a domain name with a registrar like namecheap.com - see my DNS basics tutorial)
  3. The ability to connect from anywhere, and read & send email, using a secure IMAP connection on your phone, tablet or computer
  4. Log in to webmail using any web browser on a secure HTTPS connection, read & send email
  5. Complete control over your personal communication. Your emails are stored on YOUR server, and nobody is scanning them to sell you adverts.
  6. Smart spam filtering with Spamassassin
  7. Customisable mail sorting with Sieve rules

Postfix, the Mail Transfer Agent

Postfix Logo
Postfix is the program that lets you send and receive email using Simple Mail Transfer Protocol (SMTP). Whilst you, the user, may connect to your email server using IMAP (on port 143 or 993), or POP (on port 110 or 995), email servers talk to each other using SMTP on port 25.

So, this is the basic core of the server. Without it, you wouldn’t be able to send or receive any emails!

I’ve covered the setup here:
Raspberry Pi Email Server Part 1: Postfix

Dovecot, the POP/IMAP Server

dovecotLogo-300x130_0.png
Dovecot is used for two things:

  1. It provides you with IMAP functionality
  2. It checks that you are who you say you are using Simple Authentication and Security Layer (SASL) before you send or fetch mail

If you’re not interested in connecting with IMAP on your devices, you still need Dovecot. Not only is it doing SASL for you, but Squirrelmail connects using IMAP in order to provide you with webmail.

I’ve covered Dovecot installation and configuration here:
Raspberry Pi Email Server Part 2: Dovecot

Squirrelmail, for Webmail

Squirrelmail Logo
Squirrelmail is handy because it allows you to check your email in any browser, from anywhere.

Of the first three, it’s probably the easiest to configure. I’ve covered it here:
Raspberry Pi Email Server Part 3: Squirrelmail

Spamassassin, for Marking Spam

Spamassassin Logo
Spamassassin is the program that we will use to audit incoming mail and decide whether or not it’s spam. Spamassassin doesn’t actually sort the mail into the spam folder, it only changes information in the headers based on the results of the scan. I’ve covered it here: Raspberry Pi Email Server Part 4: Spam Detection with Spamassassin.

LMTP & Sieve for Spam Sorting & Mailbox Organisation

After Spamassassin has checked incoming mail to see if it’s spam or not, we need another program to sort it into the right mail folder. This final step will be done with Dovecot’s Local Mail Transfer Protocol (LMTP) daemon and a Sieve plugin.

Sieve is a simple programming language that allows users to define what to do with incoming email based on a predefined set of rules – think “if the header contains this flag, put it in the spam folder” kind of thing and you’ll get the gist. Aside from spam filtering, Sieve can be used to automatically sort & de-clutter your inbox. These steps are covered in the final tutorial: Raspberry Pi Email Server Part 5: Spam Sorting with LMTP & Sieve

Enjoy! I’d love to hear how you get on, so leave a comment below :)

Type: 

Comments

Sam, I tried that but my problems continue albeit with a slightly different error message.
Mail Delivery Subsystem
13:23 (2 hours ago)

to me
This is an automatically generated Delivery Status Notification

THIS IS A WARNING MESSAGE ONLY.

YOU DO NOT NEED TO RESEND YOUR MESSAGE.

Delivery to the following recipient has been delayed:

john@mydomain.com.au

Message will be retried for 2 more day(s)

Technical details of temporary failure:
Google tried to deliver your message, but it was rejected by the server for the recipient domain mydomain.com.au by mail.mydomain.com.au. [my IP].

The error that the other server returned was:
451 4.3.5 : Helo command rejected: Server configuration error

Your continued help is appreciated.

TIA

John

Sounds like there may be a typo or something in the helo access restrictions part of your postfix main.cf, can you copy and paste that part of the f the please?

Do you get any errors when you restart postfix?

Sam

No errors on starting Postfix, Sam.

Herewith the main.cf:
# See /usr/share/postfix/main.cf.dist for a commented, more complete version

# Debian specific: Specifying a file name will cause the first
# line of that file to be used as the name. The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

myhostname = mail.mydomain.com.au
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = hedge.com.au, rpi, localhost.localdomain, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = ipv4
home_mailbox = Maildir/
mailbox_command =
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
smtpd_helo_required = yes
smtpd_helo_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname,
reject_unknown_helo_hostname
check_helo_access hash:/etc/postfix/helo_access
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_tls_auth_only = yes

Thanks,

John

Nothing jumps out as wrong here - I reckon it's worth checking your helo_access file has the correct syntax and has been postmapped. What is the output of:

ls -l /etc/postfix/
cat /etc/postfix/helo_access

I also sent you a test email, which bounced with the same error message (was worth checking if I could get more info than Gmail would show you in the error).

Sam

Yeah... handy in this case though, right?

Did you run that first command? I'm looking for if you've postmapped the file or not (if you have, there will be a file called /etc/postfix/helo_access.db, which is the database that is created by the postmap command - postfix reads this, not the text file /etc/postfix/helo_access.

If you've not run the command, or if you've changed the file since you last ran it, please run it again and restart postfix to see if that makes a difference.

Sam

Ah! Apologies, I ran the command and forgot to paste the output so I ran the command again and restarted. The output is:
-rw-r--r-- 1 root root 274 Jan 7 08:36 dynamicmaps.cf
-rw-r--r-- 1 root root 160 Jan 7 09:13 helo_access
-rw-r--r-- 1 root root 1826 Jan 10 11:29 main.cf
-rw-r--r-- 1 root root 1262 Jan 7 08:42 main.cf.BAK
-rw-r--r-- 1 root root 5595 Jan 7 12:17 master.cf
-rw-r--r-- 1 root root 5531 Jan 7 08:42 master.cf.BAK
-rw-r--r-- 1 root root 19707 Mar 13 2013 postfix-files
-rwxr-xr-x 1 root root 8729 Mar 13 2013 postfix-script
-rwxr-xr-x 1 root root 26498 Mar 13 2013 post-install
drwxr-xr-x 2 root root 4096 Mar 13 2013 sasl

No helo_access.db!

If you're willing to have a look I'm happy to let you ssh into it. <redacted>

Thanks.

No worries :)

Just run this command:

sudo postmap /etc/postfix/helo_access

You should see /etc/postfix/helo_access.db afterwards. Now restart postfix (sudo service postfix restart) and send a test email from your freemail account again.

I won't ssh into your server because I think you can do it yourself (and posting the UN/PW was a bit dodgy so I redacted it - remember this is a password that will let people authenticate and send mail from your account! If the user is in the "sudo" group it would be trivial for someone to pwn your server by logging on using the credentials in the comment).

You might want to set up publickey authentication so that bots can't guess your password, or at least use fail2ban to crack down on bruteforce attacks.

Sam

It works! :-) SquirrelMail is showing its received my last 2 test emails. Did I miss a step in the Postfix tutorial, Sam?
Thanks for keeping me secure. I'll check out your security ideas a bit later.
And thanks for all your help.
John

Yeah, it's right at the end:

Now tell postfix to map the file, and restart postfix:

sudo postmap /etc/postfix/helo_access
sudo service postfix restart

You're welcome :) glad it's all working now!

Sam

Is any simple way to assign defined or all accounts to go for specify email address? I wish to assign admin@ webmaster@ or someuser@ for my pi mail account. Could you help?

Hi,

Yeah it's relatively easy. I would modify /etc/aliases and then run sudo newaliases, that's how webmaster, postmaster, root etc are mapped to my account on my server.

Sam

In the instructions where it says to "Run the following commands to create the template files:" I get an error on each command:

sudo maildirmake.dovecot /etc/skel/Maildir
sudo: unable to resolve host myhostname

Before I go any further, is it required to set the hostname in /etc/hostname to the fully qualified hostname such as myhostname.com and reboot?

Or can the hostname in /etc/hostname be anything else?

I found that if I set /etc/hostname to myhostname.com then I don't get "unable to resolve host myhostname" message.

Should the unable to resolve message be ignored?

This is an error in your system configuration, not anything to do with this specific tutorial...try this:

sudo echo "foo"

... you should get the same error.

Generally when you change your hostname you have to change it in a couple of places, /etc/hostname and /etc/hosts. The first holds the hostname (e.g. "raspberrypi"), which is the obvious bit. The stranger bit is that some software expects the hostname defined there to be resolvable to an IP address, which is what /etc/hosts is all about... so on most systems you'll see something like this...

#/etc/hostname
raspberrypi
#/etc/hosts
127.0.0.1 localhost
127.0.1.1 raspberrypi

On the raspi, if you want to change your hostname you can use raspi-config to change it in both places easily:

sudo raspi-config

I expect what you've done is changed /etc/hostname without changing /etc/hosts, right? Change them so they match and reboot, see if that solves your problem.

Sam

sudo echo "foo" gives foo.

But, you are right. I only changed /etc/hostname without changing /etc/hosts. I've changed hostnames on pi's many times but forget this time. That solved that problem. Thanks!

Now, I'm into the "Initial Testing" section, read your suggestion to ssh into a second Konsole to look at log with (less +f /var/log/mail.log) and get this when doing the telnet localhost 25 command in the first ssh.

First, in main.cf I had relayhost = smtp.googlemail.com:465 and got the following:

Jan 22 19:16:43 mydomainname postfix/smtpd[2812]: connect from localhost[127.0.0.1]
Jan 22 19:18:04 mydomainname postfix/smtpd[2812]: CE95381511: client=localhost[127.0.0.1]
Jan 22 19:18:20 mydomainname postfix/cleanup[2847]: CE95381511: message-id=<20150123001804.CE95381511@mydomainname.com>
Jan 22 19:18:20 mydomainname postfix/qmgr[2398]: CE95381511: from=, size=331, nrcpt=1 (queue active)
Jan 22 19:18:21 mydomainname postfix/smtp[2848]: CLIENT wrappermode (port smtps/465) is unimplemented
Jan 22 19:18:21 mydomainname postfix/smtp[2848]: instead, send to (port submission/587) with STARTTLS
Jan 22 19:18:24 mydomainname postfix/smtpd[2812]: disconnect from localhost[127.0.0.1]

Looked up the "CLIENT wrappermode (port smtps/465) is unimplemented" and removed the :465 and got this:

Jan 22 19:26:11 mydomainname postfix/smtpd[3051]: connect from localhost[127.0.0.1]
Jan 22 19:26:45 mydomainname postfix/smtpd[3051]: 501C181516: client=localhost[127.0.0.1]
Jan 22 19:26:58 mydomainname postfix/cleanup[3055]: 501C181516: message-id=<20150123002645.501C181516@mydomainname.com>
Jan 22 19:26:58 mydomainname postfix/qmgr[3040]: 501C181516: from=, size=331, nrcpt=1 (queue active)
Jan 22 19:27:01 mydomainname postfix/smtpd[3051]: disconnect from localhost[127.0.0.1]
Jan 22 19:27:28 mydomainname postfix/smtp[3056]: connect to smtp.googlemail.com[64.233.176.16]:25: Connection timed out
Jan 22 19:27:28 mydomainname postfix/smtp[3056]: 501C181516: to=, relay=none, delay=55, delays=25/0.06/30/0, dsn=4.4.1, status=deferred (connect to smtp.googlemail.com[64.233.176.16]:25: Connection timed out)

It isn't sending email to myoutside@gmail.com.

I already had my pi successfully sending email messages when it reboots by setting up /etc/ssmtp/ssmtp.com with the following:
#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
root=postmaster

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=mail

# Where will the mail seem to come from?
#rewriteDomain=

# The full hostname
hostname=myhostname

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
#FromLineOverride=YES

AuthUser=myoutside@gmail.com
AuthPass=mypw
FromLineOverride=YES
mailhub=smtp.gmail.com:587
UseSTARTTLS=YES

Is this maybe interfering? Any help would be appreciated. Thanks.

I'm afraid I don't know, I haven't used ssmtp (or sendmail?) before. It's difficult to help when you've made a load of other changes to your configuration, because I don't know how it will affect the rest of the tutorial.

I guess you don't have the option of a static IP address? Things become so much simpler if you can get one.

Sam

Yeah, I probably do have a "mess". I have 12 (local and remote) raspberry pi's used for ownCloud (thanks to you), web cams, xbmc, weather station, heating system monitor and control, junk call blocking, backup pi, etc. When I "build" a new pi, I set a hostname and local static IP. I then run scripts that install common software (git, ssmtp, utilities, etc.) on the pi. So, all of the pi's are set up with common functions to send mail, run cron job scripts, back themselves up, notify me of power failures, etc. Then I "personalize" each pi based on its intended function.

So, I will create a "fresh" pi (I love raspberry - ha!), update, set a local static IP, NOT install any software, and THEN follow your tutorial.

As far as "I guess you don't have the option of a static IP address?". I get my tv, phone, and internet through AT&T which is on 24/7. So, technically, I don't have a static IP (at least I'm not paying for one). But, my external IP address has not changed in four years and I'm using it for my ownCloud. If my IP ever changes, my raspberry pi's will send me a text and email message almost immediately.

One important thing that I learned with 12 pi's is to provide UPS for each. I've had several power glitches that have corrupted many of them. Some just won't boot, and some "sort of" run. I also learned to re-build them from scratch rather than from a backup, because even the backups were corrupted (I unknowingly backed up "sort of functioning" pi's). Using git and cron jobs on each pi that backs each pi to another "backup pi" is a great time saver.

Thanks for you help!

Sounds like you have a pretty nice system there. I find I learn a huge amount whenever I try to network multiple machines like that and set up failsafes, you must have racked up quite a lot of experience in that department!

I should have asked you a different question really, I was wondering about a static IP because they normally have no port blocking and would cut out the whole relay issue, which is something i'm not very good at (I have a static ip and my backup has a dynamic IP, but still doesn't have outbound port blocking).

Sam

You are way beyond me in the networking stuff. I'm just plodding away and trying to learn.

When you say "static IP because they normally have no port blocking" are you talking about incoming or outgoing port blocking? I know that my uverse gateway does incoming port blocking because I have to open incoming ports to be able to access the pi's from the outside through ssh (each pi with a different ssh port set in /etc/ssh/sshd_config). I'm not sure about outbound port blocking or what device/software does this.

I'm not sure I understand the "relay issue"?

You say "I have a static ip and my backup has a dynamic IP, but still doesn't have outbound port blocking". I guess I have a static ip (in that it doesn't change, but I don't pay for one). But what do you mean that your "backup" has a dynamic IP but still doesn't have outbound port blocking? What is the "backup".

I just built a "fresh" pi and am following your tutorial.

I'm talking about your ISP's firewall, not the one you have control of on your router.

Most ISPs have configured their firewalls to block all outbound connections on port 25 to stop spam - it's apparently quite common for people to get malware that turns the infected computer into a SMTP server capable of spitting out huge quantities of spam email. So, the ISPs take the view that there shouldn't be any SMTP servers in their dynamic IP ranges and block the lot. They don't do this for static IP addresses because they're often used for legitimate SMTP servers.

From my quick glance at your sendmail configuration I assumed you were using gmail as a relay (an intermediate server) to get around that port blocking i.e. you send an email to gmail and gmail sends it on to somewhere else - if you're just sending to gmail you shouldn't need a pw?

My backup is another server I have at a different location - the internet connection there has a dynamic ip adderess but the ISP, BT, doesn't seem to be blocking port 25. It's strange really because other people with the same ISP have reported blocking!

Sam

When you say "I'm talking about your ISP's firewall, not the one you have control of on your router." Is this the firewall on the uverse gateway (in my home) that supplies my tv, phone, and internet service? Or is the ISP firewall further upstream at the ATT VRAD box in my neighborhood? Or even upstream from there?

When I log into my uverse gateway and go into Settings/Firewall/Advanced Configuration there is a section titled "Outbound Protocol Control" which says "Checking the box ALLOWS the associated traffic type through the firewall." and these are HTTP, HTTPS, FTP,
Telnet, SMTP, DNS, NetBIOS, POP3, IMAP, NNTP, IRC, H323, All Other Protocols. There's a sections on "Inbound Protocol Control" with NetBIOS" and an "Attack Detection" section with Excessive Session Detection, TCP/UDP Port Scan, Invalid Source/Destination IP address,
Packet Flood (SYN/UDP/ICMP/Other), Invalid TCP Flag Attacks (NULL/XMAS/Other), Invalid ICMP Detection, Miscellaneous.

So it looks like I CAN ALLOW SMTP traffic through the firewall in my gateway (I'm assuming that this means port 25). But that doesn't necessarily mean through a firewall upstream. I read http://www.ka9q.net/Uverse/port25.html which talks about ATT Unblocking Port 25 (but this is old). I also see under "Sending email via SMTP with Uverse... I send email smtp.gmail.com using SSL and port 465. Will this continue to work with Uverse? It should, since you're using SSL and port 465. If you were using port 25, then the answer would be 'no,' at least by default.".

So the only way to know for sure is to try it. I may be able to do that later today (I got sidetracked on something else).

So, if I can't send on 25, I may be able to call ATT and ask them to allow it (at least according to ka9q).

Why shouldn't I just pursue using SSL and port 465?

Thanks

Further upstream, I don't know where it is physically but it's somewhere between you and the rest of the internet!

You need 25 so that your server can talk to other servers on port 25. The port you use to connect to your server and submit email is a different issue - it could be 25, 465 or 587 depending on configuration. That article was talking about submission, which used to be done on port 25 too (nobody really does this any more though).

Unfortunately, you can't set up your server to do everything on port 465 because you don't know for sure that every server you want to connect to is a) listening on 465 and b) configured to accept unauthenticated email on that port.

Sam

Found out that the blockage was on planet Zurg. So I put in a call to Buzz at ATT and said "I need port 25 outbound unblocked".

A minute later Woody said the port was open.

So I did:
telnet smtp.gmail.com 25
Trying 173.194.195.109...
Connected to gmail-smtp-msa.l.google.com.

First hurdle cleared. Now onto the next step.

I'm getting excited now. Thanks for your help!

Ok, I'm on a roll now.

I'm to the point "Set up Postfix to listen on port 465 for encrypted connections - Now try openssl". Your tutorial shows the following with samhobbs in three places.

openssl s_client -connect localhost:465 -quiet
depth=0 CN = samhobbs
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = samhobbs
verify return:1
220 samhobbs.co.uk ESMTP Postfix (Debian/GNU)
quit
221 2.0.0 Bye

Mine shows:
openssl s_client -connect localhost:465 -quiet
depth=0 CN = emailserver
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = emailserver
verify return:1
220 mydomainname.com ESMTP Postfix (Debian/GNU)
quit
221 2.0.0 Bye

The first two entries show emailserver which was the pi hostname before I changed it with raspi-config to mydomainname.com.

How do I get the test to show mydomainname in place of emailserver? Or does it matter?

Congrats on clearing the blockage!

That's the common name on the self-signed "snakeoil" cert produced when you installed postfix (there's a script that creates on with the hostname of the server as the common name on the cert).

If you get a "real" cert signed like in my CAcert tutorial, the common name will be your fully qualified domain name. It's not something you need to worry about now when you're testing, but you'll want a proper cert when you're using the server for real.

Sam

Ok, I followed your tutorial on ownCloud and got a certificate. My pi ownCloud has been working great for a while.

So if I follow the CAcert tutorial again, can I just use my ownCloud CAcert certificate on my pi email server ALSO?

For the email server, I opened up IMAP Server ports in my uverse gateway. When I looked at the status, I found that ports 143, 220, 585, and 993 were opened. Do I need all four of these, or should I close some?

If you followed the tutorial before and got a cert for your domain name you don't need to do it again!

Ports 25 (server to server smtp), 465 (email submission) and 993 (secure imap) should be used. You can close the rest (unless you're using them for something else like SSH). I think you have secure and insecure ports for both POP and IMAP open.

Sam

I'm to the point in the pi email server tutorial where I have my CAcert certificate in.

I see that ssl_cert and ssl_key already exist uncommented in /etc/dovecot/conf.d/10-ssl.conf:
ssl_cert =

Somehow the above message got cut off. Reposting the complete message:

I see that ssl_cert and ssl_key already exist uncommented in /etc/dovecot/conf.d/10-ssl.conf as:

# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_cert = </etc/dovecot/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.pem

Do I add the following two lines and comment out the above two?
ssl_cert = </etc/ssl/certs/myownkey.crt
ssl_key = </etc/ssl/private/myownkey.key

(edited to repair formatting)

Pages

Add new comment