防护思路
点击放大

A lightweight Fail2ban alternative that feels as simple as UFW.
BanWatch watches SSH, web, database, mail, FTP, and VPN logs, scores suspicious activity, and automatically quarantines attacking IPs with ufw, iptables, or nftables. It is built for admins who want brute-force protection without managing Fail2ban jails, filters, actions, and regex plumbing.
Use BanWatch when you are searching for:
---
Fast path for a Linux server with Python 3.8+, sudo, and either ufw, iptables, or nftables a:
Want to first without touching firewall rules?
---
ufw, iptables, nftables, or detect-only mode./etc/banwatch/rules.json.---
BanWatch is designed for Linux servers because it reads Linux service logs and controls Linux firewalls.
| OS / Platform | Status | Notes |
|---------------|--------|-------|
| Debian 11/12 | Supported | Recommended target; works with ufw, iptables, or nftables |
| Ubuntu 20.04/22.04/24.04 | Supported | Recommended target; common paths auto-detected |
| RHEL / Rocky / AlmaLinux 8/9 | Supported | Uses /var/log/secure for SSH; firewall backend depends on installed tools |
| Fedora / CentOS Stream | Expected | Should work when Python 3.8+ and firewall tools are present |
| Arch Linux | Expected | May need custom log_paths depending on service configuration |
| Containers | Limited | Needs host log and firewall access; not the recommended deployment model |
| macOS / Windows | Not supported for protection | Useful only for de or copying files to a Linux server |
Minimum runtime: Python 3.8+, root pri, readable service logs, and one firewall backend (ufw, iptables, nftables) unless using firewall: none or --dry-run.
---
Requires Python 3.8+ and sudo. BanWatch has no external Python package dependencies.
The simplest installation path downloads the current source release, installs the CLI wrapper, and places the Python package under /usr/local/lib/banwatch:
Then run the interacti:
Optional one-shot install flows:
The script is intentionally small and POSIX sh compatible. It installs:
| Path | Purpose |
|------|---------|
| /usr/local/bin/banwatch | Executable launcher |
| /usr/local/lib/banwatch/banwatch_core/ | Python runtime package |
Config, rules, database, and reports are still created at runtime under /etc/banwatch/ and /var/log/banwatch/.
En let you customize the install without editing the script:
| | Default | Purpose |
|----------|---------|---------|
| PREFIX | /usr/local | Base install prefix |
| BIN_DIR | $PREFIX/bin | Where the banwatch launcher is written |
| LIB_ROOT | $PREFIX/lib/banwatch | Where banwatch_core/ is installed |
| BANWATCH_REPO | | Repository to download when not run from a checkout |
| BANWATCH_REF | main | Branch/ref downloaded by the install script |
| RUN_SETUP | 0 | Set 1 to run banwatch setup after installing |
| INSTALL_SYSTEMD | 0 | Set 1 to write the systemd unit after installing |
| START_SERVICE | 0 | Set 1 with INSTALL_SYSTEMD=1 to enable and start the service |
Example pinned branch/ref install:
When run from a checkout, install.sh uses the local files instead of downloading an archive.
If you use the manual layout above, make sure your launcher can import /usr/local/lib/banwatch/banwatch_core. The install script generates that launcher automatically, which is why it is preferred.
Useful for de or testing before installing globally:
The install script is now the easiest path. Longer term, the most user-friendly production install would be a signed .deb package and then an APT repository:
| Option | User command | Best for | Notes |
|--------|--------------|----------|-------|
| Install script | curl -fsSL .../install.sh | sudo sh | Fast first install | Implemented; simple and auditable |
| .deb package | sudo apt install ./banwatch.deb | Debian/Ubuntu | Handles files, permissions, man page, systemd unit, uninstall cleanly |
| APT repository | sudo apt install banwatch | Production users | Best long-term UX for updates and trust, but requires repo signing/release process |
| PyPI / pipx | sudo pipx install banwatch | Python users | Nice Python packaging, but firewall/systemd integration still needs post-install steps |
| Homebrew/Linuxbrew | brew install banwatch | De laptops | Con, less ideal for minimal servers |
| Container image | docker run ... | Testing only | Awkward for host logs and firewall control; not recommended as primary install |
If you copied files from Windows, the banwatch script may ha line endings (\r), breaking its shebang line. If you see /usr/bin/env: 'python3\r': No such file or directory, fix with:
Pull the latest files and reinstall them over the existing ones:
Updates ne /etc/banwatch/ (config, rules, database) or /var/log/banwatch/. If you copied the files from Windows, re-apply the \r fix from step 2 if needed. See CHANGELOG.md for what changed in each release; check the installed with:
---
| Command | Description |
|---------|-------------|
| sudo banwatch setup | Interacti configuration |
| sudo banwatch systemd | Write the systemd unit file (then systemctl enable --now banwatch) |
| sudo banwatch enable [--dry-run] | Start the background daemon manually (optionally detect-only) |
| sudo banwatch disable | Stop the daemon |
| sudo banwatch status | Show stats, ban list, and service breakdown |
| sudo banwatch release <IP> | Free an IP from quarantine |
| sudo banwatch report [--format html\|json\|csv] | Generate a report (default: html) |
| sudo banwatch scan | Scan existing log files from the start for past attacks |
| sudo banwatch rules [service] | Show acti with se/weight |
| banwatch test-line <service> "<log line>" | Identify which rule(s) match a line |
| sudo banwatch allowlist add\|remove <IP/CIDR> | Manage the allowlist interactively |
| sudo banwatch allowlist list | Show the allowlist |
---
BanWatch automatically finds log files for the following services. If your logs li, edit /etc/banwatch/config.json.
/var/log/nginx/access.log, /var/log/nginx/error.log |/var/log/apache2/access.log, /var/log/apache2/error.log |/var/log/httpd/access_log, /var/log/httpd/error_log, /var/log/apache/access.log, /var/log/apache/error.log |/var/log/lighttpd/access.log, /var/log/lighttpd/error.log |/var/log/caddy/access.log, /var/log/caddy/error.log |/var/log/traefik/access.log, /var/log/traefik/error.log |/var/log/haproxy.log |/var/log/mysql/error.log |/var/log/mariadb/mariadb.log |/var/log/postgresql/postgresql-*.log |/var/log/mongodb/mongod.log |/var/log/redis/redis-server.log |/var/log/elasticsearch/*.log |/var/log/cassandra/system.log |> PostgreSQL tip — log the client IP: BanWatch can only block attackers it can *see*. Postgres only includes the remote IP in its log lines when log_line_prefix contains %h; without it, lines like FATAL: unsupported frontend protocol have no IP and can't be attributed. If you haven't already, add %h (and %q so the user/db fields stop filling after the first line):
>
>
>
>
> then reload:
>
>
>
> Check it took effect — a line should now read [...] [213.209.159.66] [unknown]@[unknown] FATAL: ...:
/var/log/auth.log, /var/log/secure, /var/log/syslog | Failed passwords, invalid users |/var/log/.log, /var/log/proftpd/proftpd.log, /var/log/pure-ftpd/transfer.log, /var/log/syslog | Failed logins |/var/log/mail.log, /var/log/maillog, /var/log/postfix.log, /var/log/dovecot.log, /var/log/syslog | SASL/auth failures, IMAP/POP3 brute force |/var/log/openvpn.log, /var/log/open.log, /var/log/wireguard.log, /var/log/syslog | TLS/auth errors, invalid peers |Total: 6 service categories, 39 log file paths. All matching paths per service are watched (e.g., both access.log and error.log). Wildcards (e.g., postgresql-*.log, elasticsearch/*.log) are expanded automatically.
---
All settings are stored in plain JSON. Edit them anytime with any text editor — no need to re-run setup.
| File | Purpose | Editable? |
|------|---------|-----------|
| /etc/banwatch/config.json | Main configuration: services, log paths, thresholds, firewall | ✅ Yes |
| /etc/banwatch/rules.json | Optional custom rules o built-in signatures | ✅ Yes |
| /etc/banwatch/banwatch.db | SQLite database: quarantined IPs, attack history | ⚠️ Use CLI commands |
| /var/run/banwatch.pid | Daemon process ID + flock lock | ❌ Managed automatically |
| /var/log/banwatch/ | HTML/JSON/CS and daemon logs | ❌ Generated automatically |
config.json| Key | Description | Valid |
|-----|-------------|--------------|
| services | Which services to watch | ["ssh"], ["ssh", "web"], ["all"] |
| log_paths | O auto-detected log paths per service (multiple files watched) | {"web": ["/var/log/nginx/access.log", "/var/log/nginx/error.log"]} |
| allowlist | IPs or CIDR ranges ne | ["127.0.0.1", "192.168.1.0/24"] |
| firewall | Backend for blocking IPs | "iptables", "ufw", "nftables", "none" |
| threshold | Score threshold for quarantine (see Rules & Se) | Any integer (default: 5) |
| window | Time window for counting attempts (seconds) | Any integer (default: 300) |
| email | One bare recipient address, without a display name (optional, null to disable) | "" or null |
| report_frequency | How often to email reports | "daily", "weekly", "monthly" |
| dry_run | Detect and report only, ne | true or false (default false) |
| block_private | Allow quarantining private/loopback IPs | true or false (default false) |
| ban_duration | Seconds a quarantine lasts before auto-release | 0 (permanent, default), or e.g. 3600 |
| ban_escalation | Duration multiplier per repeat offense (see Ban List) | number ≥ 1 (default 2) |
| rules_file | Custom rules JSON merged o | path string |
| webhooks | Report digest destinations | list of {"type", "url"} |
Tip: If your logs live in non-standard locations, just edit log_paths after setup. Each service can watch multiple files — pass a list to watch them all. If you add a new service later, add it to services and add its log_paths entry.
False positives: Put admin workstations, load balancers, , and trusted private ranges in allowlist before enabling automatic firewall blocks.
Dry run: Set "dry_run": true (or sudo banwatch enable --dry-run) to see what BanWatch *would* ban without touching the firewall — great for rules before going live.
---
Each rule carries a se that maps to a weight. The detector sums the weights of matching events within the time window; when the total reaches threshold, the IP is quarantined. Auth brute-force rules are hea 404s are light.
| Se | Weight | Example |
|----------|--------|---------|
| low | 1 | Web 401/403/404/500, TLS/SSL handshake noise, connection-closed noise |
| medium | 2 | Authorization failures |
| high | 3 | SSH/FTP/DB/mail/VPN auth failures, web basic-auth failures, SQLi, path tra, XSS |
| critical | 4 | Reserved for custom rules |
With the default threshold: 5, two high-se SSH failures (3 + 3 = 6) trigger quarantine, while a lone 404 (1) needs five hits to matter.
Create /etc/banwatch/rules.json to o built-in signatures. Patterns are replaced per service; log_guesses and ports can be o too. Rules can be plain strings (se low) or objects:
Restart the daemon after editing (sudo systemctl restart banwatch). rules anytime:
---
When an IP's cumulative rule weight reaches the threshold (default: score 5 within 300s), it is:
1. Blocked via iptables, ufw, or nftables
2. Logged as quarantined in /etc/banwatch/banwatch.db only after the firewall command succeeds
3. Tracked with score, service, and timestamps
In dry_run or with firewall: "none", detection events are still recorded, but no new acti is created. Existing quarantines and firewall rules are retained: manual release and automatic expiration do not run in these modes. In blocking mode, a release or expiration is recorded only after successful firewall removal. A repeat ban reapplies the firewall rule before updating the record, including its latest service and reason.
By default private IPs are ne (RFC1918, loopback, link-local). Set "block_private": true in config to allow it — for example on isolated test networks. Trusted ranges in allowlist are always skipped first.
By default bans are permanent until you run sudo banwatch release. To avoid locking out legitimate users on a false positive, set "ban_duration" to a number of seconds (e.g. 3600). When it expires, the daemon removes the firewall rule and marks the ban expired.
Freed offenders don't get an amnesty — each time an IP is banned again it gets a longer ban. The duration is ban_duration × ban_escalation^(attempts−1):
| Offense | Duration (base 1h, escalation 2×) |
|---------|----------------------------------|
| 1st ban | 1h |
| 2nd | 2h |
| 3rd | 4h |
| 4th | 8h |
| ... | doubles each time |
After a handful of offenses the ban is weeks long — effectively permanent, while a one-off false positive only costs a few hours. ban_escalation: 1 disables escalation; ban_duration: 0 keeps permanent bans.
---
Reports include:
report_frequency.The header and email subject identify the server, report frequency and timezone. The original desktop design is retained, including KPI cards, service bars, offender ranks and separate table columns. Base styles are inline; the CSS block contains only mobile rules, including two KPI columns. No repeated containment banner is included.
Files are saved to /var/log/banwatch/. HTML reports can be emailed as multipart text/HTML via the system sendmail interface and/or pushed to webhooks as a text digest. GNU mail is retained as an HTML-only fallback when sendmail is una.
Set email_from to an authorized sender (default BanWatch <>) and optionally set report_hostname to a recognizable ser. These fields are to reject header injection. A successful email means the local transport accepted it, not that the recipient received it.
The email recipient must be a single bare address, such as . Display-name forms such as Admin <>, recipient lists, and control characters are rejected. Display names remain supported for the email_from sender, such as BanWatch <>. Use null to disable email reports.
JSON and CS save the requested export and also send the HTML email and/or webhook digest when configured, as before. These exports do not update the HTML/email comparison reference. The first successful HTML-format report establishes that reference. Email failures leave it unchanged; webhook failures do not cause duplicate email retries after an email was accepted. With webhook-only delivery, all configured hooks must accept the report before its reference ad.
The JSON export retains its original top-le: generated_at, stats, breakdown (objects containing service, total, active), and bans. CS its original se and appends banned_until; field are exported unchanged. Treat exported log-derived text as untrusted when opening CS in spreadsheet software.
Automatic report deadlines are persisted in SQLite. Failures retry after 15 minutes, then 30 minutes; after three failed attempts the next normal interval is scheduled. Restarting the daemon no longer postpones an existing deadline.
The 0.6.0 migration preser timestamps and adds an indexed epoch value using the server's local timezone. Repeated daylight-saving hours in old timezone-free records cannot be reconstructed exactly; new events are unambiguous. Ban lifecycle counts start at migration time because historical release/expiry times were not recorded. The new metric definitions establish a fresh KPI reference. Take a consistent database backup before upgrading.
Webhook configuration:
---
The daemon tails log files — it only reads new lines written after it starts. Attacks logged *before* the daemon's first start are never seen. To catch up on existing log history, run a one-off backfill scan:
This reads every configured log file from the beginning, analyzes every line with the same rules the daemon uses, and quarantines matching IPs (respecting the allowlist, pri policy, and dry-run mode). Output reports how many lines were read and how many IPs were newly quarantined:
Run it once after installing BanWatch on a system that already has log history. Since it reads whole files (including rotations), it can take a while on busy servers; it's safe to stop and re-run, as already-quarantined IPs are skipped.
> Note: the scan scores events with the current time, not the log timestamps, so events spread far apart in the log still accumulate within the configured window. This is intentional — a historical attacker who hit your postgres once every 90 seconds for an hour should be quarantined, not released because each hit was "too old".
---
Run BanWatch as a background service that starts automatically on boot. The daemon must be configured first (see Install).
The unit runs banwatch enable (Type=forking, PID file /var/run/banwatch.pid) and daemon logs go to /var/log/banwatch/banwatch.log.
Common systemctl commands:
| Command | What it does |
|---------|--------------|
| sudo systemctl enable --now banwatch | Start now + auto-start on boot |
| sudo systemctl start banwatch | Start (once) |
| sudo systemctl stop banwatch | Stop |
| sudo systemctl restart banwatch | Restart (applies config edits) |
| sudo systemctl disable --now banwatch | Stop + remo |
| sudo systemctl status banwatch | Show service state |
| journalctl -u banwatch -f | Follow the service's log output |
Prefer systemctl over sudo banwatch enable/disable when the service is installed — the two can disagree about state (the unit's PID file 's view). For manual testing, sudo banwatch enable / disable work fine without systemd.
---
1. Stop the daemon (if running) and disable the systemd service (if installed):
2. Remove the systemd unit file (if generated):
3. Remove the installed files:
4. Remo, reports, and logs (this deletes the ban list and attack history):
5. Release any firewall rules BanWatch added. Bans applied with iptables/ufw/nftables are not automatically removed on uninstall. Check what was blocked and delete the rules manually:
After uninstalling, the intended cleanup for each backend is:
The simplest manual approach: run sudo banwatch status before step 4 to list all quarantined IPs, then remo rules by IP with the commands above.
---
The executable banwatch is a small CLI wrapper. Runtime code lives in banwatch_core/:
| Module | Purpose |
|--------|---------|
| config.py | Load, save, and JSON configuration |
| signatures.py | Service signatures, se weights, and rules.json merging |
| database.py | SQLite ban list and attack history |
| firewall.py | iptables / ufw / nftables block and unblock commands |
| detector.py | Log tailing, weighted rule matching, score tracking |
| reporter.py | One data snapshot per report, exports, KPI references and scheduling |
| report_views.py | Responsi HTML and plain-text rendering |
| report_delivery.py | Multipart email transport and webhook delivery |
| monitoring.py | Read-only reader health and daemon heartbeat |
| daemon.py | Background process lifecycle + atomic flock PID lock |
| cli.py | Command-line command dispatch |
---
MIT — Use only on systems you own or ha to protect.
开发语言:Python
Star 数:3
该项目属于防护思路方向的开源资源,代码结构清晰,适合安全研究人员学习参考。项目围绕A lightweight Fail2ban alternative that feels as simple as UFW展开,提供了可运行的实现,对理解相关安全机制、工具原理与攻防技术具有较高的研究价值。
从技术实现角度看,此类项目的核心在于对底层原理的深入理解与工程化落地。开发者在研究过程中可以结合官方文档与社区讨论,梳理项目的初始化流程、核心模块职责与数据流转,逐步掌握其中的技术细节与设计思路。
1. 下载源码压缩包,解压到本地环境。
2. 根据项目类型准备对应的运行环境与依赖。
3. 按照 README 中的步骤完成配置与部署。
4. 在隔离的测试环境中运行验证,避免影响生产系统。
本资源来源于开源社区,仅供安全研究、技术学习与合法授权范围内的攻防演练使用,请勿用于任何非法或侵害他人权益的用途。使用本项目产生的一切后果由使用者自行承担。
| 资源标题 | banwatch - A lightweight Fail2ban alternative that feels as s |
| 所属分类 | 防护思路 |
| 发布作者 | admin |
| 发布时间 | 2026-10-06 00:18:38 |
| 下载权限 | 免费下载 |
| 附件数量 | 2 个下载链接 |
| 浏览次数 | 397 |
| 讨论数量 | 0 |
| 主题状态 | 正常讨论中 |