Overview — what it does
Blocklist & Reputation (/security/blocklist) tells the story of the threat ranges that Kitsune ML and Suricata IDS have been auto-blocking, and gives operators a control console to block (add) · release · re-block ranges by hand.
- The source table is
blacklist_infoon thelegacy_gloryconnection (~8,000–9,000 rows). The unit of blocking is not a single IP but a /24 range (c_class, e.g.203.0.113.0/24). - This table is written by detection collectors (
kts_tx.js·srct_tx.js) and by this operator UI; thesync_blackdaemon (sync_black.mjs) reads it and enforces the blocks as ipset/iptables DROP rules on each host. So add/release here is the control plane — actual packet dropping is applied by sync_black. - The
reasonfield's source tag ([Kitsune ...]·[Suricata Auto-Block]·[Manual] ...) and evidence figures (score · port · ja3 · hits …) are parsed into a human-readable "why it got caught" label (locale-independent parsing).
A "block" always covers an entire /24 range. Even if you enter a single IP, only the first three octets are kept and it is normalized to
x.y.z.0/24. False positives are exempted via the Whitelist, not by narrowing the block.
When to use
- To see at a glance what auto-defense is currently blocking and how much (active count · engine contribution · origin countries).
- To manually block a malicious IP you spotted in Threats, Detections, or an IP-investigation popup (deep-linked via
?add=and pre-filled). - To release a range you blocked in error, or re-block a released range that has resumed activity.
- To cross-check the representative IP of a blocked range with AbuseIPDB reputation.
Layout
Top KPIs
Computed by summary() with a 30-second cache (infr:sec:blocklist:summary).
| KPI | Meaning | Calculation |
|---|---|---|
| Active blocks | /24 ranges currently enforced | COUNT(*) WHERE is_use=1 |
| Addresses (subtitle) | Addresses covered by active blocks | active × 256 (/24 ≈ 256) |
| Today | New blocks in the last 24h | is_use=1 AND created_at >= today 00:00 |
| Auto-defense rate | Share handled automatically by AI/IDS | (kitsune + suricata) / active × 100 |
| Watched countries | Distinct countries among active ranges | COUNT(DISTINCT country) WHERE is_use=1 |
| Released | Currently released (awaiting re-block) | COUNT(*) WHERE is_use=0 |
Defense-engine contribution
Active blocks are broken down by source tag into a share bar plus tiles. Classification is by reason LIKE matching.
| Engine | Match rule | Meaning |
|---|---|---|
| Kitsune ML | reason LIKE '%Kitsune%' |
Ensemble-autoencoder anomaly detection |
| Suricata IDS | reason LIKE '%Suricata%' |
Auto-block on scan / protocol violation |
| Manual | reason LIKE '%Manual%' or %수동% |
Operator-designated block |
| Other | Everything not matched above | active − (kitsune+suricata+manual), floored at 0 |
Threat origins
blockByCountry(12) aggregates active ranges into the top 12 countries (GROUP BY country ORDER BY COUNT DESC), shown as flag emoji + rank bars. Clicking a bar filters the list to that country.
Recent defense activity feed
blockFeed(16) shows the most recent 16 rows regardless of status (ORDER BY created_at DESC, id DESC) in a 4×4 grid — a live sense of what just got caught.
Filterable block list
40 cards per page (PER_PAGE=40, ORDER BY created_at DESC, id DESC). Each card shows the range · country · source badge · evidence chip (behavior + key figure) · block time · status, with row actions [Reputation] · [Detail] · [Release]/[Re-block]. If a range is a registered internal network it gets an Internal badge and its reputation button is hidden (internal_scope.js).
Filters:
- Status —
active(default) ·released·all - Source —
kitsune·suricata·manual(empty = all) - Country — exact match (≤ 64 chars)
- Search (q) — CIDR prefix only (regex
^[0-9.]{0,20}$, e.g.211.→c_class LIKE '211.%')
How to use
Reading the reason
Each card's evidence chip picks the single highest-weight ruleset from the reason string and shows it as a label + representative figure. Among srct heuristic categories the top one is chosen by this weight order:
| Category marker | Weight | behavior key | Label | Representative figure (detail) |
|---|---|---|---|---|
[A2:...] |
6 | brute | Admin brute-force | hits=N → N hits |
[A:...] |
5 | scanner | Vulnerable-path scan | hits=N → N hits |
[D:...] |
4 | ja3 | JA3 botnet fingerprint | ja3=N → ja3×N |
[B:...] |
3 | bot_ua | Malicious bot access | hits=N → N hits |
[C:...] |
2 | err_flood | 4xx error flood | err_rate=NN% |
[E:...] |
1 | short_scan | TCP port scan | port=N or N scan |
If the source is Kitsune, or reason contains score=, it is classified as ml_anomaly (ML anomaly) ahead of any category, showing top= (largest-error component) plus score. Manual/other fall back to manual / auto.
Additional facts extracted from reason: score=, top=, port=, short_scan_sessions=, bytes=. Example reasons:
[Kitsune v2.2] anomaly top=TLS score=0.83
[Suricata Auto-Block] [A:URL] hits=42 port=443
[Manual] repeated login abuse (uid=5)
Adding a manual block
- [Manual block] in the list header opens the add modal. (When arriving from Threats / IP investigation, the IP is pre-filled from
?add=203.0.113.10.) - Enter an IP or CIDR. A single IP is expanded to
/24(203.0.113.10→203.0.113.0/24). - Enter an optional reason (≤ 160 chars) and click [Block].
- Server behavior:
- Input is normalized to
x.y.z.0/24viacclassCidr()(non-IPv4 rejected asinvalid_cidr). - For a full IPv4, the country is enriched from
ip_info_loop(else the existing value /Unknown). - If
(server_index=0, c_class)already exists it is updated tois_use=1(reactivated); otherwise a new row is inserted. The reason is"[Manual] <note> (uid=N)". - The summary cache is flushed and the refreshed list + summary are returned.
- Input is normalized to
The block hits
blacklist_infoimmediately, but actual firewall DROP is applied when each host'ssync_blackdaemon next syncs it into ipset/iptables. If that batch is not running, the block is not enforced at the firewall.
Release / Re-block
- [Release] sets
is_use=0so sync_black removes it from ipset (the row remains). Runs after a confirm dialog. - [Re-block] restores a released range to
is_use=1. - Both return
not_foundif the target row does not exist.
Polling
security-blocklist.js calls /security/blocklist/live every 30 seconds to refresh the summary and current page.
Data sources
| Item | Source | Notes |
|---|---|---|
| Block ledger | blacklist_info (legacy_glory) |
id, server_index, c_class, country, reason, is_use, created_at, updated_at · UNIQUE(server_index, c_class) |
| Country enrichment | ip_info_loop (legacy_glory) |
country lookup for full IPv4 on add |
| Internal-net check | SecurityScope::internalMatchers() |
registered instance IPs + Ops-Policy whitelist → Internal badge |
| Firewall enforcement | sync_black daemon |
blacklist_info → ipset/iptables DROP |
| Auto-block supply | kts_tx.js · srct_tx.js |
writes reason source tags & evidence strings |
| Audit log | infr_audit_log |
sec.block_add · sec.block_release · sec.block_readd |
The read backbone is App\Repositories\SecurityRepository; narration and writes are orchestrated by App\Services\Security\BlocklistService. Credentials come only from config('database.connections.legacy_glory') — never hardcoded.
Access
- Viewing the page — you must belong to the Security role area (sitemap
min_req=2). On the deployed level scale, staff (5) and admin (10) see Security screens; demo (1) is outside the security scope. - Writes (add/release/reblock) — the server enforces
infr_user_level >= 2(canWrite). Below that returns403 forbidden. So demo (1) cannot write; staff and admin can. - Every write is recorded in
InfrAuditLogand traceable in the Audit log (actor, target CIDR, reactivated flag, etc. in the meta).
Because these are destructive security actions, the UI shows a confirm (step-up) dialog before executing. Double-check the CIDR so you don't accidentally block a large range.
Troubleshooting & FAQ
- I blocked it but attacks keep coming. This screen only updates the ledger. Verify
sync_blackis running on the target host and that ipset was actually updated. Also, blocks are per-/24, so attacks from other ranges need their own blocks. - [Manual block] returns 403. Your login level is below 2. Retry as a staff/admin account (see RBAC levels).
- Input is rejected as "not a valid IP/CIDR". Only
a.b.c/a.b.c.d/a.b.c.d/nnare accepted. IPv6 and hostnames are not supported. - Country shows Unknown. There is no
ip_info_loopenrichment for that IP, or you entered a CIDR only. Adding a full IPv4 may fill in the country. - KPIs / list don't change. The summary is cached for 30 seconds. It's flushed right after a write, but collector-driven changes can lag up to 30 seconds.
- I blocked a legitimate range by mistake. Release it immediately, and if the re-block false positive recurs, add it to the Whitelist so srct_tx exempts it just before blocking.
Related
- Threats — hand off a malicious IP here via
?add=to block it - Whitelist — block exemptions (false-positive prevention, with expiry)
- Detections & JA3 — how Kitsune / srct / JA3 detection works
- Auto-block — the scheduled auto-blocking pipeline
- IP reputation (AbuseIPDB) — cross-check the representative IP
- Audit log — trace block/release history
