Allow users to send outgoing email through Postfix

Relaying

Your mail server is almost ready for use. But one puzzle piece is missing. Your users can receive emails but they cannot send them yet.

First there is a difference on how users send emails versus how servers send emails. For comparison:

  • A mail server fetches the MX record for the domain name of the recipient’s email address. That tells it which mail server to talk to. Then it opens an SMTP connection (TCP port 25) and sends the email.
  • An end user with a mail client like Thunderbird, Evolution or Mutt cannot do it this way. The mail clients have no functionality built in for that MX record fetching magic. And the user is most likely on a dynamic IP address that other mail servers do not trust and reject. End users are meant to send emails to their provider’s (your!) mail server, send login information to authenticate themselves and then send the email. This is called relaying because your mail server acts as a relay between the user and other mail servers on the internet. For security reasons the user also has to authenticate to be allowed to send emails.

I have also made a few fancy illustrations to explain it. Let me show you. 🙂

Incoming email

When someone on the internet sends an email to john@example.org, some other mail server will deliver the email using SMTP to your mail server. Postfix will determine that it’s responsible for email addresses in the example.org domain and accept the email. John can then get the email from your server.

Outgoing email (without authentication)

John is on the internet somewhere and wants to send an email to lisa@example.com. Your mail server is not responsible for the “example.com” domain so it receives John’s email and would have to forward (relay) it to the actual mail server that is responsible for …@example.com email addresses. This may seem like a harmless scenario but your mail server must deny that:

Why? Because anyone can claim to be John and make your mail server forward mail. If an attacker (like a spammer) would send millions of spam emails in John’s name through your server then other organisations will accuse you as the operator of the mail server of spamming. Your mail server would be what people call an open relay. This is not what you want because your mail server would get blacklisted and you will not be able to send out mail to most other servers. So without any proof that John is actually John your server must reject the email.

Outgoing email (with authentication)

So how does John prove his identity? He needs to use authenticated SMTP. This is similar to the previous case but John’s email program will also send his username and password.

We are making sure that his authentication happens over an encrypted connection so John’s password is safe.

Postfix setting “mynetworks”

In addition to using SMTP authentication you can tell Postfix to always allow relaying for certain IP addresses. The mynetworks setting contains the list of IP networks or IP addresses that you trust. Usually you define your own local network here. The reason John had to authenticate in the above example is because he is not sending the email from your local network.

Make Postfix use Dovecot for authentication

Enabling SMTP authentication in Postfix is surprisingly easy. You already configured Dovecot so it knows all about your users from the SQL database. So let’s just make Postfix use that by telling it to ask the Dovecot server to verify the username and password. Postfix just needs some extra configuration. Run these commands on the shell:

postconf smtpd_sasl_type=dovecot
postconf smtpd_sasl_path=private/auth
postconf smtpd_sasl_auth_enable=yes

This enables SMTP authentication and tells Postfix that it can talk to Dovecot through a socket file located at /var/spool/postfix/private/auth. Do you remember that Postfix runs in a sandboxed chroot directory? That’s at /var/spool/postfix. It cannot access any files outside of that directory. But fortunately in a previous section you edited the /etc/dovecot/conf.d/10-master.conf file and made Dovecot place a socket file into /var/spool/postfix/private/auth to allow communication from Postfix.

Enable encryption

The following settings enable encryption, set the key and certificate paths for Postfix. Just run these commands:

postconf smtpd_tls_security_level=may
postconf smtpd_tls_auth_only=yes
postconf smtpd_tls_cert_file=/etc/letsencrypt/live/webmail.example.org/fullchain.pem
postconf smtpd_tls_key_file=/etc/letsencrypt/live/webmail.example.org/privkey.pem
postconf smtp_tls_security_level=may

smtp or smtpd?

Look closely. Some settings start with “smtp_” and others with “smtpd_”. That is not a typo. “smtp_” refers to the SMTP client. That is the component that sends out emails from Postfix to other servers.

Whereas “smtpd_” means the SMTP server. That in turn is the component that receives emails from other systems – either from a remote mail server or one of your users.

The above settings allow encrypted incoming (smtpd_) and outgoing (smtp_) connections. But they do not enforce it. So if a remote mail server does not have encryption enabled we will still accept their emails. You may be tempted to enforce encryption but that would reject email communication with servers who have been configured without encryption.

However the smtpd_tls_auth_only=yes setting makes sure that the user’s authentication information (email address and password) are always encrypted between the user and your mail server.

Restrictions

In the past you may have configured your smtpd_recipient_restrictions to restrict relaying to authenticated users. Postfix nowadays has setting called “smtpd_relay_restrictions” that deals with relaying requests in the “RCPT TO” phase of the SMTP dialog. So essentially it works like the good old “smtpd_recipient_restrictions” but is checked first. smtpd_relay_restrictions has a reasonable default so authenticated relaying works automatically:

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

How SMTP authentication works

Are you curious how SMTP authentication looks on a low level? You probably are not. But let’s do it anyway. Just once so that you get the idea.

In previous versions of this guide we used “telnet” to connect to TCP port 25 and speak SMTP. But now we enforce encryption and can’t do SMTP authentication unencrypted. Let’s try it anyway:

