Cloudflare Tunnels

#cloudflare #technology #til

Every now and then I need to expose a service running on a local machine to the internet, usually just temporarily. Rather than opening ports on a router or mucking about with firewall rules, a reverse tunnel does the job nicely.

There are a number of providers offering this kind of reverse tunnel, including:[1]

I've been playing around with Cloudflare Tunnel recently. Its client, cloudflared, supports a couple of different modes: anonymous quick tunnels (transient, randomly-named URLs) and named tunnels, which are tied to a Cloudflare account and give you stable, predictable hostnames.

Quick tunnels

If you just want to throw something onto a public URL for an hour, TryCloudflare needs no account and nothing more than the cloudflared binary. For example, to share a local web server listening on port 8080:

cloudflared tunnel --url http://localhost:8080

cloudflared connects out to Cloudflare's edge and prints a randomly generated URL:

https://some-random-words-here.trycloudflare.com

Anyone with that URL can now reach your local server through Cloudflare's network.

A few caveats worth knowing before you rely on these:

  • The tunnel and its URL live only as long as the cloudflared process runs. Hit Ctrl-C and it's gone, and you'll get a fresh random URL next time you start one.
  • They're intended for testing and development only.

Named tunnels

For anything longer-lived, named tunnels give you a stable hostname of your choosing. These require a Cloudflare login, and a domain served by Cloudflare DNS.

First, authenticate and create the tunnel:

# Opens a browser window to authenticate
cloudflared tunnel login

# Creates the tunnel and a credentials file (~/.cloudflared/<UUID>.json)
cloudflared tunnel create my-tunnel

# Confirm it exists
cloudflared tunnel list

Next, create a configuration file at ~/.cloudflared/config.yml mapping hostnames to local services:

tunnel: <UUID>
credentials-file: /home/you/.cloudflared/<UUID>.json

ingress:
  # Map a public hostname to a local service
  - hostname: app.my-site.com
    service: http://127.0.0.1:8080

  # Required catch-all rule; anything unmatched gets a 404
  - service: http_status:404

Ingress rules are evaluated from top to bottom, and the last rule must be a catch-all. Services can be more than plain HTTP: SSH, RDP, arbitrary TCP, Unix sockets and more are supported. Other top-level options (like protocol and edge-ip-version) can tune how cloudflared connects to the edge.

Then route a DNS record to the tunnel and start it up:

# Creates a CNAME for app.my-site.com
cloudflared tunnel route dns my-tunnel app.my-site.com

# Start serving traffic
cloudflared tunnel run my-tunnel

A couple of additional commands that are useful for debugging:

# Sanity-check the ingress rules in your config
cloudflared tunnel ingress validate

# Check which rule a given URL would match
cloudflared tunnel ingress rule https://app.my-site.com

# See connection status for a running tunnel
cloudflared tunnel info my-tunnel

All in all, these commands provide a quick and convenient way to expose tools and services from your local machine to the internet. Especially if you already use Cloudflare as your site's DNS.

  1. Given this blog is hosted on Cloudflare Pages, this was the obvious choice for me to try first. It ended up doing everything I needed it to - so I didn't end up trying any of the other entries in the list. ↩


Comments