Raspberry Pi Email Server Part 1: Postfix

Postfix Logo

This is the first part of a five part tutorial that will show you how to install a full featured email server on your Raspberry Pi. This tutorial covers Postfix, the Mail Transfer Agent.

The parts are:
The Introduction & Contents Page (read first)
Raspberry Pi Email Server Part 1: Postfix
Raspberry Pi Email Server Part 2: Dovecot
Raspberry Pi Email Server Part 3: Squirrelmail
Raspberry Pi Email Server Part 4: Spam Detection with Spamassassin
Raspberry Pi Email Server Part 5: Spam Sorting with LMTP & Sieve

Installing Postfix

Note: While you are setting up the mail server on the Pi, it’s a good idea to turn off port forwarding rules for email to the Pi in your router’s firewall. If you don’t have any port forwarding rules now, that’s great, don’t worry – I’ll prompt you to set them up later.

First, log into your Pi with a SSH session and install postfix:

sudo apt-get update
sudo apt-get install postfix

You will see a menu with some choices. Select “Internet Site” and then set the mail name to your domain name, not including www. (e.g. samhobbs.co.uk).

The setup script will then do some automatic configuration for you. The output will look something like this:

Selecting previously unselected package postfix.                                              
(Reading database ... 67653 files and directories currently installed.)                       
Unpacking postfix (from .../postfix_2.9.6-2_armhf.deb) ...                                    
Processing triggers for man-db ...
Setting up postfix (2.9.6-2) ...
Adding group `postfix' (GID XXX) ...
Done.
Adding system user `postfix' (UID XXX) ...
Adding new user `postfix' (UID XXX) with group `postfix' ...
Not creating home directory `/var/spool/postfix'.
Creating /etc/postfix/dynamicmaps.cf
Adding tcp map entry to /etc/postfix/dynamicmaps.cf
Adding sqlite map entry to /etc/postfix/dynamicmaps.cf
Adding group `postdrop' (GID XXX) ...
Done.
setting myhostname: samhobbs
setting alias maps
setting alias database
changing /etc/mailname to samhobbs.co.uk
setting myorigin
setting destinations: samhobbs.co.uk, samhobbs, localhost.localdomain, localhost
setting relayhost: 
setting mynetworks: 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
setting mailbox_size_limit: 0
setting recipient_delimiter: +
setting inet_interfaces: all
/etc/aliases does not exist, creating it.
WARNING: /etc/aliases exists, but does not have a root alias.

You can edit all of this later.

You may also get some warnings like this:

postmulti: warning: inet_protocols: disabling IPv6 name/address support: Address family not supported by protocol

IPv6 is a new type of IP address that was introduced because we’re running out of the “old” IPv4 addresses. Not many ISPs support IPv6 yet, so you probably don’t need it. Unless you fix the warning, you’ll see it every time.

Change directory into the postfix configuration folder:

cd /etc/postfix/

Edit /etc/postfix/main.cf with your favourite command line text editor (e.g. sudo nano main.cf) and add inet_protocols = ipv4 to the end of the file.

Now is also a good time to check that your hostname is specified properly in /etc/postfix/main.cf. The setup script takes the hostname of the server and uses that, but it may not be in the right format, i.e. “samhobbs” instead of “samhobbs.co.uk”. Find the line that begins myhostname = and make sure it is your fully qualified domain name. This is important because your server will use this to talk to other mail servers, and some will reject your emails if you don’t use a fully qualified domain name to say hi! This is covered in more detail in the helo access restrictions later.

Restart postfix and you shouldn’t see the warnings any more:

sudo service postfix restart

Testing and Configuration

Before you start, it’s probably worth backing up the configuration files in their current state. This way, you’ll have something to compare to if you’re ever trying to work out which bits were defaults and which bits you changed yourself:

cd /etc/postfix
sudo cp main.cf main.cf.BAK
sudo cp master.cf master.cf.BAK

Mailbox Setup

There are a couple of different types of mailbox you can use, I’ve chosen to use a “Maildir” rather than “mbox” configuration. For users with “real” UNIX accounts on the system (like the one you’re using to log in), Maildir creates a folder in the user’s home directory and places emails inside it, one file for each email.

I prefer this to the alternatives, because it’s easier to see and understand: you can rummage around in your home folder and see all your emails as individual files.

To tell Postfix to use the Maildir format, add the following lines to /etc/postfix/main.cf:

home_mailbox = Maildir/
mailbox_command =

If there's already a line with mailbox_command, comment it out by adding a # at the start of the line.

We also need to create the mail directory and its subfolders for existing users, and add some things to /etc/skel (the template for new users) so that if you create a new account this will be done automatically.

These commands are part of Dovecot, so first we need to install it:

sudo apt-get update
sudo apt-get install dovecot-common dovecot-imapd

You will get a lot of output: some other dovecot packages will automatically be installed and the config files will be created. You will also see some errors – don’t worry about those for now, I’ll explain how to deal with them in part 2, later.

Now we can create those mail folders. Run the following commands to create the template files:

sudo maildirmake.dovecot /etc/skel/Maildir
sudo maildirmake.dovecot /etc/skel/Maildir/.Drafts
sudo maildirmake.dovecot /etc/skel/Maildir/.Sent
sudo maildirmake.dovecot /etc/skel/Maildir/.Spam
sudo maildirmake.dovecot /etc/skel/Maildir/.Trash
sudo maildirmake.dovecot /etc/skel/Maildir/.Templates

Next, copy the files over to existing users’ home directories, and change the ownership and permissions for privacy (replace USER with the username you are doing this for, and repeat for all existing usernames):

sudo cp -r /etc/skel/Maildir /home/USER/
sudo chown -R USER:USER /home/USER/Maildir
sudo chmod -R 700 /home/USER/Maildir

Initial Testing

Now, the best way to test Postfix during configuration is to use Telnet, because it is such a simple way of communicating between programs and there’s less to go wrong and get confused about.

First, install telnet:

sudo apt-get install telnet

Now, still inside the SSH session to your pi, type this command. It will connect you to port 25 on the Pi:

telnet localhost 25

You can now test sending an email using SMTP. Here are the steps:

  1. send an ehlo command to tell the server who you are, and it will tell you its capabilities
  2. use the mail from command to say who the email is from. If you are sending it from an address that exists on the server, you needn’t include the domain name (i.e. user instead of user@yourdomain.com)
  3. use the rcpt to command to tell the server where to send the email
  4. Use the data command to tell the server that you’re about to start giving it the message you want to send
  5. Type Subject: YOUR SUBJECT then enter to set a subject
  6. Type the body of your email. Once you’re done, press ENTER, then ., then ENTER again.
  7. Type quit to exit

Here’s an example:

telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 samhobbs.co.uk ESMTP Postfix (Debian/GNU)
ehlo foobar
250-samhobbs.co.uk
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
mail from: me        
250 2.1.0 Ok
rcpt to: me@outsideemail.com
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
Subject: test
This is a test email
.
250 2.0.0 Ok: queued as A639C3EE6D
quit 
221 2.0.0 Bye

Some Access Restrictions

Add the following to /etc/postfix/main.cf to restrict who can send emails to external mail servers:

smtpd_recipient_restrictions =
        permit_sasl_authenticated,
        permit_mynetworks,
        reject_unauth_destination

Reload postfix:

sudo service postfix reload
  • Line 1 begins the list of restrictions.
  • Line 2 permits users who have authenticated with Simple Authentication and Security Layer (SASL) to send email to any destination (this is part of the Dovecot config in Part 2, later).
  • Line 3 will let users send emails to any destination if they have connected from an IP address defined in mynetworks.
  • Line 4 will reject the email if none of the above conditions have been met unless the “rcpt to” address is one of the addresses that your server is accepting email to (as defined in main.cf with the mydestination parameter).

