Files
f2b/docs/security.md
Ismo Vuorinen 98b53d84b5 fix: repair Renovate config, convert Makefile to go run, update GitHub Actions (#120)
* fix: repair Renovate config and convert Makefile to go run (#117)

- Remove non-existent `github>renovatebot/presets:golang` preset that
  broke Renovate PR creation
- Replace deprecated `fileMatch` with `managerFilePatterns` in
  customManagers
- Rewrite regex to match new Makefile pattern (renovate comment above
  version variable assignment)
- Fix `matchFileNames` glob pattern (`*.mk` -> `**/*.mk`)
- Convert all tool invocations from `go install` + global binary to
  `go run tool@version` for reproducible builds
- Convert npm global tools to `npx --yes` invocations
- Remove `dev-deps` and `check-deps` targets (tools auto-download)
- Add mdformat pre-commit hook with GFM support and config
- Add `fmt-md` Makefile target for manual markdown formatting
- Update local golangci-lint pre-commit hook to use `go run`
- Apply golangci-lint v2.10.1 auto-fixes (fmt.Fprintf optimization)
- Add nolint:gosec annotations for legitimate exec.Command usage
- Exclude .serena/ from mdformat and megalinter
- Add markdown indent_size=unset in .editorconfig for CommonMark compat

* chore(deps): update GitHub Actions to latest versions

- anthropics/claude-code-action: v1.0.34 -> v1.0.64
- actions/setup-go: v6.2.0 -> v6.3.0
- actions/upload-artifact: v6.0.0 -> v7.0.0
- goreleaser/goreleaser-action: v6.4.0 -> v7.0.0
- docker/login-action: v3.6.0 -> v3.7.0
- ivuorinen/actions: v2026.01.21 -> v2026.02.24

* fix: address code review feedback

- Fix issue template YAML frontmatter (replace underscore separators
  with proper --- delimiters); exclude templates from mdformat
- Replace string(rune(n)) with strconv.Itoa(n) in test files to produce
  deterministic numeric directory names instead of Unicode characters
- Remove stale `make dev-deps` reference in README, replace with
  `make dev-setup`
- Extract ban/unban format strings into shared.MetricsFmtBanOperations
  and shared.MetricsFmtUnbanOperations constants
- Replace hardcoded coverage percentages in README with evergreen
  phrasing

* fix: address round 2 code review feedback for PR #120

- Fix corrupted path traversal example in docs/security.md
- Fix Renovate .mk regex to match nested paths (.*\.mk$)
- Update checkmake pre-commit hook to v0.3.2 to match Makefile
- Add sync.WaitGroup to unsynchronized goroutines in security tests
- Fix fmt-md target to use pre-commit run mdformat
- Pin markdownlint-cli2 to v0.21.0 in lint-md target
- Standardize //nolint:gosec to // #nosec annotations for gosec CLI

* fix(ci): install PyYAML dependency for PR lint workflow

The pr-lint workflow uses ivuorinen/actions/pr-lint which internally
calls validate-inputs running a Python script that imports yaml.
Python was set up but PyYAML was never installed, causing
ModuleNotFoundError at runtime.

* fix: address round 3 code review feedback for PR #120

- Wrap Windows-style path traversal example in backtick code span so
  backslashes render literally in docs/security.md
- Add Renovate-managed MARKDOWNLINT_CLI2_VERSION variable in Makefile
  to match the pattern used by all other tool versions
2026-03-01 19:09:17 +02:00

487 lines
16 KiB
Markdown

# Security Guide
## Security Model
f2b is designed with security as a fundamental principle. The tool handles privileged operations safely
while maintaining usability and providing clear security boundaries. Enhanced with context-aware timeout
handling, comprehensive path traversal protection, and advanced security testing with extensive
sophisticated attack vectors.
### Threat Model
**Assumptions:**
- Users may have varying privilege levels (root, sudo, regular user)
- Input may be malicious or crafted to exploit vulnerabilities
- The system may be under attack when f2b is used for incident response
- Tests should never compromise the host system
- Operations may timeout or hang, requiring graceful handling
- Advanced path traversal attacks using Unicode normalization and mixed cases may be attempted
**Protected Assets:**
- System integrity through safe privilege escalation with timeout protection
- Fail2Ban configuration and state
- User data and system logs
- Test environment isolation with comprehensive mock setup
- Path traversal protection against sophisticated attack vectors
- Context-aware operations preventing resource exhaustion
## Privilege Management
### Automatic Privilege Detection
f2b intelligently manages sudo requirements through a comprehensive privilege checking system:
#### User Categories
- **Root users (UID 0)**: Commands run directly without sudo
- **Sudo group members**: Automatic escalation for privileged operations
- **Users with sudo access**: Detected via `sudo -n true` test
- **Regular users**: Clear error messages with guidance
#### Command Classification
**Require sudo:**
- `ban`, `unban` operations
- `service` control commands
- Configuration modifications
**No sudo needed:**
- `status`, `list-jails`, `test`
- `logs`, `version`, `completion`
- Read-only operations
### Privilege Escalation Process
1. **Pre-flight Check**: Determine user capabilities before command execution
1. **Context Creation**: Create context with timeout for the operation
1. **Command Classification**: Identify if the operation requires privileges
1. **Smart Escalation**: Only add sudo when necessary for specific commands
1. **Validation**: Ensure privilege escalation succeeded with timeout protection
1. **Execution**: Run command with appropriate privileges and context
1. **Timeout Handling**: Gracefully handle hanging operations with cancellation
1. **Audit**: Log privileged operations with context information
### Error Handling
When privileges are insufficient:
```text
Error: fail2ban operations require sudo privileges. Current user: username (UID: 1000).
Please run with sudo or ensure user is in sudo group
Hint: Try running with 'sudo' or ensure your user is in the sudo group
Example: sudo f2b ban 192.168.1.100
```
## Input Validation
### IP Address Validation
Comprehensive validation with caching prevents injection attacks:
```go
func ValidateIP(ip string) error {
if ip == "" {
return fmt.Errorf("IP address cannot be empty")
}
// Check validation cache first for performance
if IsIPValidCached(ip) {
return nil
}
// Check for valid IPv4 or IPv6 address
parsed := net.ParseIP(ip)
if parsed == nil {
return fmt.Errorf("invalid IP address: %s", ip)
}
// Cache successful validation
CacheIPValidation(ip, true)
return nil
}
```
**Protected against:**
- Command injection via IP parameters
- Path traversal attempts
- Buffer overflow attacks
- Format string vulnerabilities
- Performance degradation through validation caching
### Jail Name Validation
Prevents directory traversal and command injection:
```go
func ValidateJail(jail string) error {
if jail == "" {
return fmt.Errorf("jail name cannot be empty")
}
// Allow only alphanumeric, dash, underscore, dot
validJailRegex := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
if !validJailRegex.MatchString(jail) {
return fmt.Errorf("invalid jail name: %s", jail)
}
return nil
}
```
### Advanced Path Traversal Protection
Comprehensive protection against sophisticated path traversal attacks:
```go
func ValidateFilter(filter string) error {
if filter == "" {
return fmt.Errorf("filter name cannot be empty")
}
// Path traversal protection checking for:
// - Basic directory traversal (..)
// - URL encoding (%2e%2e, %2f, %5c)
// - Null byte injection (\x00)
// - Unicode normalization attacks (\u002e\u002e, \u002f, \u005c)
if containsPathTraversal(filter) {
return fmt.Errorf("invalid filter name contains path traversal: %s", filter)
}
return nil
}
func containsPathTraversal(path string) bool {
// Comprehensive path traversal detection
dangerous := []string{
"..", "\x00",
"%2e%2e", "%2f", "%5c",
"\u002e\u002e", "\u002f", "\u005c",
}
normalized := strings.ToLower(path)
for _, pattern := range dangerous {
if strings.Contains(normalized, pattern) {
return true
}
}
return false
}
```
## Safe Command Execution
### Argument Array Pattern
**Never use shell string concatenation:**
```go
// DANGEROUS - DON'T DO THIS
cmd := exec.Command("sh", "-c", fmt.Sprintf("fail2ban-client ban %s %s", ip, jail))
// SAFE - Use argument arrays
cmd := exec.Command("fail2ban-client", "ban", ip, jail)
```
### Context-Aware Secure Runner Interface
The `Runner` interface provides safe command execution with timeout handling:
```go
type Runner interface {
CombinedOutput(name string, args ...string) ([]byte, error)
CombinedOutputWithSudo(name string, args ...string) ([]byte, error)
CombinedOutputWithContext(ctx context.Context, name string, args ...string) ([]byte, error)
CombinedOutputWithSudoContext(ctx context.Context, name string, args ...string) ([]byte, error)
}
```
### Context-Aware Implementation
```go
func (r *RealRunner) CombinedOutputWithSudoContext(ctx context.Context, name string, args ...string) ([]byte, error) {
// Validate inputs
if name == "" {
return nil, fmt.Errorf("command name cannot be empty")
}
// Build command with argument array
cmdArgs := append([]string{name}, args...)
cmd := exec.CommandContext(ctx, "sudo", cmdArgs...)
// Execute safely with timeout protection
output, err := cmd.CombinedOutput()
if ctx.Err() != nil {
return nil, fmt.Errorf("command timeout: %w", ctx.Err())
}
return output, err
}
```
**Security enhancements:**
- Context-based timeout prevention
- Graceful cancellation of hanging operations
- Resource cleanup on timeout
- Enhanced error reporting with context information
## Testing Security
### Mock-Only Testing
**Critical Rule**: Never execute real sudo commands in tests
```go
// CORRECT - Use modern standardized helpers with context support
func TestBanCommand_WithPrivileges(t *testing.T) {
// Modern standardized setup with automatic cleanup and context support
_, cleanup := fail2ban.SetupMockEnvironmentWithSudo(t, true)
defer cleanup()
// Create context with timeout for the test
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
// Test implementation with context-aware operations
err := client.BanIPWithContext(ctx, "192.168.1.100", "sshd")
// Test assertions...
}
```
### Advanced Security Test Coverage
The system includes comprehensive security testing with extensive sophisticated attack vectors:
```go
func TestPathTraversalProtection(t *testing.T) {
testCases := []struct {
name string
input string
expect bool // true if should be blocked
}{
{"Basic traversal", "../../../etc/passwd", true},
{"URL encoded", "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd", true},
{"Null byte injection", "valid\x00/../../../etc/passwd", true},
{"Unicode normalization", "/var/log/\u002e\u002e/\u002e\u002e/etc/passwd", true},
{"Mixed case", "/var/LOG/../../../etc/passwd", true},
{"Multiple slashes", "/var/log////../../etc/passwd", true},
{"Windows style", "/var/log\\..\\..\\..\etc\passwd", true},
{"Valid path", "/var/log/fail2ban.log", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
blocked := containsPathTraversal(tc.input)
assert.Equal(t, tc.expect, blocked)
})
}
}
```
### Test Environment Isolation
```go
func setupSecureTestEnvironment(t *testing.T) {
// Modern standardized setup with complete isolation and context support
_, cleanup := fail2ban.SetupMockEnvironmentWithSudo(t, true)
defer cleanup()
// All mock environment is configured with:
// - Proper isolation and privilege handling
// - Context-aware timeout operations
// - Thread-safe mock operations
// - Comprehensive path traversal protection testing
}
```
## Security Checklist
### For Contributors
**Before submitting code:**
- [ ] All user input is validated before use with caching where appropriate
- [ ] No shell string concatenation used
- [ ] Privilege escalation only when necessary with timeout protection
- [ ] Tests use mocks exclusively with context support
- [ ] No hardcoded credentials or paths
- [ ] Error messages don't leak sensitive information
- [ ] Input sanitization prevents injection attacks including advanced path traversal
- [ ] Context-aware operations implemented with proper timeout handling
- [ ] Path traversal protection covers all sophisticated attack vectors
- [ ] Thread-safe operations for concurrent access
### For Security-Critical Changes
**Additional requirements:**
- [ ] Threat model updated if attack surface changes
- [ ] Security tests added for new attack vectors with context support
- [ ] Privilege boundaries clearly documented with timeout behavior
- [ ] Code review by maintainer required
- [ ] Integration tests verify security behavior including timeout scenarios
- [ ] Path traversal protection tested against sophisticated attack vectors
- [ ] Context-aware timeout handling properly implemented
- [ ] Thread safety verified for concurrent operations
## Known Security Issues (Fixed)
### Historical Vulnerabilities
#### 1. Sudo Timeout (Fixed)
- **Issue**: Infinite wait on sudo prompt
- **Impact**: Denial of service via hanging processes
- **Fix**: 5-second timeout added to `CanUseSudo()`
#### 2. Service Command Injection (Fixed)
- **Issue**: Insufficient validation of service actions
- **Impact**: Command injection via service parameters
- **Fix**: Strict action validation implemented
#### 3. Memory Exhaustion (Fixed)
- **Issue**: Unbounded log reading
- **Impact**: Memory exhaustion via large log files
- **Fix**: Incremental reading with 1000 lines/100MB limits
#### 4. Path Traversal (Enhanced Protection)
- **Issue**: Insufficient path validation against sophisticated attacks
- **Impact**: Access to files outside intended directories
- **Fix**: Comprehensive path traversal protection with extensive test cases covering:
- Unicode normalization attacks (\\u002e\\u002e)
- Mixed case traversal (/var/LOG/../../../etc/passwd)
- Multiple slashes (/var/log////../../etc/passwd)
- Windows-style paths on Unix (`/var/log\..\..\..\etc\passwd`)
- URL encoding variants (%2e%2e%2f)
- Null byte injection attacks
#### 5. Race Conditions (Fixed)
- **Issue**: Concurrent access to shared state
- **Impact**: Data corruption in multi-threaded scenarios
- **Fix**: Thread-safe runner management with RWMutex and atomic operations
#### 6. Hanging Operations (Fixed)
- **Issue**: Operations could hang indefinitely without timeout protection
- **Impact**: Resource exhaustion and denial of service
- **Fix**: Context-aware operations with configurable timeouts and graceful cancellation
## Security Architecture
### Defense in Depth
1. **Input Validation**: First line of defense against malicious input with caching
1. **Advanced Path Traversal Protection**: Extensive sophisticated attack vector protection
1. **Privilege Validation**: Ensure user has necessary permissions with timeout protection
1. **Context-Aware Execution**: Use argument arrays with timeout and cancellation support
1. **Safe Execution**: Never use shell strings, always use context-aware operations
1. **Error Handling**: Fail safely without information leakage, include context information
1. **Audit Logging**: Track privileged operations with contextual information
1. **Test Isolation**: Prevent test-time security compromises with comprehensive mocks
1. **Performance Security**: Validation caching prevents DoS through repeated validation
1. **Timeout Protection**: Prevent resource exhaustion through hanging operations
### Security Boundaries
```text
User Input → Context → Validation → Path Traversal → Privilege Check → Safe Execution → Timeout → Audit
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
Sanitize → Create → Cache Check → Block Attack → Verify Perms → Exec w/Context → Cancel → Log
```
**Enhanced Security Flow:**
1. **Context Creation**: Establish timeout and cancellation context
1. **Input Sanitization**: Clean and validate all user input
1. **Cache Validation**: Check validation cache for performance and DoS protection
1. **Path Traversal Protection**: Block extensive sophisticated attack vectors
1. **Privilege Verification**: Confirm user permissions with timeout protection
1. **Context-Aware Execution**: Execute with timeout and cancellation support
1. **Timeout Handling**: Gracefully handle hanging operations
1. **Comprehensive Auditing**: Log all operations with context information
## Incident Response
### Security Issue Reporting
**For security vulnerabilities:**
1. **Do not** open public GitHub issues
1. Email: `ismo@ivuorinen.net` with subject "SECURITY: f2b vulnerability"
1. Include: Description, impact assessment, reproduction steps
1. Expect: Acknowledgment within 48 hours
### Security Update Process
1. **Assessment**: Evaluate impact and affected versions
1. **Development**: Create fix with security tests
1. **Testing**: Comprehensive security testing
1. **Release**: Coordinated disclosure with security advisory
1. **Communication**: Notify users via GitHub security advisories
## Security Best Practices
### For Users
- Run with minimal privileges necessary
- Regularly update to latest version
- Monitor logs for unexpected privilege escalations
- Use structured logging for audit trails
- Validate f2b binary checksums after download
### For Developers
- Follow secure coding guidelines
- Use static analysis tools (gosec, golangci-lint)
- Implement comprehensive security tests
- Document security assumptions
- Regular security code reviews
### For Deployment
- Use principle of least privilege
- Monitor privileged command execution
- Implement log aggregation and monitoring
- Regular security updates
- Network segmentation where applicable
## Security Monitoring
### Audit Points
- Privilege escalation attempts
- Failed authentication events
- Malformed input attempts
- Unusual command patterns
- File access outside expected directories
### Logging Security Events
```go
logger.WithFields(logrus.Fields{
"user": os.Getenv("USER"),
"uid": os.Getuid(),
"command": "ban",
"target_ip": ip,
"jail": jail,
"sudo_used": true,
}).Info("Privileged operation executed")
```
This comprehensive security model ensures f2b can be used safely in production environments
while maintaining the flexibility needed for effective Fail2Ban management. The enhanced security
features include context-aware timeout handling, sophisticated path traversal protection with
extensive attack vector coverage, performance-optimized validation caching, and comprehensive
audit logging for enterprise-grade security monitoring.