TL;DR: If you want to go straight to the tutorial click here.

What is a Reverse Proxy?

When a client makes a request to a server, the server responds with the requested data. This simple client/server model is the basis of the internet. This paradigm is also known as the request/response model. As the internet has grown, so has the complexity of the requests and responses.

A reverse proxy is a server that sits between the client and the origin server (the place where the content is located). It receives requests from the client and forwards them to the origin server. The origin server then responds to the request and the reverse proxy forwards the response back to the client. This allows the reverse proxy to act as a middleman between the client and the origin server. Learn more about reverse proxies here.

Why Would You Want to Use a Reverse Proxy?

There are many reasons you would want to use a reverse proxy/server. Some of the most common reasons are:

Security and Performance

A reverse proxy acts as a intermediate step the client goes through before reaching the origin server. The reverse proxy can act as a sudo-firewall between the client and the origin server and can block bad actors from taking down site by a DDoS attack.

It does this by both obscuring the origin server’s IP Address and by caching the content of the site on the reverse proxies servers. That content is loaded from the reverse proxy server before . This reduces strain on the origin servers CPU, memory and network bandwidth, ultimately reducing monthly hosting cost, because the origin server is insulated from direct contact with the client.

A reverse proxy is not a replacement for a firewall. It is an additional layer of security that can be used in conjunction with a firewall. It is still important to correctly configure your network and individual servers to be secure.

Easy Exposure of Internal Tools

A reverse proxy can also be used to expose internal tools. If a business has a tool (an internal wiki or intranet) that is only accessible from within the company’s network, a reverse proxy can be used to expose that tool to the public internet with secure authentication. This is useful if you:

  1. Want to share information with people outside of your company, but don’t want to give them access to your internal network. You can grant granular access to only specific tools like a file server.
  2. Want to share internal tools with employees who are working remotely.

Easy Development, Configuration and Deployment

The best reason to use a reverse proxy is that it makes development, configuration and deployment of your website/application much easier. In the past, developers had to configure their web server to serve content from a specific directory, to a specific port, using a specific service. This was a tedious process that took a lot of time and required a lot of manual configuration.

This was solved in part by the automation and containerisation of web servers. Couple containerisation programs like Docker with reverse proxies and you have a powerful tool for development, configuration and deployment. Now developers can simply have multiple containers on one single server configure the reverse proxy to serve that container on a specific port, on a specific domain on one server.

Why Cloudflare?

Reverse proxies are great in theory but in order to be actually useful, it requires scale. If you are a small business or a personal website, you don’t have the resources to set up an effective reverse proxy. That’s where Cloudflare comes in.

Cloudflare is a company that provides a suite of services to help make websites faster and more secure. One of the main reasons many like it is because even though it is a for-profit company focused on enterprise offerings, it has an extremely feature-rich free tier. This free tier is more than enough for most personal websites and small businesses. It’s reverse proxy service (Called Cloudflare Tunnel) is part of their Zero Trust Platform and is free to use.

While Cloudflare Zero Trust will not cost anything to set up and use it does require you to provide a credit card in order to use the service. You will not be charged unless you register more than 50 people to your Organisation

What is Zero Trust?

Zero Trust is a security model that is based on the principle of “never trust, always verify”. Cloudflare Zero Trust is geared towards corporations aiming to secure their internal network. It is a suite of tools that allows you to create a secure network that can be accessed from anywhere. Learn more about Zero Trust here.

This tutorial only is only going to cover setting up and securing a Cloudflare Tunnel, connecting your server/computer to the Cloudflare Network and exposing it to the internet. Cloudflare Zero Trust is way more expansive. If you want to learn more about the other features of Cloudflare Zero Trust, check out their documentation.

What is Cloudflare Tunnel?

Cloudflare tunnel is a piece of software that is installed on the server/computer you want to expose to the internet. It creates a secure connection between your server/computer and the Cloudflare Network. This allows you to expose your server/computer to the internet without having to configure your network or server to be exposed to the internet.

