Skip to content

Add CrowdSec IP reputation#600

Merged
lixmal merged 4 commits intomainfrom
feature/crowdsec-integration
Apr 21, 2026
Merged

Add CrowdSec IP reputation#600
lixmal merged 4 commits intomainfrom
feature/crowdsec-integration

Conversation

@lixmal
Copy link
Copy Markdown
Contributor

@lixmal lixmal commented Apr 1, 2026

Add CrowdSec IP reputation integration and trusted IP support to the reverse proxy access control UI.

  • Add CrowdSec mode selector (Off / Enforce / Observe) for domains that support it
  • Add "trusted" action for access control rules that bypass all restriction layers
  • Add trusted CIDR presets (RFC 1918, CGNAT, IPv6 ULA, Loopback) via dropdown
  • Show CrowdSec verdict metadata in event reason cells with observe-mode badges
  • Display CrowdSec auth method types (ban, captcha, throttle, unavailable) in event logs
  • Handle IPv6 CIDR suffixes (/128) alongside IPv4 (/32) for IP-type rules
image

Issue ticket number and link

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

netbirdio/docs#698

Related PRs

Summary by CodeRabbit

New Features

  • Added CrowdSec IP Reputation integration with configurable Enforce and Observe security modes.
  • Enhanced access control configuration to support CrowdSec mode selection when available on reverse proxy domains.
  • Introduced CrowdSec verdict display in event logs with detailed metadata inspection via tooltips.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Additive type and UI changes introducing CrowdSec support: new CrowdSecMode constant/type and optional crowdsec fields on reverse-proxy interfaces; access-control UI/state and serialization updated to surface/persist CrowdSec mode when a domain supports it; event cells render CrowdSec-related auth methods and verdict metadata.

Changes

Cohort / File(s) Summary
Type Definitions
src/interfaces/ReverseProxy.ts
Add CrowdSecMode constant and union type; extend AccessRestrictions with crowdsec_mode?: CrowdSecMode; add supports_crowdsec?: boolean on ReverseProxyDomain; add metadata?: Record<string,string> to ReverseProxyEvent.
Access Control Rules UI
src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx
Add supportsCrowdSec prop and crowdsecMode state; normalize CIDR/IP suffixes (/32 vs /128), bulk-add CIDR rules helpers, include crowdsec_mode in restrictions serialization, and call onChange when rules or crowdsecMode change; render CrowdSec select when supported.
Modal Integration
src/modules/reverse-proxy/ReverseProxyModal.tsx
Pass supportsCrowdSec={selectedDomain?.supports_crowdsec} into ReverseProxyAccessControlRules.
CrowdSec Select Component
src/modules/reverse-proxy/ReverseProxyCrowdSecIPReputation.tsx
New ReverseProxyCrowdSecIPReputation component rendering a labeled Select for CrowdSecMode with per-mode metadata, icons, and help tooltips.
Event Auth Display
src/modules/reverse-proxy/events/ReverseProxyEventsAuthMethodCell.tsx
Add handling for crowdsec_ban, crowdsec_captcha, crowdsec_throttle, crowdsec_unavailable with corresponding icons/labels.
Event Reason Display
src/modules/reverse-proxy/events/ReverseProxyEventsReasonCell.tsx
Render verdict badge/tooltip when metadata.crowdsec_verdict exists (and auth method not crowdsec_*); tooltip shows other metadata entries; add verdict label mapping.
Access Control Cell Display
src/modules/reverse-proxy/table/ReverseProxyAccessControlCell.tsx
Detect domain supports_crowdsec and derive hasCrowdSec; include CrowdSec row in hover entries and rule count; treat /32 and /128 as host IPs and strip both for display.

Sequence Diagram(s)

(Skipped)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • lixmal

Poem

