Load Balancing inbound SMTP connection with HAProxy

This posting is ~5 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

In my last blog post I have highlighted how HAProxy can be used to distribute client connections to two or more servers with Exchange 2013 CAS role. But there is another common use case for load balancers in a Exchange environment: SMTP. Let’s take a look at this drawing:

mailflow

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

The inbound SMTP connections are distributed to two Mail Transfer Agents (often a cluster of appliances, like Cisco IronPort or Symantec Messaging Gateway) and the MTAs forward the e-mails to the Exchange servers. Sometimes the e-mails are not directly forwarded to the Exchange servers, but to mail security appliances instead (like Zertificon Z1 SecureMail Gateway). After the e-mails have been processed by the mail security appliances, they are forwarded to the Exchange backend. Such setups are quite common. If a load balancer isn’t used, the MX records often point to the public IP address of a specific MTA. In this case, two or more MX records have to be set to ensure that e-mails can be received, even if a MTA fails.

A setup with a load balancers allows you to have a single MX record in your DNS, but two or more servers that can handle inbound SMTP connections. This makes maintenance easier und allows you to scale without having to fumble on the DNS. It’s without saying that your Load Balancer should be highly available, if you decide to realize such a setup.

It’s not hard to persuade HAProxy to distribute inbound SMTP connections. All you have to do is to add this to your haproxy.conf. To get the full config, check my last blog post about HAProxy.

The “send-proxy” parameter ensures, that the incoming IP address is forwarded to the servers behind the load balancer. This is important if you use Greylisting or real-time blacklists on your MTA or mail server. When running Postfix 2.10 or later, please make sure that you add this line to your main.cf:

This option add support for the PROXY protocol. Incoming requests are distributed alternating to the servers behind the load balancer. The “balance roundrobin” parameter ensures this. Please make sure that the MTA, that is running on your Linux host, doesn’t listen on the external IP. In my case, Postfix listens only on 127.0.0.1.

The statistics page can be used to verify the success of the configuration (click the picture to enlarge).

haproxy_smtp_roundrobin

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

Alternatively you can use Telnet to connect to the load balancer on port 25/tcp. As you can see in the screenshot, using the FQDN mailin.vcloudlab.local resulted in an alternating connection to the backend servers.

haproxy_smtp_roundrobin_check_png

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

Follow me

Patrick Terlisten

vcloudnine.de is the personal blog of Patrick Terlisten. Patrick has a strong focus on virtualization & cloud solutions, but also storage, networking, and IT infrastructure in general. He is a fan of Lean Management and agile methods, and practices continuous improvement whereever it is possible.

Feel free to follow him on Twitter and/ or leave a comment.
Patrick Terlisten
Follow me