In its present state, the email server will allow you to send external emails because the connection is originating from the Pi itself (you are logged in via SSH) and not an unknown computer. Addresses of “trusted” computers are listed under the mynetworks setting in main.cf, e.g.

mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128

Try sending an external email again, using telnet as before. You should be able to do so without any issues.

Now we want to see what kind of response someone would get if they were connecting from outside of the IP range defined in mynetworks, to make sure Pi won’t allow everyone to send outgoing emails from your server. To simulate this we can comment out permit_mynetworks under smtpd_recipient_restrictions:

smtpd_recipient_restrictions =
        permit_sasl_authenticated,
#       permit_mynetworks,
        reject_unauth_destination

Now reload the postfix configuration:

sudo service postfix reload

This will let you see what kind of response you would get if you weren’t sending the email from mynetworks. Try sending again, and you should receive an error “554: Relay access denied“:

admin@samhobbs /etc/postfix $ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 samhobbs.co.uk ESMTP Postfix (Debian/GNU)
ehlo samhobbs.co.uk
250-samhobbs
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
mail from: USER
250 2.1.0 Ok
rcpt to: me@externalemail.com
554 5.7.1 <me@externalemail.com>: Relay access denied
quit
221 2.0.0 Bye
Connection closed by foreign host.

Perfect. Leave permit_mynetworks commented out in your smtpd_recipient_restrictions (you'll see why in part 2).

Helo access restrictions

Helo access restrictions can be a very useful way of blocking spam.

Note that we’re not talking about unauthorised people being able to send email outside your network any more (that’s taken care of with the smtpd_recipient_restrictions); we’re now talking about stopping spammers from sending incoming mail to your email address.

Spammers try to conceal their identity so that they don’t end up on block lists, so they rarely use helo hostnames that could identify them – these hostnames are written to the mail log files. As a result, they often make up a random string or use an IP address instead of a domain name.

Luckily, these are easily taken care of.

Add the following to /etc/postfix/main.cf:

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
  • Line 1 requires people and programs to identify themselves when they send email, using the helo or ehlo commands I mentioned earlier.
  • Line 2 starts the list of restrictions.
  • Line 3 accepts any old rubbish in the ehlo if it comes from an IP address defined in mynetworks. If the connection isn’t connecting from an IP address in mynetworks, then the helo hostname is checked against the rest of the list.
  • Line 4 accepts any helo hostname if the client is authenticated with SASL (I added this to the tutorial recently after troubleshooting problems some people had in the comments – it allows you to connect from any network and still send messages through your Pi. Mobiles will usually work without this because most providers pass mail through their own proxies, so your Pi receives a connection from the proxy – which has a valid hostname – and not from the mobile, which may be called something like “android-b627cfe2efea7e67″).
  • Line 5 rejects connection attempts when the HELO hostname syntax is invalid.
  • Line 6 rejects non-fully qualified domain names (for example, foobar instead of foobar.com). This will also block those random strings, e.g. “kjhrsbvks”.
  • Line 7 rejects the helo hostname if it that domain doesn’t have a valid DNS A or MX record. For example, someone spamming you could make up a domain like theflyingspaghettimonster.com. If that domain doesn’t actually exist and have the right records, then your server won’t accept it as a hostname, and the email will be rejected.

If the helo hostname gets past line 7 and hasn’t been denied, it is accepted. You’d be surprised how much spam these helo access restrictions will block on their own (looking through my log files, I can see numerous spam scripts that have attempted to ehlo with my IP address), but there’s an extra step we can add in here to help:

Blocking people claiming to be your domain name

Many spammers try to send email to you after helo’ing with your own domain name. Since postfix doesn’t check whether or not they’re lying about their helo hostname, this will usually work.

But, since we’ve put permit_mynetworks at the top of the list, anyone actually sending an email from your domain will be accepted already. Anyone using your hostname who isn’t in mynetworks is an imposter.

So, add one more line to the end of the restrictions list:

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

That last line checks a file for custom rules you’ve built in. Create the file:

sudo nano /etc/postfix/helo_access

Add the following lines, edited for your domain:

samhobbs.co.uk          REJECT          Get lost - you're lying about who you are
mail.samhobbs.co.uk      REJECT          Get lost - you're lying about who you are

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

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

Now anyone who tries to ehlo with one of the hostnames you defined in that file gets rejected, and sees the “get lost” message. Your legitimate servers won’t have that problem, because they will already have been accepted higher up the list.

Neat, right? I found that little nugget of wisdom at unixwiz.net.

Moving on…

We’re almost done with Postfix now, athough there are a few bits of configuration that we’ll want to do once we’ve set up SASL with Dovecot, which I’ve chosen to lump in with the Dovecot tutorial.

In Raspberry Pi Email Server Part 2: Dovecot, we’ll set up Dovecot to provide SASL authentication and IMAP capability.

Please leave a comment if you’re having trouble with anything in this tutorial, and I’ll try and help you out!

Type: 

Comments

Dave,

Was that an email from a local user or from an external mail server?

Looks like the email was rejected because the domain used in the EHLO doesn't exist, but if it was outgoing mail it should have been authenticated (so it should have been accepted nearer the start of the list).

Sam

Hi Sam,
Exactly what I did was, I did a sudo reboot from the Pi working fine. Then sent an email from my Hotmail account to my domain email running on the Pi server, the domain points to my IP, all done from with my network (just sent an email to myself)- the email didn't get through, until I issue a sudo service postfix restart - then I get the email.

Dave.

Weird! I can't think of a reason why that would happen :/

Here's my thought process...
Postfix and Dovecot were both running before you restarted Postfix.
Unless you changed something before restarting, Postfix should come up just the same as before.
Even if it's a weird dependency problem with Postfix and Dovecot starting up in the wrong order due to the switch to sustemd (e.g. dovecot is needed for SASL, so it might need to be running before Postfix), I can't think of a reason why that would affect incoming mail, which is unauthenticated (no SASL).
Other people have reported success.
Therefore, it must be something specific to your system.

Unless you have installed another MTA that is starting at boot and preventing Postfix from binding to port 25, i think that leaves us with a configuration error.

So the only thing I can think of left to do is purge Postfix/Dovecot sudo apt-get remove --purge packagename and start the tutorial again, which should eliminate weird configuration errors from causing problems.

Sam

After a renewed effort, I solved the problem with reboot and Postfix not working issue. It was in Postfix main.cf smtpd_helo_restrictions I removed the 'reject_unknown_helo_hostname' and it all works even after a reboot or power down restart - incidentally this was from a Hotmail account to my domain.

Dave.

Whats the value for "mailbox_command =" ?
If i leave it blank like "mailbox_command =" postfix returns the following "warning: /etc/postfix/main.cf, line 44: overriding earlier entry: mailbox_command=procmail -a "$EXTENSION""

Hi,

It's supposed to be an empty list (that's a warning, not an error, so you can safely ignore it).

