Skip to content

Cloudflare Tunnel

Overview

Cloudflare Tunnel provides secure, zero-trust access to internal services without exposing ports to the internet.

Architecture

graph LR
    subgraph Internet
        Users[Users]
        CF[Cloudflare Edge]
    end

    subgraph HomeLab
        Tunnel[cloudflared]
        Services[K8s Services]
    end

    Users -->|HTTPS| CF
    CF <-->|Tunnel| Tunnel
    Tunnel --> Services

How It Works

  1. cloudflared creates outbound connection to Cloudflare
  2. Cloudflare routes traffic to tunnel endpoint
  3. Tunnel forwards to internal services
  4. No inbound firewall rules required

Deployment

Kubernetes Resources

Resource Name Namespace
Deployment cloudflared cloudflared
ConfigMap cloudflared cloudflared
Secret cloudflared-creds cloudflared

Configuration

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloudflared
  namespace: cloudflared
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: cloudflared
        image: cloudflare/cloudflared:latest
        args:
          - tunnel
          - --config
          - /etc/cloudflared/config.yaml
          - run
        volumeMounts:
        - name: config
          mountPath: /etc/cloudflared

Tunnel Configuration

Ingress Rules

tunnel: <tunnel-id>
credentials-file: /etc/cloudflared/credentials.json
ingress:
  - hostname: ajandrews.pro
    service: http://hub-web.hub.svc:80
  - hostname: api.ajandrews.pro
    service: http://hub-api.hub.svc:8080
  - hostname: grafana.ajandrews.pro
    service: http://grafana.monitoring.svc:3000
  - hostname: kibana.ajandrews.pro
    service: http://kibana.monitoring.svc:5601
  - hostname: prometheus.ajandrews.pro
    service: http://prometheus.monitoring.svc:9090
  - hostname: argocd.ajandrews.pro
    service: https://argocd-server.argocd.svc:443
  - hostname: wiki.ajandrews.pro
    service: http://wiki.wiki.svc:80
  - service: http_status:404

Exposed Services

Hostname Service Port
ajandrews.pro Hub Web 80
api.ajandrews.pro Hub API 8080
grafana.ajandrews.pro Grafana 3000
kibana.ajandrews.pro Kibana 5601
prometheus.ajandrews.pro Prometheus 9090
argocd.ajandrews.pro ArgoCD 443
wiki.ajandrews.pro Wiki 80

Adding New Service

1. Create Kubernetes Service

apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: my-namespace
spec:
  selector:
    app: my-app
  ports:
  - port: 80

2. Update Cloudflared Config

Add to ingress rules:

- hostname: myapp.ajandrews.pro
  service: http://my-app.my-namespace.svc:80

3. Create DNS Record

In Cloudflare dashboard: - Type: CNAME - Name: myapp - Target: .cfargotunnel.com - Proxy: Enabled

4. Apply Changes

kubectl rollout restart deployment/cloudflared -n cloudflared

Security Features

Zero Trust Access

  • No exposed ports
  • Encrypted tunnel
  • Cloudflare Access policies
  • Per-hostname authentication

Access Policies

Configure in Cloudflare Access:

Application Policy
ArgoCD Require auth
Grafana Require auth
Public site Allow all

High Availability

Replicas

Run multiple tunnel instances:

spec:
  replicas: 2

Health Checks

livenessProbe:
  httpGet:
    path: /ready
    port: 2000

Monitoring

Metrics

cloudflared exposes Prometheus metrics:

Metric Description
cloudflared_tunnel_connections Active connections
cloudflared_tunnel_requests Total requests
cloudflared_tunnel_errors Error count

Logs

kubectl logs -n cloudflared deployment/cloudflared

Troubleshooting

Common Issues

Issue Cause Resolution
502 Bad Gateway Service down Check target service
Connection refused Wrong service URL Verify service name
Certificate error HTTPS backend Use http or skip verify
Tunnel offline Pod crash Check pod logs

Debug Commands

# Check tunnel status
cloudflared tunnel info

# Test connectivity
cloudflared tunnel route dns <tunnel-id> test.example.com

# View logs
kubectl logs -n cloudflared -l app=cloudflared

Maintenance

Updating Tunnel

# Update image
kubectl set image deployment/cloudflared \
  cloudflared=cloudflare/cloudflared:latest \
  -n cloudflared

Rotating Credentials

  1. Generate new credentials in Cloudflare
  2. Update Kubernetes secret
  3. Restart deployment