This is the third part of a step-by-step guide for Java Web application developers
wanting to get their feet wet in the Amazon cloud. Here are the links to
Part I and
Part II, just in case.
Better Init Script
If you have installed the "official" Tomcat from the Amazon Linux repo,
you are already covered - skip this section.
The System V init scripts should follow the guidelines
described in /usr/share/doc/initscripts-version/sysvinitfiles.
At a minimum, the "bare bones" script presented in
should be enhanced to use a lock file, so as to facilitate graceful shutdowns
and correct transition between runlevels.
There are also some helper functions
in /etc/init.d/functions that the script could have utilized.
Some features of the Tomcat start/stop script, catalina.sh,
such as waiting for the process to end, rely on the use of a PID file.
In an instance that you will be running several instances of Tomcat on
a single EC2 instance :), PID files would enable you to quickly
map processes to (Tomcat) instances.
PID files normally reside in /var/run,
which is only writable by the superuser.
But you are running Tomcat as an unprivileged user, aren't you?
Therefore you need the init script to create the PID file and give
ownership to that unprivileged user before starting Tomcat…
echo "Starting Tomcat:"
# Fix pid file permissions
touch /var/run/tomcat.pid 2>&1 || RETVAL="4"
if [ "$RETVAL" -eq "0" -a "$?" -eq "0" ]; then
chown tomcat:tomcat /var/run/tomcat.pid
/bin/su -s /bin/sh - tomcat -c /opt/tomcat/bin/startup.sh
Now stop Tomcat and add the following line to /opt/tomcat/bin/setenv.sh:
Even More Sophisticated
The init script included in the official Amazon Linux Tomcat package is
over 300 lines long. It is way more configurable than my script above.
In particular, the “official” script makes running multiple
instances of Tomcat easier.
If you want to peek into that script without installing the package,
download and extract it manually:
Your instance is up and running, but if you stop and start it again,
you'll notice that its Public DNS changes.
This happens because an instance's public IP address is allocated dynamically
from a pool during launch and returned to the pool when you stop or terminate
Should your instance or Availability Zone fail, dynamic DNS changes will
take hours to propagate. Enter another elastic entity - Elastic IP address.
In Amazon EC2, an Elastic IP address is a static IP address associated
with your account, rather than with a particular instance.
To designate a particular instance to serve requests coming from the Internet,
you map one of your Elastic IP addresses to that instance.
An Elastic IP address does not cost you extra money when it is mapped to
an instance. Otherwise it incurs a small hourly fee, so do not leave your
Elastic IPs unmapped!
To allocate an Elastic IP address and map it to your freshly
Select Elastic IPs in your AWS Management Console navigation bar
and click the Allocate New Address button:
A confirmation dialog will appear. Click Yes, Allocate:
Select the newly allocated Elastic IP address and click the
Associate Address toolbar button:
Select your instance from the dropdown list and click Yes, Associate:
Go back to My Instances. You will notice that the instance's public DNS
has changed to match the Elastic IP address:
From now on, use the Elastic IP address or the updated public DNS
to access your instance. They will survive instance stops and failures.
If you prefer to use a symbolic hostname, add an entry to your
local hosts file or create DNS records as necessary.
Speaking about DNS, if your Web application will be sending email messages
to third-parties, you will need to inform Amazon about that and
request them to create reverse DNS records for your Elastic IP address
so as to reduce the chance of those messages being flagged as spam.
Here is the link to Request to Remove Email Sending Limitations
Running Tomcat on port 80
Tomcat often sits behind an HTTP server such as Apache, which
serves static content and proxies requests for dynamic content to
Tomcat. Another popular option is to use squid as a reverse proxy.
If for any reason you want Tomcat to serve all HTTP requests,
you need it to listen on port 80 (and possibly 443).
However, only the superuser can bind to TCP ports below 1024 on Linux,
so making Tomcat listen on port 80 requires some extra work.
The easiest-to-implement solution is to simply forward incoming port 80 requests
to port 8080, or whatever non-privileged port you are running Tomcat on.
The Amazon Linux AMI has iptables enabled by default,
but does not have any packet filtering rules defined as it apparently relies
on the surrounding AWS infrastructure for security.
Check that Tomcat is running and have your browser connect to your instance
without specifying a port. If you succeed, save iptables rules:
sudo /sbin/service iptables save
The rules are stored in /etc/sysconfig/iptables and applied
upon iptables (re)start, e.g. if you reboot the instance.
Note: The above rule applies to all packets arriving from
outside to any network interface. If you are running any applications
on the same instance that need to talk to Tomcat on the HTTP port,
you need to add another rule.
Finally, you need the servlets inside your Web application
to act as if the incoming requests were directed to port 80.
This will prevent the appearance of the non-privileged port
in any URLs sent back to the client.
Include the proxyPort attribute in your HTTP
connector config in server.xml:
<Connector port="8080" proxyPort="80" .../>
If you do not want to touch iptables, or if your boss is under a false
perception that it adversely affects performance, your other options are:
Run Tomcat as root. This is not a good security practice.
Good news: you don't have to build it from source, as Amazon Linux already
includes the binary:
sudo yum install jakarta-commons-daemon-jsvc
Bad news: installation of that package won't update the Tomcat startup script
/etc/init.d/tomcat6, so you'll have to figure it out yourself.
jsvc is also known to have issues with shutdowns and restarts.
Use authbind or
Problem is, they are not included in RHEL/CentOS and hence Amazon Linux, which
is a derivative of RHEL.
Set the CAP_NET_BIND_SERVICE capability on the java launcher.
I hope you have gathered some useful information from this series.
Now time has come for a vendor plug. I'll begin with an imaginary dialog with
Q: Why should I bother with Java-to-native compilation? The performance
of my app on the conventional JRE is good enough!
A: It significantly increases the resistance of your application code
to reverse engineering and tampering.
Q: But why would anyone want to protect the code of a Web application,
which does not run on end users' computers?
A: First, if you think that people who do not completely trust
their Web hosting or cloud provider's perimeter security are overly
paranoid, read this recent
by German resarchers who've found security holes in Amazon control interfaces. And then think again.
"A successful attack on a Cloud control interface grants the attacker
a complete power over the victim's account, with all the stored data included."
— All Your Clouds are Belong to Us
Then there is a particular use case: public AMIs.
If you have created a Web app that your customers run in their own
Amazon clouds, it is natural to deliver it as a custom AMI. Now,
if you want to let your prospects "see for themselves", i.e.
try your Web app against their data before making a purchase decision,
you better make sure they cannot easily tamper with it, e.g. to remove
Now if you want to see for yourself, it takes just a few easy steps:
Download an evaluation copy of
Excelsior JET for Linux and install it on a Linux
box. (If you develop in the cloud, and do not have a Linux machine handy,
you may need a larger instance with 2GB of memory or more.)
Compile your Tomcat Web applications to native code
and export them as a self-contained directory.
You may follow the video tutorial,
just make sure to select Self-contained directory as a backend.
Package the resulting directory into a single archive:
tar cvfz archive.tgz native-Tomcat-directory
Copy the archive to your Amazon EC2 instance, for example using scp:
Now you can access your natively compiled Web applications at your
EC2 instance's public DNS. Note that the JRE installed on your instance
is not used, because the package includes the Excelsior JET Runtime.