Dec 062010
 

This time i’ll show you how to randomize your smtp outbound’s IP addresses. This can be done via transport map. But, since ordinary Postfix lookup tables store information as (key, value) pairs. it will provide static value only. we need someting that can manipulate the value (right hand side) of a lookup table. In order to answer random transport value.

first come to mind was tcp_tables, tcp_tables lookup table gives some flexibility for us to execute our tiny perl script that will randomizing transport. that’s the basic idea.

Ok, here’s the first part, create perl script call random.pl, anyway this script only provide answer in “catch-all” manner. so it will randomized, all outgoing mail.

# cd /etc/postfix
# vi random.pl
#!/usr/bin/perl -w
# author: Hari Hendaryanto <hari.h -at- csmcom.com>

use strict;
use warnings;
use Sys::Syslog qw(:DEFAULT setlogsock);

#
# our transports array, we will define this in master.cf as transport services
#

our @array = (
'rotate1:',
'rotate2:',
'rotate3:',
'rotate4:',
'rotate5:'
);

#
# Initalize and open syslog.
#
openlog('postfix/randomizer','pid','mail');

#
# Autoflush standard output.
#
select STDOUT; $|++;

while (<>) {
        chomp;
        # randomizing transports array
        my $random_smtp = int(rand(scalar(@array)));
        if (/^get\s(.+)$/i) {
                print "200 $array[$random_smtp]\n";
                syslog("info","Using: %s Transport Service", $random_smtp);
                next;
        }

	print "200 smtp:";
}

Make it executable

# chmod 755 random.pl

master.cf parts

Run the scripts via postfix spawn daemon service.

127.0.0.1:2527 inet  n       n       n       -       0      spawn
          user=nobody argv=/etc/postfix/random.pl

add 5 smtp client services called rotate1, rotate2, rotate3, rotate4, rotate5, that bind to its own ip
address and has uniq syslog/helo name.

# random smtp
rotate1  unix -       -       n       -       -       smtp
          -o syslog_name=postfix-rotate1
          -o smtp_helo_name=smtp1.example.com
          -o smtp_bind_address=1.2.3.1

rotate2  unix -       -       n       -       -       smtp
          -o syslog_name=postfix-rotate2
          -o smtp_helo_name=smtp2.example.com
          -o smtp_bind_address=1.2.3.2

rotate3  unix -       -       n       -       -       smtp
          -o syslog_name=postfix-rotate3
          -o smtp_helo_name=smtp3.example.com
          -o smtp_bind_address=1.2.3.3

rotate4  unix -       -       n       -       -       smtp
          -o syslog_name=postfix-rotate4
          -o smtp_helo_name=smtp4.example.com
          -o smtp_bind_address=1.2.3.4

rotate5  unix -       -       n       -       -       smtp
          -o syslog_name=postfix-rotate5
          -o smtp_helo_name=smtp5.example.com
          -o smtp_bind_address=1.2.3.5

Before we actually implement our randomize transport, let’s make sure that the setting actually work.

Reload postfix

# postfix reload

Run this query fiew times, and you’ll see the perl script will return “random answer” transport

# postmap -q "whatever" tcp:127.0.0.1:2527
rotate1:
# postmap -q "whatever" tcp:127.0.0.1:2527
rotate5:

And so on..

Note on “whatever”, since the script acted in “catch-all” mode as i’ve mentioned earlier, what ever postfix transport_maps client asked. it will be answered with random values such as rotate1, rotate2, rotate3, rotate4, rotate5 in randomized fashion.

main.cf parts

Add these lines

transport_maps = tcp:[127.0.0.1]:2527
127.0.0.1:2527_time_limit = 3600s

Reload postfix
that’s it. example log would be like these and that’s indicate that randomizer is working.

Month date 12:26:53 host postfix-rotate1/smtp[4252]: A1CA68480A4: to=<xxx@example.com>, relay=mx.example.com.com[xx.xx.xxx.xx]:25], delay=3.6, delays=0.69/0.01/0.81/2, dsn=2.0.0, status=sent (250 ok dirdel)
--snip--
Month date 12:27:06 host postfix-rotate5/smtp[4253]: 41C2E8480A4: to=<xxx@example.net>, relay=mx.example.net[xx.xxx.xxx.xxx]:25], delay=6, delays=0.14/0.01/0.85/5, dsn=2.0.0, status=sent (250 ok dirdel)
--snip--
Month date 12:27:22 host postfix-rotate3/smtp[4277]: 4BA9F8480A4: to=<xxx@example.org>, relay=mx.example.org[xx.xxx.xx.xxx]:25], delay=7.9, delays=0.85/0.02/0.61/6.4, dsn=2.0.0, status=sent (250 ok dirdel)

