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 Aspect | Implementation | File Reference |
|---|---|---|
| Inbound Ports | None required on host | N/A |
| Outbound Connection | HTTPS to Cloudflare edge | N/A |
| Container Network | Docker bridge (default) | docker-compose.yml |
| Service Resolution | DNS-based via container name | docker-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:
| Listener | Port | Protocol | Transport Security | Configuration |
|---|---|---|---|---|
| TCP | 1883 | MQTT | None (internal only) | mosquitto.conf1 |
| WebSocket | 9001 | MQTT over WS | Via Cloudflare Tunnel | mosquitto.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:
- Cloudflare Tunnel provides network-level access control - Only authorized clients can reach the broker through the configured public hostname
- All traffic reaching the broker is trusted - The broker does not differentiate between clients
- 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 Control | Status | Implication |
|---|---|---|
| Username/Password | Disabled | Any client can connect |
| ACL (Topic Permissions) | None | All clients can access all topics |
| Client ID Restrictions | None | Any client ID accepted |
| Connection Limits | None (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
| File | Version Controlled | Contains Secrets | Purpose |
|---|---|---|---|
.env.sample | Yes | No | Template/documentation |
.env | No (ignored) | Yes | Actual secrets |
docker-compose.yml | Yes | No | References ${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:
- Filesystem Permissions: The
.envfile should have restrictive permissions (e.g.,chmod 600 .env) - Container Environment: The token is visible via
docker inspect cloudflared - Process Environment: The token is visible to processes running as root on the Docker host
- Backup Security: Ensure backups of the host system protect the
.envfile
Sources: .env.sample:1, .gitignore:1
Trust Boundaries
Security Perimeter Mapping
The system defines three trust boundaries:
| Boundary | Security Mechanism | Threats Mitigated |
|---|---|---|
| Internet → Cloudflare | DDoS protection, WAF, geo-blocking | Network attacks, malicious traffic |
| Cloudflare → Docker Host | Tunnel token authentication, TLS encryption | Man-in-the-middle, unauthorized access |
| Host → Containers | Docker network isolation, no direct exposure | Container escape attacks |
| cloudflared → mosquitto | None (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:
| Aspect | Implementation | Limitation | Risk Level |
|---|---|---|---|
| MQTT Authentication | allow_anonymous true | No username/password | Medium |
| Topic Authorization | No ACL file | Any client can access any topic | Medium |
| Message Encryption | None at MQTT layer | Traffic visible within Docker network | Low |
| Client Identification | No client certificate | Cannot verify client identity | Medium |
| Rate Limiting | Cloudflare only | No MQTT-level rate limiting | Low |
Sources: mosquitto.conf:1-6
Appropriate Use Cases
This security model is appropriate for:
- Trusted Client Environments - All connecting clients are known and trusted
- Single-Tenant Deployments - One user/organization controls all clients
- Internal IoT Networks - Devices managed by the same entity
- Development/Testing - Non-production environments
Inappropriate Use Cases
This security model is not recommended for:
- Multi-Tenant Systems - Multiple independent users/organizations
- Public MQTT Brokers - Open access to untrusted clients
- Sensitive Data - Financial, health, or personally identifiable information
- 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:
-
Topic-Based Access Control (ACL)
- Restricts wildcard subscriptions across users
- Username-based topic isolation (first topic level = username)
- ACL file configuration
-
Encrypted Retained Messages
- Uses
gocryptfsfor message encryption at rest - Automatic saving after each retained message
- Encrypted
data/directory
- Uses
Comparison:
| Feature | Main Branch | Protected-No-Wildcard Branch |
|---|---|---|
| Authentication | Anonymous | Required (ACL-based) |
| Topic Wildcards | Unrestricted | Restricted per user |
| Retained Messages | Plaintext | Encrypted (gocryptfs) |
| Access Control | None | ACL 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
.envfile 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-wildcardbranch 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