Install a Java web application on a Linux server, starting from zero.
I do this over and over again, so I wrote it down as a reference for future me.


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. I usually go with my initials.
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:~# vim /etc/ssh/sshd_config
    ---[snip]---
    [...]
    Port 144
    [...]
    PermitRootLogin no
    [...]
    ---[snip]---

Add a firewall.
root:~# vim /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]---
This tells the kernel to drop all incoming IPv4/v6 packets except ICMP (we still want ping to work) and SSH (port 144).

Reboot
root:~# reboot

Please note: From now on, you cannot login as root anymore, you have to login as 'cv' 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