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:
Mapping the requests to the appropriate Tomcat seems to sort of work with the following approach:
The worker.properties file looks like:
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!
Forcing SSL seemed to work with the following rules:
RewriteCond %{SERVER_PORT} !^443$
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R,L]
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>
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
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!
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
The normal rule I use:
RewriteCond %{HTTPS} off
# or
# RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
RewriteCond %{HTTPS} off
# or
# RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
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!
--------------------------
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!
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.
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
ASKER
* 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!