Skip to content

Add CrowdSec IP reputation and trusted IPs for reverse proxy#600

Open
lixmal wants to merge 3 commits intomainfrom
feature/crowdsec-integration
Open

Add CrowdSec IP reputation and trusted IPs for reverse proxy#600
lixmal wants to merge 3 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

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Additive type and UI changes to introduce CrowdSec support: new CrowdSecMode constant/type and optional crowdsec fields on reverse-proxy interfaces; access-control UI and serialization updated to surface and 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 optional crowdsec_mode?: CrowdSecMode; add supports_crowdsec?: boolean to ReverseProxyDomain; add metadata?: Record<string,string> to ReverseProxyEvent.
Access Control Rules UI
src/modules/reverse-proxy/ReverseProxyAccessControlRules.tsx
Add optional supportsCrowdSec prop and crowdsecMode state; convert rules↔restrictions to include crowdsec_mode; call onChange with updated restrictions when rules or crowdsecMode change; render CrowdSec Select when supported; adjust CIDR/IP handling for host suffixes.
Modal Integration
src/modules/reverse-proxy/ReverseProxyModal.tsx
Pass supportsCrowdSec={selectedDomain?.supports_crowdsec} into ReverseProxyAccessControlRules.
Event Auth Display
src/modules/reverse-proxy/events/ReverseProxyEventsAuthMethodCell.tsx
Add four CrowdSec auth_method_used cases (crowdsec_ban, crowdsec_captcha, crowdsec_throttle, crowdsec_unavailable) with new icons/labels.
Event Reason Display
src/modules/reverse-proxy/events/ReverseProxyEventsReasonCell.tsx
Render verdict badge/tooltip when metadata.crowdsec_verdict exists (and auth method is not a crowdsec_*); tooltip lists 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 in rule count; treat /32 and /128 as host IPs and strip both suffixes for display.

Sequence Diagram(s)

(Skipped)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • pascal-fischer

Poem

🐰
I padded through nets both wide and small,
Added verdict flags and a CrowdSec call.
Enforce or observe, the modes now sing,
CIDR hops trusted — a safer spring.
Hop on, dear proxy, shields up for all.

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding CrowdSec IP reputation and trusted IPs support for reverse proxy, which is reflected across multiple files.
Description check ✅ Passed Pull request description is comprehensive and follows the template structure with issue ticket, documentation section, and related PRs included.

✏️ 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 on lines +142 to 144
const suffix = rule.value.includes(":") ? "/128" : "/32";
const value = rule.type === "ip" && !rule.value.includes("/") ? `${rule.value}${suffix}` : rule.value;
if (rule.action === "allow") allowed_cidrs.push(value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Normalize ip rules to host masks unconditionally.

ip rules currently keep user-provided CIDR suffixes (e.g., /24, /64) when present, which can silently turn an IP rule into a range rule and broaden access-control scope.

Proposed fix
-      const suffix = rule.value.includes(":") ? "/128" : "/32";
-      const value = rule.type === "ip" && !rule.value.includes("/") ? `${rule.value}${suffix}` : rule.value;
+      const rawValue = rule.value.split("/")[0];
+      const suffix = rawValue.includes(":") ? "/128" : "/32";
+      const value = rule.type === "ip" ? `${rawValue}${suffix}` : rule.value;
🤖 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
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).

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