Skip to content

Commit 46cd008

Browse files
committed
refactor: [#248] implement three-network Docker segmentation for defense in depth
Replace single backend_network with three isolated networks: - database_network: Tracker ↔ MySQL (reduces attack vectors from 3 to 1) - metrics_network: Tracker ↔ Prometheus (metrics isolation) - visualization_network: Prometheus ↔ Grafana (prevents direct access) Security benefits: - MySQL isolation: Only tracker has database access (least privilege) - Metrics isolation: Grafana must query through Prometheus - Lateral movement prevention: Compromised service cannot access unrelated services - Defense in depth: Network segmentation + authentication + Docker port bindings + UFW Changes: - Modified templates/docker-compose/docker-compose.yml.tera - Replaced backend_network with three segmented networks - Added comprehensive security comments explaining topology and benefits - Services now use minimum required networks for their function - Updated src/infrastructure/templating/docker_compose/template/renderer/docker_compose.rs - Fixed test assertion to check for new metrics_network instead of backend_network - Updated docs/issues/248-docker-ufw-firewall-security-strategy.md - Marked Phase 3.2 as complete with all implementation tasks checked References: - ADR: docs/decisions/docker-ufw-firewall-security-strategy.md - Analysis: docs/analysis/security/docker-network-segmentation-analysis.md All tests pass (1562 passed), pre-commit validation successful (4m 32s)
1 parent d6cddb3 commit 46cd008

File tree

3 files changed

+67
-20
lines changed

3 files changed

+67
-20
lines changed

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -308,27 +308,27 @@ services:
308308

309309
This phase is split into two critical subtasks that implement the layered security strategy.
310310

311-
#### Subtask 3.1: Remove Obsolete UFW Firewall Configuration (estimated: 2-3 hours)
311+
#### Subtask 3.1: Remove Obsolete UFW Firewall Configuration (estimated: 2-3 hours) ✅ **COMPLETED**
312312

313313
Since Docker bypasses UFW rules for published container ports, we no longer need UFW rules for application ports. UFW should only manage SSH access.
314314

315315
**Files to Delete**:
316316

317-
- [ ] **Delete firewall configuration playbook**: `templates/ansible/configure-tracker-firewall.yml` - obsolete since Docker bypasses UFW
318-
- [ ] **Delete firewall configuration step**: `src/application/steps/system/configure_tracker_firewall.rs` - tracker ports don't need UFW rules
317+
- [x] **Delete firewall configuration playbook**: `templates/ansible/configure-tracker-firewall.yml` - obsolete since Docker bypasses UFW
318+
- [x] **Delete firewall configuration step**: `src/application/steps/system/configure_tracker_firewall.rs` - tracker ports don't need UFW rules
319319

320320
**Files to Modify**:
321321

322-
- [ ] **Remove playbook registration**: Remove `configure-tracker-firewall.yml` from `src/infrastructure/templating/ansible/template/renderer/project_generator.rs` (in `copy_static_templates` method)
323-
- [ ] **Update base firewall playbook**: Update `templates/ansible/configure-firewall.yml` to clarify it only manages SSH access (not application ports) - add comments explaining Docker bypasses UFW
322+
- [x] **Remove playbook registration**: Remove `configure-tracker-firewall.yml` from `src/infrastructure/templating/ansible/template/renderer/project_generator.rs` (in `copy_static_templates` method)
323+
- [x] **Update base firewall playbook**: Update `templates/ansible/configure-firewall.yml` to clarify it only manages SSH access (not application ports) - add comments explaining Docker bypasses UFW
324324

325325
**Validation**:
326326

327-
- [ ] Compile code and verify no broken references to deleted files
328-
- [ ] Run unit tests: `cargo test`
329-
- [ ] Run linters: `./scripts/pre-commit.sh`
327+
- [x] Compile code and verify no broken references to deleted files
328+
- [x] Run unit tests: `cargo test`
329+
- [x] Run linters: `./scripts/pre-commit.sh`
330330

331-
#### Subtask 3.2: Implement Docker Network Segmentation (estimated: 4-5 hours) 🔴 CRITICAL
331+
#### Subtask 3.2: Implement Docker Network Segmentation (estimated: 4-5 hours) ✅ **COMPLETED**
332332

333333
Implement Option A (Three-Network Segmentation) as documented in [`docs/analysis/security/docker-network-segmentation-analysis.md`](../analysis/security/docker-network-segmentation-analysis.md).
334334

@@ -341,14 +341,15 @@ Implement Option A (Three-Network Segmentation) as documented in [`docs/analysis
341341

342342
**Implementation**:
343343

344-
- [ ] **Update docker-compose template**: Modify `templates/docker-compose/docker-compose.yml.tera`
344+
- [x] **Update docker-compose template**: Modify `templates/docker-compose/docker-compose.yml.tera`
345345
- Replace single `backend_network` with three networks: `database_network`, `metrics_network`, `visualization_network`
346346
- Configure Tracker to use: `database_network` + `metrics_network`
347347
- Configure MySQL to use: `database_network` only
348348
- Configure Prometheus to use: `metrics_network` + `visualization_network`
349349
- Configure Grafana to use: `visualization_network` only
350-
- [ ] **Add security comments**: Document each service's network membership and rationale
351-
- [ ] **Update network definitions**: Define three isolated networks in networks section
350+
- [x] **Add security comments**: Document each service's network membership and rationale
351+
- [x] **Update network definitions**: Define three isolated networks in networks section
352+
- [x] **Update tests**: Fix test assertions to check for new network names
352353

353354
**Expected Network Topology**:
354355

src/infrastructure/templating/docker_compose/template/renderer/docker_compose.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,10 +407,10 @@ mod tests {
407407
"Should depend on tracker"
408408
);
409409

410-
// Verify network
410+
// Verify network segmentation (security enhancement)
411411
assert!(
412-
rendered_content.contains("- backend_network"),
413-
"Should be on backend_network"
412+
rendered_content.contains("- metrics_network"),
413+
"Should be on metrics_network for tracker ↔ Prometheus communication"
414414
);
415415
}
416416

templates/docker-compose/docker-compose.yml.tera

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ services:
3636
- TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH}
3737
- TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN}
3838
networks:
39-
- backend_network
39+
{% if prometheus_config %}
40+
- metrics_network # Prometheus scrapes metrics from tracker
41+
{% endif %}
42+
{% if database.driver == "mysql" %}
43+
- database_network # Tracker connects to MySQL
44+
{% endif %}
4045
ports:
4146
# UDP Tracker Ports (dynamically configured)
4247
{%- for port in ports.udp_tracker_ports %}
@@ -64,7 +69,10 @@ services:
6469
tty: true
6570
restart: unless-stopped
6671
networks:
67-
- backend_network
72+
- metrics_network # Scrapes metrics from tracker
73+
{% if grafana_config %}
74+
- visualization_network # Grafana queries Prometheus
75+
{% endif %}
6876
ports:
6977
- "127.0.0.1:9090:9090" # Localhost only - not exposed to external network
7078
# Grafana accesses Prometheus via Docker network: http://prometheus:9090
@@ -92,7 +100,7 @@ services:
92100
tty: true
93101
restart: unless-stopped
94102
networks:
95-
- backend_network
103+
- visualization_network # Queries Prometheus data source
96104
ports:
97105
- "3100:3000"
98106
environment:
@@ -131,7 +139,7 @@ services:
131139
- MYSQL_USER=${MYSQL_USER}
132140
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
133141
networks:
134-
- backend_network
142+
- database_network # Only accessible by tracker
135143
ports:
136144
- "3306:3306"
137145
volumes:
@@ -149,8 +157,46 @@ services:
149157
max-file: "10"
150158
{% endif %}
151159

160+
# SECURITY: Three-Network Segmentation (Defense in Depth)
161+
# =========================================================
162+
# Network isolation prevents lateral movement between services and reduces attack surface.
163+
# Each service is placed in the minimum networks required for its function.
164+
#
165+
# Network Topology:
166+
# database_network: Tracker ↔ MySQL
167+
# - Only tracker can access MySQL (reduces attack vectors from 3 services to 1)
168+
# - Prometheus/Grafana cannot access database even if compromised
169+
#
170+
# metrics_network: Tracker ↔ Prometheus
171+
# - Prometheus scrapes metrics from tracker
172+
# - Grafana cannot directly access tracker metrics
173+
#
174+
# visualization_network: Prometheus ↔ Grafana
175+
# - Grafana queries Prometheus as data source
176+
# - Grafana cannot access tracker or MySQL directly
177+
#
178+
# Security Benefits:
179+
# 1. MySQL isolation: Only tracker has database access (least privilege)
180+
# 2. Metrics isolation: Grafana must query through Prometheus (no direct tracker access)
181+
# 3. Lateral movement prevention: Compromised service cannot access unrelated services
182+
# 4. Defense in depth: Network segmentation + authentication + Docker port bindings + UFW
183+
#
184+
# See ADR: docs/decisions/docker-ufw-firewall-security-strategy.md
185+
# See Analysis: docs/analysis/security/docker-network-segmentation-analysis.md
186+
152187
networks:
153-
backend_network: {}
188+
{% if database.driver == "mysql" %}
189+
database_network:
190+
driver: bridge
191+
{% endif %}
192+
{% if prometheus_config %}
193+
metrics_network:
194+
driver: bridge
195+
{% endif %}
196+
{% if grafana_config %}
197+
visualization_network:
198+
driver: bridge
199+
{% endif %}
154200

155201
{% if database.driver == "mysql" or grafana_config %}
156202
volumes:

0 commit comments

Comments
 (0)