Compare commits

..
2 Commits
7 changed files with 54 additions and 31 deletions

No files matched your search

+1 -2
View File
@@ -40,7 +40,7 @@ ROUTER_PASSWORD=change-me
BLOCKLIST_NAME=Blocked
# Comma-separated address lists that are treated as whitelisted (never blocked).
WHITELIST_NAMES=Cloudflare,Openfactory
WHITELIST_NAMES=Cloudflare,Init7
# --- WHOIS subnet consolidation --------------------------------------------
@@ -79,7 +79,6 @@ DEFAULT_TOP_PATHS=20
# --- Whitelist sources -----------------------------------------------------
OPENFACTORY_URL=https://openfactory.net/geofeed.csv
CLOUDFLARE_URL_V4=https://www.cloudflare.com/ips-v4
CLOUDFLARE_URL_V6=https://www.cloudflare.com/ips-v6
PALOALTO_URL=https://docs-cortex.paloaltonetworks.com/r/1/Cortex-Xpanse/Scanning-activity
+15 -3
View File
@@ -63,8 +63,7 @@ intrusion-detection/
│ ├── static/ style.css + app.js (dark-theme UI)
│ └── templates/ Jinja2 page templates (dashboard, scan, reports, …)
├── data/ whitelist source files (cached locally)
│ ├── openfactory/
│ │ └── geofeed.csv
│ ├── init7/ (ranges built into app/utils/whitelists.py — no geofeed)
│ ├── cloudflare/
│ │ ├── ips-v4.txt
│ │ └── ips-v6.txt
@@ -135,13 +134,26 @@ production can set the same variables via the OS and skip `.env` entirely.
| `ROUTER_USER` | `admin` | MikroTik connection |
| `ROUTER_PASSWORD` | _(set in .env)_ | MikroTik connection |
| `BLOCKLIST_NAME` | `Blocked` | MikroTik address list |
| `WHITELIST_NAMES` | `Cloudflare,Openfactory` | MikroTik whitelists |
| `WHITELIST_NAMES` | `Cloudflare,Init7` | MikroTik whitelists |
| `WEB_HOST` | `127.0.0.1` | web UI bind host |
| `WEB_PORT` | `54666` | web UI port |
See `.env.example` for the complete list of supported variables (WHOIS
tuning, scan thresholds, whitelist source URLs, and more).
### Network context (2026-09-08)
Connectivity provider switched Openfactory → Init7 on 2026-09-08:
- WAN IPv4: `77.74.80.159/32` (init7 `77.74.80.0/24`, "Hybrid7-NET").
- IPv6: delegated `/48` `2001:1620:549f::/48` (init7 allocation `2001:1620::/30`).
- Upstream DNS: `77.109.128.2`, `213.144.129.20`.
The `Openfactory` whitelist (old AS58299 geofeed) is obsolete and must be
replaced with an `Init7` whitelist — see
[`.github/instructions/TODO.md`](.github/instructions/TODO.md) for the
switchover plan and the verified init7 CIDR ranges.
## Usage
```powershell
+1 -1
View File
@@ -11,5 +11,5 @@ This directory contains data files used by the `allucanget-ids` Intrusion Detect
- [MikroTik RouterOS IPv6 Firewall rules](./mikrotik/firewall_ipv6.json): Current MikroTik RouterOS IPv6 firewall rules.
- [MikroTik RouterOS IPv4 address list](./mikrotik/address_list_ipv4.json): Current MikroTik RouterOS IPv4 address list.
- [MikroTik RouterOS IPv6 address list](./mikrotik/address_list_ipv6.json): Current MikroTik RouterOS IPv6 address list.
- [Openfactory Geofeed](./openfactory/geofeed.csv): Contains Openfactory IP addresses and associated geolocation data from `https://openfactory.net/geofeed.csv`. Used for identifying and whitelisting traffic from known Openfactory sources.
- [Openfactory Geofeed](./openfactory/geofeed.csv): **Obsolete since 2026-09-08** (provider switched to Init7). Kept for historical reference; the replacement `Init7` ranges are documented in the switchover plan (`.github/instructions/TODO.md`).
- [PaloAlto Scanner IP list](./paloalto/scanner-ips.txt): List of known PaloAlto scanner IP addresses from `https://docs-cortex.paloaltonetworks.com/r/1/Cortex-Xpanse/Scanning-activity`, used for whitelisting traffic from PaloAlto scanners. !NOTE: This list can not be scraped easily (Javascript-based), so it is maintained manually and may be outdated. Please verify the list against the official source if needed.
+1 -1
View File
@@ -272,7 +272,7 @@ def get_scan_report(report_dir: str = REPORT_DIR) -> dict[str, list[str]]:
"/ip firewall address-list": [],
"/ipv6 firewall address-list": [],
}
# add list=Cloudflare or list=Openfactory to whitelist sections, list=Blocked to blocked section
# add list=Cloudflare or list=Init7 to whitelist sections, list=Blocked to blocked section
with open(report_path, "r", encoding="utf-8") as f:
current_section = None
for line in f:
+3 -5
View File
@@ -73,7 +73,8 @@ REPORT_RETENTION_MAX_FILES = int(
# Log sources (configurable via environment)
# ---------------------------------------------------------------------------
COPYPARTY_LOG_PATH = os.environ.get("COPYPARTY_LOG_PATH", "/srv/projects/copyparty")
COPYPARTY_LOG_PATH = os.environ.get(
"COPYPARTY_LOG_PATH", "/srv/projects/copyparty")
NPM_LOG_PATH = os.environ.get("NPM_LOG_PATH", "/mnt/backups/npm/logs")
# ---------------------------------------------------------------------------
@@ -91,7 +92,7 @@ BLOCKLIST_NAME = os.environ.get("BLOCKLIST_NAME", "Blocked")
WHITELIST_NAMES = [
name.strip()
for name in os.environ.get(
"WHITELIST_NAMES", "Cloudflare,Openfactory").split(",")
"WHITELIST_NAMES", "Cloudflare,Init7").split(",")
if name.strip()
]
@@ -131,8 +132,6 @@ WEB_PORT = int(os.environ.get("WEB_PORT", "54666"))
# Whitelist sources (configurable via environment)
# ---------------------------------------------------------------------------
OPENFACTORY_URL = os.environ.get(
"OPENFACTORY_URL", "https://openfactory.net/geofeed.csv")
CLOUDFLARE_URL_V4 = os.environ.get(
"CLOUDFLARE_URL_V4", "https://www.cloudflare.com/ips-v4")
CLOUDFLARE_URL_V6 = os.environ.get(
@@ -143,7 +142,6 @@ PALOALTO_URL = os.environ.get(
)
# Local whitelist files (checked before remote download)
OPENFACTORY_FILE = os.path.join(DATA_DIR, "openfactory", "geofeed.csv")
CLOUDFLARE_V4_FILE = os.path.join(DATA_DIR, "cloudflare", "ips-v4.txt")
CLOUDFLARE_V6_FILE = os.path.join(DATA_DIR, "cloudflare", "ips-v6.txt")
PALOALTO_FILE = os.path.join(DATA_DIR, "paloalto", "scanner-ips.txt")
+19 -19
View File
@@ -23,8 +23,6 @@ from .constants import (
CLOUDFLARE_URL_V6,
CLOUDFLARE_V4_FILE,
CLOUDFLARE_V6_FILE,
OPENFACTORY_FILE,
OPENFACTORY_URL,
PALOALTO_FILE,
PALOALTO_URL,
)
@@ -40,15 +38,26 @@ CIDR_RE = re.compile(
# Local file paths (checked first, before remote download)
# ---------------------------------------------------------------------------
# (paths live in app/constants.py as OPENFACTORY_FILE, CLOUDFLARE_V4_FILE,
# CLOUDFLARE_V6_FILE, PALOALTO_FILE; URLs as OPENFACTORY_URL, ...)
# (paths live in app/constants.py as CLOUDFLARE_V4_FILE,
# CLOUDFLARE_V6_FILE, PALOALTO_FILE; URLs as CLOUDFLARE_URL_V4, ...)
# ---------------------------------------------------------------------------
# Built-in fallback CIDR lists
# ---------------------------------------------------------------------------
#: Openfactory AS58299 colocation IP range (https://openfactory.net/geofeed.csv)
OPENFACTORY_IPV4 = ["5.226.144.0/21"]
#: Init7 (Switzerland) Ltd. ranges — AS13030, verified via RIPE RDAP 2026-09-08.
#: Includes the Hybrid7 WAN /24 (router WAN 77.74.80.159), init7 DNS
#: infrastructure, and the PPPoE gateway. init7 publishes no geofeed, so
#: these are maintained here rather than downloaded.
INIT7_IPV4 = [
"77.74.80.0/24", # Hybrid7 WAN /24
"77.109.128.0/18", # init7 DNS / infrastructure
"213.144.128.0/19", # init7 DNS / infrastructure
"5.180.132.0/22", # init7 PPPoE gateway
]
#: init7 IPv6 allocation (contains delegated /48 2001:1620:549f::/48)
INIT7_IPV6 = ["2001:1620::/30"]
#: https://www.cloudflare.com/ips-v4
CLOUDFLARE_IPV4 = [
@@ -85,7 +94,7 @@ PALOALTO_IPV6 = [
#: address-list name -> CIDR ranges (v4 + v6 mixed, split at render time)
WHITELISTS: dict[str, list[str]] = {
"Openfactory": OPENFACTORY_IPV4,
"Init7": INIT7_IPV4 + INIT7_IPV6,
"Cloudflare": CLOUDFLARE_IPV4 + CLOUDFLARE_IPV6,
"PaloAlto": PALOALTO_IPV4 + PALOALTO_IPV6,
}
@@ -141,9 +150,7 @@ def _load_local_whitelists() -> dict[str, list[str]]:
successfully parsed CIDRs are included.
"""
out: dict[str, list[str]] = {}
cidrs = _read_local_file(OPENFACTORY_FILE)
if cidrs:
out["Openfactory"] = cidrs
# Init7 has no published geofeed; its ranges are built-in (INIT7_IPV4/6).
cf_v4 = _read_local_file(CLOUDFLARE_V4_FILE)
cf_v6 = _read_local_file(CLOUDFLARE_V6_FILE)
if cf_v4 or cf_v6:
@@ -190,14 +197,7 @@ def _download_whitelists() -> dict[str, list[str]]:
Saves each download to the corresponding local file on success.
"""
out: dict[str, list[str]] = {}
try:
text = _download_text(OPENFACTORY_URL)
cidrs = extract_cidrs(text)
if cidrs:
out["Openfactory"] = cidrs
_save_local_file(OPENFACTORY_FILE, text)
except Exception as e:
print(f"Error downloading Openfactory whitelist: {e}")
# Init7 has no published geofeed; its ranges are built-in (INIT7_IPV4/6).
try:
text_v4 = _download_text(CLOUDFLARE_URL_V4)
text_v6 = _download_text(CLOUDFLARE_URL_V6)
@@ -242,7 +242,7 @@ def update_whitelists() -> None:
# Start from built-in fallbacks, then layer local, then remote
WHITELISTS = {
"Openfactory": OPENFACTORY_IPV4,
"Init7": INIT7_IPV4 + INIT7_IPV6,
"Cloudflare": CLOUDFLARE_IPV4 + CLOUDFLARE_IPV6,
"PaloAlto": PALOALTO_IPV4 + PALOALTO_IPV6,
}
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
# Shell script to be run as cron job to copy npm logs to backup location.
# Stop npm service to ensure logs are not being written to while copying.
systemctl stop npm
# Only copy new/changed log files to avoid re-copying existing backups.
# --ignore-existing: never overwrite files already present in the destination.
# -c: compare files by checksum so matching files are not re-written.
# -P: preserve permissions + show progress.
rsync -qcP --ignore-existing /data/logs/*access* /data/logs/*error* /mnt/backups/npm/
# Start npm service again after copying logs.
systemctl start npm