The Reverse Proxy Series — Part 3.1: Apache as an SSL reverse-proxy
SSL involves cryptography (SSL might also involve moving your hands to express yourself in Malaysia, but that’s another SSL). Cryptography is subject to export regulations by “the man”. This makes it somewhat illegal for the Apache Foundation to make available the pre-compiled binaries of Apache’s SSL support, which comprises of mod_ssl and OpenSSL. As a result of those restrictions you need to compile Apache+SSL on your own. While I don’t mind compiling Apache to get this working, I am aware that there are two reasons I’m willing to compile anything to make it work: (1) I’m used to UNIX; (2) I’m used to Linux. If you come from a Windows world you probably never had to compile anything; moreover you’re most likely to not even have access to development tools. Luckily for you, me and everyone else good people have pre-compiled Apache+SSL for us — Download their package .This package also contains binaries of OpenSSL, which you will also need to get things up and running. The download is a ZIP file which contains the entire Apache package — for simplicity’s sake, unpack it to c:apache.
First let’s make sure the basic settings in the httpd.conf file which came with the package are OK: look for the ServerRoot, DocumentRoot and Directory directives and verify they point to the directory you unpacked the Zip file in. For example, if you installed Apache in “d:apache”, and you find the following in the configuration file:
you should change the value to “d:/apache”. Note that you should use a UNIX-style slash (“/”), and not a Windows-style slash (“”). The default file listens to port 80 (for clear-text regular HTTP) — if you want it listening in some other port change it in the Listen directive. Now would be a good time to just start the Apache server and make sure it’s working by navigating to your server’s host-name with the port you set in the Listen directive (80 if you haven’t changed anything.) You should see the standard Apache greeting page.
Generating an SSL Certificate
If you have a bona-fida SSL certificate, use it. For testing purposes we’ll just generate our own using OpenSSL. If you have cygwin installed with OpenSSL you can just use it from there (use the same commands listed here but omit the “-config openssl.cnf” part). If you don’t have cygwin (or you don’t even know what the heck cygwin is), have no fear — the OpenSSL executable is included with Apache+SSL Win32 package. The only thing missing is a configuration file, but luckily for us the good people who created the aforementioned how-to guide have supplied us with one — download it — right click the link and select “Save As…” and save it in the bin/ directory under your Apache directory (the directory with Apache.exe and OpenSSL.exe) Open a command prompt in the bin/ directory of Apache. The next step is to run a series of commands to issue the SSL certificate. You will be asked all kinds of questions about the country you’re in, the company you work for, etc etc — in a test certificate it doesn’t really matter what you enter. What you should notice is that when you’re asked for a “Common name” (it sometimes even says “YOUR name”), what it means is the exact host name of the reverse-proxy server (for example, revproxy.company.com). Run the following commands:
openssl req -config openssl.cnf -new -out my-server.csr openssl rsa -in privkey.pem -out my-server.key openssl x509 -in my-server.csr -out my-server.cert -req -signkey my-server.key -days 365
Create a directory called SSL under the Apache conf/ directory, and move the files my-server.key and my-server.cert from where you ran OpenSSL in to the new directory. You should also move all the other generated files to a secure directory on your server.
Activating and configuring mod_ssl
A small checklist before we move forward:
- You have downloaded the Apache+SSL package and decompressed it to your hard-drive
- You have verified that the ServerRoot, DocumentRoot and Directory directives point to the right location
- You have generated the test certificates and placed them in conf/ssl (or in case you have a real SSL certificate you’ve placed its .cert and .key files in the same directory
- You drank a good cup of coffee today (optional)
If you passed the checklist, you’re good to go on to the next step. Open httpd.conf in your editor and find the LoadModule directive which loads mod_ssl; it should like something (if not exactly) like this:
#LoadModule ssl_module modules/mod_ssl.so
Remove the “#” to tell Apache to load the module. Now we need to actually enable SSL to our server. Firstly we want to make sure Apache listens on the SSL port, 443. To do that we’ll add a Listen directive to port 443, like so:
It would be best if you add the new Listen directive right below the Listen directive which handles port 80 (or 8080, or whichever port you use for HTTP). If you need this server to support only HTTPS you can simply change the
Listen 80 directive to
Listen 443 and be done with it. mod_ssl has its set of directive which configure it. Add the following directives somewhere in the httpd.conf file (doesn’t really matter where):
SSLMutex default SSLRandomSeed startup built in SSLSessionCache none
These three directives jump-start mod_ssl. You can read more about them in mod_ssl‘s documentation if you’re interested. (note: the Apache+SSL Win32 how-to lists
SSLMutex sem as the directive to use; “sem”, however, is only supported under POSIX or SystemV, both of which are from the UNIX world — it doesn’t work on Windows; default tells mod_ssl to use the best available locking mechanism according to the platform it runs on) The next set of directives to add are the following:
SSLEngine On SSLCertificateFile conf/ssl/my-server.cert SSLCertificateKeyFile conf/ssl/my-server.key
I’ve split these directives to two parts for a reason — if you’re using VirtualHosts in your Apache server you’ll want to put the second set of directives only in the websites which will run using SSL write references to the right SSL certificate for each website. (in a nutshell — the VirtualHost directive lets Apache serve different web-servers from a single Apache instance; you could, for example, have it reverse-proxy a few portals, or have it reverse-proxy using HTTP from one host name and using HTTPS from a different host name) You could test the SSL setup at this point — start Apache and navigate to https://hostname and see if you get the Apache welcome page.
Adding reverse-proxying to the mix
Now that we’ve got SSL up & running (finally!) it’s time to make sure the reverse-proxy still functions. We’ll start by configuring Apache to be an SSL terminator, which means end-users will connect using HTTPS to Apache, which will forward the requests to the internal server using HTTP, get the results back from the internal server using HTTP and send back over HTTPS to the client. Here’s how the mod_proxy directives should look like:
#Enable reverse-proxying ProxyVia on ProxyTimeout 600 #disable forward-proxying ProxyRequests Off #proxy /irj both ways ProxyPass /irj http://internal.company.com:50000/irj ProxyPassReverse /irj https://external.company.com/irj #proxy /logon both ways ProxyPass /logon http://internal.company.com:50000/logon ProxyPassReverse /logon https://external.company.com/logon
If this looks oddly familiar it’s because this is almost the same directives we used in the The Reverse Proxy Series — Part 2: IIS as a reverse-proxy in this series, almost being the key-word here. The only difference is the fact that in both
ProxyPassReverse directives we write “https://” instead of “http:// when referring to the reverse-proxy host. Yes, it’s that simple. The next (and last!) thing we need to do is make a tiny change to the ProxyMapping property in the J2EE engine. You can read all about that property and its purpose at the end of the The Reverse Proxy Series — Part 2: IIS as a reverse-proxy in this series. The only thing we need to change is to make sure the protocol is set to “https” instead of “http”, and the port set to the SSL port, 443, instead of 80. So, the property’s value would look something like this (your portal’s port might be different then 50000, and rev-proxy.corporate.com should be replaced by your reverse-proxy’s host name):
Make sure you click both “Update” and the save button (it has a disk icon) to apply the changes.
That should be it — Apache should be reverse-proxying your portal using SSL between end-users and the reverse-proxy. If you want to use SSL all the way you’ll need to enable SSL on the J2EE engine (I won’t get into that now; plenty of documentation about that), and change the mod_proxy directives to forward requests using https instead of http. If you’re having trouble with that kind of setup you might need to look into enabling mod_proxy_connect and using the AllowCONNECT directive of mod_proxy.
Find yourself tasked to reverse-proxy a portal which exposes web-applications from 4 other hosts? Think there’s no way you’re going to do it from one reverse-proxy? The next article will tell you how to use mod_rewrite to handle complex reverse-proxying scenarios.
How would you proceed though, if you had the same reverse proxy in front of a clustered J2EE with multiple app serververs, when the proxy runs BOTH http and https and the app servers all have different instance (and port numbers)?
Is it just as straight forward as adding another mapping to the same port to cover BOTH protocol mappings?
A Sample on Apache 2.0.59<br/><VirtualHost *:443><br/>RequestHeader set Front-End-Https "On"<br/>RequestHeader set ClientProtocol HTTPS<br/>SSLEngine on<br/>SSLProtocol -all +SSLv3 TLSv1<br/>SSLCipherSuite HIGH:MEDIUM:!aNULL:SHA1:MD5:HIGH:+MEDIUM<br/>SSLCertificateFile conf/ssl/crt/XXXXXX.cert<br/>SSLCertificateKeyFile conf/ssl/crt/XXXXX.cert<br/>SSLCertificateChainFile conf/ssl/crt/X.pem<br/>SSLCACertificateFile conf/ssl/crt/X.cert<br/>SSLCACertificateFile conf/ssl.crt/intermediate.crt<br/>SSLCACertificateFile conf/ssl.crt/ca.crt<br/>SSLVerifyClient optional<br/>SSLVerifyDepth 1<br/>SSLProxyEngine On<br/>ProxyTimeout 7200<br/>SSLSessionCacheTimeout 7200<br/>AllowCONNECT 443 50100 50101<br/>AllowEncodedSlashes On<br/>ProxyRequests off<br/>ProxyPreserveHost On<br/><Location /irj/portal><br/>ProxyPass https://apphost1:50101/irj/portal<br/>ProxyPassReverse https://loadbalancer/irj/portal<br/>SSLRequireSSL<br/>SetEnv force-proxy-request-1.0 1<br/>SetEnv proxy-nokeepalive 1<br/></Location><br/><Location /irj/servlet/prt/portal><br/>ProxyPass https://apphost1:50101/irj/servlet/prt/portal<br/>ProxyPassReverse https://loadbalancer/irj/servlet/prt/portal<br/>SSLRequireSSL<br/>SetEnv force-proxy-request-1.0 1<br/>SetEnv proxy-nokeepalive 1<br/></Location><br/><Location /><br/>ProxyPass https://apphost1:50101/<br/>ProxyPassReverse https://loadbalancer/<br/>SSLRequireSSL<br/>SetEnv force-proxy-request-1.0 1<br/>SetEnv proxy-nokeepalive 1<br/></Location><br/>KeepAlive On<br/>KeepAliveTimeout 7200<br/>LogLevel info<br/>ErrorLog logs/apphost1.error.log<br/>CustomLog logs/apphost1.custom.log common<br/>SSLOptions +StdEnvVars<br/></VirtualHost><br/>
I think you should define a proxymapping for each protocol with different ports, and have Apache forward to these ports.
The main problem here would be to make Apache load-balance the requests. I'm sure I've seen an Apache module that takes care of this (maybe even mod_proxy, I don't remember).
I have been doing some experiments today. The things that work are:
Use mod_proxy to proxy for example,
in a virtual host running http:
incoming on http /irj to http://internal.server:50000/irj
in a virtual host (simultaneously with the above) running https:
incoming on https /irj to the same.
No proxymappings in j2ee.
The proxying works fine EXCEPT when inbound on https, the protocol reverts to http and most generated portal links are http too.
Browsing directly to the SSL port of the J2EE is also fine. All behaves as expected, BUT we dont want SSL between Apache and J2EE.
If I set a proxymapping for SSL in J2EE it's all ok again.
However, we want here, to visit the proxy on http only, and occasionally we get a 'secure area' of the J2EE portal and need to link to an https page. With the proxymapping so defined for the SSL type of protocol, it sems to be an all or nothing type of thing.
You called have several ProxyMappings -- simply have the reverse-proxy forward HTTPS (internally) using a different port and create another ProxyMapping for that port for https.
I have a problem with my proxy:
ProxyPass /irj http://debmsu06.server.###.de:50300/irj
ProxyPassReverse /irj http://debmsu06.server.###.de:50300/irj
RewriteRule ^/$ /irj/portal [R]
If I use URL:
https://bebuyer.###.de/ goto https://bebuyer.###.de/irj/portal
but if I use
I get the info:
What is happened? How I can redirect to /irj/portal?
Of course I can use
Could you please give me some tips?
We desparately need some info on using Web Dispatcher as reverse proxy.
The scenario we have is that we have a SAP eRecruiting system which resides behind the firewall with a web dispatcher in front of it. We'd like to do the following:
1. Integrate the eRecruiting applications in SAP Portal through the Web Dispatcher. We'd like to use SSO, so I think that we need to establish the trusted relationship between the Portal and the Web Dispatcher.
2. We'd like to re-configure the SSL from the pass through to the termination and re-encryption in the Web Dispatcher.
Could you please give us some pointers? To make the thing a little bit complicated, our Web Dispatcher has a different domain from the SAP Portal.
I have to say I don't have a lot of "mileage" on the SAP Web Dispatcher, but let's try to tackle this anyway.
1. SSO in the portal is done using a simple cookie, so I doubt you'll need any kind of trust between the WD and the portal as long as that cookie is forwarded. The problem here is that because you have different domains the cookie probably doesn't get forwaded. You need to enable SSO for multiple domains, go over this: http://help.sap.com/saphelp_nw04/helpdata/en/a0/88a340fa432b54e10000000a1550b0/frameset.htm
2. Did you try following the documentation found here? http://help.sap.com/saphelp_nw04/helpdata/en/d8/a922d7f45f11d5996e00508b5d5211/frameset.htm
It explains how to do SSL termination and re-encryption.
Hi Aron,<br/><br/>Firstly the weblog is very useful and helped me in long way, till I came up to this scenario. <br/><br/>I'm trying to configure SSL reverse proxy using Apache Web Server using virtual hosts. I'm trying reverse proxy the SSL of Internal.company.com direct to SSL of external.company.com. <br/><br/>In this regard I've followed your steps but getting the following error:<br/><br/>[Wed May 24 07:03:54 2006] (20014)Error string not specified yet: proxy: pass request body failed to 220.127.116.11:50001 (external.company.com)<br/>[Wed May 24 07:03:54 2006] (20014)Error string not specified yet: proxy: pass request body failed to 18.104.22.168:50001 (external.company.com) from 22.214.171.124 ()Srikant
1. Did you load the mod_proxy_connect module? If it's not there mod_proxy can't handle the CONNECT request to establish SSL tunnels.
2. Maybe this is just a copy-paste accident, but did you notice this line:
ProxyPassReverse /logon https://internal.company.com:443:443/logon
? It should be:
ProxyPassReverse /logon https://internal.company.com:443/logon
(b.t.w, you can remove the port altogether, 443 is the standard HTTPS port anyway; the "AllowCONNECT 443" directive can also probably be removed)
Let me know if it solved your problem, good luck.
I've noticed a typing mistake in one of your openssl commands, which might frustrate many people.
openssl rsa -in -privkey.pem -out my-server.key
should in fact be
openssl rsa -in privkey.pem -out my-server.key
The "-" before the privkey.pem filename does not allow the command to go further and gives an error message:
Error opening Private Key -privkey.pem
3624:error:02001002:system library:fopen:No such file or directory:.\crypto\bio\bss_file.c:352:fopen('-privkey.pem','rb')
3624:error:20074002:BIO routines:FILE_CTRL:system lib:.\crypto\bio\bss_file.c:354:
unable to load Private Key
Apart from that, this blog post is great and very useful. Congratulations!
To clarify, I receive via http and return the url of the secure server.
ProxyPass / http://126.96.36.199:8000/
ProxyPassReverse / https://188.8.131.52/
Any help with this would be appreciated.
I have reverse proxy configured with siteminder to provide SSO......We have been provided with reverse proxy folder mapping as ssodev.company.com/itr242 and our portal address is portal.company.com:5XXXX/irj.....When we get redirected we get error saying 404 the requested resource does not exists.....How do we get this working how one can configure portal in case of reverse proxy folder mapping like this?...
need your expert help...
Hi Alon,<br/><br/>We have a apache configured as reverse proxy in DMZ.we need to setup as below <br/><br/>internal PI
httpsapache httpsinternet httpsexternal comp <br/><br/>Apache is configured to listen to both http & https simultaneously and its working fine. But when we try to call the external comp webservice on ssl from apache we get the ssl handshake failed error.<br/><br/>Apache rewrite rules.<br/><VirtualHost default:443><br/><br/># General setup for the virtual host<br/> DocumentRoot "/apache/patch2.2.13/htdocs"<br/> ServerName internal.unilever.com<br/> ProxyPreserveHost On<br/> RewriteEngine On<br/> ProxyVia on<br/> ProxyTimeout 10000<br/><br/> ProxyRequests Off<br/><br/> RequestHeader set ClientProtocol HTTPS<br/> RequestHeader set x-sap-webdisp-ap HTTP=80,HTTPS=443<br/> AllowCONNECT 443<br/>SSLEngine on<br/><br/>SSLCertificateFile "/usr/local/ssl2/bin/abc.crt"<br/>SSLCACertificateFile "/usr/local/ssl2/bin/abc.crt"<br/><br/>#x.x.x.x - external company ip address<br/><br/>RewriteCond % ^.?internal.company.com.<br/> RewriteRule ^/(SOAPtest.) https://x.x.x.x/a/b/c/z <br/><br/>The redirect happens properly but the data sent is not received on the external url.<br/>Error received in logs of apache :<br/><br/>[Tue Nov 17 18:23:55 2009] ssl_engine_kernel.c(1888): OpenSSL: Read: SSLv3 read finished A<br/>[Tue Nov 17 18:23:55 2009] ssl_engine_kernel.c(1907): OpenSSL: Exit: failed in SSLv3 read finished A<br/>[Tue Nov 17 18:23:55 2009] client 184.108.40.206 SSL Proxy connect failed<br/>[Tue Nov 17 18:23:55 2009] SSL Library Error: 336151568 error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure<br/>[Tue Nov 17 18:23:55 2009] client 220.127.116.11 Connection closed to child 0 with abortive shutdown (server internal.company.com:443)<br/>[Tue Nov 17 18:23:55 2009] (502)Error 502 occurred.: proxy: pass request body failed to x.x.x.x:443 (x.x.x.x)<br/>[Tue Nov 17 18:23:55 2009] proxy: pass request body failed to x.x.x.x:443 (x.x.x.x) from y.y.y.y ()<br/>[Tue Nov 17 18:23:55 2009] proxy_util.c(2017): proxy: HTTPS: has released connection for ()<br/>[Tue Nov 17 18:24:00 2009] ssl_engine_io.c(1869): OpenSSL: I/O error, 5 bytes expected to read on BIO#110ecded0 mem: 1110a1a10<br/>[Tue Nov 17 18:24:00 2009] (70007)The timeout specified has expired: SSL input filter read failed.<br/>[Tue Nov 17 18:24:00 2009] ssl_engine_kernel.c(1893): OpenSSL: Write: SSL negotiation finished successfully<br/>[Tue Nov 17 18:24:00 2009] client 18.104.22.168 Connection closed to child 0 with standard shutdown (server internal.company.com:443)<br/><br/>can you help me on how to authenticate apache for a remote server (in internet). Where should we put the certificate for the remote server so that the above handshake can go successfully.<br/><br/>regards,<br/>Sreekanth<br/><br/>