Deploying Play Framework to a VPS

This post will describe how to deploy a Play Framework application to a Rackspace virtual cloud server.

What is covered

  • Deploying to a Ubuntu 12.04 LTS Rackspace virtual cloud server
  • Basic security
  • Automaticaly starting the Play app
  • Using Nginx as a reverse proxy

Create an Ubuntu 12.04 LTS virtual cloud server

As of writing 12.04 is the latest Ubuntu LTS release.

From the Rackspace Control panel create a new virtual cloud server selecting Ubuntu 12.04.

For a small site a 512Mb ram instance is a good starting point.

SSH on to the server

When your server is ready you should be given the ip address, username and password.

Connect to the server using SSH replacing IP_ADDRESS with the ip address of the server.

Linux / Mac users

Open a terminal window and type the below:

ssh root@IP_ADDRESS

Window users

Download Putty SSH client.

Install system updates

After deploying a new server the first thing you should do is install the latest system package and security updates.

Run the below commands

apt-get update
apt-get upgrade
reboot

Firewall configuration

After installing the latest system package and security updates you should configure the firewall to restrict access to services on the server.

Install UFW

UFW is the Ubuntu firewall configuration tool.

apt-get install ufw

Allow HTTP inbound traffic

ufw enable http

Allow SSH inbound traffic

ufw allow from 0.0.0.0/0 to any port 22

If you have a static IP address you want to access the server from you can replace 0.0.0.0/0 with your static IP for extra security.

Enable UFW

Ensure that you have enabled SSH access first before enabling UFW or else you will not be able to access your server once enabled using SSH.

ufw enable

Disable root logins

Next we will disable directly logging in as root. This is important as most brute force / dictionary attacks are against the root account. Dissabling the root account mitagates this.

Create an admin group

By default the Rackspace Ubuntu 12.04 image does not create the admin group.

groupadd admin

Create a new user account for the user you want to log in as:

adduser myusername

Complete the questions:

Adding user `myusername'...
Adding new group `myusername'(1000) ...
Adding new user `myusername'(1000) with group `admin' ...
Creating home directory `/home/myusername'...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for myusername
Enter the new value, or press ENTER for the default
    Full Name []: MyUserName
    Room Number []:
    Work Phone []:
    Home Phone []:
    Other []:
Is the information correct? [Y/n] Y

Add the created user to the admin group:

usermod -a -G admin myusername

Update the sudoers file

visudo

Verify the below line exists:

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

Update the SSH configuration

vi /etc/ssh/sshd_config

Find the PermitRootLogin option and change yes to no.

PermitRootLogin yes

At the bottom of the file add the below, make sure to use the same username created above.

AllowUsers myusername

Restart SSH

service sshd restart

When SSH has restarted open a new SSH session without closing the current one using the new user you created. This is to verify the configuration is working. If you cannot login using your new user account you can check the configuration again in your existing session.

Passwordless logins

Next modify the SSH configuration to disable password based logins and to use keys instead. This prevents brute force password attacks.

Create a key pair

ssh-keygen -t rsa

Window users: Download PuTTYgen to create a key pair.

Upload public key

scp ~/.ssh/id_rsa.pub myusername@server_ip_address:~

Window users: Download WinSCP to upload the key you created.

Update authorized_keys

Log in to the server and run the below commands to set up the ssh authorized_keys file.

mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ./ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Add your uploaded key to the authorized_keys file.

cat ~/id_rsa.pub >> ~/.ssh/authorized_keys

Verify log in using the key

Log out of the server and log back in again. You should no longer be asked for your password unless you created one for the key you created.

Update sshd config

Update the ssh config to disable password logins.

sudo vi /etc/ssh/sshd_config

Make sure the below options are set to no.

ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no

Restart SSH.

sudo service ssh reload

Install DenyHosts

DenyHosts DenyHosts monitors failed SSH login attempts and then blocks the ip.

sudo apt-get install denyhosts

If you access the server with a static ip address add it to /etc/hosts.allow to prevent you IP accidentaly being blocked.

Install Nginx

Nginx will be used as a reverse proxy for the Play application.

Using a reverse proxy infront of the application provides some benefits:

  • If you need to deploy a new version of the application you can use Nginx to show a maintenace page for a better user experince
  • Nginx is more efficient for serving and caching static assets.
  • Nginx can be used as a load balancer
  • You can host multiple applications on a single server accessable from the same port
  • You can use Nginx access logging
  • Terminating SSL

Install Nginx

sudo apt-get install nginx

Disable server tokens

Modify the /etc/nginx/nginx.conf file disabling server_tokens. This disables the version of Nginx used in response headers which can be used to get a list of known vulnerabilities.

user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
 
events {
  worker_connections 768;
}
 
http {
  ##
  # Basic Settings
  ##
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;
  server_tokens off;
 
  ...
}

Restart Nginx:

sudo service nginx restart

Deploy the Play application

Install Java

Play requires a JVM to run. OpenJDK will be used as it’s simpler to install than Oracle’s due to Oracle’s licence.

sudo apt-get install openjdk-7-jre

Create a Play distribution

From the Play project run:

play dist

Upload the Play distribution

Replace the filename, username and server ip as required.

scp dist/myapp-1.0-SNAPSHOT.zip username@SERVER_IP:~

Set up a runtime environment

Create the below directory where the Play applications will run from.

sudo mkdir /var/play

Extract the uploaded zip file to the /var/play directory.

unzip -x ~/myapp-1.0-SNAPSHOT.zip -d /var/play

The zip contains a file named start which needs to be executable.

sudo chmod +x /var/play/myapp-1.0-SNAPSHOT/start

Create a symbolic link to the app. This will allow to do roll backs by pointing the symbolic link to different versions in the /var/play directory.

ln -s /var/play/myapp-1.0-SNAPSHOT /var/play/myapp

Create a user for the app to run as

It is best practive to let the app run as a non privalaged user.

sudo groupadd myappname
sudo useradd -g myappname -d /var/play/myapp-1.0 myappname
sudo chown -h myappname:myappname /var/play/myappname
sudo chown -HR myappname:myappname /var/play/myappname

Note:

  • sudo chown -h myappname:myappname myappname changes the group / owner of the symlink we created
  • sudo chown -h myappname:myappname myappnam changes the group / owner of the destination the symlink points to

Create an upstart job to automatically start the app

sudo vi /etc/init/myappname.conf

Paste in the below. Update the USER, GROUP, HOME variables to point to the user values created previously.

description "My Play Application"
 
env USER=
env GROUP=
env APP_HOME=
env APP_NAME=
env PORT=80
env BIND_ADDRESS=0.0.0.0
 
env EXTRA=""
 
start on (filesystem and net-device-up IFACE=lo)
stop on runlevel [!2345]
 
respawn
respawn limit 30 10
umask 022
expect daemon
 
pre-start script
    #If improper shutdown and the PID file is left on disk delete it so we can start again
    if [ -f $APP_HOME/RUNNING_PID ] && ! ps -p `cat $APP_HOME/RUNNING_PID` > /dev/null ; then
        rm $APP_HOME/RUNNING_PID ;
    fi
end script
 
exec start-stop-daemon \
  --pidfile ${APP_HOME}/RUNNING_PID \
  --chdir ${APP_HOME} \
  --chuid $USER:$GROUP \
  --exec ${APP_HOME}/bin/$APP_NAME \
  --background --start -- -Dhttp.port=$PORT -Dhttp.address=$BIND_ADDRESS $EXTRA

Start the application

sudo service myappname start

If successful the below will be displayed:

myappname start/running, process 1743

Verify it is running by using the displayed pid, 1743.

ps aux | grep 1743

A message similar to below should be displayed:

1001      1743  9.2 22.5 1387028 112872 ?      Sl   21:15   0:05 java -Dhttp.port=9000 -Dhttp.address=127.0.0.1

Nginx VHOST configuration

The below is based on the site being visiable at http://myappname.com and http://www.myappname.com. Update as required.

Create the /etc/nginx/sites-available/default/myappname.com

vi /etc/nginx/sites-available/myappname.com

Paste in the below configuration

server {
  gzip  on;
  gzip_http_version 1.0;
  gzip_vary on;
  gzip_comp_level 6;
  gzip_proxied any;
  gzip_types text/plain text/css text/javascript application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss;
 
  server_name myappname.com www.myappname.com;
 
  location = /robots.txt  { access_log off; log_not_found off; }
  location = /favicon.ico { access_log off; log_not_found off; expires max; }
 
  if ($host = myappname.com) {
    rewrite ^/(.*)$ http://www.myappname.com/$1 permanent;
  }
 
  location / {
    proxy_set_header Host $http_host;
    proxy_pass http://127.0.0.1:9000;
  }
 
}

Note:

  • requests to myappname.com are set to 301 redirect to the canonical www hostname for SEO purposes
  • Nginx is configured to automaticaly gzip responses from the Play application
  • logs are disabled for robots.txt and favicon.ico to reduce log noise

Restart Nginx

sudo service nginx reload

Update DNS

The last step is to update the DNS A records for myappname.com and www.myappname.com to point to the new server to access the site.