🐰
I hopped through code with whiskers bright,
Added verdicts, shields, and a little light.
Enforce or observe, now choices bloom,
CIDRs trimmed tidy — safer room.
A carrot for tests, and cheers to the plume.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description includes all required template sections: issue/ticket info, documentation checkbox (marked as added), and docs PR URL. The description is comprehensive with detailed bullet points about changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'Add CrowdSec IP reputation' is partially related to the changeset but omits the 'trusted IPs' aspect mentioned in the PR description and only covers part of the main changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/crowdsec-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lixmal lixmal changed the title [management, proxy] Add CrowdSec IP reputation and trusted IPs for reverse proxy Add CrowdSec IP reputation and trusted IPs for reverse proxy Apr 1, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/modules/reverse-proxy/table/ReverseProxyAccessControlCell.tsx (1)

110-117: Consider applying the same IPv6 fix to existing allowed/blocked CIDR filtering.

For consistency, the allowed/blocked IP filtering at lines 110-117 should also handle /128 suffixes if IPv6 support is being added across the feature.

Example fix for existing filters
     const allowedIps =
-      restrictions?.allowed_cidrs?.filter((c) => c.endsWith("/32")) ?? [];
+      restrictions?.allowed_cidrs?.filter((c) => c.endsWith("/32") || c.endsWith("/128")) ?? [];
     const allowedCidrs =
-      restrictions?.allowed_cidrs?.filter((c) => !c.endsWith("/32")) ?? [];
+      restrictions?.allowed_cidrs?.filter((c) => !c.endsWith("/32") && !c.endsWith("/128")) ?? [];
     const blockedIps =
-      restrictions?.blocked_cidrs?.filter((c) => c.endsWith("/32")) ?? [];
+      restrictions?.blocked_cidrs?.filter((c) => c.endsWith("/32") || c.endsWith("/128")) ?? [];
     const blockedCidrs =
-      restrictions?.blocked_cidrs?.filter((c) => !c.endsWith("/32")) ?? [];
+      restrictions?.blocked_cidrs?.filter((c) => !c.endsWith("/32") && !c.endsWith("/128")) ?? [];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/reverse-proxy/table/ReverseProxyAccessControlCell.tsx` around
lines 110 - 117, Update the CIDR filtering in ReverseProxyAccessControlCell.tsx
so the allowed/blocked IP lists treat IPv6 host addresses the same as IPv4:
change the .endsWith("/32") checks used to populate allowedIps and blockedIps to
also accept "/128" (e.g., treat strings that end with "/32" OR "/128" as
single-host IPs) and invert that logic for allowedCidrs and blockedCidrs (keep
entries that do not end with "/32" AND do not end with "/128"); update the
expressions that compute allowedIps, allowedCidrs, blockedIps, and blockedCidrs
accordingly so IPv6 single-address CIDRs are classified consistently with IPv4.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/modules/reverse-proxy/table/ReverseProxyAccessControlCell.tsx`:
- Around line 110-117: Update the CIDR filtering in
ReverseProxyAccessControlCell.tsx so the allowed/blocked IP lists treat IPv6
host addresses the same as IPv4: change the .endsWith("/32") checks used to
populate allowedIps and blockedIps to also accept "/128" (e.g., treat strings
that end with "/32" OR "/128" as single-host IPs) and invert that logic for
allowedCidrs and blockedCidrs (keep entries that do not end with "/32" AND do
not end with "/128"); update the expressions that compute allowedIps,
allowedCidrs, blockedIps, and blockedCidrs accordingly so IPv6 single-address
CIDRs are classified consistently with IPv4.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 228346a6-219f-4254-a28b-7fc853939eb2

📥 Commits

Reviewing files that changed from the base of the PR and between 0841cae and 5ddeebf.

