feat: write consolidated blocklists and mirror git-diffable router state
- Add `write_blocklist_files()` to emit final IPv4/IPv6 blocklists to `reports/app/data/` - Add `APP_REPORT_DIR` / `APP_REPORT_DATA_DIR` constants for consolidated outputs - Write firewall state snapshots to `data/mikrotik/` for git diff auditing - Capture router state before scanning; fall back to scan-only if router is unreachable - Use `split_cidr_versions` for CIDR-aware subnet splitting - Update README with output layout and git-diffable state notes
This commit is contained in:
1 parent
6005340ecb
commit
28c77691bb
4 files changed
+91
-26
No files matched your search
+33
-14
@@ -21,10 +21,11 @@ from .constants import (
|
||||
REPORT_DIR,
|
||||
SUBNET_THRESHOLD,
|
||||
)
|
||||
from .firewall import MikrotikFirewall
|
||||
from .firewall import MikrotikFirewall, write_blocklist_files
|
||||
from .copyparty import CopypartyAnalyzer
|
||||
from .npm import NpmAnalyzer
|
||||
from .ip_utils import is_whitelisted, split_versions
|
||||
from .ip_utils import is_whitelisted
|
||||
from .mikrotik import split_cidr_versions
|
||||
from .whois import consolidate_by_subnet
|
||||
|
||||
|
||||
@@ -65,10 +66,24 @@ def main() -> None:
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 1. Initialize Analyzers
|
||||
# We turn off inner write_rsc of individual analyzers to avoid duplicate/redundant rsc writes,
|
||||
# or let them write their individual ones if they want to. Writing individual scripts is harmless.
|
||||
# We'll match the individual tools' default write behavior, but generate the consolidated one.
|
||||
# --- Step 1-3: connect to MikroTik RouterOS and capture current state ---
|
||||
# If the router is unreachable we fall back to scan-only mode: still
|
||||
# generate reports and blocklists, but skip the firewall sync.
|
||||
firewall: MikrotikFirewall | None = None
|
||||
if args.update_firewall:
|
||||
firewall = MikrotikFirewall()
|
||||
try:
|
||||
firewall.connect()
|
||||
# Read current firewall rules + address lists and snapshot them to
|
||||
# reports/mikrotik/data/ and the git-diffable data/mikrotik/.
|
||||
firewall.save_state_report(args.report_dir)
|
||||
except Exception as e:
|
||||
print(f"\nWARNING: could not read MikroTik router "
|
||||
f"({firewall.router_ip}): {e}")
|
||||
firewall.disconnect()
|
||||
firewall = None
|
||||
|
||||
# --- Step 4: run copyparty + NPM analyzers -> blocklist candidates ------
|
||||
cp_analyzer = CopypartyAnalyzer(
|
||||
log_path=args.copyparty_log_path,
|
||||
report_dir=args.copyparty_report_dir,
|
||||
@@ -86,7 +101,6 @@ def main() -> None:
|
||||
error_ratio=args.error_ratio,
|
||||
)
|
||||
|
||||
# 2. Run copyparty analysis
|
||||
print("=== Running copyparty scan ===")
|
||||
cp_exists = True
|
||||
try:
|
||||
@@ -95,7 +109,6 @@ def main() -> None:
|
||||
print(f"Error running copyparty scan: {e}")
|
||||
cp_exists = False
|
||||
|
||||
# 3. Run NPM analysis
|
||||
print("\n=== Running NPM scan ===")
|
||||
npm_exists = True
|
||||
try:
|
||||
@@ -106,9 +119,11 @@ def main() -> None:
|
||||
|
||||
if not cp_exists and not npm_exists:
|
||||
print("\nBoth scans failed. Unable to generate consolidated blocklist.")
|
||||
if firewall is not None:
|
||||
firewall.disconnect()
|
||||
return
|
||||
|
||||
# 4. Consolidate block candidates
|
||||
# Consolidate block candidates
|
||||
consolidated_candidates: set[str] = set()
|
||||
if cp_exists:
|
||||
consolidated_candidates.update(cp_analyzer.block_candidates())
|
||||
@@ -119,11 +134,11 @@ def main() -> None:
|
||||
final_blocked_ips = {
|
||||
ip for ip in consolidated_candidates if not is_whitelisted(ip)}
|
||||
|
||||
# Group into subnets via WHOIS if multiple malicious IPs in same subnet (threshold = SUBNET_THRESHOLD)
|
||||
# Group into subnets via WHOIS if multiple malicious IPs in same subnet
|
||||
print("\nGrouping blocked IPs into subnets using WHOIS...")
|
||||
final_blocked_targets = consolidate_by_subnet(
|
||||
final_blocked_ips, threshold=SUBNET_THRESHOLD)
|
||||
blocked_ipv4, blocked_ipv6 = split_versions(final_blocked_targets)
|
||||
blocked_ipv4, blocked_ipv6 = split_cidr_versions(final_blocked_targets)
|
||||
|
||||
print("\n=== Consolidated Summary ===")
|
||||
print(
|
||||
@@ -133,9 +148,13 @@ def main() -> None:
|
||||
print(f"Total unified blocked IPs: {len(final_blocked_ips)}")
|
||||
print(f"Total target blocks/subnets: {len(final_blocked_targets)}")
|
||||
|
||||
if args.update_firewall:
|
||||
firewall = MikrotikFirewall()
|
||||
firewall.connect()
|
||||
# --- Step 6: write consolidated blocklists to reports/app/data ----------
|
||||
blocklist_v4_path, blocklist_v6_path = write_blocklist_files(
|
||||
blocked_ipv4, blocked_ipv6, args.report_dir)
|
||||
print(f"blocklist files:\n {blocklist_v4_path}\n {blocklist_v6_path}")
|
||||
|
||||
# --- Steps 5+7: compare current state with candidates and apply ---------
|
||||
if firewall is not None:
|
||||
try:
|
||||
changes = firewall.sync_blocklist(
|
||||
blocked_ipv4, blocked_ipv6, dry_run=args.dry_run,
|
||||
|
||||
Reference in new issue
Block a user