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

Hi Sam,

Yeah, trying out your domain on MX Tools was my first port of call too, and nothing wrong your side! :) I'll try not to worry about it too much then!
FYI that info about FQDSs I pulled from here. Not sure if it's directly relevant to my setup, but since I'm only 'raising a pet' I should be fine either way!

Once again, thanks so much for making the voyage to my own mail server a much less bumpier ride than I'd expected it to be. Long live the Pi!

Jay

Hi Sam,
I have several Pis, one is set up using your excellent tutorial with postfix and for now uses gmail as a relay, this is working fine and has been stable for quite some time. 'gmail pi'
However one of the other Pis is running server monitoring apps (nagios) and i want to configure the nagios pi to send email notifications out using the gmail pi. I think this may mean installing postfix onto the nagios pi
Is it possible to configure gmail pi to also relay to gmail the emails created by the nagios pi?

Regards
Tim

I am currently stuck at the
sudo cp -r /etc/skel/Maildir /home/USER/
sudo chown -R USER:USER /home/USER/Maildir
sudo chmod -R 700 /home/USER/Maildir

whenever i enter the User it gives the error that it is not a valid user. any ideas why it is doing that?

Hi Shaun,

Substitute USER for your actual username :)

Sam

Hi! Your blog is awesome, keep up doing good work! I have learnt so many things here! But I've got one little question: after launching a web server on my raspberry pi, I've opened port 22 for accessing my server remotely, and after a few weeks I checked my SSH logs and found that I'm constantly getting attacked by botnets. They're brutforcing my SHH server like crazy. So, should I worry about this? Thanks

Hi Vladimir,

Good question, and reading through your logs is a good habit too, I've spotted lots of problems that way.

You should disable password authentication and use publickeys instead, and you can also run fail2ban to block bad behaviour at the firewall.

Switching to publickey auth is enough to protect SSH, but the bots that are attacking it might be doing other things too (like trying common wordpress exploits) so it's worth using fail2ban to block them entirely.

Sam

Hi Sam!

I've followed this tutorial in the best way i can, but i can not seem to get it right.

First of all i use a certificate signed by startssl to be more trustworthy when i send email to other people.
I think i have been able to set up everything in part 1 & 2 but when i try to connect to the server imap.mydomain.tech and smtp.mydomain.tech i get refused acces. My ports are open on my edgerouter and i dont think my isp blocks any traffic since i use one of Europes most liberal ISP's. I can't really get my head around which account im supposed to use when logging in. My standard user on the raspberry pi is: pi. I have ofcoursed changed the password. When i try to connect to the server i get refused. Which is the correct syntax to enter my username? Is it pi@mydomain.tech or is it just pi?
When i set up the server how do i add additional users? Do i add them by adding them as an user on my pi?

Are there any logs in the pi that i can watch to determine the error?

Is there something i can try to do to determine from the ssh window if the mail server is working or not? I tried to send a mail via telnet and that did seem to work when i checked /var/log/mail.log my mail got stuck in google's servers (connection refused from their side) but i had a propper connection to them.

Well tons of questions and not to much information from my side, if there's something i need to add please tell and i will happily answer that.

Greetings Jonthan

Hi Jonathan,

Good questions! You have the right logs (/var/log/mail.log and /var/log/mail.err) - mail.log has all of the messages and errors, mail.err is a cut down version with just the errors. If you want to see how they update in real time as you attempt to log in you can do tail -f /var/log/mail.log which will give you the last few lines, and update when new messages are written to the log.

Yes, to add a new user you add a new system user (like we did with testmail).

I would check if dovecot is running properly (sudo service dovecot status), and do you get any errors when you restart it? I think that's probably why you get connection refused, and if dovecot isn't running properly then SASL won't work, so you'll get errors when you try to authenticate to send mail with postfix too, even if postfix itself is working properly.

If dovecot hasn't started properly, the log should have some hints as to why. Let me know if you get stuck!

Sam

Hi Sam and thanks for you response!

Checked the logs and now i cant even see me connecting anymore. There is one reocurring problem in the logs and that is the helo database. The log says that the file doesn't exist. What do i do to fix this issue?

Im possitive to that my ports are open in the router because i've checked them via: yougetsignals port tester.
If i reload dovecot i get a green text that says OK but if i do the same with postfix i get no response so perhaps i have missconfigured postfix in someway.

When i try to use telnet to send a message it works in the matter that the message get's queued, wheter it's sent or not i can not determine. If i try to do the same with my@mydomain.tech i get the following error code when i try to enter rcpt to:
504 5.5.2 : Helo command rejected: need fully-qualified hostname

Once again i do not provide you with any significant amount of information. Are there any logs or configurations that i can provide you with openly on the forum without being the subject of an attack?

Do you mean /etc/postfix/helo_access? Did you forget to postmap it? Run:

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

That shouldn't affect Dovecot though - you didn't post the output of sudo service dovecot status?

Sam

I followed the instructions but when went to send a test email before I went on to the dovecot tutorial I received this:
telnet localhost 25
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
Before it returns to the terminal, am I missing a step or something?

Cassie,

Postfix may not have started properly, what do you get from:

sudo service postfix status

Sam

my results were
postfix.service - LSB: Postfix Mail Transport Agent
Loaded: loaded (/etc/init.d/postfix)
Drop-In: /run/systemd/generator/postfix.service.d
└─50-postfix-$mail-transport-agent.conf
Active: active (running) since Thu 2016-05-26 03:54:06 UTC; 1 day 21h ago
Process: 1285 ExecStart=/etc/init.d/postfix start (code=exited, status=0/SUCCE SS)
CGroup: /system.slice/postfix.service
├─1576 /usr/lib/postfix/master
├─1581 qmgr -l -t unix -u
└─2272 pickup -l -t unix -u -c

May 26 03:54:06 Ajax postfix[1285]: Starting Postfix Mail Transport Agent: ...x.
May 26 03:54:06 Ajax systemd[1]: Started LSB: Postfix Mail Transport Agent.
May 26 03:54:06 Ajax postfix/master[1576]: daemon started -- version 2.11.3,...x
Hint: Some lines were ellipsized, use -l to show in full

-Cassie

I removed all the restrictions and telnet works but when I put them back on I have the same issues as before.

-Cassie

I figured out the issue, I reviewed all the lines that you instructed to add or change and I forgot to comment out one line, just one line. It works perfectly now sorry to waste your time.

-Cassie

Hello there! Thanks for the documentation, very thorough. I am having trouble with the telnet connection. I'm getting a "connection refused error" when using port 25, but can connect using port 23. For email, I need to be able to connect to port 25 though, correct?

Hi Rob,

Port 23 is a different service (telnet daemon). Postfix may not have started up if it's refusing your connection, what do you get from:

sudo service postfix status

Sam

Thanks Sam, that's exactly how I found the problem last night. In my main.cf file, I had my_hostname with an "@" mark in it, pretty silly, I changed it to "myraspi.mydomain.com" and all was fine after that. You were correct, the postfix service was not starting up properly. Thanks again, Rob

Hi.

Thanks for the tutorial :)

I have gone through part 1, 3 times already ,and although I'm more than sure that my setup matches your instructions, I never receive the test email using SMTP. I don't get any error messages during the process, I just never receive it (I also checked my spam folder etc. etc.)
Throughout Part 1, the tutorial instructed us to add lines to /etc/postfix/main.cf, and I just added everything as one block (no spaces between the various blocks of code that we had to add). Would this be the issue?

Thanks in advance :)

Hi,

Your ISP may be blocking port 25. If you telnet to me from your local network (telnet samhobbs.co.uk 25) can you make a connection or does it time out? If it times out your ISP is definitely blocking port 25, if you can connect then maybe not.

Sam

Hello Sam,

I was following the tutorial after I commented out mynetworks and added the helo step I tried to send an email again only to Relay access denied. Am I not suppose to be able to send out emails at the moment. I tried /var/log/mail.log and /var/log/mail.err and it says connection timed out. Not sure what to I should do next.
-Cassie

Hi Cassie,

You should only be able to send email after you have authenticated. With permit_mynetworks in the list, the server will allow any local user or process to send email without authentication, but when it's commented you need to authenticate with SASL first - that's why we removed it from the list, you can't test SASL restrictions properly with it in there.

Sam

I made a mistake I forgot to enter the AUTH PLAIN step. It works completely correctly.

Thanks for this tutorial.
I am at the test of initial testing.
I have my own domain, My ISP is 012.net.il and I am trying to send mail to my google account.
Port 25 is probably not blocked by ISP since I was able to telnet to your server.
Sending mail according to the article I get the following in the log:
The IP you're using to send mail is not authorized to send email directly to our servers. Please use the SMTP relay at your service provider instead. Learn more at 550 5.7.1 https://support.google.com/mail/answer/10336

So I entered my ISP smtp into the relayhost= line

This time I get from my ISP:
Relaying not allowed.

Any way to work around this?

Hi,

Sometimes you have to authenticate with your ISP's server before you can relay through it. I've never had to do that, so you're probably better off searching elsewhere for how to configure postfix to achieve that.

Sam

Thanks - will try the ISP support.

Other option maybe - my domain is registered with GoDaddy - no web hosting (I run everything from a Pi at home) but the account includes email - do you know if it is possible t do the relaying through GoDaddy?

I think they only do email forwarding, but I've never used them so I can't say for sure.

To be honest, I wouldn't trust GoDaddy with anything this important, I'd expect your ISP to be much more reliable.

Sam

Is it possible to not store the Maildir in users home directory but in /media/usbdrive/USER/Maildir ??
where USER is replaced with the corresponding username

Hi,
This maybe kind of stupid question, but please forgive a noob like myself ;)
I have used fstab to mount my ntfs formatted usb drive to /media/usb but I am struggling with permissions.
Is it possible to mount with different permissions for different subdirectories? i.e. i want PI to have ownership of /media/usb/pi and testuser to have ownership of /media/usb/testuser

Pages

Add new comment