telnet localhost smtp

The server will let you in:

Trying 127.0.0.1…
Connected to localhost.
Escape character is '^]'.
220 webmail ESMTP Postfix (Debian/GNU)

Say hello:

ehlo example.com

Postfix will present a list of features that are available for you:

250-mailtest
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING

Let me briefly explain what these lines mean:

  • PIPELINING
    This is a feature to speed up SMTP communication. Usually the remote system has to wait for a response to every command it sends. Pipelining allows the remote server to send bulks of commands without waiting for a response. Postfix will just store these commands and execute them one by one. If you told Postfix to forbid pipelining it would disconnect the remote server when it tries to send bulks of commands without waiting for the proper reply. It is mainly a feature against spamming programs that don’t behave.
  • SIZE 10240000
    The remote server is allowed to send emails up to 10 MB large. This has long been a common maximum size for emails. However nowadays 40 MB or even more are more common sizes because emails have grown larger.
  • VRFY
    Allows remote servers to verify a given name or email address. For example the remote server could send “VRFY john” and your server might respond “250 John Doe <john@example.org>”. It can be used to verify that a certain recipient email address is deliverable
  • ETRN
    A command that a remote system can send to flush the Postfix queue of mails for a certain domain. It can be used if the remote system had technical problems and failed to receive email for a while. Then it could send an ETRN command to make your server start sending outstanding emails for that domain. It is rarely used.
  • STARTTLS
    This tells the remote system that it might start switching from this unencrypted to an encrypted connection by sending the “STARTTLS” command. It will then start negotiating a TLS-encrypted connection. You could compare it to an HTTP connection that suddenly switches over to an encrypted HTTPS connection. The advantage is that you can start talking SMTP on TCP port 25 and don’t have to open up a second TCP port like 465 which is the “SSMTP” (secure SMTP) port and only accepts encrypted connections.
  • ENHANCEDSTATUSCODES
    This enables more three-digit return codes for various conditions. See the RFC2034 if you are curious.
  • 8BITMIME
    In ancient times SMTP only processed 7-bit characters. You couldn’t transfer special characters like “Ä” or “ß” without special encoding. 8BITMIME allows a transmission of emails using 8-bit characters. Still many emails are specially encoded using ISO8859-1 or UTF-8.
  • DSN
    It enables DSNs (delivery status notofications) that allows the sender to control the messages that Postfix creates when an email could not be delivered as intended.
  • SMTPUTF8
    In addition to 8BITMIME you can use UTF8 encoded characters in header fields.
  • CHUNKING
    This feature (described in RFC 3030) makes sending of large emails more efficient.

However one important line is missing here that would allow us to send our username and password:

250-AUTH PLAIN LOGIN

We told Postfix to only allow authentication when the connection is encrypted. So we are not offered authentication over this plain text connection. That’s what we want.

Are you still connected? Okay, good. So we need an encrypted connection using TLS. Using the STARTTLS feature we can switch over from unencrypted to encrypted without having to reconnect. Enter…

STARTTLS

And the server replies:

220 2.0.0 Ready to start TLS

However now it’s getting weird because you would have to speak TLS encryption which is not a language that humans speak. So let’s quit this using the “QUIT” command and do that differently.

We can use OpenSSL to help us with the decryption. Run:

openssl s_client -connect localhost:25 -starttls smtp

You will see a lot of output. OpenSSL has connected to TCP port 25 and issued a STARTTLS command to switch to an encrypted connection. So whatever you type now will get encrypted. Enter:

EHLO example.com

And Postfix will send a list of capabilities that will look like this:

250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

And now that we are using an encrypted connection Postfix offers us to authenticate. So let us send the authentication string with a Base64-encoded password:

AUTH PLAIN AGpvaG5AZXhhbXBsZS5vcmcAc3VtbWVyc3Vu

Unless you have changed John’s password to something else than “summersun” the server should accept that authentication:

235 2.7.0 Authentication successful

Excellent. You are logged in through SMTP. You could now send an email to be forwarded to another mail server. I just wanted to show that authentication works if you use an encrypted connection.

Disconnect from Postfix:

QUIT

Authentication works. Well done.

Base64-encoded passwords

Wait a minute. What was that crazy cryptic string? There was no username and password in it. Was it encrypted?

No, that was no encryption. It was merely an encoded version of the username and password. Why that? Well usually in SMTP you can only transfer ASCII characters. But the email address may contain special characters that are not covered by ASCII. So in the PLAIN method that information is Base64 encoded.

What is actually converted to Base64…

NULL-BYTE + USERNAME + NULL-BYTE + PASSWORD

So for John’s case you can easily create the Base64 string using:

printf '\0john@example.org\0summersun' | base64

As a result you will get the exact same string you used above with “AUTH PLAIN”.

The submission port

Although I have been talking about SMTP on port 25 to relay mails it is actually not the proper way. End users should not use port 25 but rather the submission service on TCP port 587 (as described in RFC 4409). The idea is to use port 25 for transporting email (MTA = mail transport agent) from server to server and port 587 for submitting (MSA = mail submission agent) email from a user to a mail server.

