Friday, May 28, 2010

Overcomming the virtual hosts/HTTPs conflict on Amazon AWS


Previously I've described the conflict between virtual hosts and HTTPs. For those that use Amazon's EC2 cloud, the below describes an elegant remedy for that conflict .

Assumed Basic Setup


For the purposes of this example, I'll assume you have an EC2 instance of Ubuntu Lucid with an apache server serving two websites: example.com and eazybusiness.com. Both have their own virtual host definitions, each containing it's corresponding site's specific configuration. If you need more information on how to configure your virtual hosts I direct you to apache's comprehensive documentation on the subject.

The virtual host definitions would look something like:

<VirtualHost *:80>
ServerName www.eazybusiness.com

ServerAdmin admin@eazybusiness.com
DocumentRoot /var/sites/eazybusiness.com/
</VirtualHost>
<VirtualHost *:80>
ServerName www.example.com

ServerAdmin admin@example.com
DocumentRoot /var/sites/example.com/
</VirtualHost>


The above definitions allow insequre, plain HTTP connections to both sites. The hostname provided by the browser client is matched against the servername's in the virtual hosts and the correct configuration is used accordingly.

Configure Amazon's Elastic Load Balancer


In order to secure our two websites, we'll first create the necessary proxy via Amazon's load balancer facility.

  • First configure your ec2 image to use the Amazon command line tools. Ubuntu this images make this easier thanks to the power of apt-get.


  • Seperate command line tools are required for ELB. Download the tools from Amazon, and unzip to somewhere on your image.

  • Set an environment variable AWS_ELB_HOME to point to where you unzipped the tools.



Now, let's create our first loadbalancer:

Here we configured loadbalacer to listen on port 443 and port 80. All requests to port 80, are to be forwarded to port 80, and requests to 443 are to be forwarded to 8445
Note: amazon's api tools default to the region us-east-1. If you wish to use any other region, you must provide the --region parameter

Create a second loadbalancer for example.com:

Note: the example.com loadbalancer will forward requests to port 443 to a different port than the eazybusiness.com loadbalancer

Next, register our instance with both load balancers:

elb-register-instances-with-lb eazybusiness --region us-west-1 --instances $instanceId
elb-register-instances-with-lb example --region us-west-1 --instances $instanceId


Configure Apache


Apache must start to list on the ports that the load balancers will forward to. To this, edit /etc/apache2/ports.conf and add the lines:

Listen 8443 https
Listen 8445 https

To confirm the ports are being listened on try the following commands:

netstat -na | more
nmap -sT -O localhost


Ensure mod_headers is enabled
sudo a2enmod headers


Configure Virtual Hosts


The virtual hosts need to be configured for SSL, and reflect the ports defined for the loadbalancers. In our example, the virtual hosts become:

<VirtualHost *:80>
ServerName www.eazybusiness.com

ServerAdmin admin@eazybusiness.com
DocumentRoot /var/sites/eazybusiness.com/
SSLEngine On
SSLCertificateFile /etc/ssl/eazybusiness.com.crt
SSLCertificateKeyFile /etc/ssl/eazybusiness.com.key
RequestHeader set X_FORWARDED_PROTO 'https'
</VirtualHost>
<VirtualHost *:80>
ServerName www.example.com

ServerAdmin admin@example.com
DocumentRoot /var/sites/example.com/
SSLEngine On
SSLCertificateFile /etc/ssl/example.com.crt
SSLCertificateKeyFile /etc/ssl/example.com.key
RequestHeader set X_FORWARDED_PROTO 'https'
</VirtualHost>

Note: the RequestHeader directive requires that you enable mod_headers for Apache.
sudo a2enmod headers


Configure DNS


The AWS console displays a list of load balancers. From this list, glean the relevant DNS names, and make the relevant alterations to the dns server like this:

eazybusiness.com. IN CNAME eazybusiness-2028819496.us-west-1.elb.amazonaws.com.
example.com. IN CNAME example-2028229496.us-west-1.elb.amazonaws.com.

Note: don't forget the proceeding dot when using full domains.

Configure Amazon Firewall


Ensure the specified ports open, which in our example are 8445 and 8443.

To confirm the port is open with Telnet try:

telnet $public_dns_of_image 8445



After completing all of the steps above you should now be able to support multiple secure websites sharing the same webserver.

References


Major credit goes to this post

Thursday, May 27, 2010

Multiple HTTPs websites on one webserver

A long time ago, servers were very slow and a web server could only handle one web site. In time, increased computing power and virtual hosts technology allowed one webserver to handle multiple web sites. At the same time as websites started to share the one webserver, HTTPS was coming to the fore as the defato means of securing web communication from fraudsters, snoopers, and other nefarious trades. Unfortunately, due to the nature of the technologies HTTPS and virtual hosts were not fully compatible.

Virtual hosts work based on host/server names. Examples of hostnames are example.com, eazybusiness.com, and blogger.com. The hostname provided by the client browser determined what one of its web sites the webserver servered to that client.

In order for a website to be able to offer the security of HTTPS, it must provide its own unique SSL certificate. This SSL cert is proof of the site's authenticity, and forms part of the protocol used to secure the communication between a user's browser client and the webserver.

The conflict between virtual hosts and HTTPs lies in the fact that the SSL protocol (the 's' in HTTPs) does not include the need for the client to pass the hostname to the server during initiation. This missing information means that if a webserver hosts multiple websites, each with their own SSL certificate, it does not know which SSL certificate to use.

Recently browsers and websites have starting supporting a technology called SNI. SNI enables the hostname to be included part of the SSL protocol. SNI works, and solves the conflict perfectly, but unfortunately the prevelance of older browsers makes using it impractical.

An alternative solution is to use multi-domain certificates. Using multi-domain certs means that all the web sites sharing a webserver can also share the one certificate. The webserver then only has the one certificate to use, and so does not need to know information about the hostname when establishing an SSL connection. This solution works, but these certificates can be expensive and come with a number of limitations:
  • There is generally a limit to the number of websites a multi-domain cert can accomodate (usually around 100)

  • Multi-domain certificates do not accomodate wild-card certificates


An third possible solution to this problem is to use a proxy, and have each website operate on a different port. My next post will contain a solution to the multiple https websites on Amazon's AWS.

Tuesday, May 25, 2010

Error sending Mail: 501 Syntax: HELO hostname

The exact error I found was:

javax.mail.MessagingException: 501 Syntax: HELO hostname


To fix it I had to set the hostname of the server


hostname $hostname.tld

Grails/Groovy deleting an element in a collection while iterating over it

I ran into a recent problem whereby I wanted to delete a sub-set of the elements in a list after processing all elements in the list. The general gist of the logic was:


apples.each { apple ->
if(apple.red) apple.delete()
}


which resulted in a java.util.ConcurrentModificationException exception.

The solution is to remove the element from the interator before deleting it.


Iterator itr = apples.iterator()
while(itr.hasNext()){
def apple = itr.next()
itr.remove()
apple.delete()
}


For more a little more detail see this thread