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.

Security Model

Relevant source files

Purpose and Scope

This document describes the security architecture of the Docker MQTT Mosquitto Cloudflare Tunnel system, including network security, transport encryption, authentication mechanisms, and secret management practices. It explains the security trade-offs in the default configuration and identifies trust boundaries.

For detailed configuration of access control lists and encryption features, see Topic Access Control (ACL)) and Encrypted Retained Messages. For production deployment hardening, see Production Considerations.


Security Architecture Overview

The system implements a multi-layered security model centered on Cloudflare Tunnel's outbound-only connection architecture. The primary security mechanism is network isolation combined with transport encryption, while the MQTT broker itself operates in an anonymous-access mode suitable for trusted environments.

graph TB
    Internet["Internet\n(Untrusted)"]
CFEdge["Cloudflare Edge\nDDoS Protection, WAF"]
CFTunnel["Cloudflare Tunnel\nTLS 1.3 Encrypted"]
Cloudflared["cloudflared container\nToken-authenticated"]
DockerNet["Docker Internal Network\n(bridge mode)"]
Mosquitto["mosquitto container\nAnonymous access"]
Internet --> CFEdge
 
   CFEdge --> CFTunnel
 
   CFTunnel --> Cloudflared
 
   Cloudflared --> DockerNet
 
   DockerNet --> Mosquitto
    
    subgraph "External Security Boundary"
        CFEdge
        CFTunnel
    end
    
    subgraph "Host Security Boundary"
        Cloudflared
        DockerNet
        Mosquitto
    end

Security Layers Diagram

Sources: README.md:15-20, mosquitto.conf:1-6


Network Security Architecture

Outbound-Only Connection Model

The system eliminates the need for inbound firewall rules by establishing an outbound-only tunnel connection from cloudflared to Cloudflare's network. This architecture prevents direct internet exposure of the MQTT broker.

Key Properties:

sequenceDiagram
    participant Host as "Docker Host"
    participant CFD as "cloudflared container"
    participant CF as "Cloudflare Network"
    participant FW as "Host Firewall"
    
    Note over CFD: Container starts with\nCLOUDFLARE_TUNNEL_TOKEN
    CFD->>CF: Outbound HTTPS connection\nAuthenticate with token
    CF-->>CFD: Tunnel established\nTLS 1.3 encrypted
    
    Note over FW: No inbound rules required\nNote over Host,CF: All traffic flows through\nestablished tunnel
    
    CF->>CFD: Route MQTT client traffic
    CFD->>CFD: Proxy to mosquitto:9001
Security AspectImplementationFile Reference
Inbound PortsNone required on hostN/A
Outbound ConnectionHTTPS to Cloudflare edgeN/A
Container NetworkDocker bridge (default)docker-compose.yml
Service ResolutionDNS-based via container namedocker-compose.yml

Sources: README.md:15-20, README.md:55-66

Docker Network Isolation

The mosquitto and cloudflared containers communicate through Docker's default bridge network. The mosquitto container is not exposed to the host network or internet directly.

graph LR
    subgraph "Docker Bridge Network"
        CF["cloudflared\ncontainer_name: cloudflared"]
MQ["mosquitto\ncontainer_name: mosquitto"]
end
    
    subgraph "Service Resolution"
        DNS["Docker DNS\nresolves 'mosquitto'\nto container IP"]
end
    
 
   CF -->|Internal proxy to mosquitto:9001| DNS
 
   DNS --> MQ
    
    Note1["No host port mapping\nfor mosquitto:9001"]

The hostname mosquitto in the public hostname configuration README.md62 is resolved by Docker's internal DNS to the IP address of the mosquitto container. This ensures traffic never traverses the host network.

Sources: README.md:62-64


Transport Security

Cloudflare Tunnel Encryption

All traffic between MQTT clients and the cloudflared container is encrypted using Cloudflare Tunnel's TLS 1.3 transport:

Encryption Scope:

graph LR
    Client["MQTT Client"]
