MX Proxy Extended, using Multiple Instances

Table of Contents

OpenBSD 4.8 AMD64, Postfix 2.7.1

Expanding on our MX Proxy example, we evolve a scenario where our needs are a little more complicated, and likewise our increased requirements for the MX Proxy In our new scenario, we deploy 3 purposed hosts, prioritising on performing their tasks while maintaining high availability:

  • The Internal Mail Server services mailbox and client access for internal users.
  • The Scanner, Filtering Server administers organisation security and useage policies.
  • The MX Proxy acts as the gateway between the internal systems and the Internet.

Our expanded mail chain looks like the below:


[ user ] <--> [ mail server ] <--> [ filter ] <--> [ MX Proxy ] <--> {Internet}

Alluding to the "Single Purpose Host" principle each server in the mail flow foci defined realm. . In reality, such segmentation is difficult with various access controls and filtering activities best implemented, at different points of the mail-chain.

A key requirement for enhanced security is greater access controls on the MX Proxy, differentiated between inbound and outbound mail. But differentiation requires multiple Mail Transfer Agents, individually listening for mail from:

  1. the host
  2. trusted/local network systems, and
  3. external systems.

Differentiation requires either:

  • Multiple [ MX Proxy ] hosts to differentiate traffic, or
  • Multiple MTA instances on the same host, one for each task.

Our priorities for the MX Proxy Service is to provide another layer of security between the Internet and the organisations network, while ensuring no mail is lost. Thus access controls (filters) on the MX Proxy must meet the following goals:

  1. Improve the security profile, documented.
  2. Low-cost performance, resource utilisation.
  3. Failover pass-through.
  4. Best performed at message entry point (MX Proxy.)

All other access controls, filtering, scanning, is delayed for the [filter] server and internal [mail server] as appropriate.

Installing a Multiple MTA Instances requires fewer resources.

The above diagram highlights the mail flow using our three MTA instances:

  • MTA Instance Primary for local submissions
  • MTA Instance Inbound filters and relays Inbound Mail (postfix-inbound)
  • MTA Instance Outbound relays Outbound Mail (postfix-outbound)

MTA Instance "Summary Filters"

1. Package Install

Install the OpenBSD Postfix Package. The Postfix "multi-instance" configuration tool uses the default install paths as the first instance, our "Primary Instance."

The compiled postfix binaries expect configuration files in /etc/postfix and the mail/queue in /var/spool/postfix These settings can be adjusted in our secondary instances:

config_directory=/etc/postfix
queue_directory=/var/spool/postfix

Update the Postfix configuration files, such that it accepts mail only from the localhost and relay's mail for internal users to the [Mail Filter Server]

File excerpt: /etc/postfix/main.cf

config_directory=/etc/postfix
queue_directory=/var/spool/postfix

alias_database = hash:$config_directory/aliases
alias_maps   = hash:$config_directory/aliases
message_size-limit = 20480000
mynetworks = 127.0.0.1
relay_domains = $config_directory/relaydomains
transport_maps = hash:$config_directory/transport

inet_interfaces = loopback-only

syslog_name = postfix-default

Because our intentions will be to run the default instance of Postfix only listening to the loop-back device, we update the master.cf file to not listen to the rest of the network.

File excerpt: /etc/postfix/master.cf

#smtp           inet    n   -   -   -   -   smtpd
127.0.0.1:smtp  inet    n   -   -   -   -   smtpd

Aliases

File excerpt: /etc/postfix/aliases

root:       control

Relay Domains

Because we support local mail delivery, we are also going to be explicit on where we want to deliver e-mail from the 'root' user. Our relay_domains file will contain the internally supported domains

File excerpt: /etc/postfix/relaydomains

example.org
example.com
example.net

Because parentdomainmatches_subdomain is the default, this will support subdomain.example.com etc.

Transport

File excerpt: /etc/postfix/transport

example.org         smtp:[ip-address]
example.com         smtp:[ip-address]
example.net         smtp:[ip-address]

Update Databases

/usr/local/sbin/postmap /etc/postfix/transport
/usr/local/sbin/postalias /etc/postfix/aliases

Start and Reload

/usr/local/sbin/postfix reload

2. MTA Instances

[Ref: Adding a second postfix instance | Managing multiple Postfix instances on a single host]

Postfix is good enough to use the same binaries to execute with different configurations, we'll configure our Instances using the ability to reference different configuration settings.

Per the Postfix documentation, we enable multi-instance support in our default instance

/usr/local/sbin/postmulti -e init

Which adds the following configuration settings:

File extract /etc/postfix/main.cf

multi_instance_directories =
multi_instance_enable = yes

We want some additional operational items defined, so we add the following configuration items.

