Install a Java Web App on Linux
I do this over and over again, so I wrote it down as a reference for future me.



Edit: I wrote this guide in 2016, it is heavily outdated. The information given here is no langer valid.

Get a Linux server

Head over to Hetzner and order one. Virtual servers (vServer) are OK if your hardware requirements are not so high. For this article, I'll use a CX20 with Debian 8.4 (minimal) OS. It comes with 50GB harddisk and 2GB RAM. Usually, it's a matter of minutes till your new server is online. You will receive an email telling you the IP address and the SSH password. You can then 'ssh' into your new account using a SSH client program (I like kitty a lot).


Prepare the system

Somebody might have eavesdropped the email with the root password in it. Therefore you want to change it:

            root:~# passwd
            Enter new UNIX password:
            Retype new UNIX password:
            passwd: password updated successfully
        

Bring your system up-to-date:

            root:~# apt-get update
            root:~# apt-get upgrade
        

Make a (normal) user account 'cv' for yourself (you don't want to be root all the time). You can chose any name you want.

            root:~# useradd -d /home/cv -m -s /bin/bash cv
            root:~# passwd cv
            Enter new UNIX password:
            Retype new UNIX password:
            passwd: password updated successfully
        

A lot of SSH attacks probe the standard port (22), so let's chose a different port, 144 in this case. I usually also disallow SSH root login:

            root:~# cat /etc/ssh/sshd_config
            ---[snip]---
            [...]
            Port 144
            [...]
            PermitRootLogin no
            [...]
            ---[snip]---
        

Add a firewall. Drop all incoming IPv4/v6 packets except ICMP (we still want ping to work) and SSH (port 144).

            root:~# cat /etc/rc.local
            ---[snip]---
            [...]
            /sbin/iptables -F
            /sbin/iptables -P INPUT DROP
            /sbin/iptables -A INPUT -p tcp -m tcp --dport 144 -j ACCEPT
            /sbin/iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
            /sbin/iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
            /sbin/iptables -A INPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
            /sbin/iptables -A INPUT -s 127.0.0.1 -j ACCEPT
            /sbin/iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
            /sbin/iptables -P FORWARD DROP
            /sbin/iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
            /sbin/iptables -P OUTPUT ACCEPT
            
            /sbin/ip6tables -F
            /sbin/ip6tables -P INPUT DROP
            /sbin/ip6tables -A INPUT -p tcp -m tcp --dport 144 -j ACCEPT
            /sbin/ip6tables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
            /sbin/ip6tables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
            /sbin/ip6tables -A INPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
            /sbin/ip6tables -A INPUT -s ::1 -j ACCEPT
            /sbin/ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
            /sbin/ip6tables -P FORWARD DROP
            /sbin/ip6tables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
            /sbin/ip6tables -P OUTPUT ACCEPT
            
            exit 0
            ---[snip]---
        

Reboot

            root:~# reboot
        

Please note: From now on, you cannot login as root anymore, you have to login as a normal user and 'su' if required. Moreover, the SSH port is not 22 anymore, so use port 144 for future logins.


Install java

Unfortunately, we cannot download Oracle Java via wget/curl without some trickery, so we have to download it from their web page and 'scp' it to the server:

            scp -P 144 server-jre-8u92-linux-x64.tar.gz cv@SERVER_IP:~
        

Over at the server:

            cv:~# su
            root:/home/cv# cd /opt
            root:/opt# tar -xf /home/cv/server-jre-8u92-linux-x64.tar.gz
            root:/opt# ln -s /opt/jdk1.8.0_92/jre/bin/java /usr/bin/java
            root:/opt# exit
            cv:~# java -version
            java version "1.8.0_92"
            Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
            Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)
        

Install web app

That depends heavily on how you did your web app (embedded http server, jetty, tomcat, etc.). So, I cannot describe this step. In the end, the web app must be reachable from within localhost, which you can check with curl:

            cv:~# curl http://localhost:8080 > test-v4.html
            cv:~# curl -g -6 http://[::1]:8080 > test-v6.html
        

... must produce some valid html output in test-v4.html and test-v6.html.


Install nginx

Installation is easy

            root:~# apt-get install nginx
        

Note: Debian is known for delivering pretty old releases. So, usually I install/upgrade nginx by hand. But for this tutorial, let's stick with what Debian gives us.

I don't like the 'sites-available'/'sites-enabled' stuff that debian-nginx is configured with out of the box, so let's change that:

            root:~# cd /etc/nginx
            root:/etc/nginx# rm -rf sites-*
            root:/etc/nginx# vim nginx.conf
                ---[snip]---
                ...
                # I don't like it
                # include /etc/nginx/sites-enabled/*
                ...
                ---[snip]---

            root:/etc/nginx# cd conf.d
            root:/etc/nginx/conf.d# cp default.conf default.conf.original
            root:/etc/nginx/conf.d# vim default.conf
                ---[snip]---
                server {
                        listen 443 ssl;
                        listen [::]:443 ssl;
                        server_name  www.myapp.de;
                        ssl on;
                        ssl_certificate      /etc/nginx/conf.d/myapp.crt;
                        ssl_certificate_key  /etc/nginx/conf.d/myapp.key;
                        charset utf-8;
                        client_max_body_size 6m;
                        client_body_buffer_size 2m;
                        proxy_buffer_size 4K;
                        proxy_buffers 8 32k;
                        location / {
                                proxy_pass   http://127.0.0.1:8080;
                        }
                }


                server {
                        listen 80;
                        listen 443;
                        server_name myapp.de;
                        return 301 https://www.myapp.de$request_uri;
                }
                ---[snip]---
        

This will tell nginx to listen on port 80 (http) and 443 (https) at the same time. If a request comes in for port 80 (http), it will be redirected (permanently) to its 443 (https) counterpart. If a request comes in for 'myapp.de' (without the 'www' subdomain), it will be redirected to the 'www' subdomain. See http://www.yes-www.org/ for more information why this is what you want.
Whatever request comes in, nginx will proxy-forward it to http://localhost:8080. This is where your web app is listening. Right now, there is no app listening on port 8080, so nginx will report an error back to a client.
You may have noticed the 'ssl_certificate' and 'ssl_certificate_key' directives. This tells nginx where to find the SSL vertificate and the private key file.

A note on SSL certificates: I'm not quite ready yet for Let's Encrypt (I don't want nobody mess with my server config..). So I still do the old-school way of paying for certificates and managing them myself. For this tutorial, let's pretend the certificate and its key is stored in the mayapp.crt/.key file. Let's also pretend the CN of the CERT is 'my-app.com', and 'www.my-app.com'

Let's protect the private key file, only root should be able to read it.

            root:/etc/nginx/conf.d# chmod 600 myapp.key
        

Now restart nginx.

            root:/etc/nginx/conf.d# service nginx restart
        

Test it

So, that's it. If all went well you should be able to access your web app from the outside world via https://SERVER_IP/. You might also register a domain and tinker with its DNS A-record, so that you can access your web app via https://www.my-app.com/.

2016-05-25