Feb 20th, 2025 - Alexander Mattoni, Head of Engineering

Securing Private Network Access with Cloudflare Tunnel

For many, Cloudflare provides an essential suite of network security tools that their organization integrates with deeply. One such tool is "Cloudflare Tunnel " - a secure way to connect resources to Cloudflare without a publicly routable IP address.

When deploying sensitive applications to Cycle, Cloudflare Tunnel can be leveraged to ensure all traffic is routed through Cloudflare before reaching your environment's load balancer. This extra layer of security provides quite a few benefits:

  • Less susceptible to attacks that would attempt to bypass Cloudflare and target load balancers/applications directly
  • DDoS Mitigation
  • Zero-Trust access without requiring a VPN
  • Potentially better integration with existing organization infrastructure

Prerequisites

In order to configure Cloudflare Tunnel for use with an environment on Cycle, you'll need to set up the following:

  1. A Cloudflare account - Cloudflare Tunnel itself is free, though some advanced features may be paid
  2. A DNS zone configured at Cloudflare
  3. A Cycle account with a hub configured

In this guide, we'll be using the Cycle Portal to set everything up, but this would also be achievable using the API.

Setting Up Cloudflare

First, we need to set up the "Zero Trust" dashboard. Log into your Cloudflare account, and select "Access" from the sidebar. For some, this may also show up as "Zero Trust" If it's not configured, go through the setup process until you land on the "Zero Trust overview" page:

welcome aboard page 1

On the left menu, click "Networks", and then "Tunnels". From here, we can manage our tunnels. Click "Create a tunnel", then choose "Cloudflared"

create a tunnel page 2

Name your tunnel something that will help you remember its purpose (like cycle-tunnel) and hit "save".

create a tunnel page 3

You'll see some information about how to connect to this tunnel using the generated token. We'll come back to this page shortly. For now, click "Next" in the bottom right corner, so we can set up the public hostname.

Adding the Public Hostname

We need to tell the tunnel to route traffic to Cycle's Load Balancer over the environment's private network. To do that, fill in the fields for whatever domain you would like requests to be forwarded for.

The real magic is the value set for the Service section. Here, we tell the tunnel to forward all traffic for this domain to the environment load balancer, which will handle the requests to forward to the relevant container.

Set the Type field to HTTP, and the URL field to env-lb, then click "Save tunnel".

create a tunnel page 4 route traffic

Now, open the tunnel page back up for editing, and keep this page open. We'll need the token when we set up the actual tunnel container on Cycle.

Deploying the Tunnel Container On Cycle

The next step is to deploy the Cloudflare Tunnel container on Cycle. This container will set up an egress connection from the Cycle environment back to Cloudflare. From there, Cloudflare will forward all inbound requests to the tunnel domain through that connection and into the Cycle environment. Then, the container will forward that request to the Cycle Load Balancer, with all of the necessary headers for the load balancer to understand its final destination container.

Import the cloudflared Container Image

On Cycle, click "Images" in the left menu, then "Sources". On the top, click "Create". (Or, click here ).

Fill in the form like this:

create a new image source

We're importing the container from Docker Hub, so nothing special is necessary to set up this image source.

Deploy the Cloudflare Container

On Cycle, navigate to the environment you'd like the tunnel to have access to, by clicking "Environments" in the left menu, and then choosing the environment from the list.

Next, in the top right corner, click "Deploy Container", and fill in the form to match this:

create new container on Cycle.io form

The public network can be set to "Egress Only", since the container just needs to create a tunnel back to cloudflare. No inbound requests will be made from the public internet into the tunnel.

Authenticate the Cloudflare Container

Next, we need to authenticate the cloudflared container. Switch back to the Cloudflare tab from earlier, and select the "Docker" environment, and copy the command.

authenticate cloudflare tunnel

Now, on Cycle, return to our cloudfared container, and navigate to the 'Config' tab on the left inside the container dialog. Under Runtime, find the Command section, and the override option for path & args.

Configure your override to look like this:

override the container runtime command in container configuration page.

This will allow the container to authenticate back to Cloudflare.

Configure DNS on Cycle

Now, switch back to your Cycle tab. If you don't have any other containers set up in this environment, you should set them up now. Configure the network to accept traffic over port 80:80 (or whatever port your container serves traffic over). It is important to NOT configure any ports for 443 (https) traffic. All https traffic is handled and terminated by the tunnel.

Next, for each container in the environment we want accessible over tunnel, we need to train Cycle's load balancer to route traffic to it for the domain you want. Traffic from the tunnel will hit the load balancer, which will then forward it to our target container for that domain.

Head over to DNS > Zones on the left. Click "Create" up top to create a new DNS zone.

This DNS zone needs to match the one you have configured on Cloudflare. Set it up as a Non-Hosted Zone. A Non-Hosted Zone doesn't require verification, and Cycle just uses it to train the load balancer how to route traffic.

DNS non hosted zone create on Cycle.io

After you've finished creating the zone, add a LINKED record to the zone by clicking "Add Record" in the top right and selecting LINKED from the dropdown. Set up your specified domain (again, matching the domain in Cloudflare). If you're using the root, enter @ in the "Name" field.

DO NOT enable TLS. Cloudflare will handle TLS for us.

Point it to whatever container you'd like that domain to resolve to within the environment containing the tunnel.

Add DNS record form

Give it a Whirl

Congratulations! If everything was configured correctly, your domain should now be resolving to the target container. But instead of hitting the environment load balancer directly, it's passing through Cloudflare and into the tunnel to get there instead!

Adding Extra Security

In the next release of the Cycle platform, we'll be introducing the ability to configure the load balancer to only bind to private IPs, which in tandem with the tunnel above, will prevent any access to containers in the environment without first going through Cloudflare.

💡 Interested in trying the Cycle platform? Create your account today! Want to drop in and have a chat with the Cycle team? We'd love to have you join our public Cycle Slack community!