Saturday 14 December 2013

Students behind a firewall? Nginx to the rescue!

I teach web development on-line. Students login into an AWS virtual machine (using SSH), write the code and then view their website over the default development HTTP port - 3000. It's quick and easy. Nothing to install and the students can focus on what they're trying to learn rather than getting bogged down in some quirk of their particular installation.

A few weeks ago Ludo who's been doing beta testing for me expressed frustration that he couldn't do the exercises from work because their firewall was blocking access to the outside world on ports 22 (SSH) and 3000. I gave this problem some thought but put it on hold while preparing for my third annual web development class at the London Perl Workshop hosted by the University of Westminster.

The big day came and I walked into a lab with over thirty eager students (more than one per terminal) expecting to write code, only to discover that a few weeks earlier the university had decided to put the same firewall constraints in place. A four hour workshop turned into a one hour lecture and it was the teacher who learnt his lesson that day!

Here's my reverse proxy solution where the student can both login and view their website from behind a firewall.

The solution is to use HTTP over port 80 for both the website and the terminal. Because port 80 is the default port for HTTP requests, it is the port which is least likely to be blocked. I performed the following steps on an AWS Ubuntu AMI but this will easily translate to any Ubuntu box.

First, an overview of the building blocks:
  • Shell In a Box: This provides a website (on port 4200 by default) delivering some very clever JavaScript and CSS making the visitor's browser into a shell terminal.
  • A web framework: This is up to you. 'Web framework' means anything which can be used to implement a website. I was using Perl's new kid on the block - the Dancer web framework which by default listens on port 3000.
  • Nginx: A reverse proxy web server, this will be setup to listen on port 80. The request http://<hostname>/terminal will be passed through to http://<hostname>:4200/ while every other request will be passed to port 3000. This means that the student's exercises can be anything as long as they don't involve implementing a url of the form http://<hostname>/terminal

Install Shell In a Box

Login and as root and run

# apt-get install shellinabox

By default Shell in a Box now has HTTPS on port 4200 but I only want to use HTTP (see Reflections below)

# /etc/init.d/shellinabox stop

Now edit this script:

# vim /etc/init.d/shellinabox

and add this line under 'Set some default values'

SHELLINABOX_ARGS="--localhost-only --disable-ssl"

Note that 'localhost-only' means that you can only access it via the reverse proxy - omit it if you want to visit http://<hostname>:4200/

Now start shellinabox to activate your changes

# /etc/init.d/shellinabox start

Install Dancer

Now if I were putting together a serious application I'd be doing a proper installation of CPAN and Perlbrew the way Gabor Szabo explains here but in this tutorial I'm doing it the quick and dirty way to avoid distraction from the goal which is understanding the reverse proxy setup.

$ sudo apt-get install libdancer-perl

Build a demo application

$ dancer -a MyWeb::App

Run it

$ cd MyWeb-App/
$ ./bin/

and visit http://<hostname>:3000/ to see that it's working.

Install Nginx

As root:

# apt-get install nginx
# sudo rm /etc/nginx/sites-enabled/default
# sudo vim /etc/nginx/sites-available/geekuni.conf

and add the lines:

server {
  proxy_set_header Host $http_host;
  proxy_set_header X-Forwarded-Host $http_host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

  location /terminal/ {
    proxy_pass http://localhost:4200/;

  location / {
    proxy_pass http://localhost:3000/;

Finally login as root and enable geekuni.conf as the Nginx config, and run it:

# ln -s /etc/nginx/sites-available/geekuni.conf \
# /etc/init.d/nginx stop
# /etc/init.d/nginx start


Shell In a Box: if you want to use it reliably across all browsers, you should get a more up-to-date version than the one in the Ubuntu repository. That involves installing it manually from here

Security: I used HTTP since with HTTPS the students would see an alarming security alert like this when they want to login:

I don't want to buy a new SSL certificate every time a student starts an AWS instance, so any ideas on a way to use HTTPS and avoid this security alert would be greatly appreciated!

1 comment:

  1. Update: The original ShellInABox repo is no longer maintained but it's being actively maintained here