File extract /etc/postfix/main.cf (add these settings if not already in the file)

multi_instance_wrapper = ${command_directory}/postmulti -p --
multi_instance_name = postfix-default
multi_instance_group = mta

Verify the configuration is operational as previously expected, including viewing the postmulti view of running services.

/usr/local/sbin/postmulti -l

3. Inbound Instance

After which we can initialise the other MTA instances (inbound and outbound)

/usr/local/sbin/postmulti -I postfix-inbound -G mta -e create

The command creates the /etc/postfix-inbound configuration directory with the following modifications:

File excerpt: /etc/postfix-inbound/main.cf

queue_directory = /var/spool/postfix-inbound
data_directory = /var/postfix-inbound

master_service_disable = 
authorized_submit_users = root
multi_instance_group = mta
multi_instance_name = postfix-inbound
multi_instance_enable = yes

Note we change the initial values for master_service_disable and authorized_submit_users so that we can accept email through inet, and accept e-mail from the root account during verification of our service.

We add the following customisations for our configuration

# -- Customisations for our configuration
relay_domains    = $config_directory/relaydomains
transport_maps   = hash:$config_directory/transport
message_size     = 20480000

mynetworks       =

# -- No Local Delivery
mydestination    =
alias_maps       =
alias_database   =
local_recipient_maps =
local_transport  = error:5.1.1 Mailbox unavailable

smtp_bind_address = 0.0.0.0
smtpd_delay_reject = yes
smtpd_helo_requried = yes

smtpd_helo_restrictions =
    reject_invalid_helo_hostname,
    reject_non_fqdn_helo_hostname,
    check_helo_access hash:$config_directory/helo_access,
    permit

smtpd_sender_restrictions =
    reject_unauth_destination,
    reject_non_fqdn_sender,
    check_sender_access hash:$config_directory/sender_access,
    reject_unknown_sender_domain,
    permit

File excerpt: /etc/postfix-inbound/master.cf

smtp           inet    n   -   -   -   -   smtpd

Incorporate chroot files

Incorporate custom configuration settings involved with OpenBSD's default chroot installation

/usr/local/sbin/postfix -c /etc/postfix-inbound check
export SUBDIR="etc usr lib"
export SRC=/var/spool/postfix
export DST=/var/spool/postfix-inbound
for subdir in $SUBDIR; do
    if [ -d $SRC/$subdir ]; then
        cp -pR $SRC/$subdir $DST
    fi
done

Relay Domains

File excerpt: /etc/postfix-inbound/relaydomains

example.org
example.com
example.net

Relay Transport

File excerpt: /etc/postfix-inbound/transport

example.org         smtp:[ip-address]
example.com         smtp:[ip-address]
example.net         smtp:[ip-address]

SMTP Connection Restrictions

File excerpt: /etc/postfix-inbound/helo_access

MY-PUBLIC-IP            REJECT Forged Source. If I needed a mirror I'd visit the bathroom
example.com             REJECT Forged Source. If I needed a mirror I'd visit the bathroom
example.org             REJECT Forged Source. If I needed a mirror I'd visit the bathroom 
subdomain.example.org   REJECT Forged Source. If I needed a mirror I'd visit the bathroom 

Where example.com and example.org are domains hosted by our Inbound.

Remember to update the db file by using postmap, and to start the your configuration files.

/usr/local/sbin/postmap /etc/postfix-inbound/helo_access
/usr/local/sbin/postmulti -i postfix-inbound -p start

Sender restrictions

check_sender_access hash:/etc/postfix-inbound/sender_access

Because we get a lot of emails purporting to be from internal, it's important to verify this before relaying mail. Remember that this instance, will only receive mail messages from external hosts.

File excerpt: /etc/postfix-inbound/sender_access

example.com             REJECT forged account 
example.org             REJECT forged account
subdomain.example.com   REJECT forged account
/usr/local/sbin/postmap /etc/postfix-inbound/sender_access
/usr/local/sbin/postmulti -i postfix-inbound -p reload

Recipient Restriction

Progressing through the initial stages of connection, mail receipt, we have focussed on low-cost activities. A few

smtpd_recipient_restrictions =
    reject_unauth_pipelining,
    reject_non_fqdn_recipient,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    check_sender_access hash:/etc/postfix-inbound/sender_access,
    check_recipient_access hash:/etc/postfix-inbound/recipient_access,
    permit

The advantage of these is that, being later in the processing stage of e-mail, it has also recieved more information about the potential message.

