Skip to content

Add toride-fail2ban crate with feature-gated modules - #3

Merged
hmziqrs merged 25 commits into
masterfrom
worktree-fail2ban
May 31, 2026
Merged

Add toride-fail2ban crate with feature-gated modules#3
hmziqrs merged 25 commits into
masterfrom
worktree-fail2ban

Conversation

@hmziqrs

@hmziqrs hmziqrs commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the toride-fail2ban crate — a Fail2Ban-style intrusion prevention library with feature-gated modules for selective compilation.

Key changes

  • Crate scaffolding with 22 modules: error, types, report, command, client, service, firewall, doctor, config, store, ban, detector, action, jail, manager, support, paths, spec, render, ini, regex_test, cli
  • Feature flags fully wired up — 9 heavy dependencies made optional, 2 unused deps removed (walkdir, globset)
  • 1349+ tests across all modules
  • Doctor engine — comprehensive diagnostic suite (config, jail, filter, action, service, firewall, log, permissions, regex, store checks)
  • Jail lifecycle — spec validation, INI rendering, atomic file writes, config test + reload
  • Security#![deny(unsafe_code)], argument redaction, advisory file locking, atomic writes

Feature flags

Feature What you get
(none) error, types, report, command trait
client fail2ban-client wrapper, DuctRunner, service, firewall
doctor diagnostic engine
config full jail engine (implies client)
jail-lifecycle spec, render, INI file management
regex-test fail2ban-regex wrapper
cli clap CLI argument types

Default features: client, doctor

Test plan

  • cargo check -p toride-fail2ban --no-default-features — compiles with only core modules
  • cargo check for each individual feature
  • cargo check -p toride-fail2ban --all-features — full compilation
  • cargo test -p toride-fail2ban --all-features — 1349 tests pass

hmziqrs added 25 commits May 30, 2026 02:48
- Replace fs2 with fd-lock (actively maintained)
- Remove process_control/wait-timeout (duct has built-in timeout)
- Drop parse_duration (conflicting semantics with humantime)
- Note iprange is stale, ipnet alone sufficient
- Add nftables async feature support note
- Specify minimum crate versions
- Cargo.toml with chrono/serde, clap, regex, ipnet, nix, which deps
- Workspace Cargo.toml with workspace members and deps
- lib.rs: Error enum (17 variants), Result alias, all module declarations
- types.rs: BanEntry, LogEntry, PlatformCommands, ScanResult, JailStatus, Fail2BanStatus
- paths.rs: XDG-compliant Fail2BanPaths resolution
- config.rs: Fail2BanConfig, JailConfig, ActionConfig, GlobalConfig with defaults/validation
- store.rs: JSON persistence with atomic writes, ban CRUD, journal tracking
- ban.rs: CidrBlock, CidrSet (v4/v6), BanManager with ban/unban/purge
- action.rs: ActionExec with template expansion, exec/dry_run/validate
- detector.rs: LogDetector with regex matching, incremental file scanning
- support.rs: Firewall/InitSystem detection, platform-specific commands
- jail.rs: Jail combining detector+ban+actions with ignore list
- manager.rs: Fail2BanManager orchestrating multiple jails
- cli.rs: clap CLI with start/stop/status/ban/unban/set/test/addjail/rmjail
- types.test.rs: 79 tests covering BanEntry, LogEntry, PlatformCommands, ScanResult, JailStatus, Fail2BanStatus
- config.test.rs: 35 tests covering defaults, serialization, validation, resolve_jail, enabled_jails
- store.test.rs: 51 tests covering CRUD, atomic writes, journal tracking, trim history, edge cases
- ban.test.rs: 54 tests covering CidrBlock, CidrSet, BanManager operations
- Fixed CidrBlock::contains IPv4 prefix-0 overflow bug
- All 219 tests passing
…ail, manager, cli modules

- action.test.rs: 60 tests for ActionVars, ActionExec, expand_command, exec, dry_run, validate
- detector.test.rs: 43 tests for LogDetector, match_line, extract_ip, incremental scanning
- paths.test.rs: 15 tests for XDG path resolution and directory creation
- support.test.rs: 40 tests for Firewall/InitSystem detection, platform commands
- jail.test.rs: 35 tests for Jail operations, ignore lists, IPv4/IPv6 handling
- manager.test.rs: 30 tests for Fail2BanManager orchestration
- cli.test.rs: 20 tests for clap CLI parsing
- All 468 tests passing
- Inline format args in action.rs, detector.rs
- Fix cast_possible_wrap with expect attribute in ban.rs
- Add #[must_use] to with_ignore_ips in jail.rs
- Fix collapsible if in detector.rs, support.rs
- Merge match_same_arms in support.rs pf_commands
- Add cfg_attr(test, allow(...)) for test-only clippy lints
- All 468 tests passing, clippy clean
…ices review

HIGH fixes:
- Jail::new() now initializes ignore_ips from config (was silently discarded)
- Action exec errors now logged via tracing::error! instead of silently swallowed
- All unban APIs now accept IpAddr instead of &str for type safety
- Store::remove_ban uses IpAddr comparison instead of string allocation