Prerequisites

This tutorial assumes you have already got the following:

  1. A Cloudflare account (FREE), sign up here

  2. A domain name that you want to use to access application on your server/computer from (like conorjwryan.com)

  3. A server that you want to expose to the internet

  4. A credit/debit card (required to use Cloudflare Zero Trust)

You must have admin privileges in order to use Cloudflare Tunnel as we need to install software

This tutorial is specifically about exposing a server to the internet using Cloudflare Tunnel. You can use your computer, but keep in mind it is important to do so securely. A misconfiguration can result in your entire network being exposed. Be careful!

Tutorial Server Setup

In this tutorial I am going to be using a server running Ubuntu 22.04 with Docker installed. I am going to use Docker to run the following:

  1. A Ghost site (on port 3001)

    This is to simulate a website that you want to expose to the internet like a blog.

    This can work easily with a WordPress site too, but I am using Ghost because it is a lot easier to set up and run for the sake of this tutorial.

  2. A Portainer instance (on Port 9433)

    This is to simulate a sensitive internal tool that only you want access to.

How to Setup a Cloudflare Tunnel

In order to set up a Cloudflare Tunnel we first need to setup the general Cloudflare Zero Trust configuration. This allows us to configure and maintain our Tunnel settings.

Setting Up Cloudflare Zero Trust

When you log into Cloudflare you’ll be greeted with the Dashboard, which has your Domains (called Zones) in the centre and a sidebar to the left.

Make sure before we continue that you have at least one Zone added to your account. You can find out how to do so here: Adding a Zone.

Click on the Zero Trust link in the sidebar. It will ask you to Choose your team name. This can be the name of your organisation or something unique to you. I’ve called mine conorjwryan-example. The idea is that <your-name>.cloudflareaccess.com is a central place people in your Organisation can access your tools in one place.

You’ll then be directed to choose a pricing plan. For this tutorial we are going to be using the free plan. Press Select Plan under the Free tier and press Continue. You’ll be asked to confirm your choice. Press Proceed to Payment.

Input your payment details (credit card or PayPal) and your billing address. After this is complete, you’ll be brought to the Zero Trust dashboard where we can now create a new Tunnel.

Setting Up a Cloudflare Tunnel

Now it’s time to set up the Cloudflare Tunnel. This can be done on the terminal but for this tutorial we are going to use the online Tunnel creation wizard.

You can also create and configure a tunnel via the terminal. You can find out how to set up a Tunnel using the terminal here: Cloudflare Tunnel Docs. This tutorial is going to use the online wizard as it is easier to follow along with.

From the Zero Trust dashboard, click on the Tunnels tab. This is where you can configure your Tunnels.

Click on the Create Tunnel button. You’ll be asked to give your Tunnel a name. I’ve called mine test-tunnel, but for yours it can either be the name of the server or network server is on e.g. office-server-tunnel or home-tunnel.

After you have named the tunnel, press Save tunnel. You’ll then be asked to Choose your environment. This is the system that you’re going to install the tunnel software on. For this tutorial I am using Ubuntu so will choose Debian and then 64-bit as the architecture.

It’ll then present you with 2 gray boxes. These are terminal commands that we put into our server to install the Cloudflare Tunnel (named cloudflared). The left box is for those that do not have the cloudflared software installed (a few extra steps) and the right box is for those that do. I do not have cloudflared installed so I will click to copy the commands in the left box.

Paste these into your terminal and it will look something like this:

To check that the installation was successful go back to the Tunnel configuration page and scroll down to the very bottom under the Connectors section. It should say Connected under Status.

With the tunnel created and working correctly we can now press Next to begin exposing our server’s ports to the internet.

Configuring a Cloudflare Tunnel

After you press Next you’ll be taken to the last section where it wants you to Route Traffic to your tunnel. This is where you can configure Public Hostnames or Private Networks to be exposed to the internet. Since this is our first time after creating the tunnel create our first Public Hostname.

After doing this initial setup of one instance then you’re able to add others.

In my case I want to expose my Ghost blog to the internet. This is running on port 3001 on my server, and I want it to be at ghost.conorjwryan.com.To do this I will input ghost in the Subdomain field and choose conorjwryan.com from the dropdown menu. Under Service Type I select HTTP and under URL I input http://localhost:3001.

The URL refers to the internal address relative to the server. So if you’re service is running locally you have to put localhost in the URL. If it’s running on another server you have to put the IP address of that server, like 192.168.0.45:3001.

After you have inputted the details press Save tunnel. You’ll be brought back to the Tunnel configuration page. To check the tunnel works I go to ghost.conorjwryan.com and it should load my Ghost blog.

It works! And because it’s Cloudflare it also provides our site with a SSL certificate. However, this does not in any way mean that our site is secure! It just means that the connection between the client and the Cloudflare server is secure. The connection between the Cloudflare server and our server is still insecure. This is better illustrated in dealing with internal tools, which I shall do next.

Adding Internal Tools

Now that the first Public Hostname has been added it redirects us back to the Tunnel configuration page. From here we can add more Public Hostnames to route our internal services to. In our case we need to add our Portainer Docker management tool.

First we press Add a public hostname and then input the details as we did before with the Ghost Blog. I want my Portainer site to be reached at portainer.conorjwryan.com and it’s running on port 9000 on my server.

All we need do is add the details in the same way we did for the Ghost site:

Having pressed Save hostname we should now be able to navigate to portainer.conorjwryan.com and see our Portainer site.

It works! BUT this internal tool is now accessible to anyone on the internet. As this is an internal tool we need to secure it. Cloudflare allows us to do this by creating an Access policy.

Securing with Access

CLoudflare Access allows us to create policies that restrict unauthorised people our internal tools. We can create policies that allow access to certain users, groups, or even IP addresses. The idea is that when someone visits our internal tool (like portainer.conorjwryan.com) they will be prompted to authenticate / login through Cloudflare Access before progressing to the internal tool.

We can then customise how this looks or even if they have to authenticate at all. An example where this would be applicable is where you wouldn’t want people having to authenticate with `Cloudflare`` unnecessarily if they’re accessing the internal tool from the internal network for example…

Having made the sites available over the internet it’s now important to secure them. On the Tunnel dashboard go to the Access tab on the left-hand side and go to ‘Applications’ and then ‘Create an Application’.

From there we press Create an Application. We are then presented with a choice of Application Types. In our case we want to choose Self Hosted as we want to secure our Portainer site.

It is worth noting that you can protect any domain on your account with Cloudflare Access, it does not need to be done through Cloudflare Tunnel exclusively.

With Self-hosted selected we are taken to the Configure app page. On this page we have to focus on two sections: Application Configuration and Identity providers.

In the Application Configuration section we need to write the name of the Application, in my case Portainer and choose a Session Duration of 1 week

Session Duration refers to the amount of time you are authenticated in Cloudflare Access before having to log back in. It is not the same being logged into the Application, but rather how often the user is prompted / re-prompted for access.

You can choose any time between Expires Immediately (where a person would need to login every time they access the site), up to a every month. It is dependant on the type of tool and the company security posture which you choose but as a nice middle ground between security and convenience I shall choose 1 Week.

It’s also important to input the same Domain information as we did when we set up the tunnel. In my case it was portainer.cjri.uk.

Now we have set up the basics of the Cloudflare Access Application now we need to scroll down to the bottom of the page where Identity providers section is located. Identity Providers are how the potential users interact with Cloudflare Access Authentication in order to determine how they get access to the Application.

The default way provided for Identity Verification is through the use of a One-time PIN which is automatically configured. When the user goes to portainer.conorjwryan.com they are greeted with a login screen where they are asked to submit their email. This email is then checked against Access List Policies (set up on the next page) to determine if they should get access. If they meet the criteria then they are emailed a link / code for access. Dependant on the Session Duration defined above is how often they have to reauthenticate by this method.

For now these settings don’t matter as much becauseOne-time PIN should be the ony option for new accounts but as you you were to add more Identity providers (like Microsoft Active Directory or Okta) keep One-time PIN selected to follow along with this tutorial.

With the app basics configured we can move onto the next stage which is configuring our Application Access Policies which determine who has access to our application (and by extension who does not).

The first step is to give this policy a name. This should relate to the people or groups involved. It is a good idea to separate different groups into different policies (which you can add later). Normally you would have a policy for administrator users and one for other users.

Each policy can determine what criteria those users have to meet to use the Application as well as specific Session Durations for each group. You can have specific policies which also block if conditions are met (good for extra security). For now because it is only me accessing this.

Below this section it might ask you to select a group, this is useful if you’re configuring many Applications and will save in configuration time. You would have different Groups of different people or IP Address ranges like the example.

For now just scroll past this until you see Create additional rules section. This is where we will be doing simple, adhoc rules for this singular policy.

Our first and only rule will be to add our email address so only we have access to the application. We do this by choosing Emails under the Selector heading and under the value, we input the emails we want to be able to access the application. To add others, simply separate the emails by the use of a comma ,

Alternatively if this was a general use internal tool like a company intranet site or knowledge portal we could change the Emails Selector to Emails ending in and type the our domain like @conorjwryan.com to allow all people with a conorjwryan.com domain to access the Application.

Progressing to the last page in this wizard we just need to change one setting, and that is to Enable automatic cloudflared authentication which helps enacting our chosen rules and Session Duration.

That is it! Now our Application is correctly configured we can press Add application in the bottom right and we’re done. We will automatically be taken to the Applications Dashboard and we should be able to see our newly created Access Application

Now that our application is configured we can go to portainer.conorjwryan.com and see that we are greeted with the following logon page:

It works! Now we need to input our email and press Send me a code. Because we added our email to the Access Policy we should be able to get an email delivered to our inbox.

This email normally arrives in a few seconds but can take up to a minute. If you do not receive an email from Cloudflare check your Spam folder. Otherwise you have misconfigured your Access Policy.

We have an email from Cloudflare, now we can click the link or type in the code.

Success! We have correctly configured Cloudflare Access to protect our internal applications from the unauthorised people. Only those authorised can access the tool remotely. We can now do this to any number of tools / websites, we need to.

There is one more thing to consider, however the topic of HTTPS / SSL and secure connections to our Application.

Ensuring Connections are Secure (HTTPS)

As noted previously, while we have a secure connection between the client and Cloudflare Tunnel we do not have a secure connection between Cloudflare Tunnel and our Application. This is because we are using HTTP and not HTTPS.

Theoretically this leaves us vulnerable if someone was to intercept the connection between Cloudflare Tunnel and our Application. They could see the data being sent between the two and potentially steal sensitive information.

We can illustrate the problem by changing our Tunnel Public Hostname portainer.conorjwryan.com settings to use the secure port of our application (9443), from our current insecure port (9000).

If we then go onto portainer.conorjwryan.com we are greeted with the following error:

Our Application Portainer is now telling us that we are using an insecure connection to our secure connection. This is because we are using HTTP and not HTTPS.

You might say this is a simple fix “We can fix this by changing the Service from HTTP to HTTPS, simple!” This would change our Tunnel connection from to our Application from http://localhost:9000 to https://localhost:9443.

However, if we do this we are greeted with the following error:

How do we fix this? We need to use a valid certificate. We can do this by using Cloudflare Origin Certificates.

Error 502

We get this error because although we are connecting via HTTPS on a secure port (9443), there is no way that Cloudflare Tunnel can verify that connection. Our application Portainer has a self-signed certificate (created during installation) but this is not legitimate and Cloudflare shows the error as a precautionary measure as there is a high likelihood the connection is compromised / under attack.

This verification is done by the presence of an officially signed certificate for our domain that our Application uses to show the Cloudflare that the connection is legitimate and secure.

There are two ways we can approach this error: Ignore it or fix it.

Ignore 502 Error

There is no way that we can ignore this error from the client side as it’s a security risk but we can tell Cloudflare Tunnel to ignore the error. This has to be done on the `Public Hostname Page`` settings for our tool.

