Reverse Proxy with SSL
Stephen Hyde
Posted on August 14, 2022
Introduction
It's often useful to make an application under local development available temporarily on the public internet. At Blended Edge, for example, our work with automating integrations means we run a lot of OAuth2 flows, and using OAuth2 for an application under development (usually) requires a publicly available endpoint with an SSL certificate.
Ngrok is a reverse proxy tool that makes this very simple, but it's also fairly easy to set up a reverse proxy using an AWS EC2 instance, certbot from Let's Encrypt, and the ssh
command.
This walk-through assumes some basic knowledge of DNS, ssh
, and EC2 instances (or similar cloud compute resources).
Create a Remote Server
We're going to start by launching an EC2 instance on AWS to act as our remote server. This could of course be done with any cloud provider, such as Azure or Digital Ocean.
Set the instance name as "Reverse-Proxy", and under "Application and OS Images" select Ubuntu. At the time of writing this the default Ubuntu image is 22.04.
I have an existing SSH key pair that I will use for accessing this instance, but if you don't have one or would like to use a new key pair simply click "Create new key pair".
Create a new security group that allows HTTP/HTTPS traffic from any IP address, but limit the SSH traffic to just your own IP.
Click "Launch Instance", navigate to the instances overview page, and wait for the status checks on the new instance to pass. This may take a couple of minutes.
Once the instance is ready, go to the overview page for the instance and click "Connect" in the upper right hand corner. Follow the steps to connect.
# if this is a new ssh key you may need to specify the path to the key
# ssh -i /path/to/key.pem <user>@<address>
ssh ubuntu@ec2-13-59-152-133.us-east-2.compute.amazonaws.com
Install Nginx
Update the package list, upgrade the packages, and install nginx.
sudo apt update
sudo apt upgrade -y
sudo apt install nginx
If you send a GET request to your instance's IP address via a web browser or curl you should get the default nginx page.
curl http://13.59.152.133
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
Domain and SSL
Point a domain to the public IP address of your instance. The nameservers for my domain hydewd.com
are with DigitalOcean, so in my case updating the A
record to point the domain at my instance looks like this:
Note: If you are doing any kind of serious or long term work with this setup you should allocate an elastic IP address, associate it with your instance, and point your domain to the elastic IP.
Install certbot
from Let's Encrypt to handle automatically setting up the SSL certificate.
sudo apt install certbot python3-certbot-nginx -y
Now run have certbot
make the request for an SSL certificate. Here I am specifying that I want to cover both the naked domain hydewd.com
as well as www.hydewd.com
. You will be prompted to provide an email address for renewal notices, asked to agree to the terms of service, and asked if you are willing to share your email with the Electronic Frontier Foundation (this last one is optional).
sudo certbot --nginx -d hydewd.com -d www.hydewd.com
If everything has worked you should now be able to load the default nginx page at https://<your-domain>
.
Nginx Configuration
The next step is to tell nginx to redirect http/https requests to another port (8080
). In the following step we will tunnel that port to our local machine.
sudo vi /etc/nginx/sites-available/default
Within the location
block of the default server
block, add a line to forward traffic to http://127.0.0.1:8080
.
We will be changing the contents of the location
block. Here is what it looks like to begin with:
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
Change the contents of the location
block to:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:8080/;
}
Save the file, and ensure that the configuration is valid with the command sudo nginx -t
. If there is no error, go ahead and restart nginx to apply the latest changes.
sudo nginx -s reload
SSH Remote Forwarding
The final step is to tunnel the requests from the server's port 8080
to the local machine. This can be done with the ssh
remote forwarding command.
ssh -R 8080:localhost:8080 ubuntu@ec2-18-217-182-155.us-east-2.compute.amazonaws.com
Now run a server locally on port 8080
. Here, for example, is a simple node server using the express framework that will reply to all GET
requests with some information about what the server sees in the incoming request.
mkdir local-server && cd local-server
npm init -y
npm install express
Create a file called index.js
with the following contents:
const express = require('express')
const app = express()
const port = 8080;
app.get('*', (req, res) => {
const { baseUrl, headers, hostname, ip, method, originalUrl, path, protocol } = req;
res.json({
baseUrl,
headers,
hostname,
ip,
method,
originalUrl,
path,
protocol
})
});
app.listen(port, () => console.log(`App listening at http://localhost:${port}`));
node index.js
Sending a query to your domain will now return a response from your local express server.
curl -s 'https://www.hydewd.com/some/path?some=query&another=123'
Example response:
{
"baseUrl": "",
"headers": {
"host": "www.hydewd.com",
"x-real-ip": "104.1.2.3",
"connection": "close",
"user-agent": "curl/7.68.0",
"accept": "*/*"
},
"hostname": "www.hydewd.com",
"ip": "::ffff:127.0.0.1",
"method": "GET",
"originalUrl": "/some/path?some=query&another=123",
"path": "/some/path",
"protocol": "http"
}
Note that the header x-real-ip
displays the IP address of the end user making the request, not the IP of the instance. This is a result of the configuration line proxy_set_header X-Real-IP $remote_addr;
.
Conclusion
This approach will work for some basic use cases, but is overall quite limiting. In a future post we will look at using Traefik to provide a more feature rich setup.
Helpful Links
Digital Ocean Guides: How to Install Nginx
Nginx Docs: Reverse Proxy Documentation
Nginx Docs: Installing Lets Encrypt
Posted on August 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.