Performance fixes:
- LazyLock for fallback IP regex (was recompiled on every extract_ip call)
- Single captures() call replaces is_match()+captures() double-match
- MatchDetail.ip is now Option<IpAddr> (parsed once, not string round-trip)
- default_prefix() helper eliminates repeated match expression

MEDIUM fixes:
- config.rs: load() distinguishes NotFound from permission denied IO errors
- store.rs: load() adds file path context to IO/JSON errors
- store.rs: save() cleans up temp file on rename failure
- Removed 4 dead error variants (InvalidTemplate, UnsupportedPlatform, PidFile, Signal)
- Added SECURITY doc comment on run_command shell execution
- CidrBlock fields now private with accessor methods

All 468 tests passing, clippy clean.
…ocs, final sweep

Performance:
- Pre-parse ignore_ips into IgnoreEntry enum at Jail construction (no re-parse per check)
- expand_command uses chained replaces instead of HashMap allocation
- Store clear_expired uses extend_from_slice instead of clone

API Design:
- ExecutionMode enum replaces boolean dry_run across Jail and Manager APIs
- cli module made pub(crate) (library shouldn't expose CLI types)
- Removed unused LogEntry struct from types.rs
- Removed dead iptables_commands/pf_commands from support.rs

Edge-case tests added:
- ban: duration=0, duration=u64::MAX, IPv6, CidrSet IPv4-mapped-IPv6, overlapping blocks
- detector: non-UTF8 content, regex matching empty string, set_position beyond EOF
- config: zero ban_time allowed, corrupt existing file, malformed field types
- store: non-UTF8 content, malformed IP in remove_ban

Documentation:
- # Errors sections added to key public methods (config, store, ban, detector, jail)

Final sweep fixes:
- Config load/save error messages now include file path context
- Manager status now populates pattern from config
- Dead code annotations on cli::execution_mode()
- Clippy annotations on with_ignore_ips builder

All 455 tests passing, clippy clean.
…writes, non-UTF-8 detector, expanded config validation, Pf commands fix, rollback on ban failure
… errors instead of ignoring, expand test coverage across all modules
…ction resolution, manager running state, nftables set-based commands, Display impls
…e ban persist order with rollback, add concurrency docs and audit report
Security fixes:
- shell_escape double-quote bypass guard (check_dq_placeholders)
- Single-pass template expansion (no placeholder corruption)
- Temp file race condition (SystemTime nonce instead of PID)
- fsync on write-mode fd (OpenOptions instead of File::open)
- File permission enforcement (0o700 on Unix)
- Orphan temp file cleanup on startup

Correctness fixes:
- Log rotation detection (offset > file_size resets to 0)
- IPv6 fallback regex in detector
- Max line length guard (1 MiB) to prevent OOM
- Empty regex pattern rejection
- scan_interval upper bound validation
- Pattern max length validation
- CidrBlock custom Deserialize with prefix validation
- CLI default config path uses dirs::config_dir()
- BanManager::unban() CIDR-aware matching
- Rollback consistency (persist before firewall command)
- Store::add_ban takes &BanEntry
- Destructure entry in scan loop (no clone)

Performance fixes:
- Removed wasteful String allocation in match_line
- ActionVars::new takes owned String (no double alloc)
- make_action_vars takes IpAddr by value (Copy type)

Clippy fixes:
- Collapsible if in expand_template
- Inclusive range in expand_template
- map_or in detector.rs
- needless_range_loop, used_underscore_binding, len_zero in tests
- unfulfilled_lint_expectations in cli.rs
…rvice, spec modules with tests, examples, fixtures, and feature flags
…regex parsing, add regex_test tests and doctor_report example
…optional, remove unused walkdir/globset

Features:
- client: fail2ban-client wrapper, DuctRunner, service, firewall (dep: duct)
- doctor: diagnostic engine (dep: regex, ipnet)
- config: full jail engine, implies client (dep: regex)
- jail-lifecycle: spec, render, INI management (dep: typed-builder, indoc, fd-lock, fs-err, tempfile, ipnet)
- regex-test: fail2ban-regex wrapper (dep: regex)
- cli: clap argument types (dep: clap)

Default features: client, doctor
Always compiled: error, types, report, command (trait + FakeRunner)
…pdate action/error/firewall/ini/jail/service modules

- doctor: 1917-line expansion with comprehensive check suite (config, jail, filter, action, service, firewall, log, permissions, regex, store checks)
- spec: add validation, IpOrCidr types, DurationSpec, GlobalSpec, serde helpers
- regex_test: add RegexTester with fail2ban-regex output parsing
- Add 1349+ tests across spec, regex_test, render, command, doctor, action, ini, jail, service, error modules
- Update action, error, firewall, ini, jail, manager, service with refinements
- Add README.md with crate documentation and feature flag usage guide
@hmziqrs
hmziqrs merged commit 62afe41 into master May 31, 2026
0 of 10 checks passed
@hmziqrs
hmziqrs deleted the worktree-fail2ban branch May 31, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant