Monday, December 28, 2020

How to deploy a single instance SSL-enabled Tomcat application on AWS Elastic Beanstalk

The Problem

I run a simple low-traffic website hosted on Elastic Beanstalk and I did not want to pay for the $15-$20 / month for an auto-scaled environment, which requires an elastic load balancer -- hence the higher cost. Instead, I can run a single EC2 instance and cut my costs in half from ~$30 to ~$15. 

The catch is that by dropping the elastic load balancer, you also lose the ability to terminate the SSL (HTTPS) connection and the ability to use Amazon's free SSL certificate service. So what do you do? 


The Solution

Summary

We can extend our Elastic Beanstalk application hosting using the EB Extensions features. The following changes will tell Elastic Beanstalk to:

1. get a free SSL certificate from Letsencrypt.org using one of their tools (certbot-auto) which is downloaded and executed after the instance is set up, but before Apache HTTP server is started. The SSL certificates will be stored in /etc/letsencrypt/live folder. The SSL certificate will be valid for 3 months, but you can renew it as often as you need (see step 4). 

2. Configure Apache to use those SSL certificates and start listening on port 443 for SSL connections. 

3. Updated the security group for the EC2 instance is updated to allow traffic from the internet to port 443. 

4. Schedule a cron job to run every month to renew the certificate


Step 1. Create .ebextensions Folder 

By including a new folder in the top level of the WAR file called .ebextensions with the following files:




If you are using maven, you need to add the following bit to your pom.xml to get the .ebextension directory to be bundled in the right place in the war:


Step 2. Configure Maven war plugin


<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-war-plugin</artifactId>

<version>3.3.1</version>

<configuration>

<webResources>

<resource>

<directory>src/main/ebextensions</directory>

<targetPath>.ebextensions</targetPath>

<filtering>true</filtering>

</resource>

</webResources>

</configuration>

</plugin>


Step 3. Add ssl.conf File

This file will update the Apache configuration to look at the SSL certificates generated by letsencrypt.org. Update the domain (in red) for your domain name. 

Listen 443

<VirtualHost *:443> 

  ServerName mydomain.com

  SSLEngine on 

  SSLCertificateFile    "/etc/letsencrypt/live/www.mydomain.com/fullchain.pem"

  SSLCertificateKeyFile "/etc/letsencrypt/live/www.mydomain.com/privkey.pem"


  <Proxy *> 

    Require all granted 

  </Proxy> 

  ProxyPass / http://localhost:8080/ retry=0 

  ProxyPassReverse / http://localhost:8080/ 

  ProxyPreserveHost on 


  ErrorLog /var/log/httpd/elasticbeanstalk-ssl-error_log 


</VirtualHost>



Step 4. Add http-instance-single.config File

This file will update the security group resource to allow traffic in port 443 (HTTPS Port) and it will also configure the necessary command to run to get the SSL certificates from letsencrypt.org. Make sure you update the configuration in red for your own email and domain name. 


Resources:

  sslSecurityGroupIngress: 

    Type: AWS::EC2::SecurityGroupIngress

    Properties:

      GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}

      IpProtocol: tcp

      ToPort: 443

      FromPort: 443

      CidrIp: 0.0.0.0/0

      

commands:

  00_getSSLcertificate: 

    command: | 

      sudo service httpd stop

      mkdir -p /opt/certbot

      wget https://dl.eff.org/certbot-auto -O /opt/certbot/certbot-auto;chmod a+x /opt/certbot/certbot-auto

      sudo mkdir -p /var/www/challenge

      sudo /opt/certbot/certbot-auto certonly --debug --non-interactive --email myEmail@gmail.com --agree-tos --standalone --domains www.mydomain.com --keep-until-expiring -w /var/www/challenge

  10_renewSSLCertificate:

    command: '(crontab -l ; echo ''0 6 * * * root /opt/certbot/certbot-auto renew --standalone --pre-hook "service httpd stop" --post-hook "service httpd start" --force-renew'') | crontab -'

    



Troubleshooting

1. Make sure your domain name is pointing to the Elastic beanstalk environment that you are changing with this single instance. If you don't want to experiment on your live website, you can create a test subdomain like (testssl.mydomain.com) and use that to experiment on until you know you have the right configuration in place to update your real website. 

2. View the logs in AWS Console or SSH into the instance and view the logs in /var/log/eb-activity.log
tail -f /var/log/eb-activity.log
That log should contain the errors for both letsencrypt as well as anything specific to apache. 

3. Execute the commands on your own. If the automated solution does not work, you can SSH insto the instance and execute the command on your own to see what may not work. If you find that you need to add a new command, add it to the commands block in the http-instance-single.config file. Then build and deploy your application again. 

4. In order for letsencrypt utility to work, it needs to start its own HTTP web server to validate that you own the domain for which it is issuing a certificate. To do this certbot-auto will need to bind itself to port 80 and it cannot do this if something else is running on that port. In this case you will see an error like "Cannot bind to port 80 ...". The solution is to stop any services that run on that port. Hence, the first command above is sudo service httpd stop which stops the Apache http service. If you have another type of service (e.g. nginx) on your Elastic Beanstalk configuration, you need to make sure that service is stopped before certbot-auto is executed. 





Sunday, December 30, 2012

How I doubled my Internet Speed

Recently I got an email from Comcast that they have doubled my internet speed, but in order to access it, I had to get a new cable modem that supported docsis 3.0. It sounded a bit too good to be true, but I thought I would give it a try since I had been using the same cable modem for over 7 years. Before buying a new modem, I decided to test my speed at speedtest.net and it came out to be 20Mbps.

