Categories
Linux

How to run multiple webservers with one IP-address

With only one public IP-address you’ll have to use different ports if you want to use multiple webservers. But that’s kind of useless as you can’t ask users to type example.com:81 in the browser instead of just example.com. So how do we solve this problem?

Use a reverse proxy

By directing all traffic on port 81(HTTP) and 443(HTTPS) to a server running a reverse-proxy you’ll be able to choose what server the traffic should be forwarded to based on the host header that the browser sends with the request.

Graphic illustrating how we're gonna use HAProxy to run multiple servers with just one public IP-address.
How we’re gonna use HAProxy to solve our problem.

There is two popular proxy options: HAProxy and NGINX. In this guide we’ll be using HAProxy on Ubuntu 18.04. We will also set up Lets Encrypt to handle TLS-certificates.

Install HAProxy

HAProxy is easy to install, so let’s get to it.

root@tops-lamprey:~# sudo apt update
root@tops-lamprey:~# sudo apt dist-upgrade
root@tops-lamprey:~# sudo apt install haproxy

So now we have haproxy installed and it’s time to configure it.

Configuring HAProxy

In this setup we have two webservers each hosting a seperate website. Server 1 with the IP-address 10.0.0.10 and Server 2 with the IP-address 10.0.0.20. Server 1 hosts example1.com, while Server 2 hosts example2.com. Now it’s time to configure it.

Open the /etc/haproxy/haproxy.cfg file and add a frontend and three backends to the bottom of the file like this:

frontend http_front
        bind :443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
        bind :80
        default_backend web_1

        redirect scheme https code 301 if !{ ssl_fc }

        # Use the web_encrypt backend if this is a request for domain verification.
        acl is_letsencrypt path_beg -i /.well-known/acme-challenge/
        use_backend web_encrypt if is_letsencrypt

        # Decide on what host to use
        acl is_example1 hdr_end(host) -i example1.com
        acl is_example2 hdr_end(host) -i example2.com

        use_backend web_1 if is_example1
        use_backend web_2 if is_example2

backend web_1
        option forwardfor
        server server1 10.0.0.10:80

backend web_2
        option forwardfor
        server server2 10.0.0.20:80

backend web_encrypt
        server letsencrypt 127.0.0.1:8888

Currently we have set the web_1 backend as the default, but you can set it to whatever fits you.

Get ready for certificates and add a default one

You’ll need a certificate folder that HAProxy can check for certificates, so let’s create one:

root@tops-lamprey:~# sudo mkdir /etc/haproxy/certs

HAProxy needs a certificate in that folder to start and we don’t have any yet. Therefore we just create a self-signed one. You can just press enter on all fields:

root@tops-lamprey:~# sudo openssl genrsa -out domain.key 2048 &&
sudo openssl req -new -key domain.key -out domain.csr &&
openssl x509 -req -days 3650 -in domain.csr -signkey domain.key -out domain.crt &&
sudo cat domain.key domain.crt >> /etc/haproxy/certs/aaaaa.default.pem &&
rm domain.crt domain.csr domain.key

Now you can restart HAProxy like this:

root@tops-lamprey:~# sudo systemctl restart haproxy

And then access the IP of the server running HAProxy in the browser to check that you get a certificate warning.

Firefox certificate warning.
Did you get a warning? Then everything is going as planned.

Create valid certificates

The proxy host will also run Let’s Encrypt certbot to create and renew certificates. Since the proxy will handle all TLS/HTTPS traffic for you, you’ll have to make sure the backends are using HTTP.

First off, let’s install Let’s Encrypt:

root@tops-lamprey:~# sudo apt-get install software-properties-common && sudo add-apt-repository universe && sudo add-apt-repository ppa:certbot/certbot && sudo apt-get update && sudo apt-get install certbot

Then create a script that will be used to move new and renewed certificates into the /etc/haproxy/certs/ folder. The script also needs to reload HAProxy to make use of the new certificates when they change.

Create /root/renew.sh with the following content:

#!/bin/sh

set -e

for domain in $RENEWED_DOMAINS; do
        TARGET_FILE="/etc/haproxy/certs/${domain}.pem"
        cat "/etc/letsencrypt/live/$domain/fullchain.pem" \
        "/etc/letsencrypt/live/$domain/privkey.pem" > $TARGET_FILE
        chmod 400 $TARGET_FILE
done

systemctl reload haproxy.service

Then make it executable:

root@tops-lamprey:~# sudo chmod +x /root/renew.sh

Make sure this script is used when ceritificates are renewed by editing /lib/systemd/system/certbot.service like this:

[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/python-certbot-doc/html/index.html
Documentation=https://letsencrypt.readthedocs.io/en/latest/
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew --deploy-hook /root/renew.sh
PrivateTmp=true

Set up DNS

Make sure that both example1.com and example2.com points to the IP-address of HAProxy. It can take some time for the changes to come into effect, so give it some time to update.

Get valid certificates

Now you just have to get the certificates. Remember to replace <your email> with your own email:

root@tops-lamprey:~# sudo certbot certonly --standalone --http-01-port 8888 --preferred-challenges "http-01" --agree-tos -m <your email> -d example1.com --deploy-hook /root/renew.sh
root@tops-lamprey:~# certbot certonly --standalone --http-01-port 8888 --preferred-challenges "http-01" --agree-tos -m <your email> -d example2.com --deploy-hook /root/renew.sh

Congratulations, you now have two webservers behind the same IP-address.

2 replies on “How to run multiple webservers with one IP-address”

Thanks for your great article. Could you clarify where haproxy should be installed? In server 1, server 2 or in another separated server ?

Vinn

Either of those options will work if the goal is simply to use HAProxy as the entry point and then direct traffic to the current server(s).

Leave a Reply

Your email address will not be published. Required fields are marked *