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 createdsudo 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 canonicalwww
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.