Reverse Proxy
This recipie aims to explain what a reverse proxy is, why you may want to use one, and your options for setting one up in the HPC Cloud.
Introduction
A reverse proxy acts as intermediary that forwards the client’s requests to one or more different internal services, like a web server or work press blog.
A reverse proxy, is placed near the server serving client requests. Reverse proxies are used to improve servce security and stability. A reverse proxy may balance load across, cache content from, or simply redirect traffic to a number of servers. In addition we encourage projects to use reverse proxies to conserve the limited number of IPv4 addresses we have available.
Setup
Depending on your requirements, we provide two recipies:
Define a proxy using the OpenStack LoadBalancer service: cloud-native
Manage your own instance running NGINX configured as reverse proxy: classic
Going the cloud native way saves you another VM to manage. The classic route allows you to do TLS termination on the proxy [^1].
Cloud Native
The following steps outline how to test and deploy a reverse proxy using the OpenStack CLI. This setup demonstrates the use of a reverse proxy to expose two hypothetical web servers running in the same private network. Only a single public IP address is required, which can be allocated through the OpenStack dashboard (GUI).
Create private network
Set the following variables to your preferences:
FLOATING_IP="<your-floating-ip>"
DOMAIN_1="www.mysite01.com"
DOMAIN_2="www.mysite02.com"
SUBNET_RANGE="192.168.0.0/28"
Assume that the domain names for the services are registered in DNS.
This is meant to ease the use of the instructions below. You can, of course, enter values in place of using the variables below.
openstack network create priv-net00
openstack subnet create sub-net00 --network priv-net00 \
--subnet-range "${SUBNET_RANGE}" \
--dns-nameserver 130.183.9.32 --dns-nameserver 130.183.1.21
Create the following route to allow access to public internet for instance updates and installation
openstack router create rout00
openstack router set rout00 --external-gateway cloud-public
openstack router add subnet rout00 sub-net00
Create servers
Launch two virtual machines (VMs) within the previously created private network. Each VM should have Apache HTTP Server installed and running to serve as a basic web server for testing the reverse proxy setup. Using the follwing user data script ensures that Apache is installed and running as soon as the VM boots up.
cat <<EOF > web-init.sh
#!/bin/bash
DEBIAN_FRONTEND=noninteractive apt-get update
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
DEBIAN_FRONTEND=noninteractive apt-get install -y apache2
systemctl start apache2
systemctl enable apache2
IP=\$(hostname -I | awk '{print \$1}')
echo "Hallo from my web server: \$IP" | tee /var/www/html/index.html
EOF
Using the following commands to create the VMs
openstack server create server01 --image "Ubuntu 24.04" --flavor mpcdf.small \
--network priv-net00 --security-group web\
--security-group default --user-data web-init.sh
openstack server create server02 --image "Ubuntu 24.04" --flavor mpcdf.small \
--network priv-net00 --security-group web\
--security-group default --user-data web-init.sh
Create Loadbalancer
The reverse proxy is implemented using the OpenStack Load Balancer service (Octavia). This allows incoming requests to a single public IP address to be intelligently routed to the appropriate backend Apache web servers. The load balancer acts as a reverse proxy by distributing traffic based on defined listener rules and pool configurations.
pip install python-octaviaclient
openstack loadbalancer create --name load-bal00\
--vip-subnet-id sub-net00\
--vip-address "${FLOATING_IP}"
openstack loadbalancer show load-bal00
Create Listener
Create a listener on the load balancer to handle incoming HTTP requests. The listener defines the protocol and port on which the load balancer will accept traffic—typically HTTP on port 80. This can be changed accoring to the incoming traffic.
openstack loadbalancer listener create --name http-listener01\
--protocol HTTP --protocol-port 80 load-bal01
Create a pool
Normally, pools are used to group multiple backend servers for redundancy and load distribution. However, in this setup, each pool will contain only a single server to simulate different services behind the reverse proxy.
openstack loadbalancer pool create --name web-pool01\
--lb-algorithm ROUND_ROBIN --loadbalancer load-bal01
openstack loadbalancer pool create --name web-pool02\
--lb-algorithm ROUND_ROBIN --loadbalancer load-bal01
L7 policy
To route traffic to the correct backend server based on the requested URL path, create L7 policies and rules. These policies inspect incoming HTTP requests and redirect them to the appropriate pool based on path patterns.
openstack loadbalancer l7policy create --name l7-mysite01\
--action REDIRECT_TO_POOL --redirect-pool web-pool01 http-listener01
openstack loadbalancer l7policy create --name l7-mysite02\
-action REDIRECT_TO_POOL --redirect-pool web-pool02 http-listener01
openstack loadbalancer l7rule create --type HOST_NAME\
--compare-type EQUAL_TO --value "${DOMAIN_1}" l7-mysite01
openstack loadbalancer l7rule create --type HOST_NAME\
--compare-type EQUAL_TO --value "${DOMAIN_2}" l7-mysite02
Populate the pool
Now that the pools are created, you need to add your web servers (VMs) as members of each pool. Each member represents one of your backend Apache servers that will serve requests.
VM_IP1=$(openstack server show server01 -f value -c addresses | grep -oP "(?<=\[')[^']+(?='\])")
VM_IP2=$(openstack server show server02 -f value -c addresses | grep -oP "(?<=\[')[^']+(?='\])")
openstack loadbalancer member create --subnet-id sub-net00\
--address "${VM_IP1}" --protocol-port 80 web-pool01
openstack loadbalancer member create --subnet-id sub-net00\
--address "${VM_IP1}" --protocol-port 80 web-pool02
Testing
To test the reverse proxy setup and verify that the internal IP addresses of the backend web servers are different while the public IP remains the same, you can open a web browser and go to the corresponding web url. The private IP shows up for both and they are different. This means they all point to different internal servers. You can also verify that both domains point to the same publi IP by using the https://www.nslookup.io/website-to-ip-lookup/.
Classic
Network
First set up a private network: https://docs.mpcdf.mpg.de/doc/cloud/technical/network.html#private-networks-and-routers
For the sake of this example we assume the following:
$NET_NAME
resolves to the network nameSUBNET_NAME
resolves to the subnet name192.168.0.0/24
is the subnet ip range
The network section in the cloud documentation has instructions on how to setup a network.
Create security groups and rules
We create two security groups: one to add to the proxy the other to add to the nodes hosting the applications. Then add a rule allowing all tcp
traffic from the proxy to the machines hosting the applications. Finally add rules to the proxy to restrict traffic as is appropriate for the application, in this case:
Allow
HTTP
(port80
) access from the internet (0.0.0.0/0
) to support certbot certificate verificationAllow
HTTPS
(port443
) from the internet (0.0.0.0/0
) to access our web services from anywhereAllow
SSH
(port22
) from local MPCDF networks (10.0.0.0/8
and130.183.0.0/16
)
# create security groups
openstack security group create apps
openstack security group create proxy
# allow all traffic from proxy to apps
openstack security group rule create apps --remote-group proxy --protocol tcp
# allow access to proxy
openstack security group rule create proxy --remote-ip "0.0.0.0/0" --protocol=tcp --dst-port=80
openstack security group rule create proxy --remote-ip "0.0.0.0/0" --protocol=tcp --dst-port=443
openstack security group rule create proxy --remote-ip "10.0.0.0/8" --protocol=tcp --dst-port=22
openstack security group rule create proxy --remote-ip "130.183.0.0/16" --protocol=tcp --dst-port=22
Network configuration
Now create network ports for your application servers and the proxy and associate the appropriate security groups
openstack port create proxy \
--network $NET_NAME \
--fixed-ip "subnet=$SUBNET_NAME,ip-address=192.168.0.3" \
--security-group=proxy
openstack port create app1.0 \
--network $NET_NAME \
--fixed-ip "subnet=$SUBNET_NAME,ip-address=192.168.0.10" \
--security-group=apps
openstack port create app2.0 \
--network $NET_NAME \
--fixed-ip "subnet=$SUBNET_NAME,ip-address=192.168.0.20" \
--security-group=apps
External access
Get a floating IP from the cloud-public
network and associate it with the port of the proxy port:
openstack floating ip create cloud-public --port proxy
Server Names
You can use the DNS names we provide for your HPC Cloud project [^2]. If you control your own domain add an A
record pointing to the floating IP you assigned to the proxy. You can look up the value of the floating ip from the dashboard or using the openstack CLI:
openstack floating ip list --fixed-ip-address 192.168.0.3
Create the servers
Create the proxy server:
openstack server create proxy --flavor=mpcdf.small --image="Ubuntu 24.04" --key-name=fberg --port=proxy
openstack server create app1.0 --flavor=mpcdf.small --image="Debian 12" --key-name=fberg --port=app1.0
openstack server create app2.0 --flavor=mpcdf.small --image="Debian 12" --key-name=fberg --port=app2.0
Configure the Reverse proxy
SSH into your proxy using the floating IP:
PROXY_IP="$(o floating ip list --fixed-ip-address 192.168.0.3 -f value -c 'Floating IP Address')"
ssh "$PROXY_IP" -l root
Install nginx [^3] and certbot [^4]. The instructions on certbot also show you how to run certbot to get a certificate for the domain you setup. It should configure an entry for the domain in the nginx configuration. Look for an entry with server_name
set to the doamin(s) name you setup above.
From the proxy you can also SSH into the instances running your applications. The following example assumes that your applications will be available at port 80 on their servers.
Single domain
Support you want you applications to be available at https://examnple.com/app1
and https://examnple.com/app2
. When you asked for the certificate certbot should have generated an virtual host entry in the NGINX configuration for server_name example.com
, look for it. It should look something like this:
server {
server_name example.com; # managed by Certbot
root /var/www/html;
...
location / {
...
}
listen 443 ssl; # managed by Certbot
listen [::]:443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/app1.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/app1.example.com/privkey.pem; # managed by Certbot
...
}
Now add two new location blocks, so the server entry looks like this:
server {
server_name example.com; # managed by Certbot
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
....
location / {
...
}
location /app1 {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://192.168.0.10/;
}
location /app2 {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://192.168.0.20/;
}
listen 443 ssl; # managed by Certbot
listen [::]:443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/app1.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/app1.example.com/privkey.pem; # managed by Certbot
...
}
You may also want to diable buffering on the proxy with proxy_buffering off;
. NGINX provides documentation for the many reverse proxy settings[^5][^6]. Don’t touch the lines # managed by Certbot
.
Restart nginx:
systemctl restart nginx
Now going to https://example.com/app1
with your browser will get to the nginx proxy and be redirected to the application running on the server app1.0
. Similarly, https://example.com/app2
will be directed to the application running on the server app2.0
.
Note that this configuration assumed your applications will be available at port 80 on their servers.
Multiple domains
Suppose you have setup A records for your applications called app1.example.com
and app2.example.com
. Each domain will need a virtual host in the NGINX configuration. You should find an entry in your NGINX configuration looking like this:
server {
server_name app1.example.com; # managed by Certbot
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
location / {
...
}
listen 443 ssl; # managed by Certbot
listen [::]:443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/app1.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/app1.example.com/privkey.pem; # managed by Certbot
...
}
Edit the location entry for /
:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://192.168.0.10/;
}
Where 192.168.0.10
is the IP of the instance running app1. For app2, it would then look like this:
server {
server_name app2.example.com; # managed by Certbot
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://192.168.0.20/;
}
listen 443 ssl; # managed by Certbot
listen [::]:443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/app2.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/app2.example.com/privkey.pem; # managed by Certbot
...
}
In this setup I assume you grabbed seperate certificates for your domains. certbot
lets you get one certificate for multiple domains as well by repeating the -d
option. In that case the ssl_certificate
and ssl_certificate_key
entries will look slightly different.
Restart nginx:
systemctl restart nginx
Now going to https://app1.example.com
with your browser will get to the nginx proxy and be redirected to the application running on the server app1.0
. Similarly, https://app2.example.com
will be directed to the application running on the server app2.0
.
Notes
You may also want to diable buffering on the proxy with proxy_buffering off;
. NGINX provides documentation for the many reverse proxy settings[^4][^5]. Don’t touch the lines # managed by Certbot
.
[^1]: wikipedia.org: TLS termination proxy [^2]: docs.mpcdf.mpg.de Automated domain name service [^3]: nginx.org instlalation instructions [^4]: certbot.eff.org instructions [^5]: docs.nginx.com setting up an NGINX reverse proxy [^6]: nginx.com NGINX proxy module parameter reference