Sam

Hi Sam,

Is there a way of running multiple domains on the same server. I have three domains which I would like to ideally run on the same mail server. Is there a way of running virtual hosts and is there a quick tutorial you could write to change modify this setup to add in the additional domains.

Thanks
Greg

Greg,

All you need to do is append the new domains to mydestination in main.cf, and create an MX record for each of your new domains pointing to your server.

Emails sent to foo@domain1.com and foo@domain2.com will land in system user foo's mailbox.

It's also possible to set up virtual mailboxes etc. if you want foo@domain1.com to be separate to foo@domain2.com, but that's quite a lot more complicated, and beyond the scope of this tutorial.

Sam

I have followed the entirety of this guide closely, only making a slight difference in setting up Postfix so it uses my ISP's SMTP as a relay (as they block 25 and I couldn't get it running on 587), but I am stuck at doing an AUTH PLAIN command, it spits out "503 authentication not enabled". I triple checked everything, but still am totally clueless what could be wrong. Where should I look?

Hi,

When you say "this guide" do you mean this part, or this part and all other parts?

Have a look at this comment. Do you think it's the same problem? Can you check what I recommended in the comment please?

Sam

I followed this guide for the most part, I only needed a different guide to figure out how to use a SMTP relay, as I could not get the Pi itself to do so (port 25 is blocked by the ISP, and it just timed out on port 587 despite being forwarded). I now use the ISP's SMTP, logging in with the credentials of the email account we never use. This way, I have been successful in sending emails from my domain under Postfix. But now with Dovecot, I cannot get the authorization going. Here's a snap of my 10-master.conf, I can't find any gross errors that could set off Postfix myself: http://i.imgur.com/0o48yRU.png