6 thoughts on “Load Balancing inbound SMTP connection with HAProxy

  1. Pablo

    It doesnt work for me, i receive an error in the smtp gateway:

    500 Command unrecognized “PROXY TCP4 192.168.0.31 192.16”

    Reply
  2. michael

    Hi thanks for your howto,

    Im facing a problem with my postfix-dovecot HA with haproxy.
    dovecot : 2:2.2.19
    haproxy : 1.5.14
    postfix : 2.11.2-1

    I ‘ve followed these links :
    http://wiki2.dovecot.org/HAProxy
    http://blog.haproxy.com/2012/06/30/efficient-smtp-relay-infrastructure-with-postfix-and-load-balancers/

    this is my haproxy configuration

    #postfix
    listen smtp
    bind mail.xx.xx:465
    balance roundrobin
    timeout client 1m
    timeout connect 5s
    no option http-server-close
    mode tcp
    option smtpchk
    option tcplog
    server tst tst.xxx:10465 send-proxy
    server tst2 tst2.xxx:10465 send-proxy
    server tst3 tst3.xxx:10465 send-proxy

    #dovecot
    listen imap
    bind mail.xxx.xx:993
    timeout client 1m
    no option http-server-close
    balance leastconn
    stick store-request src
    stick-table type ip size 200k expire 30m
    mode tcp
    option tcplog
    server tst tst.xxx:10993 send-proxy-v2
    server tst2tst2.xxx:10993 send-proxy-v2
    server tst3 tst3.xxx:10993 send-proxy-v2

    postix main.cf
    #Haproxy proxy protocol
    postscreen_upstream_proxy_protocol = haproxy

    postfix master.cf
    #haproxy
    10465 inet n – n – 1 postscreen
    smtpd pass – – n – – smtpd

    dovecot dovecot.conf
    #trust haproxy requests
    haproxy_trusted_networks = 172.20.1.3
    haproxy_timeout = 5

    inet_listener imap_haproxy {
    port = 10993
    haproxy = yes
    }

    test with thunderbird client
    imap logs, don’t know why user is empty …

    dovecot: imap-login: Disconnected: Too many invalid commands (no auth attempts in 0 secs): user=, rip=my public ip, lip=haproxy public ip, session=

    smtp logs, i get a timeout …
    postfix/postscreen[16654]: CONNECT from [my public ip]:49942 to [my haproxy public ip]:465
    postfix/postscreen[16654]: PREGREET 166 after 0 from [my public ip ]:49942: 2231161115733+0Eb213131177173>r/213177i223k”FjA#144145153vP\155HL190

    Do you have an idea ? Thanks

    Kind regards !

    Reply
  3. michael

    Hi thanks for your howto,

    Im facing a problem with my postfix-dovecot HA with haproxy.
    dovecot : 2:2.2.19
    haproxy : 1.5.14
    postfix : 2.11.2-1

    I ‘ve followed these links :
    http://wiki2.dovecot.org/HAProxy
    http://blog.haproxy.com/2012/06/30/efficient-smtp-relay-infrastructure-with-postfix-and-load-balancers/

    this is my haproxy configuration

    #postfix
    listen smtp
    bind mail.xx.xx:465
    balance roundrobin
    timeout client 1m
    timeout connect 5s
    no option http-server-close
    mode tcp
    option smtpchk
    option tcplog
    server tst tst.xxx:10465 send-proxy
    server tst2 tst2.xxx:10465 send-proxy
    server tst3 tst3.xxx:10465 send-proxy

    #dovecot
    listen imap
    bind mail.xxx.xx:993
    timeout client 1m
    no option http-server-close
    balance leastconn
    stick store-request src
    stick-table type ip size 200k expire 30m
    mode tcp
    option tcplog
    server tst tst.xxx:10993 send-proxy-v2
    server tst2tst2.xxx:10993 send-proxy-v2
    server tst3 tst3.xxx:10993 send-proxy-v2

    postix main.cf
    #Haproxy proxy protocol
    postscreen_upstream_proxy_protocol = haproxy

    postfix master.cf
    #haproxy
    10465 inet n – n – 1 postscreen
    smtpd pass – – n – – smtpd

    dovecot dovecot.conf
    #trust haproxy requests
    haproxy_trusted_networks = haproxy private ip
    haproxy_timeout = 5

    inet_listener imap_haproxy {
    port = 10993
    haproxy = yes
    }

    test with thunderbird client
    imap logs, don’t know why user is empty …

    dovecot: imap-login: Disconnected: Too many invalid commands (no auth attempts in 0 secs): user=, rip=public ip, lip=haproxy public ip, session=

    smtp logs, i get a timeout …
    postfix/postscreen[16654]: CONNECT from [my public ip]:49942 to [my haproxy public ip]:465
    postfix/postscreen[16654]: PREGREET 166 after 0 from [159.180.255.62]:49942: 2231161115733+0Eb213131177173>r/213177i223k”FjA#144145153vP\155HL190

    Do you have an idea ? Thanks

    Kind regards !

    Reply
  4. Jonas

    I would not recommend this approach when the backend SMTP servers are Exchange, as Exchange ESMTP does not support the PROXY command (send-proxy in HAProxy).
    Using send-proxy with default Exchange SMTP (receive connectors) will result in tarpitting, possibly causing the HAProxy check to timeout, though this could be avoided by disabling tarpitting on the receive connectors, it could cause other problems as Exchange will respond with “500 Unknown command” to the initial check.

    Safest approach is just to skip the “send-proxy”, and it will work just fine.

    Reply

Leave a Reply to Pablo Cancel reply

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

I accept!