Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

This documentation is part of the "Projects with Books" initiative at zenOSmosis.

The source code for this project is available on GitHub.

Cloudflared Tunnel Connector

Loading…

Cloudflared Tunnel Connector

Relevant source files

Purpose and Scope

This document details the cloudflared container component, which acts as the secure tunnel connector between the Cloudflare network and the local Mosquitto MQTT broker. It explains the container’s configuration, how it authenticates with Cloudflare’s infrastructure, how it establishes and maintains the tunnel connection, and how it routes incoming traffic to the Mosquitto service.

For broader system architecture context, see System Architecture. For Docker Compose orchestration details, see Docker Compose Orchestration. For environment variable configuration, see Environment Variables.


Container Overview

The cloudflared service is defined in docker-compose.yml:11-17 and runs the official Cloudflare Tunnel connector image. This container establishes an outbound-only persistent connection to Cloudflare’s network, eliminating the need for open inbound ports on the host system.

Container Specification

PropertyValuePurpose
Service NamecloudflaredDocker Compose service identifier
Container NamecloudflaredRuntime container name
Imagecloudflare/cloudflared:latestOfficial Cloudflare tunnel connector
Commandtunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}Tunnel execution with token authentication
Restart Policyunless-stoppedAutomatic restart except on manual stop
Environment VariablesCLOUDFLARE_TUNNEL_TOKENTunnel authentication credential

Sources: docker-compose.yml:11-17


Container Configuration Architecture

The following diagram maps the Docker Compose configuration elements to their runtime purposes:

Sources: docker-compose.yml:11-17

graph TB
    subgraph "docker-compose.yml Definition"
        SERVICE["services.cloudflared"]
IMAGE["image: cloudflare/cloudflared:latest"]
CONTAINER["container_name: cloudflared"]
COMMAND["command: tunnel --no-autoupdate run --token"]
RESTART["restart: unless-stopped"]
ENV_SECTION["environment:\n- CLOUDFLARE_TUNNEL_TOKEN"]
end
    
    subgraph "Runtime Configuration"
        TOKEN_VAR["${CLOUDFLARE_TUNNEL_TOKEN}\nfrom .env file"]
CMD_EXEC["Executed Command:\ntunnel --no-autoupdate run --token <actual_token>"]
end
    
    subgraph "Operational Behavior"
        CONTAINER_PROC["cloudflared Process"]
AUTO_RESTART["Automatic Restart\non Failure"]
NO_UPDATE["Update Mechanism\nDisabled"]
end
    
 
   SERVICE --> IMAGE
 
   SERVICE --> CONTAINER
 
   SERVICE --> COMMAND
 
   SERVICE --> RESTART
 
   SERVICE --> ENV_SECTION
    
 
   ENV_SECTION --> TOKEN_VAR
 
   COMMAND --> TOKEN_VAR
 
   TOKEN_VAR --> CMD_EXEC
    
 
   CMD_EXEC --> CONTAINER_PROC
 
   RESTART --> AUTO_RESTART
 
   COMMAND --> NO_UPDATE
    
 
   CONTAINER_PROC --> NO_UPDATE
 
   CONTAINER_PROC --> AUTO_RESTART

Tunnel Authentication

The cloudflared connector authenticates with Cloudflare’s infrastructure using a cryptographically secure tunnel token. This token is generated during the Cloudflare Tunnel creation process and links the local connector to a specific tunnel configuration on Cloudflare’s network.

sequenceDiagram
    participant User as "Administrator"
    participant CF_UI as "Cloudflare Dashboard"
    participant Env as ".env File"
    participant Compose as "docker-compose"
    participant CFD as "cloudflared Container"
    participant CF_Net as "Cloudflare Network"
    
    User->>CF_UI: Create tunnel via Zero Trust dashboard
    CF_UI->>CF_UI: Generate CLOUDFLARE_TUNNEL_TOKEN
    CF_UI-->>User: Display token in connector setup
    
    User->>Env: Create .env file with token
    Note over Env: CLOUDFLARE_TUNNEL_TOKEN=eyJh...
    
    User->>Compose: docker compose up
    Compose->>Env: Read CLOUDFLARE_TUNNEL_TOKEN
    Compose->>CFD: Start container with token in command
    Note over CFD: tunnel run --token <token>
    
    CFD->>CF_Net: Initiate tunnel connection with token
    CF_Net->>CF_Net: Validate token signature
    CF_Net->>CF_Net: Retrieve tunnel configuration
    CF_Net-->>CFD: Tunnel established
    
    Note over CFD,CF_Net: Persistent connection maintained