Which stage of the tutorial are you at? Once port 465 is set up we disabled plaintext auth on non-TLS channels with this parameter:

smtpd_tls_auth_only = yes

If that is the case, you won't be able to authenticate on port 25 without using STARTTLS (with openssl s_client). Try doing the test on port 465 and see if you get the same error?

If not, please post the test session (all input and output, but redact the authentication string).

Sam

Well yes, it would - we didn't configure port 587 at all.

Sam

I think you misunderstood, your ISP almost certainly does not block port 465, which is what you should be using for submission (client to server).

Port 25 is for server to server communication, which you have changed to 587, but that shouldn't change the port you are using to submit authenticated mail to your server.

What happens of you do the test on port 465?

Sam

I haven't yet set port 465 up, since I am still stuck at the step before that, the one where you send an email when authenticating with SASL, after telling Postfix to use Dovecot SASL. It's somehow not getting the fact it should be authenticating.

Daniel,

You're making it really difficult for me to help you! You said you followed "the entirety of this guide closely" but you can't have done, or you would either be at a stage where authentication is enabled on non-TLS ports (before setting up port 465) or after authentication is disabled on plaintext ports (after setting up port 465).

Either way, you should be able to do a test on one of the ports but you seem to have done parts of the tutorials selectively and tried to test on port 587 after following part of another guide.

The other guide you followed should only have affected server to server communication, switching from port 25 to 587. It may have set up authentication between your server and your ISP's email server. Don't confuse that with what's in this tutorial.

The authentication test in this tutorial is for email submission - your server will require users to authenticate before it will let them send emails, so that bots can't use your server to send spam. This tutorial guides you through enabling authentication on port 25, and then (after setting up port 465) disabling it on port 25 without STARTTLS.

At no point does it mention authentication on port 587. It sounds like you're at a stage where you haven't disabled plaintext auth on port 25, so please just do the test on port 25. I don't know why you're using port 587 to test.

Sam

First of all, thank you so much for these guides; they have been enormously useful to us as we're transitioning from an Apache/Sendmail configuration to Nginx/Postscript.

One thing that would be useful to know would be how to work with multiple domains. We'd also love to how to automatically forward a copy of each message to an external address.

And finally, just a quick note in regards to one issue we had on Debian Jessie. When we followed your instructions here, an error showed up after adding "mailbox_command =" to /etc/postfix/main.cf. The solution was to comment out the existing "mailbox_command = procmail -a "$EXTENSION"."

This is a really simple setup, you can add extra domains by adding the domain to mydestination but email to foo@domain1.com and foo@domain2.com will be delivered to the same inbox. If you want to separate the domains completely you'll have to configure virtual mailboxes, which is outside the scope of this tutorial.

Thanks for reporting the mailbox command thing :)

Sam

Hi Sam

I needed to setup fetchmail to collect e-mails from different accounts like gmail, yahoo and others.
I encountered a weird behavior: fetchmail logs in into e.g. yahoo with ssl, gets all perfect but it stops when getting the first message. It seems like that fetchmail has a problem to log in into the local SMTP server which is secured with TLS/SSL (as you suggest in your tutorial).
Do I need to disable TLS/SSL on the local Postfix server or do you have a smarter solution in mind?

