Link to home
Start Free TrialLog in
Avatar of arktech
arktech

asked on

Configuring Apache for forced SSL with Virtual Hosts on a multi-homed box

Okay, I am incredibly frustrated.  While I can get individual aspects of this working beautifully, I can't seem to put it all together.   I have a box -- dmz.app.com -- that will host an instance of Apache that is intended to sit in front of three distinct sites -- call them a.app.com, b.app.com and c.app.com.  dmz.app.com is multi-homed, with three public IP addresses (outside.100, outside.101 and outside.102), each of which maps to a unique internal IP address (inside.10, inside.11, inside.12).  The distinct sites are configured through DNS so that a.app.com = outside.100, b.app.com = outside.101 and c.app.com = outside.102.  For each site, there is a dedicated Tomcat server -- call them a-tomcat, b-tomcat and c-tomcat.  The idea is simple; for any request on any of the three *.app.com domains, force the request to use SSL (HTTPS over port 443) and route it to the appropriate *-tomcat server.

Forcing SSL seemed to work with the following rules:

RewriteCond %{SERVER_PORT} !^443$
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R,L]

Open in new window


Mapping the requests to the appropriate Tomcat seems to sort of work with the following approach:

# This was needed because Apache couldn't resolve the host uniquely due to the triple-homing
ServerName dmz.app.com

NameVirtualHost inside.11
NameVirtualHost inside.12
NameVirtualHost inside.13

<VirtualHost inside.11>
RewriteEngine On
RewriteOptions Inherit

DocumentRoot "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/a"
ServerName a.app.com
ErrorLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/error.log"
TransferLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/access.log"
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/my.crt"
SSLCertificateKeyFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/my.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>
BrowserMatch ".*MSIE.*" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
CustomLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/ssl_request.log" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
<Location "/*/WEB-INF/*">
     deny from all
</Location>

JkMount /* a-worker

JkMount /myapp/ a-worker
JkMount /myapp/* a-worker
JkMount /myapp* a-worker
</VirtualHost>                                  

<VirtualHost inside.12>
RewriteEngine On
RewriteOptions Inherit

DocumentRoot "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/b"
ServerName b.app.com
ErrorLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/error.log"
TransferLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/access.log"
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/my.crt"
SSLCertificateKeyFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/my.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>
BrowserMatch ".*MSIE.*" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
CustomLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/ssl_request.log" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
<Location "/*/WEB-INF/*">
     deny from all
</Location>

JkMount /* b-worker

JkMount /myapp/ b-worker
JkMount /myapp/* b-worker
JkMount /myapp* b-worker
</VirtualHost>                                  

<VirtualHost inside.13>
RewriteEngine On
RewriteOptions Inherit

DocumentRoot "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/c"
ServerName c.app.com
ErrorLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/error.log"
TransferLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/access.log"
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/my.crt"
SSLCertificateKeyFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/my.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>
BrowserMatch ".*MSIE.*" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
CustomLog "C:/Program Files/Apache Software Foundation/Apache2.2/logs/ssl_request.log" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
<Location "/*/WEB-INF/*">
     deny from all
</Location>

JkMount /* c-worker

JkMount /myapp/ c-worker
JkMount /myapp/* c-worker
JkMount /myapp* c-worker
</VirtualHost>                                  

Open in new window


The worker.properties file looks like:

workers.java_home="/Program Files/Java/jdk1.6.0_21"

# The advanced router LB worker
worker.list=a-worker, b-worker, c-worker

# Define workers using ajp13
worker.a-worker.port=8009
worker.a-worker.host=a-tomcat
worker.a-worker.type=ajp13

worker.b-worker.port=8009
worker.b-worker.host=b-tomcat
worker.b-worker.type=ajp13

worker.c-worker.port=8009
worker.c-worker.host=c-tomcat
worker.c-worker.type=ajp13

Open in new window


Putting all of these pieces together yields really inconsistent, funky results.  When I point my browser to https://b.app.com/myapp/ or https://b.app.com/myapp, life is wonderful and happy.  When I point my browser to http://b.app.com/myapp/ or http://b.app.com/myapp, I get a 400 Bad Request (Your browser sent a request that this server could not understand.  Reason: You're speaking plain HTTP to an SSL-enabled server port.  Instead use the HTTPS scheme to access this URL, please.).  But if I point my browser to http://b.app.com or http://b.app.com/ or https://b.app.com or https://b.app.com/, I wind up at https://a.app.com/myapp/!

This cannot possibly be as hard as I seem to have made it.  Can somebody please point me in the direction toward sanity?  Thank you!
Avatar of arktech
arktech

ASKER

To clarify what I mean by "point me in the direction toward sanity", answers to the following will be awarded points:

* The outside.10x -> inside.1x -> y.app.com structure was somewhat unintentional.  Originally, I thought unique external IP addresses were required to support SSL; our hosting provider thought we needed unique internal IP addresses.  Are we overcomplicating this?  Is there a better way?  Would we sacrifice any scalability/flexibility by using a different approach?

* What mod_rewrite techniques have you successfully used to force SSL with multiple virtual hosts?

Thanks!
Avatar of Steve Bink
Avatar of arktech

ASKER

I did play with SSLRequireSSL, but it's not really the behavior I was after.  In our current configuration (in which Apache is only sitting in front of one of the three environments), attempts to connect via http: are automatically redirected to the https: URL.  We managed that by including a simple redirect index.html in .htdocs.  Unfortunately, that's where the multi-domain/multi-homed complications are causing problems.  I set up a separate subdirectory for each domain (.htdocs/a, .htdocs/b, .htdocs/c) with an index.html in each that redirects to the https: URL for http: connections.  Unfortunately, attempts to connect to b.app.com get redirected to https://a.app.com, no doubt due to some misconfiguration, but I can't seem to figure it out.
ASKER CERTIFIED SOLUTION
Avatar of arober11
arober11
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The normal rule I use:

RewriteCond %{HTTPS} off
# or
# RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
Avatar of arktech

ASKER

arober11, you are a shining example of why I love Experts Exchange!  Everything is working perfectly now for all four flavors of https: URLs (https://b.app.com, https://b.app.com/, https://b.app.com/myapp and https://b.app.com/myapp/).  Using the http: form of all four yields the same result as before:

---------------------------------------------------
Bad Request

Your browser sent a request that this server could not understand.
Reason: You're speaking plain HTTP to an SSL-enabled server port.
Instead use the HTTPS scheme to access this URL, please.

    Hint: https://b.app.com/
---------------------------------------------------

Now, given that this response includes a hyperlink to a valid URL, I can live with this.  That said, if you know of an easy fix for it, I'm all ears!
Avatar of arktech

ASKER

Found it; fixed it!  I changed my NameVirtualHost and VirtualHost directives to explicitly specify a.app.com:443 etc. instead of just a.app.com.  Now all eight permutations are working as intended!  Thank you again for the help!  It's amazing how such a little oversight -- HTTP_HOST vs. SERVER_NAME -- can cause so much hair-pulling.
Avatar of arktech

ASKER

Fantastic!  Exactly the tip I needed.  Once I had this in place, the rest came together quickly.  Thank you, arober11, for spotting the subtle little mistake that was causing me so much frustration!
Thanks