Allow user to send outoing email through Postfix

Relaying

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

There is a subtle difference about how emails are sent to the internet. Mail servers do that differently than end users:

  • 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 and sends the email.
  • An end user with a mail client like Thunderbird, Evolution or Mutt cannot use this way. The mail clients have no functionality built in for that MX record fetching thingy. And the user is most likely on a dynamic IP address that other mail servers do not trust and reject. Such clients are meant to talk 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.
  • An end user using webmail is a simpler case. If the webmail service runs on the mail server itself then it can just talk to Postfix on localhost which makes it trusted. So no special authentication is required. (Hint: $mynetworks)

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 use POP3 or IMAP to fetch 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 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 blared out.

Postfix setting “mynetworks”

In addition to using SMTP authentication you can tell Postfix to always relay email 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.

If your users are using the webmail interface running on your mail server then they will not need to authenticate. Roundcube sends email to localhost which is trusted according to the mynetworks setting.

Make Postfix use Dovecot for authentication

Enabling SMTP authentication in Postfix is surprisingly easy. You already configured Dovecot for user authentication. 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 other with “smtpd_”. That is not a typo. “smtp_” refers to the SMTP client. That is the component that sends out emails from your Postfix.

Whereas “smtpd_” means the SMTP server. That in turn is the component that receives emails from other systems – either 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.

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 plaintext 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 complicated 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

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. And the password can contain virtually anything that is definitely not part of 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 home ISPmay block this portallows 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. Remove the ‘#’ character on all lines of this section:

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=$mua_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

Basically this new 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

By now a user can submit emails to the mail server as long as they authenticate properly. Then they can send arbitrary emails. Postfix will not check who you are (sender) and who you send the email to (recipient). You could set your sender address to impersonate anyone. That does not guarantee that your email reaches its recipient due to further security mechanism like SPF but it can still confuse the recipient. So many email service providers have restrictions that you can only send emails if the sender matches your usual email address.

It may be a good idea to set that restriction for your mail server, 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). So the restrictions should work. You can try to send email as a different user than you are logged in. You should get rejected. Your mail.log would read something like:

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

61 thoughts on “Allow user to send outoing email through Postfix”

  1. Florian Effenberger

    Probably I didn’t wait long enough, but I had to reload postfix before the authentication changes came to effect. (IIRC, the config file is parsed after changes, but didn’t seem to work for me.)

  2. Hey, I’ve tried the “Protecting against forged sender addresses” but it does not work for me. I can be anyone and write there is no reject. 🙁

    I hope somebody can help me with this. It’s a major thing that really should work.

    1. I’ve set some “smtpd_recipient_restrictions=” in the main.cf file. Do these override what is set by master.cf submission “-o smtpd_recipient_restrictions=”? or does master.cf override main.cf features?

      1. Yes it overrides, you need to remove the empty statement
        -o smtpd_recipient_restrictions=

    2. I’ve found the solution. If you want to “Protecting against forged sender addresses” with submission port 587,
      you need to add:

      -o smtpd_sender_restrictions=reject_sender_login_mismatch
      -o smtpd_sender_login_maps=mysql:/etc/postfix/mysql-email2email.cf

      It would be good to add this also to main.cf:

      smtpd_sender_restrictions = reject_sender_login_mismatch
      smtpd_sender_login_maps = mysql:/etc/postfix/mysql-email2email.cf

      In my use case I wanted to be able to send with the aliases that are connected to a email account.
      So I’ve changed the mysql query in /etc/postfix/mysql-email2email.cf

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

      This allows to use the aliases for a email-account to also be used as a email name.

      1. Christoph Haas

        All options from the main.cf are automatically used in services in the master.cf. So if you set them in your master.cf you do not need to copy them to the “submission” service.

        The UNION is a good idea. We may need to consider catchall accounts though.

        1. It’s convenient to have a dedicated catchall@example.com account for catchall emails and share its mail folders with john, possibly in read-only mode, via dovecot shared folder functionality, instead of sending all catchall emails directly to john. That way if john has to step down and his mailbox has to be deleted, you wouldn’t lose the past catchall emails, nor would they be mixed with john’s personal emails. So if alice were to assume john’s role and would need access to the catchall emails, all you would have to do is give her access to the catchall@example.com‘s folders. catchall@example.com is not meant to be logged into, so it could have some impossible password set, and since that account won’t be sending any emails, there would be no issue with using the catchall alias in the UNION like that.

          1. If you want to allow virtual aliases and not only virtual users to submit outgoing mail you only have to change one configuration parameter for the service “submission” in the file “master.cf” like this:

            -o smtpd_sender_login_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-email2email.cf

            Rewriting the file “/etc/postfix/mysql-email2email.cf” isn’t needed.

          2. Felix Pernat

            I really like Fredriks answer. It works fine (unfortunately I could not reply to his message directly).
            Only to stick with the format used throughout this guide, I added the line
            ” -o smtpd_sender_login_maps=$mua_sender_login_maps”
            to master.cf, and then define this variable in main.cf:
            “mua_sender_login_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-email2email.cf”

            This is basically the very same as Fredriks version, and up to your preference if you go this (a bit unDRY) extra step just for consistency. Happy codin’

  3. So far, this has been a great guide!
    “Fortunately we already have a mapping like that: our email2email mapping that we used earlier as a workaround for catchall forwardings. ” but unfortunately I followed the advice and skipped the catchall, but now unclear if I should simply create an empty file at this point or somehow use contents from the catchall chapter?

  4. Adon Irani

    How can i avoid the NOQUEUE: reject – Relay access denied when my satellite servers try to relay an external (e.g. gmail) address via my main mail server? Everything else is working, and I have the satellite server IPs in mynetworks + permit_mynetworks in smtpd_relay_restrictions.

    Is it safe to add permit_mynetworks to smtpd_recipient_restrictions?

  5. Florian Effenberger

    Do the lines

    -o smtpd_client_restrictions=$mua_client_restrictions
    -o smtpd_helo_restrictions=$mua_helo_restrictions
    -o smtpd_sender_restrictions=$mua_sender_restrictions

    make sense actually, given that the variables are not defined? Normally, the restrictions are inherited from main.cf, but when the variables are undefined, isn’t the restriction blank then?

    1. Florian Effenberger

      Sorry, I was blind, I just saw the respective paragraph mentioning to comment it out 🙂

  6. Florian Effenberger

    If you set smtpd_sasl_auth_enable=yes in main.f instead of only in the submission section of master.cf (“-o smtpd_sasl_auth_enable=yes”), authenticated mail submission could also possible at port 25. Not sure if that negatively affects the signing and Milter, because it might be harder to distinguish between external servers and clients.

    Some more settings that might make sense for main.cf are

    delay_warning_time = 4h
    message_size_limit = 41943040 (or any other value, the default seems rather low)
    disable_vrfy_command = yes
    smtpd_helo_required = yes
    smtpd_sasl_authenticated_header = yes
    smtpd_discard_ehlo_keywords = silent-discard, dsn
    smtpd_tls_received_header = yes
    show_user_unknown_table_name = no
    authorized_flush_users = root
    authorized_mailq_users = root
    enable_long_queue_ids = yes

  7. Hi everyone.
    I have encountered a problem in passwords.
    If my password is 1234, or 1234567890, authentication does not work through openssl s_client -connect localhost: 25 -starttls smtp.
    but it works if my password is “X1234X” or “summersun”, or “pollaloca”.
    All passwords work in all other modes for example in roundcube, but not SSL SSL AUTN PLAIN openssl s_client -connect localhost: 25 -starttls smtp.
    I suppose that the conversion of the password using “printf ‘\ 0john@example.org \ 01234′ | base64″ does not do it correctly, I do not know.
    I have tried using other tools to compare, in the shell, but I have not found any, it seems that Debian 10, no longer maintains the alternative to the reverse process to “printf ‘\ 0john@example.org \ 01234′ | base64″

    This has happened to someone else?

    Greetings and thank you very much for these and other fantastic tutorials.

    1. Christoph Haas

      Maybe that’s a formatting issue here on the website but you have been using “\ 0123” (you escaped the space) instead of ‘\0123’ (escaping the zero to get a null-byte). Could that be the issue?

      1. No, I try to explain myself better.
        I create a new user, real example “password@localred.net”
        I assign an password, it will be “5566778899” (all numbers)
        I verify that I access by mutt or by roundcube, I access without problem.
        I’m going to check with ssl, first I need to pass everything to B64, well I pass it.
        Pattern: NULL-BYTE+USERNAME+NULL-BYTE+PASSWORD
        printf ‘\0password@localred.net\05566778899’ | base64 (without spaces)
        Result: AHBhc3N3b3JkQGxvY2FscmVkLm5ldC02Njc3ODg5OQ ==
        Well, I try it with:
        openssl s_client -connect localhost: 25 -starttls smtp
        ..
        EHLO
        ..
        AUTH PLAIN AHBhc3N3b3JkQGxvY2FscmVkLm5ldC02Njc3ODg5OQ ==
        535 5.7.8 Error: authentication failed:

        Change the password to “A5566778899”
        I try it through mutt and roundcube, I authenticate without problem.
        I try SSL again,
        I pass the credentials by B64, same user different password.
        printf ‘\0password@localred.net\0A5566778899’ | base64
        AHBhc3N3b3JkQGxvY2FscmVkLm5ldABBNTU2Njc3ODg5OQ ==
        openssl s_client -connect localhost: 25 -starttls smtp
        ..
        EHLO
        ..

        AUTH PLAIN AHBhc3N3b3JkQGxvY2FscmVkLm5ldABBNTU2Njc3ODg5OQ ==
        235 2.7.0 Authentication successful

        I have done all this as I wrote it, so as not to give false information.

        No one has happened this ??
        Greetings and thanks.

        1. The problem is printf sees a ‘\’ followed by up to 3 digits as an octal value. You can verify that by piping the output of printf into e.g. hd or hexdump.

          Try this instead:

          $ printf ‘\0%s\0%s’ ‘password@localred.net’ ‘5566778899’ | base64
          AHBhc3N3b3JkQGxvY2FscmVkLm5ldAA1NTY2Nzc4ODk5

          Here the first argument to printf is a format string where the %s’s are replaced with the next two arguments to printf, so it should work even if the username or password starts with digits (or contains a ‘\’ or ‘%’).

  8. Hi Christoph,

    Very good tutorial. I’m using it every time since 2016! Now I’m migrating from Debian 8 to 10 and I decided to do it from scratch… Everything works fine but I don’t know why my submission port is still closed and I can’t sent email e.g. from Roundcube. Log:

    [26-Jan-2020 18:03:35 Europe/Berlin] ERROR: Failed to connect socket: Connection refused ()
    [26-Jan-2020 18:03:35 +0100]: SMTP Error: Connection failed: Failed to connect socket: Connection refused in /usr/share/roundcube/program/lib/Roundcube/rcube.php on line 1667 (POST /?_task=mail&_unlock=loading1580058215937&_lang=undefined&_framed=1&_action=send)

    If i set $config[‘smtp_port’] = 25 instead 587 in /etc/roundcube/config.inc.php I can send the email. $default_host and $smtp_server are set to the same value: ‘tls://mydomain.xxx’. What’s wrong?
    `
    I your older tutorial I found the different config value for submission section in master.cf: ‘-o smtpd_sasl_type=dovecot’ which is not existing in newest master.cf file. Should it will drop?

    Regards,
    Blazej

  9. Hi again,

    Please ignore above, the issue was because typo error into master.cf. Now everything working fine.

    Thanks a lot for many many good work with tutorial!

    Regards,
    Blazej

  10. Please help on this when STARTTLS Command is given

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

    Posted Error Log
    postfix/smtpd[8082]: connect from localhost.localdomain[127.0.0.1]
    Jan 29 19:05:19 localhost postfix/smtpd[8082]: SSL_accept error from localhost.localdomain[127.0.0.1]: -1
    Jan 29 19:05:19 localhost postfix/smtpd[8082]: warning: TLS library problem: error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:332:

    1. I’m getting a similar error:

      CONNECTED(00000003)
      139773874234496:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:332:

        1. My bad: In hastily cutting and pasting the Postfix main.cf edits, I didn’t update the letsencrypt keys path for smtpd_tls_cert_file and smtpd_tls_key_file. Fixing the bogus paths made the error go away.

          1. Christoph Haas

            Thanks that you took the time to comment on what went wrong. That will help others who search for the error. And being hasty… well… sounds like me. 🙂

  11. I have a new stupidity on my part and I’m guessing it would be easy to pin down:

    I’m unable to send to off-server addresses. It acts like Postfix is testing sending privileges based on the recipient rather than the sender. If I do this:

    swaks –to ***@gmail.com –server localhost

    Postfix logs:

    Mar 14 14:04:57 *server* postfix/smtpd[4615]: connect from localhost[::1]
    Mar 14 14:04:57 *server* postfix/smtpd[4615]: NOQUEUE: reject: RCPT from localhost[::1]: 554 5.7.1 : Relay access denied; from= to= proto=ESMTP helo=
    Mar 14 14:04:57 *server* postfix/smtpd[4615]: disconnect from localhost[::1] ehlo=1 mail=1 rcpt=0/1 quit=1 commands=3/4

    I can send mail from Thunderbird to addresses in the virtual_user or virtual_alias tables. There are many posts about this problem on various sites but few of them get properly answered (either with a thoughtful response — something more than a web link to the Postfix documentation — or a detail of what was done wrong). Buster VPS server.

    1. I’ve reinstalled and this is still not working:

      Mar 21 07:31:29 *mydomain* postfix/submission/smtpd[26529]: connect from *home*
      Mar 21 07:31:29 *mydomain* postfix/submission/smtpd[26529]: NOQUEUE: reject: RCPT from *home*: 554 5.7.1 : Relay access denied; from= to= proto=ESMTP helo=*LAN_IP*
      Mar 21 07:31:37 *mydomain* postfix/submission/smtpd[26529]: disconnect from *home* ehlo=2 starttls=1 auth=1 mail=1 rcpt=0/1 quit=1 commands=6/7

      I reason that the helo from *LAN_IP* shouldn’t be a problem as authentication is in play. I can receive e-mails but I can’t send to other domains.

      1. I solved my problem… I hope.

        I uncommented the ” -o smtpd_recipient_restrictions=” line in /etc/postfix/master.cf in the submission configuration and everything seems to be working. Contrary to the explanation of the smtpd restrictions given in the tutorial, smtpd_recipient_restrictions is not empty. It is “smtpd_recipient_restrictions = reject_unauth_destination check_policy_service unix:private/quota-status”.

        The “reject_unauth_destination” appears to be the problem

        Is everything covered in smtpd_relay_restrictions such that smtpd_recipient_restrictions can indeed be empty?

  12. Up to this point everything working OK, apart from Roundcube (which won’t allow login to my test user account (seems to be a TLS issues).
    However having done a successful test smtp login and edited the submission section of /etc/postfix/master.cf, I reload postfix and get:
    $ sudo service postfix reload
    [….] Reloading Postfix configuration…/usr/sbin/postconf: warning: /etc/postfix/master.cf: undefined parameter: mua_sender_restrictions
    /usr/sbin/postconf: warning: /etc/postfix/master.cf: undefined parameter: mua_client_restrictions
    /usr/sbin/postconf: warning: /etc/postfix/master.cf: undefined parameter: mua_helo_restrictions
    done.
    Similar, if more prolix, warnings if I restart postfix. Are these warnings harmless? you don’t mention them.

  13. Having searched using the warning string it seems that postfix 3.4.8 has some missing lines in the submissions section (not evident when they are commented out) of /etc/postfix/master.cf.

    I’ve added:

    # extra definitions added to fix undefined mua warnings.
    # source of correction John Greene, on 7-Jan-2020 at:
    # https://serverfault.com/questions/952776/warning-undefined-parameter-mua-sender-restrictions-when-postconf-n
    #
    -o smtpd_restriction_classes = mua_sender_restrictions, mua_client_restrictions, mua_helo_restrictions
    -o mua_client_restrictions = permit_sasl_authenticated, reject
    -o mua_sender_restrictions = permit_sasl_authenticated, reject
    -o mua_helo_restrictions = permit_mynetworks, reject_non_fqdn_hostname, reject_invalid_hostname, permit
    #

    This fixes the warnings. Hopefully they also make sense.

    1. I can confirm this : I had the exact same behavior as Marjorie today. I also applied her fix.
      Christoph, if you could confirm we’re not making any mistakes by adding those 3 lines in master.cf ?

      1. Well… After a few days, I noticed in my syslog :
        postfix/smtpd[10228]: fatal: invalid “-o smtpd_restriction_classes” option value: missing ‘=’ after attribute name

        So, I’m not that certain I’m doing the right thing. (and I’m 100% sure I don’t fully understand the settings in this file…)

        Christoph, if you could save my life on this, once again ?

        1. The log entry is literally telling you that you omitted the “=” after “-o smtpd_restriction_classes”.

          “smtpd_restriction_classes” is the attribute name.

  14. Hey guys,

    just wondering if someone could shed some light on something for me! With regards to the smtpd_relay_restrictions and smtpd_recipient_restrictions.. I was not able to send mail from roundcube web client until I made my smtpd_recipient_restrictions read the following:

    smtpd_recipient_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination check_policy_service unix:private/quota-status

    my smtp_relay_restrictions reads the following:

    smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination

    If I don’t have “permit_mynetworks, permit_sasl_authenticated” in the recipient restrictions setting line I get the following error in roundcube:

    SMTP Error (554): Failed to add recipient “test@example.com” (5.7.1 : Relay access denied).

    Thoughts? Forgive me if I’m missing something trivial… thanks!

  15. Hallo Christoph,
    muss die grüne Box (Restrictions) auch noch in die main.cf?

    Verstehe leider nicht den Zusammenhang, ob es rein muss oder nicht.

    Danke!

  16. To anyone setting this up on Ubuntu rather than Debian, or otherwise encountering errors relating to the `$mua_*` variables referenced by the `submission` service in `master.cf`, it is worth noting that those `$mua_*` variables do not have default values on Ubuntu, and so should be explicitly declared in `main.cf`. Check the values being currently used with e.g. `postconf mua_client_restrictions`, etc., and the defaults with `postconf -d mua_client_restrictions`, etc.

    If the variables are not set by default, here are some sane defaults for you to include in your `main.cf`. As always, run `postfix check` afterwards to ensure your config does not have any issues, then reload the Postfix service (`postfix reload` or `systemctl reload postfix`) for the changes to actually take effect:

    “`
    ### /etc/postfix/main.cf

    mua_client_restrictions = permit_sasl_authenticated reject
    mua_sender_restrictions = permit_sasl_authenticated reject
    mua_helo_restrictions =
    permit_mynetworks
    reject_non_fqdn_hostname
    reject_invalid_hostname
    permit
    “`

    Thanks for publishing this excellent guide, Christoph, and maintaining it for so many years! I only just stumbled upon it yesterday when looking for guides to help me with coordinating Postfix and Dovecot using a database backend to implement catch-all mailboxes and mailbox quotas, and it is surely a valuable asset to the community 🙂 I’m surprised that I didn’t find this a few years ago when I was first delving into email servers.

    1. Christoph Haas

      Hi Jivan… thanks for the hint. However I get warnings about the mua_* settings in Debian, too. It’s still not clear to me if part of the default configuration is missing.

      And thanks for being part of the cloud-free mail server owners. 🙂

      1. Yes, I git those warnings as well on a vanilla Debian Buster system. I guess that part of the default configuration is really missing. As mentioned earlier the hints on https://serverfault.com/questions/952776/warning-undefined-parameter-mua-sender-restrictions-when-postconf-n actually make the warnings go away. Actually almost all comments on this page are on exactly this one topic.

        Apparently, one can define oneself some restriction classes, see http://www.postfix.org/RESTRICTION_CLASS_README.html and the commented out Debian Buster seems to make use of that without main.cf being in sync with master.cf

        Anyway, thanks for the great guide once again!

  17. Rene Rasmussen

    Thanks for a superb guide. I followed it with success some years ago and am happy to say this time it also works 🙂

    There was only one thing I ran into. I could not send email from the server. Receiving email and sending “locally” to another email address on the same server was not a problem.
    The message in the log told me that relay for the external address was not allowed.
    To make it work I had to add “sasl_authenticated_users” to smtpd_recipient_restrictions

    My Roundcube is on another sub domain though, maybe that is the cause? But using an external client would probably have had the same problem then. (I didn’t try with an external client).

  18. Is there a conflict in the definition of smtpd_relay_restrictions on this page? the green restrictions box shows one set of values (permit_mynetworks permit_sasl_authenticated defer_unauth_destination ) and the submission protocol section shows a different set of values (-o smtpd_relay_restrictions=permit_sasl_authenticated,reject). The green restrictions box appears to be written for a line in main.cf and i thought those would be incorporated in services in master.cf. Am i misunderstanding something? thanks..

  19. Hi,
    I have a problem authenticating to remote smtp because the password contains accented characters (àòù and so on….)
    in posftix logs I see that these characters are not interpreted correctly and are shown as question marks (?) .
    Are utf8 characters accepted in configuration files in postfix ?

  20. great guide!

    I have set
    mydestination = localhost

    internal messages eg root@localhost now get directed to root@domain.com which returns a
    postfix/lmtp[23598]: 68A61140A57: to=, relay=mx.domain.com[private/dovecot-lmtp], delay=0.1, delays=0.06/0/0/0.04, dsn=5.1.1, status=bounced (host mx.domain.com[private/dovecot-lmtp] said: 550 5.1.1 User doesn’t exist: root@domain.com (in reply to RCPT TO command))

    I’ve obviously missed something here?

      1. Hi Christoph.
        Thanks for the response.
        The domain entry is there, but there aren’t any aliases. What is the correct content to get messages sent to the OS mailbox (/root/mail or /var/mail/root)?

        1. Christoph Haas

          You have defined “localhost” as your local domain. But you are expecting to get email to @domain.com locally. There seems to be a misunderstand. Please read the page about the different types of domains again.

          1. Thanks for the reply again.
            I set the mydestination to localhost based on “Or you just set it to “mydestination = localhost” if you rather want to use “scully.example.com” as a virtual domain. Some parts of your system may send emails to root@localhost so this is a sane setting.” and then created domain.com in the virtual_domains table for relevant users [root is not in there as a user], but I guess I have not understood it correctly. Obviously this is wrong as I am getting the 550/5.5.1 errors.
            Should I have set up an alias for root@localhost.

            PS. I am not trying to email root@domain.com – these are being sent internally by the system to root@localhost but ending up pointed at root@domain.com (I think) – I am not sure where this comes from

          2. Turns out I set the
            /etc/mailname
            domain.com

            but I have, in /etc/hosts
            127.0.1.1 mx.domain.com mx

            Once I corrected this mismatch (set /etc/mailname to mx.domain.com), the mails were delivered correctly.

            Thank you again for the guide, the patience, and the help.

  21. In the paragraph after the third picture, I think you wanted to write “is not blared out”.

  22. Hey, thanks for the guide however I ran into this error when trying to connect using “openssl s_client -connect localhost:25 -starttls smtp”

    I have seen the same error in the commends but I checked the paths for the certificate files.

    SSL_accept error from localhost[127.0.0.1]: -1

    postfix/submission/smtpd[18968]: warning: TLS library problem: error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:332:

    postfix/submission/smtpd[18968]: lost connection after STARTTLS from localhost[127.0.0.1]

  23. Sali
    würde noch ergänzen wollen, entfernen der IP Adresse des Senders auf dem Submission-Port, sonst geht mit jeder Mail die User IP ins Netz raus.

    apt install postfix-pcre

    master.cf
    submission inet n – y – – smtpd
    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=$mua_client_restrictions
    -o smtpd_helo_restrictions=$mua_helo_restrictions
    -o smtpd_sender_restrictions=$mua_sender_restrictions
    -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
    -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    -o milter_macro_daemon_name=ORIGINATING
    -o cleanup_service_name=ascleanup <——-

    cleanup unix n – y – 0 cleanup
    ascleanup unix n – y – 0 cleanup <——-
    -o header_checks=pcre:/etc/postfix/header_checks_submission <——–

    /etc/postfix/header_checks_submission
    /^Received: from [^ ]+ \([^ ]+ \[[IPv0-9a-f:.]+\]\)\s+(.* \(Postfix\) with .+)$/ REPLACE Received: $1

    Gruß Leo

  24. Hi and thank you for this incredibly helpful tutorial.

    In order to allow users to have a more flexible SMTP setup, I enabled port 465. Along with the above settings I managed to set / uncomment this configuration in master.cf (please comment for improvements):

    smtps inet n – y – – smtpd
    -o syslog_name=postfix/smtps
    -o smtpd_tls_wrappermode=yes
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_reject_unlisted_recipient=no
    -o smtpd_client_restrictions=$mua_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

  25. I need to block recipients not in database 50000 emails.
    I also forward any emails of our users to their external email. incase if their email client hacked and everyone get spammed through our server.

    Only admin (local domain) send out emails.

    In case someone hack admin or any other loopholes in code , POSTFIX should not allow it.

    Any ideas how to implement with mysql query

Leave a Reply

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

Scroll to Top