disclaimer:
I’m not taking any responsible if the reader “misuse” this tutorial.the tutorial is provide as-is for experimental purposes.

use Sys::Syslog qw(:DEFAULT setlogsock);

  96 Responses to “Postfix Randomizing Outgoing IP Using TCP_TABLE And Perl”

Comments (96)
  1. Hi Leenoux, thank you for a wonderfull script.

    I am using it for over 2 years without issues but I have just noticed a strange behaviour/bug?

    It always uses the first MX record. When the recepient server replay with 4xx code than postfix should try a different MX server, but it does not. It just queues the message and tries the same MX server again.

    Without this script it works fine.

    Do you know where the issue could be? What should I fix? Thank you

  2. I have a question about headers. The rotation may or may not be working. Part of my header says server3, but the ip addresses being shown is actually server1. No matter which hostname is shown, it always shows the same xx.xxx.xxx.9 ip address. Server3 would be the xx.xxx.xxx.11 address. Any thoughts? Here is the header.

    Authentication-Results: hotmail.com; spf=pass (sender IP is XX.XXX.XXX.9) smtp.mailfrom=admin@mydomain.com; dkim=pass header.d=mydomain.com; x-hmca=pass header.id=marketing@mydomain.com
    X-SID-PRA: marketing@mydomain.com
    X-AUTH-Result: PASS
    X-SID-Result: PASS
    Received: from server3.mydomain.com ([XX.XXX.XXX.9]) by SNT0-MC1-F43.Snt0.hotmail.com with Microsoft SMTPSVC(6.0.3790.4900);
    Tue, 15 Apr 2014 15:12:07 -0700

  3. show me your master.cf configuration for ip XX.XXX.XXX.9 and xx.xxx.xxx.11 (smtp_bind_address), don’t forget to check dns part, are server1 and server3 point to the right ip address (both A record and PTR record)?

  4. DNS is fine okay

    .9 is pointing to server1
    .11 is pointing to server3
    .12 is pointing to server4

    PTR records are all set up correctly too.

    Here is my master.cf minus some stuff

    smtp inet n – n – – smtpd
    #smtp inet n – – – 1 postscreen
    #smtpd pass – – – – – smtpd
    #dnsblog unix – – – – 0 dnsblog
    #tlsproxy unix – – – – 0 tlsproxy
    submission inet n – n – – smtpd
    -o syslog_name=postfix/submission
    -o smtpd_tls_security_level=encrypt
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_client_restrictions=permit_sasl_authenticated,reject
    # -o milter_macro_daemon_name=ORIGINATING
    smtps inet n – n – – smtpd
    -o syslog_name=postfix/smtps
    -o smtpd_tls_wrappermode=yes
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_client_restrictions=permit_sasl_authenticated,reject

    127.0.0.1:10025 inet n – – – – smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
    yahoo unix – – n – – smtp
    -o syslog_name=postfix-yahoo
    #random
    127.0.0.1:2527 inet n n n – 0 spawn
    user=nobody argv=/etc/postfix/random.pl
    # random smtp
    rotate1 unix – – n – – smtp
    -o syslog_name=postfix-rotate1
    -o myhostname=server1.mydomain.com
    -o smtp_helo_name=server1.mydomain.com
    -o smtp_bind_address=xxx.xxx.xxx.9

    rotate2 unix – – n – – smtp
    -o syslog_name=postfix-rotate2
    -o myhostname=server3.mydomain.com
    -o smtp_helo_name=server3.mydomain.com
    -o smtp_bind_address=xxx.xxx.xxx.11

    rotate3 unix – – n – – smtp
    -o syslog_name=postfix-rotate3
    -o myhostname=server4.mydomain.com
    -o smtp_helo_name=server4.mydomain.com
    -o smtp_bind_address=xxx.xxx.xxx.12

  5. master.cf part is okay, still not clear your real configuration on DNS, you can check your A record an PTR record at least, let me know your real hostname so i can check it out from here.

  6. server1.iaminsuranceagent.com
    server2…
    server3…
    server4…

  7. looks fine from here
    # dig a server1.iaminsuranceagent.com +short
    50.245.192.9
    # dig -x 50.245.192.9 +short
    server1.iaminsuranceagent.com.
    # dig a server3.iaminsuranceagent.com +short
    # dig -x 50.245.192.11 +short
    server3.iaminsuranceagent.com.

    might be hotmail resolver, just give another try

  8. Thanks for a great script. I got it working. But there is one question. Please tell me, how do I make it so tough to bind to each (rigidly designate) IP-address separate address Mail-From. For example:
    rotate1 Mail-From – info@mail1.my-site.com
    rotate1 Mail-From – info@mail2.my-site.com
    rotate1 Mail-From – info@mail3.my-site.com
    It is necessary for the proper email provider is authenticated when checking SPF, DKIM-recording. For each IP/domen, its separate SPF, DKIM-record.

  9. i’m not really sure, maybe you should check this postfix feature: sender_dependent_default_transport_maps

  10. hy Leenoux, thanks for your amazing tutorials and scripts.

    I try to applicate this on zpanel/centos 6 without success how can i update my postfix for use tcp tables???

    thanks in advance cool dude!

  11. try download source package from here http://postfix.wl0.org/

  12. Hi!
    My program sends letters and mailing at the same address all mail-FROM (Return-Path 🙂
    To in headings incoming letters correctly formed DKIM signing, I need to replace the address of mail-FROM (Return-Path 🙂 to the address from which you are sending.
    To this I added to each transport one line:

    rotate1 unix – n – smtp
    -o smtp_bind_address = hhh.hh.28.20
    -o smtp_helo_name = mail1.oni4you.com
    -o myhostname = mail1.oni4you.com
    -o syslog_name = postfix-rotate1
    -o sender_canonical_maps = hash :/ etc/postfix/canonical_maps1.cf

    rotate2 unix – n – smtp
    -o smtp_bind_address = hhh.hh.28.21
    -o smtp_helo_name = mail2.oni4you.com
    -o myhostname = mail2.oni4you.com
    -o syslog_name = postfix-rotate2
    -o sender_canonical_maps = hash :/ etc/postfix/canonical_maps2.cf

    rotate3 unix – n – smtp
    -o smtp_bind_address = hhh.hh.30.83
    -o smtp_helo_name = oni4you.com
    -o myhostname = mx.oni4you.com
    -o syslog_name = postfix-rotate3

    Then I created 2 files
    canonical_maps1.cf
    @ oni4you.com @ mail1.oni4you.com
    and
    canonical_maps2.cf
    @ oni4you.com @ mail2.oni4you.com
    performed
    # Postmap hash :/ etc / postfix / canonical_maps
    for each file,

    But it does not work! Could you tell me where is the error?

  13. I’m not sure if this site is still alive in 2020, but I have a question about the “catch-all” behavior with this script. Is it possible to use it in conjunction with a regular transport_maps table in order to define specific destinations for discard or relay to other smtp servers while allowing all non-matching messages to be fed to the random.pl script?

  14. well, it’s still alive. sure, go ahead, i’ll give my best shot to answer your question. i’m little bit rusty though 😀

  15. Good to hear, Hari! I’ll rephrase my question with an example. If I’m using the script on this page with an array of IP addresses, and the following defined in /etc/postfix/main.cf:

    transport_maps = hash:/etc/postfix/transport, tcp:127.0.0.1:2527

    And, if I put the following in /etc/postfix/transport:

    user1@example.com smtp:[1.2.3.4] #relay this user to another SMTP server

    everything will work just fine. The email address above will get relayed elsewhere and all other non-matching email will get a random transport entry via your script. However, if I put the following in /etc/postfix/transport:

    example.com smtp:[1.2.3.4] #send ALL email for example.com to another SMTP server

    this does not work at all. This entry is ignored and email for example.com follows the same path as all other email. Any ideas why the pattern matching doesn’t work here, and any suggestions to fix?

    Thx!

  16. can i see your main.cf, transport_maps part. AFAIK, i will put all catch-all and exclussion in tcp_tables perl script(need some modification).

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

*