UP | HOME

Exposing a web-server (e.g. JuptyerHub) on a Multipass virtual machine

[2021-07-14 Wed]

This will demonstrate how a web-server running within a Multipass virtual machine (guest) can be made visible on the IP address of the Linux host. This uses iptables configured via ufw rules. Note, that I have no idea on how safe this setup is, use at your own peril!

Setup

I set this up on a Ubuntu 20.04 LTS host system, although other Linux distros should work too. Note that as I did this not on a fresh install of Ubuntu, I maybe missing some bits. But I think this is what you'd do for the basic setup:

$ sudo apt update
$ sudo apt upgrade

# install and setup ufw (if not installed already)
$ sudo apt install ufw
$ sudo ufw allow http
$ sudo ufw allow https
$ sudo ufw limit ssh # don't forget this, otherwise you'll lock yourself out
$ yes | sudo ufw enable

Note that this setup uses the ufw firewall which is a frontend for iptables. It simply sets the iptable rules. If you use something else, e.g. iptables straight, you should still be able to follow this.

Multipass setup

Multipass is an easy way to spin up virtual machines (guests). It installs via snap:

$ snap install multipass
$ multipass launch -m2G -c2 -n "tljh" lts
$ multipass shell tljh

This launches a Ubuntu LTS guest system (the lts option) with 2GB of memory and 2 CPUs with the name tljh. The last command then opens a shell within the guest.

Guest setup

Within the guest, install The Littlest JupyterHub (or any other web-server for that matter). I use my setup on https://github.com/mauro3/JupyterHubWithJulia for this:

## install TLJH via https://github.com/mauro3/JupyterHubWithJulia
git clone https://github.com/mauro3/JupyterHubWithJulia.git
## get a config-file
git clone https://gist.github.com/b2a548771163d27dfca005f2bcc7030b.git JupyterHubWithJulia-def-settings
cd JupyterHubWithJulia
cp ../JupyterHubWithJulia-def-settings/settings_tljh_julia.sh .
chmod u+x settings_tljh_julia.sh
# nano settings_tljh_julia.sh # update as needed (should be ok as is)
sudo ./tljh_install.sh

After some time passed… this now launched a JupyterHub server on the guest (note that I disabled the Julia installation for this demo).

Now the networking setup on the host

The JupyterHub-server on the guest is now merrily serving the JupyterHub on the host-machine internal IP address which was assigned at guest creation. This IP-address can be checked with

$ multipass list

let's assume for the following that the IP is 1.2.3.4. Note: re-setting up the mutipass guest will change the assigned ip address!

If you point a browser launched on your host system to 1.2.3.4, it will show the JupyterHub login page (you can login with admin and a password of your choosing).

But how can we forward this web-server, such that it can be accessed from external machines?

The setup described below follows this forum-post; other potentially useful links are 1 and 2.

Misc setup

First we need to allow IPv4 traffic forwarding on the host. Uncomment the line

net.ipv4.ip_forward=1

in /etc/sysctl.conf.

To reload this file run

$ sudo sysctl -p

Check that cat /proc/sys/net/ipv4/ip_forward returns 1.

Iptables setup

With iptables the following will setup (on the host) the forwards the traffic to ports 443 and 80 to the guest. This assumes that your outgoing network connection is called eth1, but do find out by running ip addr. Execute on the host:

$ sudo iptables -t nat -I PREROUTING 1 -i eth1 -p tcp --dport 443 -j DNAT --to-destination 1.2.3.4:443
$ sudo iptables -I FORWARD 1 -p tcp -d 1.2.3.4 --dport 443 -j ACCEPT

$ sudo iptables -t nat -I PREROUTING 1 -i eth1 -p tcp --dport 80 -j DNAT --to-destination 1.2.3.4:80
$ sudo iptables -I FORWARD 1 -p tcp -d 1.2.3.4 --dport 80 -j ACCEPT

Note that this adds rules for both http and https.

You can test now whether this works by pointing a browser running on another computer at your host-computer (either at its IP or at its domain-name if it has one). You should see the Jupyterhub login (or whatever web-page the guest is serving).

Note, this change is not permanent. It can be made permanent via a bunch of different methods; here I use ufw.

UFW setup

For this you need to edit the file /etc/ufw/before.rules (on the host). You need to insert this at the top of the file:

#############
# Forward HTTP traffic to multipass virtual-machine instance
# https://discourse.ubuntu.com/t/multipass-port-forwarding-with-iptables/18741
# Note that the IP address will change when re-doing the multipass instance.
*nat
:PREROUTING ACCEPT [0:0]
-I PREROUTING 1 -i eth1 -p tcp --dport 443 -j DNAT --to-destination 1.2.3.4:443
-I PREROUTING 1 -i eth1 -p tcp --dport 80  -j DNAT --to-destination 1.2.3.4:80
COMMIT
##############

Then at the top of the existing *filter section add this:

##############
# Also needed to forward HTTP traffic to multipass virtual-machine instance
-I FORWARD 1 -p tcp -d 1.2.3.4 --dport 443 -j ACCEPT
-I FORWARD 1 -p tcp -d 1.2.3.4 --dport 80  -j ACCEPT
###########

Reload ufw with

$ sudo ufw reload

Done! Probably best to reboot the machine to check whether all is working as is intended: everything setup here should start running automatically after a reboot.