Thanks a lot, mate.

Cheers,
Frank

Hi Frank,

I keep meaning to try out Fetchmail, but I've never used it.

Just had a quick look at the manual, can't you use the --ssl option?

Causes the connection to the mail server to be encrypted via SSL, by negotiating SSL directly after connecting (SSL-wrapped mode).

Sam

Hi Sam

I already use ssl. I use fetchmail since a few year on our Win3k8R2 server running under CygWin. It delivers to the IBM Domino Server.
I want to shift that to the Raspberry because of better spam filtering, virus checking (and more) and to get it away from that server. IBM Domino has rules but they aren't that good, they are more simple things for sorting e-mails in your folders.

I get the following on the RPI when I do a "telnet 127.0.0.1 25" as root:

Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

That's it. If I type two times exit, nothing happened. It exits to telnet when I press '^]'.

When I enter as a user the command "fetchmail -v" I get that:

8 messages for [pop3 server] at ________ (12345 octets).
fetchmail: POP3> LIST 1
fetchmail: POP3< +OK 1 12345
fetchmail: POP3> RETR 1
fetchmail: POP3< +OK
reading message [pop3 server]:1 of 8 (12345 octets)
Trying to connect to 127.0.0.1/25...connected.

and that's it. Sitting there until a timeout occurs.

If you want to configure fetchmail, that's pretty simple, but it's a bit tricky because the error message tell often something misleading. That one below works.
I tried both configurations: system wide and per user. I reckon per user is better and more flexible when it comes to the retrieval. That can be done different for each user.

1. Create a file with nano and set the access right for the user after that:

nano /home/USER/.fetchmailrc

Apply that after you have put in the configuration below:
chown USER:USER .fetchmailrc
chmod 600 .fetchmailrc

2. Do it the following way. If you put all parameters in each "poll" command, it can get confusing.

set no bouncemail
set no spambounce

defaults
port 995
proto POP3
timeout 10
antispam -1
batchlimit 100
no keep
fetchall
smtpaddress 127.0.0.1
ssl

poll [pop3 server]
user "[your login]" there with password "[your password]" is "[local user name]"

If not all POP3 accounts support SSL, please remove the ll from the defaults and add "options ssl" at the end of the "user" line.
When you don't mention the "proto" fetchmail tries IMAP, POP3 etc. until it has success. You can enter "proto imap" either, if your provider supports it.

3. add to crontab with "crontab -e". As you see, I have webmin installed which makes control much easier. Enter something like that but don't use Webmin for configuration, it won't work the way it writes it into the .fetchmailrc file:

*/5 * * * * /etc/webmin/fetchmail/check.pl --null

4. Restart the cron jobs

Now fetchmail should run and deliver your e-mails, but my one gets stuck... :(

Please try, if you enter the same problem, like I do, if you like.

Cheers,
Frank

Thanks Sam

I must have been blind... I forgot the 'hash:' before the virtual mailboses path.

Ok, one problem solved, new to go ;)

1. MailScanner[2242]: Error in configuration file line 169, directory /var/spool/mqueue for outqueuedir does not exist (or is not readable)

Ok, that's fixable. I don't know how, but I'll cross check that.

2. fetchmail...
Well, it works pretty well on my Win Server but when that on RPI runs, it tells me that it can't deliver to my mailbox USER (e.g. the user is 'willy' on the RPI). It tells me that it can't 'even deliver to willy!' (but willy is there).
When I enter willy@mydomain.com it always wants to send the message over the internet relay. How can I configure that?
How can I prevent postfix from bouncing back messages? The other side shouldn't know that it's classified as spam or if it's a nonexisting user.
To disable the bounce seems a bit harsh to me.

Cheers,
Frank

I mean, I can't send my email to my direction of hotmail with the port 25 opened in my router and I read that my ISP, as I said, is locking output communications on the port 25.

Pages

Add new comment