Token Lifecycle

Token Security Properties

  • Single-Use Binding: Each token is bound to a specific tunnel UUID in Cloudflare’s infrastructure
  • Environment Injection: Token is injected via environment variable substitution at container start
  • Never Logged: The --token flag ensures the token does not appear in process listings
  • Git Exclusion: The .env file containing the token is excluded via .gitignore

Sources: docker-compose.yml14 docker-compose.yml:16-17 .env.sample1 README.md53


Connection Establishment

The cloudflared container establishes an outbound-only connection to Cloudflare’s edge network. This inverted architecture eliminates the need for inbound firewall rules or public IP addresses.

Tunnel Command Structure

The command executed by the container is:

tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}
FlagPurpose
tunnelPrimary cloudflared operation mode
--no-autoupdateDisables automatic binary updates to ensure container image control
runExecutes the tunnel connector
--tokenProvides authentication credential inline

Sources: docker-compose.yml14

Connection Flow

Sources: docker-compose.yml14 README.md64 README.md67


Traffic Routing Mechanism

The cloudflared container acts as a local reverse proxy, receiving traffic from the Cloudflare tunnel and forwarding it to the mosquitto service using Docker’s internal DNS resolution.

graph LR
    subgraph "Cloudflare Configuration"
        CF_HOST["Public Hostname:\nmqtt.example.com"]
CF_SERVICE["Service Type: HTTP"]
CF_URL["URL: mosquitto:9001"]
end
    
    subgraph "Docker Network"
        DNS["Docker Internal DNS"]
MOSQ_NAME["Container Name:\nmosquitto"]
MOSQ_IP["Container IP:\n172.x.x.x"]
end
    
    subgraph "cloudflared Container"
        CFD_PROC["cloudflared Process"]
RESOLVER["DNS Resolver"]
HTTP_CLIENT["HTTP Client"]
end
    
 
   CF_HOST --> CF_SERVICE
 
   CF_SERVICE --> CF_URL
    
 
   CF_URL -.->|'mosquitto' hostname configured in tunnel| CFD_PROC
 
   CFD_PROC --> RESOLVER
 
   RESOLVER --> DNS
 
   DNS --> MOSQ_NAME
 
   MOSQ_NAME --> MOSQ_IP
 
   MOSQ_IP -.-> HTTP_CLIENT
    
 
   HTTP_CLIENT -->|HTTP to 172.x.x.x:9001| MOSQ_IP

Internal DNS Resolution

Routing Configuration

The routing is configured in the Cloudflare Dashboard during tunnel setup:

  • Public Hostname: User-defined subdomain (e.g., mqtt.example.com)
  • Service Type: HTTP (internal protocol between cloudflared and mosquitto)
  • Service URL: mosquitto:9001 (Docker service name and port)

The mosquitto hostname in the service URL is resolved by Docker’s internal DNS to the IP address of the container named mosquitto as defined in docker-compose.yml6

Sources: README.md:62-67 docker-compose.yml:4-6


Protocol Translation

The cloudflared container performs protocol translation between external HTTPS/WSS connections and internal HTTP/WebSocket connections.

External vs Internal Protocols

DirectionProtocolPortEncryption
Client → Cloudflare EdgeHTTPS/WSS443TLS (Cloudflare-managed)
Cloudflare Edge → cloudflaredTunnel ProtocolN/AEncrypted tunnel
cloudflared → mosquittoHTTP/WebSocket9001Unencrypted (internal network)

The external connection is encrypted end-to-end from client to Cloudflare’s edge, while the internal connection between cloudflared and mosquitto is unencrypted but isolated within Docker’s private bridge network.

Sources: README.md67 README.md82


Operational Characteristics

No Exposed Ports

The cloudflared container exposes no ports to the host system. All communication occurs via:

  1. Outbound tunnel connection to Cloudflare’s network
  2. Internal Docker network for communication with the mosquitto container

This design eliminates attack surface by ensuring no listening ports on the host machine.

Sources: docker-compose.yml:11-17

Restart Behavior

The container uses the unless-stopped restart policy defined in docker-compose.yml15 This ensures:

  • Automatic recovery: Container restarts on crashes or Docker daemon restarts
  • Persistent operation: Container continues running across reboots
  • Manual control: Container only stops when explicitly commanded via docker compose down or docker stop

Update Management

The --no-autoupdate flag in docker-compose.yml14 disables cloudflared’s built-in automatic update mechanism. This ensures:

  • Version pinning: Updates are controlled via Docker image version
  • Predictable behavior: No unexpected binary changes during operation
  • Image-based updates: Updates occur through docker compose pull and container recreation