For comparison:

TCP Port25587
Service nameSMTPSubmission
EncryptionOptionalMandatory
AuthenticationOptionalMandatory
Meant forServer-to-ServerUser-to-Server
Your ISPmay block this portshould allows this port

I hope that makes the distinction a bit clearer. So let’s enable the submission service. All Postfix services are declared in the /etc/postfix/master.cf file. Please edit the file and look for the submission section that is commented out by default. Turn that section into the following. Basically I removed the ‘#’ character on all lines of this section and removed the lines with the mua_* variables.

submission inet n       -       y       -       -       smtpd
 -o syslog_name=postfix/submission
 -o smtpd_tls_security_level=encrypt
 -o smtpd_sasl_auth_enable=yes
 -o smtpd_tls_auth_only=yes
 -o smtpd_reject_unlisted_recipient=no
 -o smtpd_recipient_restrictions=
 -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
 -o milter_macro_daemon_name=ORIGINATING

Make sure to start the first line in the first column and indent the following lines.

This service uses the “smtpd” daemon (see the last word of the first line) which is the piece of software that responds if you open an SMTP connection on TCP port 25. But it gets a few extra options set…

  • in the /var/log/mail.log mark the connections to the submission port as “postfix/submission” (syslog_name)
  • enforce encryption on this port (smtpd_tls_security_level)
  • enable authentication (smtpd_sasl_auth_enable)
  • enforce encryption during authentication (smtpd_tls_auth_only)
  • allow sending emails to recipients outside of this mail server (smtpd_reject_unlisted_recipient)
  • the restrictions point to $mua… configuration variables (MUA = mail user agent) that you can set in your main.cf. The defaults for these four (client, helo, sender, recipient) restrictions are just empty. So unless you want to enforce extra restrictions I suggest you comment out or delete these lines.
  • allow relaying if the sender was authenticated (smtpd_relay_restrictions)
  • send the string ORIGINATING to milter services (milter_macro_daemon_name) – you can just leave it like that

Restart the Postfix server:

systemctl restart postfix

Your users can now use the submission port to send email. They just use the port 587 in their mail clients instead of port 25.

Port 465?

This TCP port belongs to the “submission over TLS” service. It is used for the submission service but expects an encrypted connection from the first byte. This port is hardly ever used so you don’t have to care about it. The submission service you just configured is also encrypted but uses the STARTTLS mechanism to switch to a TLS connection after the welcome message.

Protecting against forged sender addresses

A “forged” sender address means that someone claims to be someone else. Let’s say that John has authenticated and the mail server trusts him. Nothing keeps John from impersonating someone else and sending email in his name? Most email service providers have restrictions that you can only send emails if the sender matches your actual email address. Let’s do that, too.

Postfix provides a setting called smtpd_sender_login_maps for that purpose. From the “maps” part you can deduce that it expects a mapping again. This time the two columns mean…

  • Left column: who you claim to be (email’s sender address)
  • Right column: who you logged in as (user name after authentiation)

As we use the email address also as a user name we simply need a mapping where the email address is listed on both sides. Fortunately we already have a mapping like that: our email2email mapping that we used earlier as a workaround for catchall forwardings. Let’s reuse it. Please run…

postconf smtpd_sender_login_maps=mysql:/etc/postfix/mysql-email2email.cf

This sets the parameter both for the SMTP port (25) and the submission port (587). Defining these maps is not enough though. You also need to make Postfix act on this. Edit the /etc/postfix/master.cf again and in the submission section add the following option. Make sure the line is indented like all other options:

 -o smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject

Restart Postfix after this change:

systemctl restart postfix

You can now try to send email as a different user than you are logged in. One way to test it is to create a new identity in Roundcube and use that:

With this wrong sender address any email should get rejected. Your mail.log would read something like:

NOQUEUE: reject: RCPT from foo.bar[…]: 553 5.7.1 <someoneelse@example.com>: Sender address rejected: not owned by user john@example.org; from=<someoneelse@example.com> to=<…>

Allow aliases?

If you want to allow users to send as one of their aliases you could create a new *.cf file with a mapping query like this:

SELECT email FROM virtual_users WHERE email='%s' UNION SELECT destination FROM virtual_aliases WHERE source='%s'

33 thoughts on “Allow users to send outgoing email through Postfix”

  1. Hello,
    The whole tutorial is excellent, it helped me a lot to understand some concepts that are fuzzy, thank you very much for this contribution.

    I have a problem, and I think it is at this stage of the setup.
    What I try is to send an email from my domain server to an external server for example gmail or outlook or mail.ru but I have no success.

    I always get the response “” …… 5.7.1 : Relay access denied …… ”

    I have finished making all the settings including the DNS DKIM.
    I don’t know what you would recommend me to review.

    Thanks.

    1. Christoph Haas

      Hi. Good to have you here. Regarding your relaying issue: did you enable authentication in the mail client when you send an email? Feel free to paste the relevant lines of your mail.log here. That should give an idea.

  2. Thanks

    Thanks, I also identify that the problem only occurs when I use an external client (thunderbird) from outside my local network. With the Roundcube web client I have no problems sending emails to other servers.

    Here the log.

    ——— mail.log———
    Dec 10 19:22:26 correo postfix/smtpd[14573]: connect from 1-2-3-4.provedor.internet.net[1.2.3.4]
    Dec 10 19:22:27 correo postfix/trivial-rewrite[14575]: warning: do not list domain sekkom.com in BOTH mydestination and virtual_mailbox_domains
    Dec 10 19:22:27 correo postfix/smtpd[14573]: NOQUEUE: reject: RCPT from 1-2-3-4.provedor.internet.net[1.2.3.4]: 554 5.7.1 : Relay access denied; from= to= proto=ESMTP helo=
    Dec 10 19:22:46 correo dovecot: imap-login: Login: user=, method=PLAIN, rip=192.168.30.6, lip=192.168.30.6, mpid=14577, TLS, session=
    ———

    1. Christoph Haas

      Please check the SMTP server settings in Thunderbird. It should use STARTTLS on port 587, “normal password” and the email address must be used as a login name there.

      A successful relaying session should look like this:

      Dec 11 11:00:49 tron postfix/submission/smtpd[749443]: 814CC5F51B: client=unknown[91.106.x.y], sasl_method=PLAIN, sasl_username=email@christoph-….de
      Dec 11 11:00:49 tron postfix/cleanup[749469]: 814CC5F51B: message-id=
      Dec 11 11:00:49 tron postfix/qmgr[553694]: 814CC5F51B: from=, size=661, nrcpt=1 (queue active)
      Dec 11 11:00:50 tron postfix/smtp[749508]: 814CC5F51B: to=<…@gmail.com>, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c00::1b]:25, delay=0.67, delays=0.08/0.01/0.15/0.43, dsn=2.0.0, status=sent (250 2.0.0 OK 1639220450 w22si20…30 – gsmtp)
      Dec 11 11:00:50 tron postfix/qmgr[553694]: 814CC5F51B: removed

      Also please check why sekkom.com is used both as a local and virtual domain. If you are unsure what I mean please re-read https://workaround.org/ispmail/bullseye/types-of-email-domains/

  3. Thanks,
    I went back to check what I had made a mistake in checking was that it was not using port 587, the client is using port 25, and port 587 is not open in the firewall either. I fixed that. . On the other hand, I already corrected the problem of using both virtual and local.

    ———– thanks Christoph mail.log————–
    Dec 11 12:44:20 correo postfix/submission/smtpd[10793]: 4BA081C02CF: client=1-2-3-4.provedor.internet.net[1.2.3.4], sasl_method=PLAIN, sasl_username=horacio@sekkom.com
    Dec 11 12:44:20 correo postfix/cleanup[10803]: 4BA081C02CF: message-id=
    Dec 11 12:44:20 correo postfix/qmgr[10786]: 4BA081C02CF: from=, size=681, nrcpt=1 (queue active)
    Dec 11 12:44:25 correo postfix/smtp[10804]: 4BA081C02CF: to=, relay=mxs.mail.ru[94.100.180.31]:25, delay=5.3, delays=0.57/0.01/3.4/1.3, dsn=2.0.0, status=sent (250 OK id=1mw5U0-0007gL-Mg)
    Dec 11 12:44:25 correo postfix/qmgr[10786]: 4BA081C02CF: removed

  4. Regarding the last part of this page about allowing users to use an alias to send out emails, I created a file called mysql-virtual-alias-send.cf, and I added the below content to this file, then edited the “smtpd_sender_login_maps” info.
    user = mailserver
    password =
    hosts = 127.0.0.1
    dbname = mailserver
    query = SELECT email FROM virtual_users WHERE email=’%s’ UNION SELECT destination FROM virtual_aliases WHERE source=’%s’

    Then, I get a mysql syntax error in my mail.log:
    warning: mysql:/etc/postfix/mysql-virtual-alias-send.cf: query failed: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘@domain.com’ UNION SELECT destination FROM virtual_aliases WHERE sourc…’ at line 1
    Dec 16 22:08:58 mail postfix/submission/smtpd[13538]: warning: mysql:/etc/postfix/mysql-virtual-alias-send.cf lookup error for “user@domain.com”

    Is there an error in the mysql syntax?

    1. Christoph Haas

      Sorry, the web site formatted the hyphens incorrectly. Fixed it. Please copy/paste it again.

      1. Thank you Christoph! That fixed it.

        Thank you for your work on these tutorials. My mail server is working fine now.

  5. This dont solve any problem if HACKER using your email user name and pass to send emails to the world.

    I have 50000 user emails. i want emails only send to them.

  6. Eben van Deventer

    Hi, this is a truly remarkable piece of work here, the sheer complexity of mail servers and the interaction between the different cogs that make everything work seemed unsurmountable until I read your detailed guide. I am however stuck and cannot figure out why I cannot get a secure smtp connection, logs show:

    Jan 25 21:36:31 sogo postfix/smtpd[962]: connect from localhost[::1]
    Jan 25 21:37:44 sogo postfix/smtpd[962]: SSL_accept error from localhost[::1]: -1
    Jan 25 21:37:44 sogo postfix/smtpd[962]: warning: TLS library problem: error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:331:
    Jan 25 21:37:44 sogo postfix/smtpd[962]: lost connection after STARTTLS from localhost[::1]
    Jan 25 21:37:44 sogo postfix/smtpd[962]: disconnect from localhost[::1] ehlo=1 starttls=0/1 commands=1/2

    From trying to connect with telnet, the above seems to be an issue with telnet, however even with telnet-ssh I get the same issue and cannot figure out why my server refuses to accept SMTP connections.

    /etc/postfix/master.cf looks correct:

    #
    # Postfix master process configuration file. For details on the format
    # of the file, see the master(5) manual page (command: “man 5 master” or
    # on-line: http://www.postfix.org/master.5.html).
    #
    # Do not forget to execute “postfix reload” after editing this file.
    #
    # ==========================================================================
    # service type private unpriv chroot wakeup maxproc command + args
    # (yes) (yes) (no) (never) (100)
    # ==========================================================================
    smtp inet n – y – – smtpd
    #smtp inet n – y – 1 postscreen
    #smtpd pass – – y – – smtpd
    #dnsblog unix – – y – 0 dnsblog
    #tlsproxy unix – – y – 0 tlsproxy
    submission inet n – y – – smtpd
    -o syslog_name=postfix/submission
    -o smtpd_tls_security_level=encrypt
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_tls_auth_only=yes
    -o smtpd_reject_unlisted_recipient=no
    # -o smtpd_client_restrictions=
    # -o smtpd_helo_restrictions=$mua_helo_restrictions
    # -o smtpd_sender_restrictions=$mua_sender_restrictions
    -o smtpd_recipient_restrictions=
    -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    -o milter_macro_daemon_name=ORIGINATING
    -o smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject

    Any help here would be greatly appreciated. Again, thanks for the awesome work.

    1. I found the issue after reading through your Ansible Playbook and seeing this missing command there, which I could not find in the tutorial:

      postconf inet_interfaces=all

  7. Hi Christoph, I have been following your excellent directions, however I keep getting “Sender address rejected: not owned by user” errors when trying to smtp send from another server. I checked several times that I have mysql-email2email.cf set up correctly. Should the virtual_aliases table be set up with email addresses in both source and destination? The user address supplied in the error message only has the username portion and not the domain.

  8. Aaron Van Stralen

    Hello Christoph, this is an excellent guide that works great. However, I’ve had one issue that perhaps you can shed some light on. I am able to use mail clients to send email by connecting and authenticating on port 25 with STARTTTLS just the same as port 587. Zero difference. However, I’ve noticed that I could send an email with a from address other than the one I’ve logged in as ONLY when done on port 25. But if I use port 587, it is rejected as expected “Sender address rejected: not owned by user.” First, should I be able to log in a authenticate when connecting on port 25? Second, is this expected behavior?

    Thanks for any help or ideas.

    For reference this is the relevant part of the master.cf file:

    smtp inet n – y – – smtpd
    #smtp inet n – y – 1 postscreen
    #smtpd pass – – y – – smtpd
    #dnsblog unix – – y – 0 dnsblog
    #tlsproxy unix – – y – 0 tlsproxy
    submission inet n – y – – smtpd
    -o syslog_name=postfix/submission
    -o smtpd_tls_security_level=encrypt
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_tls_auth_only=yes
    -o smtpd_reject_unlisted_recipient=no
    -o smtpd_recipient_restrictions=
    -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    -o milter_macro_daemon_name=ORIGINATING
    -o smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject

    1. Aaron Van Stralen

      I actually found the issue I had and fixed it.

      To fix it, I had to add the option of “-o smtpd_sender_restrictions=reject_sender_login_mismatch” after “smtp inet n – y – – smtpd”

      So here is the new relevant part of the master.cf file:

      smtp inet n – y – – smtpd
      -o smtpd_sender_restrictions=reject_sender_login_mismatch
      #smtp inet n – y – 1 postscreen
      #smtpd pass – – y – – smtpd
      #dnsblog unix – – y – 0 dnsblog
      #tlsproxy unix – – y – 0 tlsproxy
      submission inet n – y – – smtpd
      -o syslog_name=postfix/submission
      -o smtpd_tls_security_level=encrypt
      -o smtpd_sasl_auth_enable=yes
      -o smtpd_tls_auth_only=yes
      -o smtpd_reject_unlisted_recipient=no
      -o smtpd_recipient_restrictions=
      -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
      -o milter_macro_daemon_name=ORIGINATING
      -o smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject

  9. Joao Almeida

    STARTTLS
    220 2.0.0 Ready to start TLS
    openssl s_client -connect localhost:25 -starttls smtp
    Connection closed by foreign host.

      1. Daniél Lecoq

        You probably tried
        AUTH PLAIN AGpvaG5AZXhhbXBsZS5vcmcAc3VtbWVyc3Vu
        Unless you have john@example.com with password summersun(perhaps you changed the password in the roundcube exercise), you have to make your own string.
        printf ‘\0john@example.org\0summersun’ | base64
        so change john@example.com and summersun, to whatever you have.

      2. I made the same mistake. Probably a sign I need more coffee or that it is time to go to bed. You should quit the telnet connection before you run the openssl command (exactly like the tutorial says to do).

  10. Boris Gereg

    Hi, I have mail and web server at the same machine, latest versions, mail server configured according to your guide, everything is working like a charm, until my attempt to send mail using PHP mail() function from local web server to my local mail account. If I use any UTF-8 character (or non-ASCII probably) in From or Subject field, Postfix throws this:

    Oct 8 20:45:38 myHostname postfix/lmtp[4116209]: 5F757A3E: to=, relay=myHostname.myDomain.sk[private/dovecot-lmtp], delay=0.1, delays=0.07/0.01/0.01/0, dsn=5.6.7, status=bounced (SMTPUTF8 is required, but was not offered by host myHostname.myDomain.sk[private/dovecot-lmtp])

    After removing UTF-8 character the mail is delivered as expected.

    Searching the internet suggests relation to Postfix compatibility_level smtputf8_enable settings, but I do not think I want to mess with back compability settings.

    I have tried telnet localhost 587 and ehlo response contained 250-SMTPUTF8, like in your feature listing in your article above. I have noticed in your article, when you start TLS, ehlo does not list 250-SMTPUTF8 any more. Why is SMTPUTF8 not available after STARTTLS? Is this the actual problem instead of back compatibility issue?

    Thanks.

  11. In my old “ISP Mail” configuration I was able to send out email as any address I wanted. This fit nicely with the catchall setup we have running. I could reply to any email I received on any address as that address. Thunderbird has an option for this called “Reply from this identity when delivery headers match:”. Would it be possible to describe what I would need to do/change specifically to allow this? This section mentions steps to deliberately disallow this or only work with the alias list, which is too much hassle to maintain.

  12. First thing Thank you for the best tutorials ever i use this since my first Mailserver on debian jessy 🙂
    I liked to use this mail as Alias function but i don’t know how i have to implement it with this:
    “Allow aliases?

    If you want to allow users to send as one of their aliases you could create a new *.cf file with a mapping query like this:
    SELECT email FROM virtual_users WHERE email=’%s’ UNION SELECT destination FROM virtual_aliases WHERE source=’%s’

    What i have to do after i created that *cf file? how i have to import this into the postfix config so i can use the alias to send a mail with my alias mail

    Thank you fo help 🙂

    1. Christoph Haas

      Since Jessy? Wow, that’s loyal. 🙂
      Basically you would add the mapping to your sender_login_maps setting. E.g…
      smtpd_sender_login_maps=mysql:/etc/postfix/mysql-email2email.cf,mysql:/etc/postfix/thhunders-funky-aliases-map.cf

      1. Thank you i got it 🙂
        and yes your Tutorials are realy realy good to go with it to make an nice and smoth mailserver at low cost without any unwanted tools that are not needed to go with and cost resources that i can use for other things 😉
        very helpfull please go further with it 🙂

  13. Hello Christoph!

    Awesome Tutorial!
    Thanks to your guide I was able to set up two mailservers and I could understand the underlying technology! Thank you!

    Everything works like a charm, except today when I wanted to send an email and noted the following errors in /var/log/mail.log:

    Feb 14 19:03:18 mail postfix/smtp[262170]: 3E20D405FA: host mx.mail.dehosted.net[93.159.105.25] said: 450-4.7.1 This email was rejected because it violates our security policy 450-4.7.1 CYREN IP reputation determined a medium risk associated with the sender address (ip4-address-of-mailserver). 450 4.7.1 The host (ip4-address-of-mailserver)/24 is not yet authorized to send emails to this server. (in reply to end of DATA command)
    ^[[CFeb 14 19:03:20 mail postfix/smtp[262170]: 3E20D405FA: to=, relay=mx.mail.dehosted.net[93.159.105.25]:25, delay=5.4, delays=0.22/0.02/2.8/2.4, dsn=4.7.1, status=deferred (host mx.mail.dehosted.net[93.159.105.25] said: 450-4.7.1 This email was rejected because it violates our security policy 450-4.7.1 CYREN IP reputation determined a medium risk associated with the sender address (ip4-address-of-mailserver). 450 4.7.1 The host (ip4-address-of-mailserver)./24 is not yet authorized to send emails to this server. (in reply to end of DATA command))

    And the second try with my second mailserver fails, too with the same error, but adds a TLS Error: mail.log:

    Feb 14 20:27:08 mail postfix/smtp[348157]: C8D777D885: host mx.mail.dehosted.net[93.159.105.25] said: 450-4.7.1 This email was rejected because it violates our security policy 450-4.7.1 Inbound connection is not secured by TLS. 450 4.7.1 The host (ip4-address-of-mailserver)/24 is not yet authorized to send emails to this server. (in reply to end of DATA command)
    Feb 14 20:27:08 mail postfix/smtp[348157]: SSL_connect error to mx.mail.dehosted.net[93.159.105.25]:25: lost connection
    Feb 14 20:27:08 mail postfix/smtp[348157]: C8D777D885: Cannot start TLS: handshake failure
    Feb 14 20:27:09 mail postfix/smtp[348157]: C8D777D885: to=, relay=mx.mail.dehosted.net[93.159.105.25]:25, delay=664, delays=659/0.02/3.6/0.95, dsn=4.7.1, status=deferred (host mx.mail.dehosted.net[93.159.105.25] said: 450-4.7.1 This email was rejected because it violates our security policy 450-4.7.1 The host (ip4-address-of-mailserver)/24 is not yet authorized to send emails to this server. 450 4.7.1 Inbound connection is not secured by TLS. (in reply to end of DATA command))

    I don’t understand why these TLS Errors in the last log occur. I have setup everything according to your tutorial, and got never such errors…

    I don’t know what to do.

    Do you have a clue?
    I know you’re managing this project in your spare-time. So thank you very much for your time 🙂

    Daniel

  14. Claudio Junior

    First of all thanks for this tutorial. Helped me a lot.
    Today I use ispconfig as a server manager and I think it’s very good.
    I left sasl active on port 25 and I was having problems ejecting direct emails on the server with stolen users and passwords. In my case I don’t have much control over the users/passwords and the users fall straight into attacks, either virus or social engineering.
    What I did was, have firewall rules on port 587, imap and others. Have rules that limit the amount of emails on this port and apply email and user policy. I’m in Brazil and in this case I release only local ASN IPs for the ports above.
    On port 25, I have rbls lists, and several spam filters, which manage to block well. But I had to close the sending of authenticated emails, because the policy control did not work.

  15. Thanks as always for creating & maintaining this guide. I use my mailserver for local storage at home. I’ve switched to Ubuntu server, but the Debian guide still works great.

    Perhaps a mention about using relayhost and fetchmail to send/receive via an ISP mailbox? I’m pretty sure you wrote about it in the past, but I can’t seem to find it.

    1. Christoph Haas

      I’m not sure if I mentioned that. But using fetchmail is actually very simple. You just need to have “localhost” in your “mynetworks” in the /etc/postfix/main.cf to allow any emails from localhost. And then configure fetchmail to send emails to localhost. That’s a totally valid option. The only drawback is that your incoming emails are a bit delayed and that you may not be able to do proper spam scanning yourself.

  16. Thoughts on use of port 465 and “Implicit TLS”.

    I believe this has come up before, but I can’t find any comments on the topic. All of this is based on RFC 8314 which is a proposed standard from 2018.

    In section 3.
    “To encourage more widespread use of TLS and to also encourage greater consistency regarding how TLS is used, this specification now recommends the use of Implicit TLS for POP, IMAP, SMTP Submission”.

    Also in section 3.3
    “It is desirable to migrate core protocols used by MUA software to Implicit TLS over time”.

    And last in section 4.
    “MSPs that support POP, IMAP, and/or Message Submission SHOULD provide and support instances of those services that use Implicit TLS.”

    To me, this all implies that port 465 should be supported and used.

    Have you had a chance to look at this RFC Christoph, and any comments on having both 465 and 587?

  17. Hello,
    Thanks for the great guide.
    I followed the Bullseye guide and I, almost, got everything working.
    I can send emails to an external email server but can not receive any emails. Neither from my domain nor from external servers.
    I did 3 tests sending emails using roundcube.
    Here are the log entries for the 3 tests.
    I just can not figure out what I did wrong.

    From one user to another user in the same virtual domain (mydomain.ca) using roundcube. (Failed)
    ——————————————–
    Aug 3 02:43:13 scan postfix/submission/smtpd[1795]: connect from scan.mydomain.ca[1.1.1.93]
    Aug 3 02:43:13 scan postfix/submission/smtpd[1795]: C93832C057A: client=scan.mydomain.ca[1.1.1.93], sasl_method=LOGIN, sasl_username=jack@mydomain.ca
    Aug 3 02:43:13 scan postfix/cleanup[1800]: C93832C057A: message-id=
    Aug 3 02:43:14 scan postfix/qmgr[1400]: C93832C057A: from=, size=596, nrcpt=1 (queue active)
    Aug 3 02:43:14 scan postfix/submission/smtpd[1795]: disconnect from scan.mydomain.ca[1.1.1.93] ehlo=2 starttls=1 auth=1 mail=1 rcpt=1 data=1 quit=1 commands=8
    Aug 3 02:43:14 scan postfix/smtp[1802]: C93832C057A: to=, relay=none, delay=1, delays=0.38/0.01/0.62/0, dsn=5.4.6, status=bounced (mail for mydomain.ca loops back to myself)
    Aug 3 02:43:14 scan postfix/cleanup[1800]: CD4702C0586: message-id=
    Aug 3 02:43:14 scan postfix/bounce[1806]: C93832C057A: sender non-delivery notification: CD4702C0586
    Aug 3 02:43:14 scan postfix/qmgr[1400]: CD4702C0586: from=, size=2525, nrcpt=1 (queue active)
    Aug 3 02:43:14 scan postfix/qmgr[1400]: C93832C057A: removed
    Aug 3 02:43:14 scan postfix/smtp[1802]: CD4702C0586: to=, relay=none, delay=0.11, delays=0.01/0/0.1/0, dsn=5.4.6, status=bounced (mail for mydomain.ca loops back to myself)
    Aug 3 02:43:14 scan postfix/qmgr[1400]: CD4702C0586: removed
    ********************************************

    From an external email server to my server. (Failed)
    ——————————————–
    Aug 3 02:52:10 scan postfix/smtpd[1891]: connect from mail1.externaldomain.com[2.2.2.60]
    Aug 3 02:52:10 scan postfix/smtpd[1891]: NOQUEUE: reject: RCPT from mail1.externaldomain.com[2.2.2.60]: 554 5.7.1 : Relay access denied; from= to= proto=ESMTP helo=
    Aug 3 02:52:10 scan postfix/smtpd[1891]: disconnect from mail1.externaldomain.com[2.2.2.60] ehlo=1 mail=1 rcpt=0/1 data=0/1 rset=1 quit=1 commands=4/6
    ********************************************

    From my domain (using roundcube) to an external server. (Success)
    ——————————————–
    Aug 3 02:55:25 scan postfix/submission/smtpd[1913]: connect from scan.mydomain.ca[1.1.1.93]
    Aug 3 02:55:25 scan postfix/submission/smtpd[1913]: CBA582C0584: client=scan.mydomain.ca[1.1.1.93], sasl_method=LOGIN, sasl_username=jack@mydomain.ca
    Aug 3 02:55:25 scan postfix/cleanup[1916]: CBA582C0584: message-id=
    Aug 3 02:55:26 scan postfix/qmgr[1400]: CBA582C0584: from=, size=584, nrcpt=1 (queue active)
    Aug 3 02:55:26 scan postfix/submission/smtpd[1913]: disconnect from scan.mydomain.ca[1.1.1.93] ehlo=2 starttls=1 auth=1 mail=1 rcpt=1 data=1 quit=1 commands=8
    Aug 3 02:55:28 scan postfix/smtp[1918]: CBA582C0584: to=, relay=mail1.externaldomain.com[2.2.2.60]:25, delay=2.4, delays=0.27/0.01/1.2/0.93, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 48EF9140569)
    Aug 3 02:55:28 scan postfix/qmgr[1400]: CBA582C0584: removed
    ********************************************

    1. Christoph Haas

      Hi Ragai. The cause might actually be simple. Seeing “mail for mydomain.ca loops back to myself” in the logs means, that your server does not feel responsible for the “mydomain.ca”. Please make sure that you have added that domain properly to the “virtual_domains” table. Is the mysql*cf file properly set in /etc/postfix? Have you checked with “postconf -q …” whether Postfix queries the domain from MySQL?

      1. Hi Christoph, thanks for the prompt response.
        Yes, you’re right. The mysql-virtual-mailbox-domains.cf file had the wrong query line. Let’s blame my glasses as I checked these files 4 times before.
        Now I can send & receive emails. Thanks
        But I just got the below sieve errors in the log.
        Should I apply the same chown & chgrp commands that are mentioned for /etc/dovecot/sieve/learn-…. files to the files in the /etc/dovecot/sieve-after directory as well? Or they need different permissions?

        Aug 3 12:38:48 scan dovecot: lmtp(7341): Connect from local
        Aug 3 12:38:48 scan dovecot: lmtp(jack@mydomain.ca): Error: sieve: open: failed to open: open(/etc/dovecot/sieve-after/spam-to-folder.svbin) failed: Permission denied (euid=5000(vmail) egid=5000(vmail) missing +r perm: /etc/dovecot/sieve-after/spam-to-folder.svbin, we’re not in group 0(root), dir owned by 0:0 mode=0755)
        Aug 3 12:38:48 scan dovecot: lmtp(jack@mydomain.ca): Error: open(/etc/dovecot/sieve-after/spam-to-folder.svbin.scan.7341.13351bd7c8799c1e) failed: Read-only file system
        Aug 3 12:38:48 scan dovecot: lmtp(jack@mydomain.ca): Error: sieve: binary /etc/dovecot/sieve-after/spam-to-folder.svbin: save: failed to create temporary file: open(/etc/dovecot/sieve-after/spam-to-folder.svbin.) failed: Read-only file system
        Aug 3 12:38:48 scan dovecot: lmtp(jack@mydomain.ca): sieve: msgid=: stored mail into mailbox ‘INBOX’
        Aug 3 12:38:48 scan postfix/lmtp[7340]: D5A072C0559: to=, relay=scan.mydomain.ca[private/dovecot-lmtp], delay=2.4, delays=2.3/0.01/0.01/0.04, dsn=2.0.0, status=sent (250 2.0.0 slKrCBjYy2StHAAA0b0dDg Saved)
        Aug 3 12:38:48 scan dovecot: lmtp(7341): Disconnect from local: Client has quit the connection (state=READY)
        Aug 3 12:38:48 scan postfix/qmgr[6237]: D5A072C0559: removed

        Thanks

        1. Hi Christoph,
          I tried applying the same ownership & permissions as the files in /etc/dovecot/sieve directory. This seems to fix this issue as no more errors show in the /var/log/mail.err.

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to Top