Linux Containers: Part 7, HTTP and HTTPS Routing

Its been a little bit since I touched on the topic of LXC containers but I want to discuss something else involving those. We’ve set up an OpenVPN container and were able to connect to it as well as have a secure connection, but what if we could also host numerous containers each with their own website?

This was the original reason why I explored containers. I purchased a KVM (mostly work with VPSes) and I toyed with OpenVZ but it didn’t fit my needs for what I needed. After doing some research I tried LXC and fell in love with it. This brought me to exploring different aspects, one of which is creating a container for each website I want to host.

This can be thought of similar to virtual hosts in Apache and other web hosts, where you tell the software to point a request to a specific container. However, unlike both virtual hosting and the OpenVPN guide, each website will have its own internal IP and we don’t need to play with iptables. What we are going to do is install nginx on the host machine, and then create a basic Ubuntu template and set up Apache inside of that. I’ll assume you’ve read the previous articles so if you haven’t, we’ll be drawing some attention from those into this. Fair warning.

Why nginx?
Personally I love using nginx as a proxy. Its very easy and durable while not adding a lot of overhead like Apache does. Another viable option could be lighttpd but I’ve lost a lot of interest in that as a useful web server. If you want to use it though then awesome. This guide will focus on nginx though.

Setting Up the Host
The host machine I use is Ubuntu, so if you’re using a Debian-based system you’ll be fine. This part is easy since we only need to do two steps, lets install nginx first.

Code:

sudo apt-get install nginx

It’ll install nginx and the needed files and start the web server. We’ll configure it later though. Lets work on the container now.

Code:

sudo lxc-create -n website1 -t ubuntu

This will create a container named “website1” using the “ubuntu” template. Depending on how lucky you are it could take a short amount of time and you’re right into the console of it or it’ll have to download the ISO and set it up. Either way exit out of the container’s console (log in and run the shutdown command as root), then we’ll start the container back up in daemon mode:

Code:

lxc-start -n website1 -d

Configuring the Container
This just makes life easier for us. Next go back into the console for the new container and log in (ubuntu:ubuntu). Once you do that install any web server you fancy. You can also install any other software you’ll need like database server, but we’ll just focus on the web server as everything else should be localhost-bound anyways.

By default the web server will be listening on port 80 which is fine for this. If you change it though make note of what you change it to as we’ll need it soon enough (for sake of this guide lets just figure it stays at 80).

Next thing we need to do is get the IP of the container (the private IP that is). Run ifconfig or ip addr and note down what the IP is for eth0. For this guide we’ll say its 10.8.0.42. Exit out of the console (ctrl+a then ctrl+q) to return back to the host.

Setting Up nginx
We’re almost there! All we need to do now is set up nginx. Browse to /etc/nginx/sites-enabled/ there you should see a symlink’ed file “default”. This is the default page nginx shows when you browse to the host’s IP address. Delete everything inside of there and place this in there instead:

If you’ve used Apache or other web servers as a proxy this should make sense to you. If not, the basic idea is to tell nginx to listen on port 80 and if the requested domain is “website1.tld” (our “website1” container in this case), and trying to reach any location, we send the request to our container’s web server instead and let it process the request. The “proxy_set_header” lines are kind of there for convenience but should be added for any scripts/uses that might come up. It basically allows you set the IP of the visitor instead of the main machine (i.e.: for me getting the remote address would show 10.8.0.1 instead of their real IP).

Save the file and reload nginx:

Code:

service nginx reload

If all goes well browse to website1.tld (after pointing it to your host’s public IP) and you should see your website instead of the default nginx one.

Handing HTTP and HTTPS Traffic
We’re able to browse HTTP traffic but what about HTTPS? Well, unfortunately its not as easy as just routing it to the container’s HTTPS port (443 in this case). While the web server inside of the container will need the SSL certificate stuff, so will nginx. There’s also a few additions we need ot make to our server block (separate from the one above but placed in the same file...so there’ll be two server {} portions). These changes are made below:

ssl on - Tells nginx that we want HTTPS (SSL) support enabled for these connections

ssl_certificate / ssl_certificate_key - This is to let nginx process SSL requests. These will also need to be used by the container’s web server so you can just store them once and reference them via the full path in nginx like ssl_certificate_key is

add_header Front-End-Https on - Not going to lie not sure why this needs to be set or what it does but it needed to be there for HTTPS to route correctly

proxy_set_header X-Forwarded-proto https - Needed so the container’s web server sees it as HTTPS traffic as well (don’t know, again another one that needed to be set)

To be fair I’m not a pro at nginx or understand most of the settings, but I do know these settings work.