Sources: docker-compose.yml:14-15


Connection State Diagram

Sources: docker-compose.yml:14-15


Environment Variable Configuration

The cloudflared container requires exactly one environment variable:

CLOUDFLARE_TUNNEL_TOKEN

  • Source: Defined in .env file (see .env.sample1)
  • Injection Method: Environment variable substitution in docker-compose.yml14
  • Format: Base64-encoded JSON containing tunnel credentials
  • Scope: Container-level environment variable
  • Security: Must not be committed to version control

The token is referenced twice in the configuration:

  1. In the command field via shell substitution: ${CLOUDFLARE_TUNNEL_TOKEN}
  2. In the environment section for explicit passing to the container

Sources: docker-compose.yml14 docker-compose.yml:16-17 .env.sample1


Security Model

Zero Inbound Exposure

The cloudflared container implements a security model where:

  • No listening ports: Container binds no ports to the host interface
  • Outbound-only connections: All connections are initiated from inside the network
  • Tunnel-based access: External access is only possible through the authenticated tunnel
  • No direct routes: No firewall rules or port forwarding required

Authentication Flow

Sources: docker-compose.yml14 docker-compose.yml:16-17 README.md53


Common Configuration Reference

Minimal Configuration

The cloudflared service requires minimal configuration:

Configuration Dependencies

DependencyLocationPurpose
Tunnel Token.env fileAuthentication credential
Tunnel ConfigurationCloudflare DashboardRouting and hostname mapping
Docker NetworkImplicit (default bridge)Container-to-container communication
mosquitto Servicedocker-compose.yml:4-9Target service for traffic forwarding

Sources: docker-compose.yml:11-17


Integration with Mosquitto

The cloudflared container integrates with the mosquitto container through Docker’s internal networking:

Service Discovery

  • DNS Name: mosquitto (from docker-compose.yml4)
  • Container Name: mosquitto (from docker-compose.yml6)
  • Target Port: 9001 (WebSocket listener)
  • Resolution: Automatic via Docker’s embedded DNS server
graph TB
    subgraph "Host System"
        HOST["No Exposed Ports"]
end
    
    subgraph "Docker Bridge Network"
        CFD_CONTAINER["cloudflared Container\nNo EXPOSE directive"]
MOSQ_CONTAINER["mosquitto Container\nNo EXPOSE directive"]
DOCKER_DNS["Docker DNS Server"]
end
    
    subgraph "External Network"
        CF_EDGE["Cloudflare Edge\nPublic HTTPS:443"]
end
    
 
   CF_EDGE <-->|Outbound tunnel connection| CFD_CONTAINER
 
   CFD_CONTAINER <-->|DNS query: mosquitto| DOCKER_DNS
 
   DOCKER_DNS -->|Resolves to container IP| MOSQ_CONTAINER
 
   CFD_CONTAINER -->|HTTP:9001| MOSQ_CONTAINER
    
 
   HOST -.->|No direct connection| CFD_CONTAINER
 
   HOST -.->|No direct connection| MOSQ_CONTAINER

Network Isolation

Both containers run on the same Docker bridge network without exposing ports to the host:

Sources: docker-compose.yml:4-6 docker-compose.yml:11-17 README.md64


Troubleshooting Connection Issues

Token Validation Failures

If the cloudflared container fails to establish a tunnel:

  1. Verify token format: Token should be a long base64-encoded string
  2. Check.env file: Ensure CLOUDFLARE_TUNNEL_TOKEN=<token> with no extra spaces
  3. Confirm tunnel exists: Verify the tunnel is active in Cloudflare Dashboard
  4. Review logs: docker logs cloudflared will show authentication errors

DNS Resolution Failures

If cloudflared cannot reach the mosquitto container:

  1. Verify container names: Both containers must use the names defined in docker-compose.yml
  2. Check network: Both containers must be on the same Docker network (default bridge)
  3. Confirm mosquitto is running: docker ps should show both containers as “Up”
  4. Test DNS resolution: docker exec cloudflared nslookup mosquitto should resolve

Service URL Configuration

The service URL in Cloudflare Dashboard must match the Docker configuration:

  • Correct: mosquitto:9001 or http://mosquitto:9001
  • Incorrect: localhost:9001, 127.0.0.1:9001, mqtt:9001

Sources: docker-compose.yml:4-6 README.md64 README.md84

Dismiss

Refresh this wiki

Enter email to refresh