Last update: Tue May 11 00:47:04 JST 2004
Keywords: outlook web access, OWA, front-end-https, proxy, libproxy.so, protect OWA, front-end-proxy, header, SSL, front, end, mod_proxy, apache, 2000, 2003, NT, windows
[I have successfully implemented this solution with SSL-aware Apache 1.3.27, 1.3.29, and 1.3.33 on Slackware Linux 9.x, Redhat Linux 7.3, and Redhat Enterprise Linux 3.1. I'm sure this can be done on any Unix that Apache 1.3.x compiles on. I have no idea how to do this on Apache 2.x as I'm lazy and have not yet moved my web servers to it. Also, this "HOWTO" does not cover setting up an OWA or SSL-aware Apache server. That's up to you. Sorry.]
Outlook Web Access 2000 and 2003 (OWA) runs on Microsoft's Internet Information Service (IIS). IIS is considered by many to be the swiss cheese of web servers (and I agree with this... sort of). I would never consider putting an IIS box on a publicly accessible network simply because it's just too much work to secure.
The point of having an OWA server is moot if one is not willing to allow access to it from the world. So, we need to somehow make the OWA service publicly accessible while not actually making the box itself publicly accessible (hopefully that makes sense. ;-). The answer? Proxy incoming requests with something a little more secure - namely Apache. Apache comes with a nice proxy module called mod_proxy. It does
almost everything we need it to.
INTERNET +--------------+
| | APACHE PROXY |
| +--------------+
+----------+ |
| FIREWALL |------------------
+----------+ Service net
|
|
+------------+
Int Net -----| OWA SERVER |
+------------+
Remote users want to access OWA. They type
https://webmail.somedomain.com into their browser. Their workstation/laptop then asks their DNS for the IP address of webmail.somedomain.com. The DNS replies with the IP address of the APACHE PROXY. The browser then connects to the proxy and sends its request. The APACHE PROXY then connects to the the internal OWA SERVER and requests the objects on behalf of the remote user. Simple enough.
Be aware that in this example, the Apache "proxy" is on a locked down service network. Nothing gets in or out of this net unless it is required. For the Apache "proxy", connection requests destined for port 443 (https) are allowed through the firewall from the world. Connection requests from the Apache "proxy" are also allowed through to destination port 80 on the internal OWA server. That's it. If the Apache "proxy" generates any traffic other than that, alarms go off (i.e. WARNING! WARNING! You may be 0wn3d!). I highly recommend this configuration for all pubicly accessible services.
Now, to make this bit of magic work, we need to do several things.
Configure Apache to use the mod_proxy module.
Patch the mod_proxy module so that it includes an additional header when sending proxied requests to OWA. OWA requires this header so that it knows it is being proxied by an SSL enable front-end.
Add several ProxyPass configuration directives to the Apache config file.
And finally add an entry to the Apache server's /etc/hosts file.
1. Configure Apache to use the mod_proxy module.
Ensure Apache's configuration file (httpd.conf) includes the following two lines.
LoadModule proxy_module /usr/lib/apache/mod_proxy.so
AddModule mod_proxy.c
NOTE: After adding the above lines to the Apache config file, Apache may not start. It all depends how your distibution's version of Apache was compiled or how you compiled it yourself. We still have to modify the mod_proxy module, compile it, and install it before any of this will work anyway.
2. Patch the mod_proxy module so that it includes an additional header when sending proxied requests to OWA. OWA requires this header so that it knows it is being proxied by an SSL enable front-end.
Modify the source of mod_proxy to include the "front-end-https" header in all proxied requests to OWA. The source for Apache can be downloaded from
http://httpd.apache.org/download.cgi. It should also be included with your distribution (perhaps on a source CD). If you download the source from the Apache website, make sure it is the same version you are currently working with. The file that needs to be modified is
<apache source dir>/src/modules/proxy/proxy_http.c.
Somewhere around line 400 in proxy_http.c include the following line of code (You'll need a basic understanding of C to figure out where to put this.):
ap_table_set(req_hdrs, "front-end-https", "on");
After you've modified the source, recompile mod_proxy using the following command (while in the
/src/modules/proxy directoy).
apxs -i -c *.c
This command will compile mod_proxy.
Now, copy the compiled proxy (libproxy.so) to your Apache module directory (i.e. /usr/local/apache/libexec - check your httpd.conf to make sure).
3. Add these ProxyPass configuration directives to your Apache config file. You may place them in the main section of the config or in a VirtualHost section. It all depends how you have your server configured. I've done it in both and either will work.
ProxyPass /exchange http://webmail.somedomain.com/exchange/
ProxyPassReverse /exchange http://webmail.somedomain.com/exchange/
ProxyPass /exchweb http://webmail.somedomain.com/exchweb/
ProxyPassReverse /exchweb http://webmail.somedomain.com/exchweb/
ProxyPass /public http://webmail.somedomain.com/public/
ProxyPassReverse /public http://webmail.somedomain.com/public/
ProxyPass /iisadmpwd http://webmail.somedomain.com/iisadmpwd/
ProxyPassReverse /iisadmpwd http://webmail.somedomain.com/iisadmpwd/
NoCache *
[ Updated: I changed the first two proxy configuration directives. The first ProxyPass and ProxyPassReverse directives were proxying /mail. I changed this to /exchange as that is the OWA default. In my office, we cofigured the OWA server so that /mail was /exchange. I thought this may confuse folks as it confused me when I re-read it today. (Thanks to Helmut!) ]
4. And finally add an entry to the Apache server's /etc/hosts file.
You may notice the ProxyPass directives redirect several directories to webmail.somedomain.com. But how can we redirect to webmail.somedomain.com if the Apache server is webmail.somedomain.com? We need to add an entry to the /etc/hosts file pointing webmail.somedomain.com to the internal OWA IP address.
192.168.0.100 webmail.somedomain.com
Be sure your server is configured to look in your hosts file before consulting the DNS. Check your /etc/host.conf file to make sure. It should read like this.
order hosts, bind
multi on
Now start Apache. Watch your logs for any errors. Make sure the appropriate access is configured through your firewall (world --> proxy dst tcp 443 and proxy --> internal OWA server dst tcp 80). Also remember to watch the logs on your internal OWA server.
Some things to watch include
Browser warnings about secure and insecure items on the same page - this could be a sign that the mod_proxy patch isn't working
Apache complaining about bad directives in its config file - could be a misspeled directive or a configuration conflict with your distro's version of Apache
Apache warning it could not find a certain object or could not find the OWA server - make sure you've added the right IP address to your /etc/hosts and that you've redirected all the required directories in your Apache config file (ProxyPass and ProxyReverse directives).
If you try implementing this and have problems or (sorry -- I'm bombarded with questions) have something you think I should add (or modify) to these instructions, feel free to contact me at mikeg@3cx.org. When I finally get around to moving my servers to Apache 2.0, I'll write up another "HOWTO" or possibly expand this one.
This is awesome. Thank you for this excellent HOWTO.
Excellent writeup. This is exactly what I'm trying to do. One queston though, could you tell me precisely where to inject the line above into the proxy_http.c file?
Here is the section around line 400:
if (r->proxyreq == PROXY_PASS) {
const char *buf;
/*
* Add X-Forwarded-For: so that the upstream has a chance to determine,
* where the original request came from.
*/
ap_table_mergen(req_hdrs, "X-Forwarded-For", r->connection->remote_ip);
/* Add X-Forwarded-Host: so that upstream knows what the
* original request hostname was.
*/
if ((buf = ap_table_get(r->headers_in, "Host"))) {
ap_table_mergen(req_hdrs, "X-Forwarded-Host", buf);
}
/* Add X-Forwarded-Server: so that upstream knows what the
* name of this proxy server is (if there are more than one)
* XXX: This duplicates Via: - do we strictly need it?
*/
ap_table_mergen(req_hdrs, "X-Forwarded-Server", r->server->server_hostname);
}
Thanks
Nevermind, I patched and installed mod_proxy_add_forward from ports (FreeBSD) instead.
I found that to get it to compile in 1.3.22 source, it is not "req_hdrs" but "reqhdrs"
Thank you. Works perfectly on OpenBSD 4.0. Apache source on OpenBSD is in src.tar.gz.