Expanding the generic rule, we can add blacklists with instructions such as the following.

    smtpd_recipient_restrictions =
        permit_mynetworks,
        reject_unauth_destination,
        reject_invalid_hostname,
        reject_unauth_pipelining,
        reject_non_fqdn_sender,
        reject_unknown_sender_domain,
        reject_non_fqdn_recipient,
        reject_unknown_recipient_domain,
        check_helo access       hash:$config_directory/helo_access,
        check_sender_access     hash:$config_directory/sender_access,
        check_recipient_access  hash:$config_directory/recpient_access,
        reject_rhsbl_recipient  bogusmx.rfc-ignorant.org,
        reject_rhsbl_recipient  dsn.rfc-ignorant.org,
        reject_rbl_client       zen.spamhaus.org,
        reject_rbl_client       dnsbl.njabl.org,
        permit      

Take care to monitor the effectiveness, or stability of the remote services being used. Likewise, ensure that your use is compliant with the acceptable use of those remote services.

File excerpt: /etc/postfix-inbound/recipient_access

10.20.30.40             REJECT forged account 
127.0.0.1               REJECT forged account
10.11.32.55             OK
/usr/local/sbin/postmap /etc/postfix-inbound/recipient_access
/usr/local/sbin/postmulti -i postfix-inbound -p reload

Start and Reload

Normally, we would need to enable the postfix-inbound instance with the following command

/usr/local/sbin/postmulti -i postfix-inbound -e enable

which would make at least the following addition to the main.cf file.

multi_instance_enable = yes

and we can start using the below command.

/usr/local/sbin/postmulti -i postfix-inbound -p start

Unknown Cost/Value

The rejectunverifiedsender is another anti-spam feature that will reject a large number of UCE, but at a cost that needs to be verified before enabled in your configuration.

    
reject_unverified_sender

Outbound Instance

Use submission port instead of smtp, and enable smtp for localhost.

/usr/local/sbin/postmulti -I postfix-outbound -G mta -e create

Creates the postfix-outbound instance with the following initial settings, customised.

File excerpt: /etc/postfix-outbound/main.cf

queue_directory = /var/spool/postfix-outbound
data_directory = /var/postfix-outbound

# -- initially created by postmulti 
#    the first 2 tuned before starting
master_service_disable = 
authorized_submit_users = root
multi_instance_group = mta
multi_instance_name = postfix-outbound
multi_instance_enable = yes

# -- Customisations for our configuration
message_size     = 20480000

mynetworks       = $config_directory/mynetworks

# -- No Local Delivery
mydestination    =
alias_maps       =
alias_database   =
local_recipient_maps =
local_transport  = error:5.1.1 Mailbox unavailable

File excerpt: /etc/postfix-outbound/master.cf

#smtp           inet    n   -   -   -   -   smtpd
submission      inet    n   -   -   -   -   smtpd

The submission port (587) differs from the smtp (25) but simplifies our configuration. For the Outbound configuration (no filtering.).

Incorporate chroot files

Incorporate custom configuration settings involved with OpenBSD's default chroot installation

/usr/local/sbin/postfix -c /etc/postfix-outbound check
export SUBDIR="etc usr lib"
export SRC=/var/spool/postfix
export DST=/var/spool/postfix-outbound
for subdir in $SUBDIR; do
    if [ -d $SRC/$subdir ]; then
        cp -pR $SRC/$subdir $DST
    fi
done

Trusted SMTP Hosts

File excerpt: /etc/postfix-outbound/mynetworks

# List ip-addresses of hosts in your network you trust
# - in our context: either the [Mail Server] or [Internal Firewall]
# - or both

Start and Reload

Normally, we would need to enable the postfix-outbound instance with the following command

/usr/local/sbin/postmulti -i postfix-outbound -e enable

which would make at least the following addition to the main.cf file.

multi_instance_enable = yes

and we can start|reload using the below command.

/usr/local/sbin/postmulti -i postfix-outbound -p start

5. Logs

Do not forget to add the new postfix logs to newsyslog

We should have a line in /etc/rc.conf.local with at least the following:

syslogd_flag="-a /var/spool/postfix/dev/log -a /var/spool/postfix-inbound/dev/log -a /var/spool/postfix-outbound/dev/log"

6. Verification

You can test, verify, behaviour of the above options by using the warnifreject to increase verbosity in your log file.

    warn_if_reject

    Change the meaning of the next restriction, so that it logs a warning instead of rejecting
    a request (look for logfile records that contain "reject_warning"). This is useful for
    testing new restrictions in a "live" environment without risking unnecessary loss of mail. 

7. Monitoring

use postmulti to monitor the status of running services. Postmulti makes it easier to review all running instances, or individual instances.

For example:

Instead of using mailq to look at the mail queue, you would use

To view all instances

$ sudo /usr/local/sbin/postmulti -x mailq

or to see the mailq for a a specific postfix instance

$ sudo /usr/local/sbin/postmulti -i postfix-inbound -x mailq