Skip to content

Commit b8679fe

Browse files
committed
fix: [#18] skip firewall configuration in container-based E2E tests
- Remove iptables permission detection fallback from ConfigureFirewallStep - Add TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER env var to explicitly skip firewall step - Accepts only "true" or "false" (case-sensitive, lowercase) for type safety - E2E config tests automatically set the env var to skip firewall playbook - Update documentation in templates/ansible/README.md UFW/iptables requires kernel capabilities (CAP_NET_ADMIN, CAP_NET_RAW) not available in unprivileged Docker containers. Container-based E2E tests now explicitly skip the firewall configuration step while VM-based tests continue to run it normally.
1 parent d51b156 commit b8679fe

File tree

4 files changed

+61
-13
lines changed

4 files changed

+61
-13
lines changed

src/application/command_handlers/configure/handler.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,25 @@ impl ConfigureCommandHandler {
164164
.map_err(|e| (e.into(), current_step))?;
165165

166166
let current_step = ConfigureStep::ConfigureFirewall;
167-
ConfigureFirewallStep::new(Arc::clone(&self.ansible_client))
168-
.execute()
169-
.map_err(|e| (e.into(), current_step))?;
167+
// Allow tests or CI to explicitly skip the firewall configuration step
168+
// (useful for container-based test runs where iptables/ufw require
169+
// elevated kernel capabilities not available in unprivileged containers).
170+
let skip_firewall = std::env::var("TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER")
171+
.map(|v| v == "true")
172+
.unwrap_or(false);
173+
174+
if skip_firewall {
175+
info!(
176+
command = "configure",
177+
step = "configure_firewall",
178+
status = "skipped",
179+
"Skipping UFW firewall configuration due to TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER"
180+
);
181+
} else {
182+
ConfigureFirewallStep::new(Arc::clone(&self.ansible_client))
183+
.execute()
184+
.map_err(|e| (e.into(), current_step))?;
185+
}
170186

171187
// Transition to Configured state
172188
let configured = environment.clone().configured();

src/application/steps/system/configure_firewall.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,22 @@ impl ConfigureFirewallStep {
9999
);
100100

101101
// Run Ansible playbook (SSH port already resolved during template rendering)
102-
self.ansible_client.run_playbook("configure-firewall")?;
103-
104-
info!(
105-
step = "configure_firewall",
106-
status = "success",
107-
"UFW firewall configured successfully with SSH access preserved"
108-
);
109-
110-
Ok(())
102+
match self.ansible_client.run_playbook("configure-firewall") {
103+
Ok(_) => {
104+
info!(
105+
step = "configure_firewall",
106+
status = "success",
107+
"UFW firewall configured successfully with SSH access preserved"
108+
);
109+
Ok(())
110+
}
111+
Err(e) => {
112+
// Propagate errors to the caller. Tests that run in container environments
113+
// should explicitly opt-out of running this step (for example via an
114+
// environment variable) instead of relying on runtime error detection.
115+
Err(e)
116+
}
117+
}
111118
}
112119
}
113120

src/bin/e2e_config_tests.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ struct CliArgs {
112112
pub async fn main() -> Result<()> {
113113
let cli = CliArgs::parse();
114114

115+
// Set environment variable to skip firewall configuration in container-based tests
116+
// UFW/iptables requires kernel capabilities not available in unprivileged containers
117+
std::env::set_var("TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER", "true");
118+
115119
// Initialize logging with production log location for E2E tests using the builder pattern
116120
LoggingBuilder::new(std::path::Path::new("./data/logs"))
117121
.with_format(cli.log_format.clone())

templates/ansible/README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,21 @@ This directory contains Ansible playbook templates for the Torrust Tracker Deplo
2222

2323
- **`wait-cloud-init.yml`** - Waits for cloud-init to complete on newly provisioned VMs
2424

25-
### Configuration Files
25+
### System Configuration
26+
27+
- **`configure-security-updates.yml`** - Configures automatic security updates
28+
29+
- Sets up unattended-upgrades for automatic security patches
30+
31+
- **`configure-firewall.yml.tera`** - Configures UFW (Uncomplicated Firewall) with SSH lockout prevention
32+
33+
- ⚠️ **Critical**: This playbook configures restrictive firewall rules
34+
- Automatically preserves SSH access on the configured port to prevent lockout
35+
- **Container Limitation**: Requires kernel capabilities (CAP_NET_ADMIN, CAP_NET_RAW) not available in unprivileged containers
36+
- **Automatic Skip**: Container-based E2E tests automatically skip this step via `TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER` environment variable
37+
- Accepted values: `"true"` or `"false"` (case-sensitive, lowercase only)
38+
- Example: `TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER=true`
39+
- **VM-only**: This playbook is only executed in VM-based deployments and tests### Configuration Files
2640

2741
- **`ansible.cfg`** - Ansible configuration
2842
- **`inventory.yml.tera`** - Inventory template file (processed by Tera templating engine)
@@ -35,12 +49,19 @@ For a typical deployment:
3549
2. **`update-apt-cache.yml`** - Update package cache (if needed, skip in CI)
3650
3. **`install-docker.yml`** - Install Docker
3751
4. **`install-docker-compose.yml`** - Install Docker Compose (optional)
52+
5. **`configure-security-updates.yml`** - Configure automatic security updates
53+
6. **`configure-firewall.yml.tera`** - Configure UFW firewall (VM-only, skipped in containers)
3854

3955
## CI/Testing Considerations
4056

4157
- The `update-apt-cache.yml` playbook is separated from installation playbooks to avoid CI issues
4258
- In E2E tests, you can skip the cache update step to avoid network timeouts
4359
- The installation playbooks assume the cache is already up-to-date or will handle missing packages gracefully
60+
- **Firewall configuration** is automatically skipped in container-based E2E tests because:
61+
- UFW/iptables require kernel-level capabilities (`CAP_NET_ADMIN`, `CAP_NET_RAW`)
62+
- Docker containers run unprivileged by default and lack these capabilities
63+
- The deployer sets `TORRUST_TD_SKIP_FIREWALL_IN_CONTAINER=true` for container tests (accepts `"true"` or `"false"` only)
64+
- VM-based tests (LXD) have full kernel access and run the firewall playbook normally
4465

4566
## Template Processing
4667

0 commit comments

Comments
 (0)