Using multiple "myhostname" in postfix
Does postfix allow to dynamically change "myhostname" ? I rtfmed a lot but did not found anything qualitative. My installation is a postfix multiple domain, multiple ip (tweaked in master.cf) and multiple dkim sig but the myhostname directive in master.cf isn't included.
That kind of stuff:
192.168.1.12:smtp inet n - n - - smtpd -o myhostname=mail.mail2.com -o smtpd_banner=mail.mail2.com -o smtp_helo_name=mail.mail2.com
Anyway, banner and helo are OK, but myhostname is still the one included in main.cf
Solution 1:
Postfix is highly configurable. so much so that rtfming seems only slightly more advantageous than perusing the source (only joking).
There are in fact numerous ways of dynamically overriding the static configuration. At a very minimum, some understanding of the Postfix workflow is required. this is in the overview.
It is also useful to understand:
- different Postfix daemons reference different portions of the
main.cf
static configuration. - most built-in Postfix services are customisable;
- ad-hoc custom services (milters etc) can be plugged into the workflow;
- if all else fails, multiple Postfix instances can be deployed; each
referring to separate
main.cf
andmaster.cf
static configurations
Per the OP, wrt conditionally sending mail to some external recipients:
Methods of interest are those that are conditional upon either:
- the
smptd
listening port - the sender domain name
Apparently specifically in this regard, and for reasons that will soon hopefully become clear, Wietse Venema made some *key changes in Postfix 2.7 to do with content filtering &so to summarise:
pre Postfix 2.7 :
where unspecified, the default nexthop destination is $myhostname (ie:
localhost
).
Postfix 2.7:
where unspecified, the default nexthop destination is the recipient domain.
transport conditional upon sender domain is supported out-of-the-box via the use of
sender_dependent_default_transport_maps
This distinction is of issue because there are still folk stuck on pre 2.7 Postfix installs.. ie: for some Red Hat users relying upon the official rpms, then for example, users still on centOS 5x => Postfix 2.3 & for centOS 6x => Postfix 2.6 (but i might be wrong, dyor etc)
Postfix 2.7:
Things got easier! below are a couple of methods.
The first method involves setting up a couple of smtpd
daemons listening on different non-standard ports and uses custom transports conditional upon which smtpd
port we submit our outbound email to:
- port 10026 => custom1_smtp
- port 10027 => custom2_smtp
This is made possible by dynamically defining a custom filter service & overriding the content_filter
directive in the smtpd service. the filter is a dummy. we're not really going to be writing & deploying our own custom filter service. instead we just hijack a postfix smtp
service instance for the sole purpose of gaining access to and dynamically overriding the myhostname
directive before the email is finally pushed out to the wild.
This can only happen in Postfix 2.7 (and maybe subsequent versions above) because the specified transports' default nexthop destination is defined to be the recipient domain. In versions below Postfix 2.7, it is defined as $myhostname
and so the email loops back into Postfix, causing it to bork.
The second method is by the book and uses sender_dependent_default_transport_maps to conditionally route our outbound email to a couple more custom-defined transports; and depends upon the sender's domain name:
- parrots.tld => custom3_smtp:
- penguins.tld => custom4_smtp:
The sender_dependent_default_transport_maps
act like sender_dependent_relayhost_maps but instead of routing outgoing mail from a specified sender domain to a specified destination domain, sender_dependent_default_transport_maps
are used to internally route the outgoing mail from a specified sender (domain) to a specified (custom-defined) transport defined in master.cf
.
We add the following directive to /etc/postfix/main.cf
:
sender_dependent_default_transport_maps = hash:/etc/postfix/sender_transports
Then create the mapping file /etc/postfix/sender_transports
:
@parrots.tld custom3_smtp:
@penguins.tld custom4_smtp:
&build the database using postmap
:
postmap hash:/etc/postfix/sender_transports
Then we define all our custom transports in /etc/postfix/master.cf
:
custom4_smtp unix - - n - - smtp
-o myhostname=mailer.external.penguins.tld
-o smtp_bind_address=m.n.o.p
-o smtp_helo_name=penguins.tld
custom3_smtp unix - - n - - smtp
-o myhostname=mailer.external.parrots.tld
-o smtp_bind_address=i.j.k.l
-o smtp_helo_name=parrots.tld
custom2_smtp unix - - n - - smtp
-o myhostname=mailer.external.cats.tld
-o smtp_bind_address=e.f.g.h
-o smtp_helo_name=cats.tld
custom1_smtp unix - - n - - smtp
-o myhostname=mailer.external.dogs.tld
-o smtp_bind_address=a.b.c.d
-o smtp_helo_name=dogs.tld
# our main internal entry for the dogs site; all outgoing dogs traffic is
# sent to this (non-standard) port and routed to our custom1_smtp transport
10026 inet n - n - - smtpd
-o myhostname=mailer.internal.dogs.tld
-o content_filter=custom1_smtp:
# our main internal entry for the cats site; all outgoing cats traffic is
# sent to this (non-standard) port and routed to our custom2_smtp transport
10027 inet n - n - - smtpd
-o myhostname=mailer.internal.cats.tld
-o content_filter=custom2_smtp:
# default smtpd entry; outgoing traffic sent to this port (25) will get routed
# subject to the conditions in our sender_dependent_default_transport_maps file
smtp inet n - n - - smtpd
-o myhostname=mailer.internal.tld
# hoorah.
#
before Postfix 2.7 :
The way commonly suggested by the Postfix pixies is to set up multiple Postfix instances.
But here's where an understanding of the Postfix workflow comes in handy.. because by devising a custom-defined Postfix service chain, it is also achievable with just a single Postfix instance. nothing in main.cf
needs to be changed. static configuration is dynamically overridable from within the custom service chains we define in master.cf
.
Each service reads it's own subset of all the directives residing in the main.cf
config. so as we progress along our custom service chain, we can override any settings as may be permitted from within each respective Postfix service in the chain.
(Additionally, note that we could even write our own custom replacement for any of the default Postfix daemons. which is pretty cool).
In /etc/postfix/master.cf
:
# each Postfix service reads it's own subset of directives from main.cf config.
# many settings can be dynamically overridden as we move thro the service chain..
# here we override the $myhostname setting depending upon which port we send our
# outgoing smtp email to.
# custom2 cats service chain
custom2_cleanup unix n - - - 0 cleanup -o queue_service_name=custom2_qmgr
custom2_qmgr fifo n - n 300 1 qmgr -o rewrite_service_name=custom2_rewrite
custom2_rewrite unix - - n - - trivial-rewrite -o default_transport=custom2_smtp:
custom2_smtp unix - - n - - smtp
-o myhostname=mailer.external.cats.tld
-o smtp_bind_address=e.f.g.h
-o smtp_helo_name=cats.tld
# custom1 dogs service chain
custom1_cleanup unix n - - - 0 cleanup -o queue_service_name=custom1_qmgr
custom1_qmgr fifo n - n 300 1 qmgr -o rewrite_service_name=custom1_rewrite
custom1_rewrite unix - - n - - trivial-rewrite -o default_transport=custom1_smtp:
custom1_smtp unix - - n - - smtp
-o myhostname=mailer.external.dogs.tld
-o smtp_bind_address=a.b.c.d
-o smtp_helo_name=dogs.tld
# our main internal entry for the dogs site; all outgoing dogs traffic is
# sent to this (non-standard) port and routed to our custom1 service chain
10026 inet n - n - - smtpd
-o myhostname=mailer.internal.dogs.tld
-o cleanup_service_name=custom1_cleanup
# NB: attempting to override the content_filter directive
# *will not work* in postfix < 2.7 since an unspecified nexthop
# destination defaults to localhost and causes the email to loop
# -o content_filter=custom1_smtp:
# our main internal entry for the cats site; all outgoing cats traffic is
# sent to this (non-standard) port and routed to our custom2 service chain
10027 inet n - n - - smtpd
-o myhostname=mailer.internal.cats.tld
-o cleanup_service_name=custom2_cleanup
# yeehar! acu ;)
#
NOTE1: is a caveat:
while directives such as cleanup_service_name
and rewrite_service_name
are documented in the configuration parameters, they do not appear to be documented as configurable options parameters for any of the Postfix daemons?!
So at least this seems to be an undocumented approach and hence may well be frowned upon or even possibly deemed as evil in official circles. ~i wouldn't know as i was never able to form a cogent enough question to ever brave the [email protected]
official mailing lists!
NOTE2:
there are some folk out there attempting to do this kind of thing in other imaginative ways such as setting up a header_checks FILTER in order overcome the default localhost
nexthop destination by dynamically cooking-up a complete transport:recipient-domain-destination
:
In /etc/postfix/master.cf
:
custom2_smtp unix - - n - - smtp
-o myhostname=mailer.external.cats.tld
-o smtp_bind_address=e.f.g.h
-o smtp_helo_name=cats.tld
custom2_cleanup unix n - - - 0 cleanup
-o header_checks=regexp:/etc/postfix/custom2_header_checks
#conditional processing contingent upon entry via some non-standard port
10027 inet n - n - - smtpd
-o myhostname=mailer.internal.cats.tld
-o cleanup_service_name=custom2_cleanup
Then in /etc/postfix/custom2_header_checks
, pull the recipient domain out of the header-fields with a regex and provide it to the filter directive thus :
/^To:.*@(.*)$/ FILTER custom2_smtp:$1
While this may seem like a good idea, & partially does work, it is nevertheless certainly a horrid hack; is potentially insecure & at least fails if/when the outgoing email contains multiple recipients. duh!
NOTE3:
if none of the above makes any sense whatsoever, then you probably need to do a bit more rtfming ;)
But seriously, i know there's quite a lot of stuff here.. for various reasons beyond my control, the business is stuck on Postfix 2.3 and i have needed to spend more than 5mins sorting their requirements out; to the extent that my hourly rate has diminished to that of a costa rican pineapple picker (only i do not benefit from their costa' living). so i have tried to lay this all out while it's fresh in my mind and in terms that do not just parrot the official documentation; hopefully so this may help the next poor soul who happens down this particular path ;)
Solution 2:
After a lot of testing, i must conclude that you cannot do this. It IS possible to dynamically change myhostname, it just doesn't affect the Received header.
With a test having banner use $myhostname, i can see that it changes when i use
-o myhostname=test.test.test
but the received header does not.
I tried using, in main.cf
mydomain=test.test.test
myhostname=$mydomain
This does affect both the Received header and banner. But trying to override the $mydomain
-o mydomain=test.test.test
only affects the banner again.
Thus it seems that the variable used in the Received header is filled from $myhostname before overriding $myhostname from the command line arguments.