CFEdge["Cloudflare Edge"]
Tunnel["Encrypted Tunnel\nTLS 1.3"]
CFD["cloudflared"]
Plain["Plaintext Proxy"]
MQ["mosquitto:9001"]
Client -->|MQTT over WebSocket/HTTP| CFEdge
 
   CFEdge -->|Encrypted| Tunnel
 
   Tunnel -->|Encrypted| CFD
 
   CFD -->|Plaintext internal| Plain
 
   Plain --> MQ
  • Encrypted: Internet → Cloudflare Edge → cloudflared container
  • Plaintext: cloudflared container → mosquitto container (internal Docker network)

The plaintext segment is considered secure because it operates within the Docker bridge network, isolated from external access.

Sources: README.md:15-20

MQTT Protocol Listeners

The mosquitto service configures two listeners with different security characteristics:

ListenerPortProtocolTransport SecurityConfiguration
TCP1883MQTTNone (internal only)mosquitto.conf1
WebSocket9001MQTT over WSVia Cloudflare Tunnelmosquitto.conf:4-5

Port 9001 is the only listener routed through the Cloudflare Tunnel README.md62 Port 1883 is accessible only within the Docker network.

Sources: mosquitto.conf:1-5, README.md:62


Authentication and Authorization

Anonymous Access Model

The system is configured with anonymous access enabled, meaning no authentication is required at the MQTT broker level:

listener 1883
allow_anonymous true

listener 9001
protocol websockets

Configuration: mosquitto.conf2

This configuration has the following security implications:

Trust Model:

The anonymous access model operates under the assumption that:

  1. Cloudflare Tunnel provides network-level access control - Only authorized clients can reach the broker through the configured public hostname
  2. All traffic reaching the broker is trusted - The broker does not differentiate between clients
  3. Internal Docker network is trusted - No authentication within the container network

Sources: mosquitto.conf:1-6

Access Control Limitations

The default configuration provides no MQTT-level access control :

Security ControlStatusImplication
Username/PasswordDisabledAny client can connect
ACL (Topic Permissions)NoneAll clients can access all topics
Client ID RestrictionsNoneAny client ID accepted
Connection LimitsNone (default)No rate limiting at MQTT layer

For environments requiring MQTT-level access control, see Topic Access Control (ACL)).

Sources: mosquitto.conf:1-6


Secret Management

Cloudflare Tunnel Token

The CLOUDFLARE_TUNNEL_TOKEN is the primary secret in the system, used to authenticate the cloudflared container with Cloudflare's network.

graph TB
    Dashboard["Cloudflare Zero Trust Dashboard"]
Token["CLOUDFLARE_TUNNEL_TOKEN\n(secret value)"]
EnvSample[".env.sample\n(template)"]
EnvFile[".env\n(actual secret)"]
GitIgnore[".gitignore"]
CFD["cloudflared container\nenvironment variable"]
Dashboard -->|Generate during tunnel creation| Token
 
   Token -->|Developer copies| EnvFile
 
   EnvSample -.->|Template reference no actual token| EnvFile
 
   GitIgnore -->|Excludes from VCS| EnvFile
 
   EnvFile -->|Read at startup| CFD
    
    Note1["Never committed\nto version control"]

Secret Lifecycle Flow

Sources: README.md:47-51, .env.sample:1, .gitignore:1

Version Control Protection

The .gitignore file prevents secrets from entering version control:

.env
data/*

Configuration: .gitignore:1-2

FileVersion ControlledContains SecretsPurpose
.env.sampleYesNoTemplate/documentation
.envNo (ignored)YesActual secrets
docker-compose.ymlYesNoReferences ${CLOUDFLARE_TUNNEL_TOKEN} variable
graph TB
    subgraph "Secure Storage"
        EnvFile[".env file\nFilesystem permissions"]
FS["Host filesystem\nOwner: user\nMode: 0600 recommended"]
end
    
    subgraph "Runtime Access"
        Docker["Docker Engine\nReads .env"]
CFD["cloudflared container\nEnvironment variable"]
end
    
    subgraph "Version Control"
        Git["Git repository"]
GitIgnore[".gitignore\nExcludes .env"]
Remote["Remote repository\nGitHub"]
end
    
 
   EnvFile --> FS
 
   FS --> Docker
 
   Docker --> CFD
    
 
   GitIgnore -.->|Prevents| Git
 
   Git --> Remote
    
    Note1["Token visible in:\n- .env file\n- Docker inspect output\n- Container environment"]

The docker-compose.yml uses variable interpolation to inject the token at runtime without embedding it in version-controlled files.

Sources: .gitignore:1-2, .env.sample:1

Secret Storage Security

Security Considerations:

  1. Filesystem Permissions: The .env file should have restrictive permissions (e.g., chmod 600 .env)
  2. Container Environment: The token is visible via docker inspect cloudflared
  3. Process Environment: The token is visible to processes running as root on the Docker host
  4. Backup Security: Ensure backups of the host system protect the .env file

Sources: .env.sample:1, .gitignore:1


Trust Boundaries

Security Perimeter Mapping

The system defines three trust boundaries:

BoundarySecurity MechanismThreats Mitigated
Internet → CloudflareDDoS protection, WAF, geo-blockingNetwork attacks, malicious traffic
Cloudflare → Docker HostTunnel token authentication, TLS encryptionMan-in-the-middle, unauthorized access
Host → ContainersDocker network isolation, no direct exposureContainer escape attacks
cloudflared → mosquittoNone (trusted internal network)N/A (assumes trusted communication)

Critical Assumption: Traffic that reaches the mosquitto container is assumed trusted because it has passed through the cloudflared proxy, which only accepts traffic from the authenticated Cloudflare Tunnel.

Sources: README.md:15-20, mosquitto.conf:1-6


Security Limitations and Assumptions

Default Configuration Limitations

The system makes the following security trade-offs:

AspectImplementationLimitationRisk Level
MQTT Authenticationallow_anonymous trueNo username/passwordMedium
Topic AuthorizationNo ACL fileAny client can access any topicMedium
Message EncryptionNone at MQTT layerTraffic visible within Docker networkLow
Client IdentificationNo client certificateCannot verify client identityMedium
Rate LimitingCloudflare onlyNo MQTT-level rate limitingLow

Sources: mosquitto.conf:1-6

Appropriate Use Cases

This security model is appropriate for:

  1. Trusted Client Environments - All connecting clients are known and trusted
  2. Single-Tenant Deployments - One user/organization controls all clients
  3. Internal IoT Networks - Devices managed by the same entity
  4. Development/Testing - Non-production environments

Inappropriate Use Cases

This security model is not recommended for:

  1. Multi-Tenant Systems - Multiple independent users/organizations
  2. Public MQTT Brokers - Open access to untrusted clients
  3. Sensitive Data - Financial, health, or personally identifiable information
  4. Compliance Environments - Systems requiring audit trails or access logging

Enhanced Security Options

The repository provides an alternative branch with additional security features:

Protected No-Wildcard Branch

The protected-no-wildcard branch README.md:5-11 implements:

  1. Topic-Based Access Control (ACL)

    • Restricts wildcard subscriptions across users
    • Username-based topic isolation (first topic level = username)
    • ACL file configuration
  2. Encrypted Retained Messages

    • Uses gocryptfs for message encryption at rest
    • Automatic saving after each retained message
    • Encrypted data/ directory

Comparison:

FeatureMain BranchProtected-No-Wildcard Branch
AuthenticationAnonymousRequired (ACL-based)
Topic WildcardsUnrestrictedRestricted per user
Retained MessagesPlaintextEncrypted (gocryptfs)
Access ControlNoneACL file (mosquitto/aclfile)

For implementation details, see Topic Access Control (ACL)) and Encrypted Retained Messages.

Sources: README.md:5-11


Security Best Practices

Deployment Recommendations

Checklist for Production Deployment

Mandatory:

  • Restrict .env file permissions (chmod 600 .env)
  • Review Cloudflare Zero Trust access policies
  • Configure Cloudflare WAF rules if needed
  • Document which clients should have access
  • Plan for token rotation procedures

Recommended:

  • Enable Cloudflare Access policies for additional authentication
  • Implement connection monitoring/alerting
  • Use the protected-no-wildcard branch for multi-user scenarios
  • Configure Docker resource limits
  • Implement backup procedures for retained messages (if used)

Optional:

  • Add TLS termination at Mosquitto level for defense in depth
  • Implement application-layer message encryption
  • Deploy in a dedicated Docker network with network policies
  • Add connection logging and SIEM integration

For production considerations, see Production Considerations.

Sources: README.md:5-11, mosquitto.conf:1-6, .gitignore:1-2