Then I searched on gooogle to see if my old modem, the Motorola sb5100 supports Docsis 3.0 and sure enough Comcast was right, it did not.

Then, I looked at the Comcast approved device list to find a cable modem that supported the new speed tier.


Tuesday, September 4, 2012

Eclipse SVN bad_record_mac

If you are running eclipse and the SVN plugin (Subversive) is showing the following error, continue reading for the fix.

Problem:
Some resources were not updated.
svn: E175002: Received fatal alert: bad_record_mac
svn: E175002: PROPFIND request failed on '/svn/remote-remind/trunk'

Fix:
1. Add the following line to the eclipse.ini file in your eclipse installation directory.
-Dsvnkit.http.sslProtocols=SSLv3  
2. Restart eclipse.
3. Repeat the same operation as before and it should complete without any errrors.

The problem is caused by a bug in SVNKit which is the connecting software layer between Subversive and SVN. If you want more details about the bug, read more at 
http://issues.tmatesoft.com/issue/SVNKIT-176

Monday, June 4, 2012

Eclipse Java Template for Apache Commons Logging

If you use Eclipse and want to save some typing, here is a template for Eclipse to generate an Apache Commons Logging Log for the class you are currently working on.


${:import(org.apache.commons.logging.Log,org.apache.commons.logging.LogFactory)}
private static final Log LOG = LogFactory.getLog(${primary_type_name}.class);


To use it, open preferences > java > editor > templates, create a new template with the name "log" and paste the code above in the Pattern field.

Eclipse Slf4j Logger Template

If you use eclipse and want to save some typing, here is a template for Eclipse to generate an Slf4j Logger for the class you are currently working on.

    ${:import(org.slf4j.Logger,org.slf4j.LoggerFactory)}
        private static final Logger LOG = LoggerFactory.getLogger(${primary_type_name}.class);

To use it, open preferences > java > editor > templates, create a new template with the name "log" and paste the code above in the Pattern field.

Thursday, March 29, 2012

BodyEngine.com BMI Calculator

I use BodyEngine.com a lot and they recently added a BMI Calculator widget. I am adding it here for convenience. If you want to get the code for your own website, go to their Free Weight Calculators page.


Powered by BMI Calculator

Monday, February 6, 2012

Transport error: 415 Error: Cannot process the message because the content type ...


Today I ran into the following error while using an Axis2 Java Client to consume a WCF Soap Service.

Caused by: org.apache.axis2.AxisFault: Transport error: 415 Error: Cannot process the message because the content type 'application/soap+xml; charset=UTF-8; action="http://myservice.com/mySoapService/"' was not the expected type 'text/xml; charset=utf-8'.

This problem is caused because the WCF SOAP service is using SOAP 1.1 while the Axis2 Client defaults to SOAP 1.2

The fix is to set the Soap Version URI in the Axis2 ServiceClient options as follows:


stub._getServiceClient()
       .getOptions()
       .setSoapVersionURI(
                   SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);

Wednesday, October 26, 2011

BodyEngine.com

Being a software developer, it is very easy to sit down in front of the computer for hours at a time and live a very sedentary lifestyle. I started looking for a way to get back into shape and a friend of mine suggested an app called BodyEngine. The app is very slick and easy to use -- I can appreciate good software when I see it. With only a few clicks I was able to see that I am currently overweight by 5 lb. I can also see how many calories I need to eat and how much to exercise to reach my goal weight of 175 lb. The app is free, so give it a try to see in what category you fall and create your own personalized fitness plan.  http://www.bodyengine.com

Saturday, August 27, 2011

How to ignore self-signed ssl certificate verification in maven svn scm


I was trying to prepare a release for a maven project with the command
mvn release:prepare but the maven scm plugin could not commit the updated pom file because the certificate is self-signed.
 
My setup is Window 7, Collabnet SVN Client 1.6.6, Maven 2.2.1 but the solution is not specific to Windows or collabnet svn client.


...
[INFO] Checking in modified POMs...
[INFO] Executing: cmd.exe /X /C "svn --non-interactive commit --file C:\Users\myname\AppData\Local\Temp\maven-scm-1429998188.commit --targets C:\Users\myname\AppData\Local\Temp\maven-scm-1933937231031554007-targets"
[INFO] Working directory: G:\Projects\my-project\
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Unable to commit files
Provider message:
The svn command failed.
Command output:
svn: Commit failed (details follow):
svn: OPTIONS of 'https://svn.mysite.com/my-project/trunk/': Server certificate verification failed: issuer is not trusted (https://svn.mysite.com)
...

...read on to see how to get around this problem...


Wednesday, May 18, 2011

Test-Driven Development - Are you doing it right?

Recently I have been doing test-driven development (TDD) exclusively in both Java and C#, and I have made great progress in doing it the right way. I was fortunate to learn the right way from Uncle Bob who my employer invited for a TDD in-house training course for our engineering team.

So how do you know that you are doing it right?

Here are the signs that you are on the right path:
  • You are writing more tests than ever before 
  • Your test coverage is creeping up to > 90%
  • You create a test class before creating the class under test
  • You write a failing unit test before making any code changes
  • Your test classes are longer (more lines) than the class under test
  • You start to feel itchy when you write a line of code that is not making a test pass
  • You are more confident in the code you are writing
  • You are refactoring your tests
  • You are not deploying and debugging every time you change the code
  • You are debugging tests rather than production code
Doing TTD is not easy and requires that you stop following old habits and force yourself to do it properly for a few weeks. For me it was a slow off-and-on process, but as I do more and more of it, I find that I am getting my development speed back, with an added boost of confidence provided by over 90% code coverage. Yes, I have tests to prove it!