Skip to main content
  1. Posts/

Terraform Your Tunnels

·3 mins·
Table of Contents

Ok. So this might not be that new for everyone, but I think it still a cool way to do this so here is a new post about it.

Tunnels #

Recently I started using CloudFlare Tunnels more and more in my home projects (as well). Not going to argue about the fact weather you should use CF Tunnels - or not - I leave this decision to you. For me I am more or less OK with the concerns as:

  • I put authentication in front of the tunnel
    alt text
  • I mostly expose non-mission-critical (ie. media, such as Plex, Jellyfin or ) services with it

Terraform #

Anyway, I am more into the technical discussion part of this. So when you set up a tunnel, the various web applications need to have their hostname defined on the tunnel (not to mention the tunnel itself); of course you could probably do this via the GUI (really? nah…in 2024 we know better!) or via cloudflared but since we all know and love/hate Terraform - why not use this tool?

OK, I might be walking into this Maslow’s instrument but come on, it is soo easy…

Lets see what we need:

  • obviously you need a Cloudflare Account
  • a server running web services and cloudflared (in my case this is a server running https://casaos.zimaspace.com/ currently)
  • and a machine running Terraform
  • direnv

Simple yeah?

Prep work #

Ok, lets get prepared

First I set up in the folder my direnv with a simple .envrc file

alt text

You can get the values of those from Cloudflare. The one thing that tripped me over at first was the API token that you want to set up with these permissions to avoid disappointment:

alt text

Then we are good to go Terraform it…

In my case I created a few files (you know, separation and all!)

alt text

  • provider.tf is the configuration for CloudFlare
  • tunnels.tf is for the tunnel’s config
  • ingress_rules.tf are the public host names I want to use on my host
  • and variables.tf of course to create the variables matching to what I use in the .envrc file

Now I am not going to recreate my tunnel, it is pretty straightforward - but the output should be something along the lines of:

tofu show
# cloudflare_record.dns_records["casa"]:
resource "cloudflare_record" "dns_records" {
    allow_overwrite = false
    content         = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1.cfargotunnel.com"
    created_on      = "2024-10-26T12:31:00.209146Z"
    hostname        = "casa.domain.com"
    id              = "d04604ed446f4ac39fc05402fa226093"
    metadata        = {
        "auto_added"             = "false"
        "managed_by_apps"        = "false"
        "managed_by_argo_tunnel" = "false"
    }
    modified_on     = "2024-10-26T12:31:00.209146Z"
    name            = "casa"
    proxiable       = true
    proxied         = true
    tags            = []
    ttl             = 1
    type            = "CNAME"
    zone_id         = "d0c0fbdcd95438bc60860f16d45737db"
}

...number of other DNS records ..

# cloudflare_zero_trust_tunnel_cloudflared.existing_tunnel:
resource "cloudflare_zero_trust_tunnel_cloudflared" "existing_tunnel" {
    account_id   = "1443fe12026b33d56dcc26a9deed0667"
    cname        = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1.cfargotunnel.com"
    id           = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
    name         = "docker-tunnel-new"
    secret       = (sensitive value)
    tunnel_token = (sensitive value)
}

# cloudflare_zero_trust_tunnel_cloudflared_config.tunnel_config:
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "tunnel_config" {
    account_id = "1443fe12026b33d56dcc26a9deed0667"
    id         = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
    tunnel_id  = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"

    config {
        ingress_rule {
            hostname = "casa.domain.com"
            service  = "http://localhost:8080"
        }

 ... and so on ...

        ingress_rule {
            service = "http_status:404"
        }
    }
}


Outputs:

credentials_json = (sensitive value)
tunnel_id = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
tunnel_name = "docker-tunnel-new"
tunnel_token = (sensitive value)

(note: yes, at some point I swapped from Terraform to OpenTofu but the process is the same with the good old TF as well…)

If you want to see the full code, you can grab it from my repo

Thanks for tunning in!