Skip to content

feat(abuse-blocker): add backend runtime - #208

Draft
l0nelynx wants to merge 4 commits into
remnawave:devfrom
l0nelynx:staging/abuse-blocker-runtime
Draft

feat(abuse-blocker): add backend runtime#208
l0nelynx wants to merge 4 commits into
remnawave:devfrom
l0nelynx:staging/abuse-blocker-runtime

Conversation

@l0nelynx

Copy link
Copy Markdown

Suggested PR title

feat(abuse-blocker): add backend runtime

Summary

This PR adds the backend runtime for the optional abuseBlocker Node plugin introduced by #207 and implemented on Node in remnawave/node#46.

It persists idempotent abuse reports, serializes repeat-offender escalation per user, exposes administrative reporting and manual-review APIs, refreshes Node-local temporary blocks, and integrates alert-level events with the existing Telegram and backend-webhook notification pipelines.

This is PR 3 of 3. It targets dev and is intentionally opened as a Draft until the two shared packages in the dependency chain are published and the registry dependencies and lockfiles can be finalized.

Motivation

Node has the connection-level information and must perform low-latency scan detection locally. The backend has the durable, cross-restart state required to track repeated blocking incidents across Nodes and make account-level decisions.

This split keeps high-volume rolling windows in bounded Node RAM while centralizing audit history, repeat-offender policy, notifications, and manual review in PostgreSQL.

Persistence

The migration adds two tables:

  • AbuseBlockerReports stores the Node-generated eventId, user and Node references, severity, score, source IP, action, detection time, and the complete report JSON.
  • AbuseBlockerUserState stores the durable strike level, last blocking incident, manual-review state, whether the plugin disabled the account, and review timestamps/action.

Reports are upserted by eventId. An updated report may expand evidence, but it does not score or escalate again. Report truncation intentionally leaves strike and manual-review state untouched, and no automatic report retention policy is introduced in this PR.

Repeat-offender escalation

Blocking reports are processed in a transaction guarded by a PostgreSQL advisory transaction lock derived from userId. This serializes concurrent reports for the same user while allowing different users to progress independently.

The default chain is:

  1. First blocking incident: persist strike 1; the detecting Node has already applied the initial 10-minute block.
  2. Second incident within seven days: persist strike 2 and ask that Node to refresh the current source-IP block to one hour.
  3. Third incident within seven days: persist strike 3, disable the user through the existing UsersService, and require manual review.
  4. Later incidents: preserve the audit report without repeating disablement or notifications.

If the gap from the previous blocking incident exceeds the configured repeat window, the chain starts again at strike 1.

The backend consumes the policy snapshot carried by the Node report, including repeatBlockSeconds and repeatWindowSeconds, so the escalation decision matches the configuration active when the event was detected.

Collection and Node commands

  • Node health processing requests a report flush when either Torrent Blocker or Abuse Blocker has buffered reports.
  • Torrent and abuse collections are executed independently, so failure in one plugin does not prevent processing of the other.
  • The existing Node Axios client gains collect and refresh-block operations from the extended Node contract.
  • A repeat incident refreshes only the reported source IP on the reporting Node. Global IP blocking and cross-Node score aggregation are intentionally out of scope.

Collection retains the Node plugin's destructive-flush semantics: a batch can be lost if transport fails after Node has returned it.

Administrative API

The authenticated admin API adds routes under /api/node-plugins/abuse-blocker for:

  • paginated reports filtered by user, Node, severity, rule, action, and date range;
  • aggregate statistics and top users/Nodes;
  • the manual-review queue;
  • report truncation;
  • idempotent enable and keep_disabled review actions.

enable clears plugin strike/review state and enables the user through the standard user service. keep_disabled clears the review workflow while preserving DISABLED status. Existing audit reports are retained for both actions.

The current Users model does not expose a separate UUID column. Therefore, the review route's UUID parameter resolves the existing UUID-shaped vlessUuid. This can be changed to the numeric user ID or shortUuid if maintainers prefer a different public identifier.

Notifications

This PR adds:

  • notification scope abuse_blocker;
  • event abuse_blocker.report;
  • Telegram rendering and the optional TELEGRAM_NOTIFY_ABUSE_BLOCKER switch;
  • backend-webhook delivery and schema registration.

Severity behavior is deliberate:

  • suspicious reports are database-only;
  • alert, initial block, repeat block, and account-disable events are sent to enabled Telegram/backend-webhook integrations;
  • duplicate eventId updates and post-disable incidents do not send repeated notifications.

Compatibility and release gates

  • Depends on feat(node-plugins): add abuse blocker configuration schema #207 and publication of @remnawave/node-plugins@0.7.0.
  • Depends on feat(plugin): add abuse blocker runtime node#46 and publication of the extended @remnawave/node-contract (expected 3.3.0, subject to maintainer release coordination).
  • The backend contract release version will be coordinated after review.
  • Package versions and lockfiles are deliberately not changed in this Draft because the repository workflows publish shared packages when their package manifests change.
  • Existing installations without abuseBlocker remain compatible; the feature is opt-in.
  • The branch uses locally packed versions of both dependent packages for build and test validation until their registry releases are available.

Tests

Added nine focused runtime tests covering:

  • the 10-minute -> one-hour -> disable escalation chain;
  • reset after the seven-day repeat window;
  • no repeated disablement or notifications after manual review is required;
  • database-only suspicious reports;
  • duplicate eventId idempotency;
  • Node block refresh;
  • standard user-service disablement and plugin ownership tracking;
  • enable and idempotent keep_disabled review actions.

Local validation:

npx oxfmt --check <changed files>
npx oxlint
npx tsx --test tests/abuse-blocker/abuse-blocker.runtime.test.ts
npx cross-env NODE_ENV=production rspack build
cd libs/contract
npx tsc -p tsconfig.backend.json --noEmit
npx tsc -p tsconfig.frontend.json --noEmit
npx prisma validate

All nine focused tests, lint, changed-file formatting, production build, both contract typechecks, and Prisma schema validation pass locally. The advisory-lock path is implemented and covered through escalation unit tests; a live PostgreSQL concurrency integration test is not claimed by this Draft.

Dependency chain

Before this Draft is marked ready, the shared packages will be published, registry dependencies and lockfiles will be finalized, the branch will be rebased with signed commits if necessary, and the full repository CI suites will be rerun.

@snyk-io

snyk-io Bot commented Aug 15, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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