It’s not always possible to afford the hardware for a full DMZ, and although most modern routers have the capability to expose your network publicly with some safeguards in place, it’s often best to utilize an edge network to protect yourself from malicious ne’er-do-wells. Let’s look at how we can make use of cloudflared to quickly open a secure tunnel to Cloudflare’s edge network so we can expose our app publicly while leveraging Cloudflare’s security.
Why?
Even the free tier of CloudFlare is great value. Here is a list of some basic free security features that doing this provides.
- Origin IP Obfuscation: Your server’s real IP is hidden behind Cloudflare’s network, reducing direct attack exposure.
- Basic DDoS Mitigation: Cloudflare’s free plan includes protection against many types of DDoS attacks, absorbing and filtering out malicious traffic before it reaches your network.
- Free SSL/TLS Encryption: Cloudflare provides free SSL/TLS certificates and encryption for traffic between users and its edge network, ensuring secure data transmission.
- Free DNS Management with DNSSEC: You get robust DNS management and DNSSEC support at no extra cost, which helps protect against DNS spoofing.
- Basic Firewall Rules: While advanced WAF features are part of paid plans, the free plan still lets you create custom firewall rules to block or challenge suspicious traffic.
Requirements
- A cloudflare account
- cloudflared
- A private k8s cluster
- sealed-secrets (optional)
Installing cloudflared
If you are not running a debian system you can refer to the above documentation. Otherwise we can use the cloudflare package repository:
Add the cloudflare GPG key:
1sudo mkdir -p --mode=0755 /usr/share/keyrings
2curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
Add the repository to apt sources:
1echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
Install the binary:
1sudo apt-get update && sudo apt-get install cloudflared
Creating your tunnel
Now we have the requirments satisifed we can login and create our tunnel:
Login to Cloudflare via the cloudflared CLI:
1cloudflared tunnel login
Open the provided unique URL and log in. You will be presented with your registered domains. Authorize the tunnel for your required domain.
You should have the certificate downloaded as soon as the tunnel has been authorized.
1Leave cloudflared running to download the cert automatically.
22025-03-30T16:16:02Z INF Waiting for login...
32025-03-30T16:16:07Z INF You have successfully logged in.
4If you wish to copy your credentials to a server, they have been saved to:
5/home/conner/.cloudflared/cert.pem
Let’s now create the tunnel and name it appropriately:
1cloudflared tunnel create <tunnel-name>
You should see cloudflared create your credentials accordingly:
1Tunnel credentials written to /home/conner/.cloudflared/123e4567-e89b-12d3-a456-426614174001.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
2
3Created tunnel <tunnel-name> with id 123e4567-e89b-12d3-a456-426614174001
Note: Look at the following ID. This will be unique to you. Everywhere you see this ID in the following commands needs to be replaced by the ID you generate here.
Loading the tunnel credentials into k8s
We will be using sealed-secrets for this to enable GitOps, but you can do this with any secret.
Note: You can check out the bootstrap post here if you want some help deploying sealed-secrets.
Create the generic secret YAML using your credentials provided above:
1kubectl create secret generic tunnel-credentials \
2--from-file=credentials.json=/home/conner/.cloudflared/123e4567-e89b-12d3-a456-426614174001.json --dry-run=client -o yaml > cloudflare-tunnel-secret.yaml
DO NOT UPLOAD THIS FILE TO GIT DO NOT UPLOAD THIS FILE TO GIT. You can skip the rest of this step if you plan on just applying the secret directly without backing it up in a Git repository.
Note: kubeseal can take your current context as the destination for the sealed secret. You might want to amend the above YAML with your chosen namespace (e.g., cloudflared).
Seal it so it’s encrypted and can safely be uploaded to a Git repository. Swap the controller flags out according to your deployed sealed-secrets namespace:
1kubeseal --controller-namespace=sealed-secrets --controller-name=sealed-secrets -o yaml < cloudflare-tunnel-secret.yaml > sealed-cloudflare-tunnel-secret.yaml
Creating a tunnel DNS record
You can create a DNS record in your cloudflared dashboard and associate it to the tunnel. A quicker way is to just use the CLI again.
1cloudflared tunnel route dns 123e4567-e89b-12d3-a456-426614174001 <domain>
Deploying cloudflared
You can use the cloudflared Helm chart or follow the documentation and apply the rendered manifests directly. I’d recommend the former, so let’s go over the configuration for it.
Add the Cloudflare Helm repository:
1helm repo add cloudflare https://cloudflare.github.io/helm-charts
2helm repo update
Populate a cloudflared-values.yaml file. Insure you amend the sevice and hostname to your current confiugration.
1cloudflare:
2 tunnelName: "my tunnel"
3 tunnelId: "123e4567-e89b-12d3-a456-426614174001"
4 # Omit the secret because we already have a sealed secret in place.
5 secret: ""
6 secretName: "tunnel-credentials"
7 enableWarp: false
8 ingress:
9 - hostname: <hostname>
10 service: "https://somesvc.somenamespace.svc.cluster.local:80"
11image:
12 repository: cloudflare/cloudflared
13 pullPolicy: IfNotPresent
14 tag: "2025.2.1"
Apply the helm chart:
1helm install cloudflared cloudflare/cloudflared-tunnel -f cloudflared-values.yaml
Once this is applied check the status of the cloudflared pods. You should see it spin up. As long as your tunnel can reach your internal k8s service your application should now be publicly exposed behind Cloudflare.