cloudflare

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 0 Imported by: 0

README

cloudflare

Cloudflare for Kubernetes estates, as reusable mechanism:

Artifact What Status
charts/cloudflared The in-cluster end of a Cloudflare Tunnel — cloudflared as a plain Deployment, token from any Secret shipped
pkg/tunnel Pulumi Go component: tunnel + DNS records + ingress rules (+ opt-in certificate packs) from a config struct shipped

Published to oci://ghcr.io/truvity/charts/cloudflared on every tag; the Go module is github.com/truvity/cloudflare.

The rule that makes this repository public

Mechanism only. Nothing here names an account, a zone, a hostname, a cluster or a secret path. Every such thing is an input with a neutral default, and the consuming estate supplies it from its own (private) repository. hack/leak-canary.sh enforces this in CI, and public history cannot be unpublished — so the rule is mechanical, not remembered.

The same rule shapes the planned Go component: credentials come in as a provider, secrets (the tunnel token) go out as Pulumi Outputs and the caller decides where they live. No secret store, no cloud SDK, no zone-level configuration — plenty of Cloudflare plans have none.

pkg/tunnel

import (
    "github.com/pulumi/pulumi-cloudflare/sdk/v6/go/cloudflare"
    "github.com/pulumi/pulumi-random/sdk/v4/go/random"
    "github.com/truvity/cloudflare/pkg/tunnel"
)

provider, _ := cloudflare.NewProvider(ctx, "cf", &cloudflare.ProviderArgs{ApiToken: token})
secret, _ := random.NewRandomBytes(ctx, "tunnel-secret", &random.RandomBytesArgs{Length: pulumi.Int(32)})

tun, err := tunnel.New(ctx, "mycluster", tunnel.Args{
    AccountID: accountID,
    Name:      "mycluster",
    Ingress: []tunnel.Ingress{
        // Exact hosts before wildcards; deeper wildcards before broader
        // ones — cloudflared's `*` spans dots and first match wins.
        {Hostname: "app.example.com", Service: "https://gateway-internal.envoy-gateway-system.svc:443",
         CAPool: "/etc/cloudflared/certs/ca.pem"},
    },
    DNS: &tunnel.DNS{ZoneID: zoneID, Names: []string{"app.example.com"}},
    // Certificates: opt-in Advanced Certificate packs — off by default,
    // because plenty of plans have no zone-level features.
}, secret.Base64, pulumi.Provider(provider))

// tun.Token is a secret Output — store it wherever YOUR estate keeps
// secrets (a Kubernetes Secret for charts/cloudflared, SSM, SOPS...).

The contract, in one line each: Args is plain yaml-taggable data (a consumer unmarshals its own config file into it, Validate() checks it); the tunnel secret is a caller input, never derived; the token comes back as a secret Output for the caller to store; no zone settings are ever touched. Child resources are deterministically named (tunnel-<name>, ingress-<name>, cname-<name>-<slug>, acm-<name>-<slug>) so an estate migrating existing top-level resources can alias onto them.

charts/cloudflared

helm install cloudflared oci://ghcr.io/truvity/charts/cloudflared \
  --version <tag> --namespace cloudflare-system --create-namespace \
  --set secretName=cloudflared-tunnel-token

The chart assumes a remotely-managed tunnel (cloudflared tunnel run --token-file …): ingress rules live in Cloudflare, the pod only needs the token. Create the Secret with whatever owns secrets in your estate — External Secrets, SOPS, kubectl create secret generic … --from-literal tunnel-token=….

Value Default Notes
replicaCount 2 two replicas = two tunnel connections; set podDisruptionBudget.enabled for drains
image.repository / image.tag cloudflare/cloudflared / pinned Renovate bumps the tag here
secretName / secretKey cloudflared-tunnel-token / tunnel-token mounted at /secrets/<key>, read with --token-file
caSecretName "" Secret with ca.pem; mounted at /etc/cloudflared/certs/ca.pem so the tunnel config's originRequest.caPool can verify private origins
extraArgs [] appended to cloudflared tunnel … run
nodeSelector, tolerations, affinity, topologySpreadConstraints, priorityClassName, podAnnotations, podLabels empty scheduling is the estate's
resources 50m / 64Mi, limit 256Mi

Network policies are deliberately not in the chart: the pod needs egress to Cloudflare's edge (7844/udp+tcp, 443/tcp) and to the origins the tunnel routes to, and only the estate knows those.

Development

devbox shell        # or direnv
just check          # lint + golden renders + leak canary (+ go build/vuln)
just golden         # regenerate tests/golden after a template change — review the diff

Every tests/cases/<chart>/<case>/values.yaml is rendered and compared byte-for-byte with tests/golden/<chart>/<case>.yaml; a template change is reviewed as a diff, with no cluster involved.

Releasing

Push a tag vX.Y.Z. The shared release workflow creates the GitHub Release and pushes every chart at that version — a chart's own version field is a placeholder that never moves.

Licence

MIT — see LICENSE.

Documentation

Overview

Package cloudflare is the module root of github.com/truvity/cloudflare.

The module ships Cloudflare mechanism for Kubernetes estates: the cloudflared Helm chart under charts/, and (planned) pkg/tunnel — a Pulumi Go component that creates a tunnel, its DNS records and ingress rules from a config struct, returning the tunnel token as an Output for the caller to store. Nothing in this module names an account, a zone or a secret store; those are the caller's inputs.

Directories

Path Synopsis
pkg
tunnel
Package tunnel provisions a remotely-managed Cloudflare Tunnel — the tunnel itself, its ingress rules, the DNS records that point hostnames at it, and (opt-in) the Advanced Certificate packs — from one config struct.
Package tunnel provisions a remotely-managed Cloudflare Tunnel — the tunnel itself, its ingress rules, the DNS records that point hostnames at it, and (opt-in) the Advanced Certificate packs — from one config struct.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL