Skip to content

Commit 11165fb

Browse files
committed
docs: [#248] update security guide with Docker/UFW layered approach
Updated docs/user-guide/security.md to reflect the correct Docker/UFW security architecture after discovering Docker bypasses UFW rules. Changes: - Documented layered security approach (UFW + Docker networking) - Explained why UFW cannot protect Docker-published ports - Added service exposure strategy (Public/Localhost/Internal) - Documented three-network segmentation architecture - Added security best practices and warnings - Removed incorrect assumptions about UFW protecting Docker ports - Added DO/DON'T sections for security configuration Security architecture now correctly documents: ✅ UFW protects SSH access (instance-level) ✅ Docker port bindings control service exposure (service-level) ✅ Network segmentation isolates service communication ✅ 66% reduction in MySQL attack surface (3→1 service) Phase 5 (Documentation) in progress - user security guide updated.
1 parent 317c47f commit 11165fb

File tree

2 files changed

+173
-51
lines changed

2 files changed

+173
-51
lines changed

docs/issues/248-docker-ufw-firewall-security-strategy.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,12 @@ If network segmentation breaks functionality:
496496
- [ ] Add validation logic to detect misconfigured port bindings (future work)
497497
- [ ] Document testing procedures in `docs/e2e-testing/`
498498

499-
### Phase 5: Documentation and Review (estimated: 1-2 hours)
499+
### Phase 5: Documentation and Review (estimated: 1-2 hours) 🚧 **IN PROGRESS**
500500

501-
- [ ] **Review and update user security guide**: Review `docs/user-guide/security.md` and verify it aligns with the new Docker/UFW security strategy - update any outdated assumptions about UFW protecting Docker ports
502-
- [ ] Update user guide with security strategy explanation
503-
- [ ] Document deployment security best practices
504-
- [ ] Add warnings about Docker port binding risks
501+
- [x] **Review and update user security guide**: Review `docs/user-guide/security.md` and verify it aligns with the new Docker/UFW security strategy - update any outdated assumptions about UFW protecting Docker ports
502+
- [x] Update user guide with security strategy explanation
503+
- [x] Document deployment security best practices
504+
- [x] Add warnings about Docker port binding risks
505505
- [ ] Create troubleshooting guide for firewall issues
506506
- [ ] Review all documentation for accuracy and completeness
507507
- [ ] Security audit of final implementation

docs/user-guide/security.md

Lines changed: 168 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,97 +8,219 @@ Security is a critical aspect of production deployments. The Torrust Tracker Dep
88

99
## Firewall Configuration
1010

11-
### Automatic Firewall Setup
11+
### Layered Security Approach
12+
13+
**CRITICAL**: The deployer uses a **layered security approach** combining UFW firewall and Docker networking to protect your deployment. Understanding how these layers work together is essential for secure deployments.
14+
15+
#### Security Architecture
16+
17+
The deployer implements security at two levels:
18+
19+
1. **Instance-Level Security (UFW)** - Protects the VM itself
20+
21+
- Denies all incoming traffic by default
22+
- Allows only SSH access for administration
23+
- **Does NOT control Docker container ports** (Docker bypasses UFW)
24+
25+
2. **Service-Level Security (Docker)** - Controls service exposure
26+
- Public services have explicit port bindings (Tracker, Grafana)
27+
- Internal services have NO port bindings (MySQL)
28+
- Localhost-only services bind to `127.0.0.1` (Prometheus)
29+
- Docker network segmentation isolates service communication
30+
31+
#### Why UFW Cannot Protect Docker Ports
32+
33+
**Important**: Docker manipulates iptables directly and **bypasses UFW rules** for published container ports. This is documented behavior (see [Docker documentation](https://docs.docker.com/engine/network/packet-filtering-firewalls/)).
34+
35+
```yaml
36+
# This port binding BYPASSES UFW firewall rules
37+
services:
38+
mysql:
39+
ports:
40+
- "3306:3306" # ⚠️ PUBLICLY ACCESSIBLE despite UFW rules!
41+
```
1242
13-
**CRITICAL**: The `configure` command automatically configures UFW (Uncomplicated Firewall) on virtual machines to protect internal services from unauthorized external access.
43+
Docker routes container traffic in the NAT table, meaning packets are diverted before reaching the INPUT and OUTPUT chains that UFW uses. Therefore:
1444
15-
During the `configure` step, the deployer:
45+
- ✅ UFW protects the VM and SSH access
46+
- ❌ UFW **does not** protect Docker-published ports
47+
- ✅ Docker port bindings control service exposure
48+
49+
### Automatic Firewall Setup
50+
51+
During the `configure` command, the deployer:
1652

1753
1. **Installs UFW** - Ensures the firewall is available
1854
2. **Sets restrictive policies** - Denies all incoming traffic by default
1955
3. **Allows SSH access** - Preserves SSH connectivity (configured port)
20-
4. **Allows tracker services** - Opens only necessary tracker ports:
21-
- UDP tracker ports (configured in environment)
22-
- HTTP tracker ports (configured in environment)
23-
- HTTP API port (configured in environment)
24-
5. **Enables the firewall** - Activates rules to protect the system
56+
4. **Enables the firewall** - Activates rules to protect SSH access
57+
58+
**Note**: UFW only controls SSH access. Application ports are controlled by Docker port bindings in the docker-compose configuration.
2559

26-
### Why Firewall Configuration Matters
60+
###Service Exposure Strategy
2761

28-
The Docker Compose configuration (`templates/docker-compose/docker-compose.yml.tera`) exposes several service ports that should **NOT** be publicly accessible:
62+
### Service Exposure Strategy
2963

30-
**Exposed Ports in Docker Compose**:
64+
The Docker Compose configuration (`templates/docker-compose/docker-compose.yml.tera`) controls which services are accessible from the internet through **explicit port bindings**:
65+
66+
**Service Exposure Levels**:
3167

3268
```yaml
3369
services:
34-
# Tracker - Public ports (UDP/HTTP tracker, HTTP API)
70+
# ✅ PUBLIC SERVICES - Explicit port bindings
3571
tracker:
3672
ports:
37-
- "6969:6969/udp" # ✅ Public - UDP tracker
38-
- "7070:7070" # ✅ Public - HTTP tracker
39-
- "1212:1212" # ✅ Public - HTTP API
73+
- "6969:6969/udp" # Public - UDP tracker
74+
- "7070:7070" # Public - HTTP tracker
75+
- "1212:1212" # Public - REST API
76+
77+
grafana:
78+
ports:
79+
- "3100:3000" # Public - Monitoring UI (authenticated)
4080
41-
# Prometheus - INTERNAL ONLY
81+
# 🔒 LOCALHOST-ONLY SERVICES - Bound to 127.0.0.1
4282
prometheus:
4383
ports:
44-
- "9090:9090" # ⚠️ INTERNAL - Metrics UI
84+
- "127.0.0.1:9090:9090" # Accessible only from VM host
4585
46-
# MySQL - INTERNAL ONLY
86+
# 🔒 INTERNAL-ONLY SERVICES - No port bindings
87+
mysql:
88+
# No ports section - completely internal
89+
# Accessed via Docker network: mysql:3306
90+
```
91+
92+
**Security Properties**:
93+
94+
- **Public Services** - Have `ports:` section binding to `0.0.0.0` (accessible externally)
95+
- **Localhost Services** - Bind to `127.0.0.1` (accessible only from VM host via SSH)
96+
- **Internal Services** - No port bindings (accessible only via Docker internal networks)
97+
98+
### Network Segmentation
99+
100+
The deployer implements **three isolated Docker networks** for defense-in-depth security:
101+
102+
```yaml
103+
networks:
104+
database_network: # Tracker ↔ MySQL only
105+
metrics_network: # Tracker ↔ Prometheus only
106+
visualization_network: # Prometheus ↔ Grafana only
107+
108+
services:
109+
tracker:
110+
networks:
111+
- database_network # Can access MySQL
112+
- metrics_network # Can be scraped by Prometheus
113+
114+
mysql:
115+
networks:
116+
- database_network # Isolated - only Tracker can access
117+
118+
prometheus:
119+
networks:
120+
- metrics_network # Can scrape Tracker metrics
121+
- visualization_network # Can be queried by Grafana
122+
123+
grafana:
124+
networks:
125+
- visualization_network # Can query Prometheus only
126+
```
127+
128+
**Security Benefits**:
129+
130+
1. **Reduced Attack Surface**: MySQL accessible from 1 service (Tracker) instead of 3 services
131+
2. **Lateral Movement Prevention**: Compromised Grafana cannot access MySQL or Tracker
132+
3. **Principle of Least Privilege**: Services can only communicate where necessary
133+
4. **Compliance**: Aligns with PCI DSS, NIST 800-53, CIS Docker Benchmark
134+
135+
### Security Comparison
136+
137+
**Without proper configuration** ⚠️:
138+
139+
```yaml
140+
# INSECURE - All services on one network with public port bindings
141+
services:
47142
mysql:
48143
ports:
49-
- "3306:3306" # ⚠️ INTERNAL - Database
144+
- "3306:3306" # ⚠️ MySQL publicly accessible!
145+
networks:
146+
- backend_network
50147
```
51148

52-
**Without firewall protection**, services like Prometheus (port 9090) and MySQL (port 3306) would be accessible from the internet, potentially exposing:
149+
- ❌ Internal services exposed to internet
150+
- ❌ All services can communicate (no segmentation)
151+
- ❌ Docker bypasses UFW firewall rules
152+
- ❌ High attack surface
53153

54-
- **Prometheus** - Internal metrics, performance data, system topology
55-
- **MySQL** - Database access (even with authentication, this is a security risk)
154+
**With deployer configuration** ✅:
56155

57-
**With firewall protection** (UFW configured by `configure` command):
156+
```yaml
157+
# SECURE - Network segmentation + no public port bindings
158+
services:
159+
mysql:
160+
# No ports section - internal only
161+
networks:
162+
- database_network # Only Tracker can access
163+
```
58164

59-
- **Tracker ports** - Accessible externally (UDP tracker, HTTP tracker, HTTP API)
60-
- 🔒 **Prometheus port** - Blocked from external access
61-
- 🔒 **MySQL port** - Blocked from external access
62-
- **SSH access** - Preserved for administration
165+
- Internal services not publicly accessible
166+
- ✅ Network segmentation limits lateral movement
167+
- ✅ UFW protects SSH access
168+
- Reduced attack surface
63169

64170
### E2E Testing vs Production
65171

66172
**E2E Testing (Docker Containers)**:
67173

68-
- Uses Docker containers instead of VMs for faster test execution
69-
- Firewall **NOT** configured inside containers (containers provide isolation)
70-
- Services exposed for testing purposes
71-
- ⚠️ **NOT suitable for production use**
174+
- Uses Docker containers for faster test execution
175+
- Firewall **not configured** inside containers (container isolation sufficient)
176+
- Services may be exposed for testing purposes
177+
- ⚠️ **NOT production-grade security**
72178

73179
**Production Deployments (Virtual Machines)**:
74180

75181
- Uses real VMs (LXD, cloud providers)
76-
- Firewall **automatically configured** by `configure` command
77-
- Only tracker services exposed externally
78-
- ✅ **Production-ready security posture**
182+
- UFW **configured automatically** for SSH protection
183+
- Docker port bindings control service exposure
184+
- Network segmentation isolates services
185+
- ✅ **Production-ready security**
79186

80187
### Firewall Rules Applied
81188

82-
The deployer configures these firewall rules during the `configure` step:
189+
The deployer configures these UFW firewall rules during `configure`:
83190

84191
```bash
85-
# SSH Access (required for management)
192+
# SSH Access (required for administration)
86193
ufw allow <ssh-port>/tcp
87194
88-
# UDP Tracker Ports (configured in environment)
89-
ufw allow <udp-port>/udp
90-
91-
# HTTP Tracker Ports (configured in environment)
92-
ufw allow <http-port>/tcp
93-
94-
# HTTP API Port (configured in environment)
95-
ufw allow <api-port>/tcp
96-
97195
# Default policies
98-
ufw default deny incoming # Block everything else
196+
ufw default deny incoming # Block all incoming traffic
99197
ufw default allow outgoing # Allow outbound connections
198+
ufw enable # Activate firewall
100199
```
101200

201+
**Note**: Application ports (Tracker, Grafana, Prometheus, MySQL) are **not** managed by UFW. They are controlled by Docker port bindings in the docker-compose.yml configuration.
202+
203+
### Security Best Practices
204+
205+
**DO**:
206+
207+
- ✅ Use the deployer's default docker-compose template (network segmentation included)
208+
- ✅ Review port bindings before deploying (`build/{env}/docker-compose/docker-compose.yml`)
209+
- ✅ Keep internal services without port bindings (MySQL)
210+
- ✅ Use `127.0.0.1` bindings for localhost-only access (Prometheus)
211+
- ✅ Apply security updates to the VM regularly
212+
- ✅ Use strong SSH keys and disable password authentication
213+
- ✅ Monitor logs for suspicious activity
214+
215+
**DON'T**:
216+
217+
- ❌ Add port bindings to internal services (e.g., `3306:3306` for MySQL)
218+
- ❌ Disable UFW firewall on production VMs
219+
- ❌ Remove network segmentation from docker-compose.yml
220+
- ❌ Assume UFW protects Docker-published ports
221+
- ❌ Expose Prometheus/MySQL publicly
222+
- ❌ Use default passwords for services
223+
102224
### Verifying Firewall Configuration
103225

104226
After running the `configure` command, verify firewall rules:

0 commit comments

Comments
 (0)