Load-balancing Microsoft Exchange with nginx+ – Part 3: Configuring nginx+ for Microsoft Exchange

nginxIn part 2 of this series I installed nginx+ on both HA1 and HA2.

In this part, I configure nginx+ to support Microsoft Exchange 2010/13.

Other articles in the series:

  1. Installing and configuring keepalived
  2. Installing nginx+
  3. Configuring nginx+ for Microsoft Exchange
  4. Configuring Microsoft Exchange
  5. Tidying up

First, find your Exchange front-end SSL certificate and its serial number:

certutil -store my

Export the certificate (along with the private key) so it can be imported onto the nginx+ VMs:

certutil -exportpfx -p "password" -privatekey serialnumber mail.mdb-lab.com.pfx

Copy the PFX file to HA1 and HA2. Check the file came across okay:

openssl pkcs12 -info -in mail.mdb-lab.com.pfx

Import the certificate (you will be asked for the password you specified in the preceding step):

openssl pkcs12 -in mail.mdb-lab.com.pfx -nocerts -nodes -out mail.mdb-lab.com.key.enc
openssl pkcs12 -in mail.mdb-lab.com.pfx -clcerts -nokeys -out mail.mdb-lab.com.cer
openssl pkcs12 -in mail.mdb-lab.com.pfx -out cacerts.crt -nodes -nokeys -cacerts

The first command extracts the private key, the second the certificate, and the third the CA certificate(s).  Next make the private key ready for nginx+

openssl rsa -in mail.mdb-lab.com.key.enc -out mail.mdb-lab.com.key

Check the private key is correct:

openssl rsa -in mail.mdb-lab.com.key -check

Move the certificate, private key and CA certificates to /etc/nginx/ssl/

rm -f mail.mdb-lab.com.key.enc
rm -f mail.mdb-lab.com.pfx
mkdir -p /etc/nginx/ssl
mv -f mail.mdb-lab.com.* /etc/nginx/ssl/

Edit /etc/nginx/nginx.conf and make sure the following global settings are in place:

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log info;
pid /var/run/nginx.pid;
events {
     worker_connections 1024;
}

Add the following lines to the http block in /etc/nginx/nginx.conf, replacing values for your CAS servers where necessary:

http {
     log_format main '$remote_addr - $remote_user [$time_local] '
          '"$request'' $status $body_bytes_sent '
          '"$http_user_agent" "$upstream_addr"';
     #set the log
     access_log /var/log/nginx/access.log main;
     keepalive_timeout 3h;
     proxy_read_timeout 3h;
     tcp_nodelay on;

     upstream exchange {
          zone exchange-general 64k;
          server 172.17.80.21:443; # Replace with IP address of a your CAS
          server 172.17.80.22:443; # Replace with IP address of a your CAS
          sticky learn create=$remote_addr lookup=$remote_addr
                    zone=client_sessions:10m timeout=3h;
     }

     server {
          # redirect to HTTPS
          listen 80;
          location / {
               return 301 https://$host$request_uri;
               }
     }

     server {
          listen 443 ssl;
          client_max_body_size 2G;
          ssl_certificate /etc/nginx/ssl/mail.mdb-lab.com.cer;
          ssl_certificate_key /etc/nginx/ssl/mail.mdb-lab.com.key;
          ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
          status_zone exchange-combined;
          # redirect from main page to /owa/
          location = / {
               return 301 "/owa/";
          }
     }

     location = /favicon.ico {
          empty_gif;
          access_log off;
     }

     location / {
          proxy_pass https://exchange;
          proxy_buffering off;
          proxy_http_version 1.1;
          proxy_request_buffering off;
          proxy_set_header Connection ''Keep-Alive'';
     }
}

Add the stream block to /etc/nginx/nginx.conf also:

stream {

     upstream exchange-smtp {
          zone exchange-smtp 64k;
          server 172.17.80.31:25; # Replace with IP address of a your Hub Transport
          server 172.17.80.32:25; # Replace with IP address of a your Hub Transport
     }

     upstream exchange-smtp-ssl {
          zone exchange-smtp-ssl 64k;
          server 172.17.80.31:465; # Replace with IP address of a your Hub Transport
          server 172.17.80.32:465; # Replace with IP address of a your Hub Transport
     }

     upstream exchange-smtp-submission {
          zone exchange-smtp-submission 64k;
          server 172.17.80.31:587; # Replace with IP address of a your Hub Transport
          server 172.17.80.32:587; # Replace with IP address of a your Hub Transport
     }

     upstream exchange-imaps {
          zone exchange-imaps 64k;
          server 172.17.80.21:993; # Replace with IP address of a your CAS
          server 172.17.80.22:993; # Replace with IP address of a your CAS
     }

     server {
          listen 25; #SMTP
          status_zone exchange-smtp;
          proxy_pass exchange-smtp;
     }

     server {
          listen 465; #SMTP SSL
          status_zone exchange-smtp-ssl;
          proxy_pass exchange-smtp-ssl;
     }

     server {
          listen 587; #SMTP submission
          status_zone exchange-smtp-submission;
          proxy_pass exchange-smtp-submission;
     }
}

Test the configuration before putting it live:

nginx -t

If everything is correct, it will yield the following:

Config okay

Modify iptables to allow traffic through the host firewall:

for i in {25,80,135,139,443,465,587,60000,60001}; do iptables -I INPUT -p tcp --dport $i -m state --state NEW,ESTABLISHED -j ACCEPT; done

Save the new iptables rulebase:

service iptables save

To get nginx+ running we need to disable SELinux temporarily:

setenforce 0

Edit /etc/selinux/config:

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of these two values:
#     targeted - Targeted processes are protected,
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

Change SELINUX=enforcing to SELINUX=permissive

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=permissive
# SELINUXTYPE= can take one of these two values:
#     targeted - Targeted processes are protected,
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

Start the service:

service nginx start

Make sure the config is the same on both HA1 and HA2. In part 5 I’ll configure rsync to ensure the configs are kept in sync.

That’s it for configuring nginx+.  In part 4 I’ll configure Exchange to support our nginx+ configuration.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.