Underneath where the Service is set there is the Advanced application settings section. Here we go to expand the TLS section. We then turn off can set the TLS Client Authentication to off. This will tell Cloudflare Tunnel to ignore the error and allow the connection to be made.

After we are finished we can press Save hostname in the bottom right of the page.

If we navigate to portainer.conorjwryan.com we see that we can use the site as normal:

It works, the 502 Error is now gone. This again does not mean our Application is fully secure but that we have chosen to ignore the message, our users won’t know any different.

This is not recommended as it leaves our connection vulnerable to attack. Use this at your own risk or only during development, never in production.

In order to fully fix this error we need to use a valid certificate. We can do this by using Cloudflare Origin Certificates.

Proper Configuration of Application HTTPS/TLS Connections

To truly fix this error we need to get the application to present the right certificate to the Cloudflare Tunnel so that it can verify the connection is secure. Each application has a different way of doing this, either it is done during setup of the container/image/configuration file or it is done after the face in the web interface.

In this specific case I am going to show you how to do this with Portainer via the web interface.

Other applications will have different ways of doing this but the principle is the same.

Navigating to the application portainer.conorjwryan.com and logging we need to go to Settings. In the case of Portainer this is on the left-side menu. We scroll down the page until we find the SSL certificate section:

As seen from the image above we need two files in oder for our Application to work with Cloudflare Tunnel:

An SSL Certificate and an SSL Key.

We can get these from Cloudflare they are called Origin Certificates.

First, we need to navigate back to our main Cloudflare Dashboard:

1
https://dash.cloudflare.com/

Once we are there we need to click on our domain, or Zone (as they are called in Cloudflare):

In my case I select conorjwryan.com:

From there we need to go to the SSL/TLS section in the left-hand menu. From there we need to go to the Origin Server section and click Create Certificate:

From there we need to fill out the form:

We need to make sure the Generate private key and CSR with Cloudflare is selected. We also need to make sure that the Private key format is set to RSA (2048).

For the list of hostnames we need to add the apex domain and the subdomain we are using for our Application:

1
2
conorjwryan.com
portainer.conorjwryan.com

If you are using a different subdomain you need to add that instead of portainer.conorjwryan.com.

While the default use of the wildcard domain *.conorjwryan.com would work, it is not recommended as it is less secure and it is harder to differentiate in the list on the dashboard.

15 years is the default certificate validity and is fine for our purposes.

Press Create and you’ll be directed to the following page:

We need to copy the Private Key and the Origin Certificate into separate files. You can use the Copy to clipboard button to copy the certificates into a text editor like notepad, test editor, or nano.

Make sure you copy the Private Key and the Origin Certificate into separate files

Once we have the Private Key and the Origin Certificate we need to go back to our Application web interface and add the files to the correct fields like so:

Now we can press Save and now we can go back to our Cloudflare Tunnel and turn off the No TLS Verify option:

We also need to add the subdomain of our Application to the Origin Server Name field. In my case I put portainer.conorjwryan.com.

We go back to our web browser and refresh the page:

Conclusion

During this tutorial we have learned how to use Cloudflare Tunnel to expose a local Docker container to the internet securely. We have learned how do this specifically with internal tools such as Portainer, which require Cloudflare Access to be configured to allow only authorised people access to the application.

We have also learned how to use Cloudflare to generate Origin Certificates for our Application in order to use a secure HTTPS connection and stop the 502 Error from occurring. Using the lessons learned in this post you can safely and securely expose your containerised applications to the internet.

Until next time,
Conor