* 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
16 KiB
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 truetest - Regular users: Clear error messages with guidance
Command Classification
Require sudo:
ban,unbanoperationsservicecontrol commands- Configuration modifications
No sudo needed:
status,list-jails,testlogs,version,completion- Read-only operations
Privilege Escalation Process
- Pre-flight Check: Determine user capabilities before command execution
- Context Creation: Create context with timeout for the operation
- Command Classification: Identify if the operation requires privileges
- Smart Escalation: Only add sudo when necessary for specific commands
- Validation: Ensure privilege escalation succeeded with timeout protection
- Execution: Run command with appropriate privileges and context
- Timeout Handling: Gracefully handle hanging operations with cancellation
- Audit: Log privileged operations with context information
Error Handling
When privileges are insufficient:
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:
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:
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:
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:
// 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:
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
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
// 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:
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
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
- Input Validation: First line of defense against malicious input with caching
- Advanced Path Traversal Protection: Extensive sophisticated attack vector protection
- Privilege Validation: Ensure user has necessary permissions with timeout protection
- Context-Aware Execution: Use argument arrays with timeout and cancellation support
- Safe Execution: Never use shell strings, always use context-aware operations
- Error Handling: Fail safely without information leakage, include context information
- Audit Logging: Track privileged operations with contextual information
- Test Isolation: Prevent test-time security compromises with comprehensive mocks
- Performance Security: Validation caching prevents DoS through repeated validation
- Timeout Protection: Prevent resource exhaustion through hanging operations
Security Boundaries
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:
- Context Creation: Establish timeout and cancellation context
- Input Sanitization: Clean and validate all user input
- Cache Validation: Check validation cache for performance and DoS protection
- Path Traversal Protection: Block extensive sophisticated attack vectors
- Privilege Verification: Confirm user permissions with timeout protection
- Context-Aware Execution: Execute with timeout and cancellation support
- Timeout Handling: Gracefully handle hanging operations
- Comprehensive Auditing: Log all operations with context information
Incident Response
Security Issue Reporting
For security vulnerabilities:
- Do not open public GitHub issues
- Email:
ismo@ivuorinen.netwith subject "SECURITY: f2b vulnerability" - Include: Description, impact assessment, reproduction steps
- Expect: Acknowledgment within 48 hours
Security Update Process
- Assessment: Evaluate impact and affected versions
- Development: Create fix with security tests
- Testing: Comprehensive security testing
- Release: Coordinated disclosure with security advisory
- 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
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.