📒 Files selected for processing (6)
  • src/interfaces/ReverseProxy.ts
  • src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx
  • src/modules/reverse-proxy/ReverseProxyModal.tsx
  • src/modules/reverse-proxy/events/ReverseProxyEventsAuthMethodCell.tsx
  • src/modules/reverse-proxy/events/ReverseProxyEventsReasonCell.tsx
  • src/modules/reverse-proxy/table/ReverseProxyAccessControlCell.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx`:
- Around line 142-144: The ip rule normalization currently preserves
user-supplied CIDR suffixes which can widen access; update the logic that builds
allowed_cidrs so when rule.type === "ip" you first strip any existing CIDR
suffix from rule.value and then append the host mask (use "/128" for IPv6 when
rule.value includes ":" else "/32" for IPv4), and continue to push that
normalized value into allowed_cidrs when rule.action === "allow" (refer to the
variables rule.type, rule.value, suffix, value, and allowed_cidrs in
ReverseProxyAccessControlRules.tsx).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d8f49ff-ff0a-4c01-a546-7e18182e1263

📥 Commits

Reviewing files that changed from the base of the PR and between d882360 and 57faaf5.

📒 Files selected for processing (3)
  • src/interfaces/ReverseProxy.ts
  • src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx
  • src/modules/reverse-proxy/table/ReverseProxyAccessControlCell.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/interfaces/ReverseProxy.ts

Comment thread src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx
@jfrconley
Copy link
Copy Markdown

Is there a reason this hasn't been released yet with the publishing of netbird 0.69.0? The docs mention enabling it per resource, but this can't be done until the dashboard has been updated

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx (1)

144-148: ⚠️ Potential issue | 🟠 Major

IP-type rules still accept and preserve user-supplied CIDR suffixes.

validateRule accepts IP values with CIDR suffixes (e.g., 10.0.0.0/24) because cidr.isValidAddress treats CIDRs as valid, and the !rule.value.includes("/") guard here then skips the host-mask normalization, silently widening an "IP" rule into a range. Normalize unconditionally for type === "ip": strip any existing suffix and append /32 or /128.

Proposed fix
-      const suffix = rule.value.includes(":") ? "/128" : "/32";
-      const value =
-        rule.type === "ip" && !rule.value.includes("/")
-          ? `${rule.value}${suffix}`
-          : rule.value;
+      let value = rule.value;
+      if (rule.type === "ip") {
+        const raw = value.split("/")[0];
+        value = `${raw}${raw.includes(":") ? "/128" : "/32"}`;
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx` around lines
144 - 148, The current normalization logic uses the guard
!rule.value.includes("/") so user-supplied CIDR suffixes are preserved; change
it so that for rule.type === "ip" you always normalize: compute suffix =
rule.value.includes(":") ? "/128" : "/32", strip any existing CIDR by taking the
substring before the first "/" (or otherwise remove trailing "/..."), then set
value = `${strippedAddress}${suffix}`; update the assignment that currently uses
the conditional ternary (referencing rule.value, suffix, and value) and ensure
validateRule still accepts IP inputs before this normalization.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx`:
- Around line 144-148: The current normalization logic uses the guard
!rule.value.includes("/") so user-supplied CIDR suffixes are preserved; change
it so that for rule.type === "ip" you always normalize: compute suffix =
rule.value.includes(":") ? "/128" : "/32", strip any existing CIDR by taking the
substring before the first "/" (or otherwise remove trailing "/..."), then set
value = `${strippedAddress}${suffix}`; update the assignment that currently uses
the conditional ternary (referencing rule.value, suffix, and value) and ensure
validateRule still accepts IP inputs before this normalization.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55d8013d-cf23-4da3-aed7-86b765be7773

📥 Commits

Reviewing files that changed from the base of the PR and between 57faaf5 and fcfc0bf.

⛔ Files ignored due to path filters (1)
  • src/assets/integrations/crowdsec.png is excluded by !**/*.png
📒 Files selected for processing (2)
  • src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx
  • src/modules/reverse-proxy/ReverseProxyCrowdSecIPReputation.tsx

@heisbrot heisbrot changed the title Add CrowdSec IP reputation and trusted IPs for reverse proxy Add CrowdSec IP reputation Apr 21, 2026
@lixmal lixmal merged commit e9ac1a1 into main Apr 21, 2026
7 checks passed
@lixmal lixmal deleted the feature/crowdsec-integration branch April 21, 2026 10:29
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.

3 participants