Skip to content

[management, proxy, self-hosted] Add CrowdSec AppSec request inspection to the reverse proxy - #6917

Open
lixmal wants to merge 6 commits into
mainfrom
reverse-proxy-crowdsec-appsec
Open

[management, proxy, self-hosted] Add CrowdSec AppSec request inspection to the reverse proxy#6917
lixmal wants to merge 6 commits into
mainfrom
reverse-proxy-crowdsec-appsec

Conversation

@lixmal

@lixmal lixmal commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes

Adds CrowdSec AppSec (WAF) request inspection to the reverse proxy. Where the existing CrowdSec integration asks whether the client IP is known bad, this asks whether the request itself is an attack: each HTTP request on an opted-in service is mirrored to the Security Engine's AppSec endpoint, which answers allow, ban, or captcha. It is off by default and enabled per service.

  • Add an appsec_mode (off/enforce/observe) to service access restrictions, HTTP services only, alongside the existing CrowdSec mode
  • Implement the AppSec protocol in the proxy: request mirroring, verdict handling, capped body buffering that leaves the request forwardable, and a bounded inspection timeout
  • Fail closed in enforce mode when the engine cannot produce a verdict, and record the verdict in the access log in observe mode without blocking
  • Keep credentials the proxy already withholds from backends out of the mirrored request: session cookie, header-auth values, OIDC session token, and password/PIN fields, with only the values replaced so the rest stays inspectable
  • Advertise a supports_appsec cluster capability so the option only appears where an endpoint is configured, which keeps this opt-in for self-hosted and bring-your-own-proxy deployments
  • Share the proxy-wide body-buffering allowance with the agent-network request capture instead of opening a second pool, and record in the access log whenever a body went uninspected
  • Enable the endpoint in the self-hosted setup script when CrowdSec is selected

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • This change does not modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — OR I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See CONTRIBUTING.md.

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

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#889


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Summary by CodeRabbit

  • New Features

    • Added CrowdSec AppSec (WAF) request inspection for HTTP services with off, observe, and enforce modes (enforce can block; observe records verdicts).
    • Added appsec_mode to access restrictions plus capability reporting (supports_appsec) for proxy clusters and reverse-proxy domains.
    • Added CLI/configuration options for the AppSec endpoint, timeouts, mirrored body limits, and inspection concurrency/budget; proxy advertises AppSec support to services.
  • Bug Fixes

    • Improved safe handling for oversized and upgrade requests to preserve streams correctly.
    • Validation and fail-closed behavior now correctly reject/deny when AppSec isn’t applicable or isn’t available.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

Next included review available in 3 minutes.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7dcc5f7e-aee4-432d-b1a7-2d5fb2da306d

📥 Commits

Reviewing files that changed from the base of the PR and between c512bf2 and 86b98c1.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (41)
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/shared/grpc/proxy.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_proxy_capability_test.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/appsec/client.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/auth/header.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/oidc.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/netutil/upgrade.go
  • proxy/internal/restrict/restrict.go
  • proxy/internal/restrict/restrict_test.go
  • proxy/lifecycle.go
  • proxy/lifecycle_test.go
  • proxy/management_integration_test.go
  • proxy/server.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/proxy_service.proto
📝 Walkthrough

Walkthrough

Changes

CrowdSec AppSec support is added across provisioning, management capability propagation, service restriction persistence and validation, proxy startup, request mirroring, and middleware enforcement. HTTP services can use enforce, observe, or off modes with body limits, redaction, verdict mapping, and failure handling.

CrowdSec AppSec integration

Layer / File(s) Summary
CrowdSec provisioning
infrastructure_files/getting-started.sh
Creates the AppSec listener on port 7422, installs AppSec collections, and configures the proxy endpoint.
Management capability propagation
management/internals/modules/reverseproxy/..., management/server/store/..., shared/management/http/api/*, shared/management/proto/proxy_service.proto
Propagates supports_appsec from proxy capabilities through cluster and domain models to API responses.
Service restriction contracts and persistence
management/internals/modules/reverseproxy/service/..., management/server/store/sql_store.go, shared/management/proto/proxy_service.proto, shared/management/http/api/*
Adds appsec_mode, validates HTTP-only enabled modes, serializes restrictions, and restores stored restrictions from JSON.
AppSec inspection client
proxy/internal/appsec/*
Mirrors requests to CrowdSec, redacts configured credentials, handles body limits, budgets, concurrency, and maps engine actions to verdicts.
Proxy middleware enforcement
proxy/internal/auth/*, proxy/internal/restrict/restrict.go, proxy/internal/netutil/upgrade.go, proxy/internal/middleware/bodytap/request.go
Registers per-domain settings, performs inspection, records metadata, blocks enforce-mode denials, and handles upgrade requests.
Proxy startup and mapping
proxy/cmd/proxy/cmd/root.go, proxy/lifecycle.go, proxy/server.go
Adds AppSec configuration, initializes the shared client, advertises capability support, and passes service modes into domain mappings.
Compatibility and validation
proxy/internal/auth/*_test.go, proxy/internal/appsec/client_test.go, proxy/management_integration_test.go, management/internals/modules/reverseproxy/service/service_test.go
Updates registrations and mocks for changed interfaces and validates conversion, enforcement, redaction, buffering, concurrency, and failure behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProxyCLI
  participant ProxyServer
  participant AuthMiddleware
  participant AppSecClient
  participant CrowdSec
  participant Backend

  ProxyCLI->>ProxyServer: Provide AppSec URL and limits
  ProxyServer->>AppSecClient: Initialize shared client
  ProxyServer->>AuthMiddleware: Configure AppSec middleware
  AuthMiddleware->>AppSecClient: Inspect inbound HTTP request
  AppSecClient->>CrowdSec: Send redacted mirrored request
  CrowdSec-->>AppSecClient: Return allow, captcha, or ban action
  AppSecClient-->>AuthMiddleware: Return verdict and body release
  AuthMiddleware->>Backend: Forward allowed request
Loading

Possibly related PRs

  • netbirdio/netbird#6886: Both changes modify the service restrictions loading path in management/server/store/sql_store.go.
  • netbirdio/netbird#6887: Both changes modify AccessRestrictions conversion and validation in service.go.

Suggested reviewers: mlsmaycon

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.33% 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
Title check ✅ Passed The title clearly summarizes the main change: adding CrowdSec AppSec request inspection to the reverse proxy.
Description check ✅ Passed The description is mostly complete and covers the feature, tests, and docs, but the issue ticket and Stack sections are still blank.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch reverse-proxy-crowdsec-appsec

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shared/management/proto/proxy_service.proto`:
- Around line 214-216: Update the documentation comment for the appsec_mode
field to state that enabled modes ("enforce" and "observe") are supported only
for HTTP services and are invalid for TCP/UDP/TLS services, rather than being
ignored. Preserve the existing mode values and field definition.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f5316be8-28c1-4fb1-a74a-c3f6eb5770a8

📥 Commits

Reviewing files that changed from the base of the PR and between 2beb90a and 842b196.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/shared/grpc/proxy.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/appsec/client.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/auth/header.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/oidc.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/netutil/upgrade.go
  • proxy/internal/restrict/restrict.go
  • proxy/lifecycle.go
  • proxy/management_integration_test.go
  • proxy/server.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/proxy_service.proto

Comment thread shared/management/proto/proxy_service.proto
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Release artifacts

Built for PR head 86b98c1 in workflow run #18183.

Artifact Link
All release artifacts Download
Linux packages Download
Windows packages Download
macOS packages Download
UI artifacts Download
UI GTK3 artifacts Download
UI macOS artifacts Download

GHCR images (amd64)

This comment is updated by the Release workflow. Artifact links expire according to the workflow retention policy.

@lixmal
lixmal force-pushed the reverse-proxy-crowdsec-appsec branch from 842b196 to cbe2836 Compare July 27, 2026 13:09
@lixmal
lixmal changed the base branch from reverse-proxy-allow-match-or to main July 27, 2026 13:11
@lixmal lixmal closed this Jul 27, 2026
@lixmal lixmal reopened this Jul 27, 2026
@lixmal
lixmal force-pushed the reverse-proxy-crowdsec-appsec branch from cbe2836 to b936400 Compare July 27, 2026 16:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@proxy/internal/appsec/client.go`:
- Around line 453-456: Update the Cookie redaction logic around
redactCookieHeader to iterate over all values returned by dst.Values("Cookie"),
redact each value using req.RedactCookies, and replace the complete Cookie
header set so no later value bypasses redaction. Preserve unchanged values while
ensuring the header is rewritten with the processed values.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 10698771-9b54-4f6f-b7f4-d2dbc5b0ed42

📥 Commits

Reviewing files that changed from the base of the PR and between 842b196 and b936400.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/shared/grpc/proxy.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/appsec/client.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/auth/header.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/oidc.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/netutil/upgrade.go
  • proxy/internal/restrict/restrict.go
  • proxy/lifecycle.go
  • proxy/management_integration_test.go
  • proxy/server.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/proxy_service.proto
🚧 Files skipped from review as they are similar to previous changes (35)
  • management/server/store/store_mock.go
  • proxy/internal/netutil/upgrade.go
  • management/server/store/store.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • proxy/management_integration_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • proxy/internal/auth/header.go
  • shared/management/http/api/types.gen.go
  • management/internals/modules/reverseproxy/domain/domain.go
  • proxy/internal/auth/oidc.go
  • shared/management/http/api/openapi.yml
  • management/internals/shared/grpc/proxy.go
  • proxy/internal/middleware/bodytap/request.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • proxy/internal/restrict/restrict.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/lifecycle.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • infrastructure_files/getting-started.sh
  • proxy/internal/auth/appsec_test.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • proxy/server.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • shared/management/proto/proxy_service.proto
  • proxy/internal/auth/middleware_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/appsec/client_test.go

Comment thread proxy/internal/appsec/client.go Outdated
@lixmal
lixmal force-pushed the reverse-proxy-crowdsec-appsec branch 3 times, most recently from 735ebba to dcb942a Compare July 27, 2026 17:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
proxy/internal/appsec/client.go (1)

442-458: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Cookie redaction still only checks the first Cookie value; unfixed from prior review.

dst.Get("Cookie") reads only the first value in http.Header["Cookie"]. Per RFC 9113 §8.2.3, HTTP/2 clients (mainstream browsers included) are permitted to split the Cookie header into multiple header fields for HPACK compression efficiency, so a session cookie can legitimately land in a second or later Cookie header that this code never inspects or redacts. That value would still be forwarded intact to the AppSec engine even when it names a cookie in req.RedactCookies.

Proposed fix
-	if cookie := dst.Get("Cookie"); cookie != "" {
-		if redacted := redactCookieHeader(cookie, req.RedactCookies); redacted != cookie {
-			dst.Set("Cookie", redacted)
-		}
+	cookies := dst.Values("Cookie")
+	for i, cookie := range cookies {
+		cookies[i] = redactCookieHeader(cookie, req.RedactCookies)
+	}
+	if len(cookies) > 0 {
+		dst["Cookie"] = cookies
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proxy/internal/appsec/client.go` around lines 442 - 458, Update redactSecrets
to inspect all Cookie header values via dst.Values("Cookie") rather than only
dst.Get("Cookie"), redact each value with redactCookieHeader using
req.RedactCookies, and replace the header values while preserving unmodified
values.
🧹 Nitpick comments (1)
proxy/internal/auth/middleware.go (1)

372-375: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider a higher log level for inspection failures.

In enforce mode this path fails closed with a 403, yet the only signal is a Debug line. An unreachable or misconfigured AppSec engine would silently 403 all opted-in traffic with nothing in default-level logs. Warnf (or a rate-limited warn) would make the outage visible; the per-request verdict metadata already covers the access-log side.

♻️ Proposed change
 	result, err := mw.appsec.Inspect(r.Context(), req)
 	if err != nil {
-		mw.logger.Debugf("AppSec inspection failed for %s: %v", r.Host, err)
+		mw.logger.Warnf("AppSec inspection failed for %s: %v", r.Host, err)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proxy/internal/auth/middleware.go` around lines 372 - 375, Update the AppSec
inspection error handling around mw.appsec.Inspect to log failures at warning
level instead of debug level, preserving the existing message, context, and
enforcement behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@proxy/internal/appsec/client.go`:
- Around line 442-458: Update redactSecrets to inspect all Cookie header values
via dst.Values("Cookie") rather than only dst.Get("Cookie"), redact each value
with redactCookieHeader using req.RedactCookies, and replace the header values
while preserving unmodified values.

---

Nitpick comments:
In `@proxy/internal/auth/middleware.go`:
- Around line 372-375: Update the AppSec inspection error handling around
mw.appsec.Inspect to log failures at warning level instead of debug level,
preserving the existing message, context, and enforcement behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3aabb737-1204-4303-bb21-06c36c1b6bb6

📥 Commits

Reviewing files that changed from the base of the PR and between b936400 and 04dc639.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/shared/grpc/proxy.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/appsec/client.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/auth/header.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/oidc.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/netutil/upgrade.go
  • proxy/internal/restrict/restrict.go
  • proxy/lifecycle.go
  • proxy/management_integration_test.go
  • proxy/server.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/proxy_service.proto
🚧 Files skipped from review as they are similar to previous changes (36)
  • proxy/internal/auth/oidc.go
  • proxy/internal/netutil/upgrade.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • shared/management/proto/proxy_service.proto
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/server/store/store.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store_mock.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • proxy/internal/appsec/body.go
  • management/internals/shared/grpc/proxy.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/auth/header.go
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • shared/management/http/api/openapi.yml
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • proxy/server.go
  • infrastructure_files/getting-started.sh
  • shared/management/http/api/types.gen.go
  • management/server/store/sql_store.go
  • proxy/internal/restrict/restrict.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/service/service.go
  • proxy/management_integration_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/lifecycle.go
  • proxy/internal/auth/middleware_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • proxy/internal/appsec/client_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
proxy/internal/appsec/client.go (1)

373-391: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider reserving the actual body size, not always the full cap, from the shared Budget.

The oversize check above already guarantees r.ContentLength <= c.maxBodyBytes when known, yet the reservation always draws down c.maxBodyBytes regardless. Since net/http bounds reads on a Content-Length request body to the declared length, reserving min(r.ContentLength, c.maxBodyBytes) when the length is known (falling back to the cap only for chunked/unknown-length bodies) would be equally safe and reduce contention on the shared Budget — which now also backs agent-network capture per the PR objectives.

Proposed refactor
 	// A Content-Length over the cap is known to be too large before reading.
 	if r.ContentLength > c.maxBodyBytes {
 		return nil, BypassOversize, noopRelease, nil
 	}
 
-	// Reserve the whole cap rather than the eventual length: the reservation
-	// has to be made before the body is read, and until then the only bound
-	// known is the cap. Skipping inspection when the pool is drained keeps a
-	// burst of large bodies from being an out-of-memory lever; the bypass is
-	// recorded so the gap in coverage is visible.
+	// Reserve only what the body can actually consume: the declared
+	// Content-Length when known (net/http bounds reads to it), otherwise the
+	// cap for chunked/unknown-length bodies. Skipping inspection when the pool
+	// is drained keeps a burst of large bodies from being an out-of-memory
+	// lever; the bypass is recorded so the gap in coverage is visible.
+	reserve := c.maxBodyBytes
+	if r.ContentLength >= 0 && r.ContentLength < reserve {
+		reserve = r.ContentLength
+	}
 	release = noopRelease
 	if c.budget != nil {
-		if !c.budget.Acquire(c.maxBodyBytes) {
+		if !c.budget.Acquire(reserve) {
 			c.logger.Debugf("appsec buffer budget exhausted, inspecting headers and URI only")
 			return nil, BypassBudget, noopRelease, nil
 		}
 		var once sync.Once
-		release = func() { once.Do(func() { c.budget.Release(c.maxBodyBytes) }) }
+		release = func() { once.Do(func() { c.budget.Release(reserve) }) }
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proxy/internal/appsec/client.go` around lines 373 - 391, Update the budget
reservation logic in the body-reading flow to reserve the known Content-Length
when it is nonnegative, using c.maxBodyBytes only for unknown or chunked bodies.
Preserve the existing oversize rejection, BypassBudget behavior, and release
callback, ensuring the acquired amount and released amount use the same computed
reservation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@proxy/lifecycle.go`:
- Around line 192-194: Update the server configuration construction in New to
forward cfg.CrowdSecAppSecMaxConcurrent and cfg.MiddlewareCaptureBudgetBytes
into the corresponding serverConfig fields, alongside the existing
CrowdSecAppSec settings. Preserve the explicitly configured concurrency and
capture-budget values instead of leaving them at zero defaults.

---

Nitpick comments:
In `@proxy/internal/appsec/client.go`:
- Around line 373-391: Update the budget reservation logic in the body-reading
flow to reserve the known Content-Length when it is nonnegative, using
c.maxBodyBytes only for unknown or chunked bodies. Preserve the existing
oversize rejection, BypassBudget behavior, and release callback, ensuring the
acquired amount and released amount use the same computed reservation.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a877343-4a1f-4815-a5e0-903093c3d541

📥 Commits

Reviewing files that changed from the base of the PR and between 04dc639 and dcb942a.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (38)
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/shared/grpc/proxy.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/appsec/client.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/auth/header.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/oidc.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/netutil/upgrade.go
  • proxy/internal/restrict/restrict.go
  • proxy/lifecycle.go
  • proxy/management_integration_test.go
  • proxy/server.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/proxy_service.proto
🚧 Files skipped from review as they are similar to previous changes (34)
  • proxy/internal/netutil/upgrade.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • proxy/internal/auth/oidc.go
  • management/server/store/store_mock.go
  • proxy/internal/auth/header.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • proxy/internal/middleware/bodytap/request.go
  • shared/management/http/api/types.gen.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/server/store/store.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • shared/management/proto/proxy_service.proto
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/server/store/sql_store_service_test.go
  • proxy/internal/appsec/body.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/shared/grpc/proxy.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • proxy/management_integration_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/restrict/restrict.go
  • infrastructure_files/getting-started.sh
  • shared/management/http/api/openapi.yml
  • proxy/server.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/server/store/sql_store.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/middleware.go

Comment thread proxy/lifecycle.go Outdated
@lixmal
lixmal force-pushed the reverse-proxy-crowdsec-appsec branch from dcb942a to 26f88d2 Compare July 27, 2026 17:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
proxy/internal/appsec/client.go (1)

504-520: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Cookie redaction still only covers the first Cookie header value.

This exact pattern was flagged in a prior review round (marked "Addressed in commit dcb942a"), but the code shown here still uses dst.Get("Cookie"), which only returns the first value when multiple Cookie header entries exist. Any later Cookie value — potentially the proxy's own session cookie — is mirrored to the AppSec engine unredacted.

🔒 Proposed fix to redact every Cookie header value
-	if cookie := dst.Get("Cookie"); cookie != "" {
-		if redacted := redactCookieHeader(cookie, req.RedactCookies); redacted != cookie {
-			dst.Set("Cookie", redacted)
-		}
+	if cookies := dst.Values("Cookie"); len(cookies) > 0 {
+		for i, cookie := range cookies {
+			cookies[i] = redactCookieHeader(cookie, req.RedactCookies)
+		}
+		dst["Cookie"] = cookies
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proxy/internal/appsec/client.go` around lines 504 - 520, Update redactSecrets
to process every Cookie header value rather than using dst.Get("Cookie") once.
Iterate over dst.Values("Cookie"), apply redactCookieHeader to each value with
req.RedactCookies, and replace the Cookie header values while preserving
unchanged values and redacting any sensitive cookies in later entries.
🧹 Nitpick comments (1)
proxy/internal/appsec/client.go (1)

217-224: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Idle connection pool doesn't scale with configured concurrency.

MaxIdleConnsPerHost is fixed at 32 while MaxConcurrent defaults to 256 and can be configured up to 4096. Once in-flight inspections exceed 32, excess connections are torn down instead of reused, adding a fresh handshake per burst request on this hot path.

♻️ Suggested fix: scale the pool with the configured bound
+	idleConnsPerHost := maxConcurrent
+	if idleConnsPerHost <= 0 || idleConnsPerHost > 256 {
+		idleConnsPerHost = 256
+	}
 	return &Client{
 		...
 		http: &http.Client{
 			Timeout: timeout,
 			Transport: &http.Transport{
 				MaxIdleConns:        100,
-				MaxIdleConnsPerHost: 32,
+				MaxIdleConnsPerHost: idleConnsPerHost,
 				IdleConnTimeout:     90 * time.Second,
 			},
 		},
 	}, nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proxy/internal/appsec/client.go` around lines 217 - 224, Update the
http.Transport configuration in the client constructor to derive
MaxIdleConnsPerHost from the configured MaxConcurrent bound instead of keeping
it fixed at 32, ensuring the idle pool scales with allowed inspection
concurrency while preserving the existing transport settings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@proxy/internal/appsec/client.go`:
- Around line 504-520: Update redactSecrets to process every Cookie header value
rather than using dst.Get("Cookie") once. Iterate over dst.Values("Cookie"),
apply redactCookieHeader to each value with req.RedactCookies, and replace the
Cookie header values while preserving unchanged values and redacting any
sensitive cookies in later entries.

---

Nitpick comments:
In `@proxy/internal/appsec/client.go`:
- Around line 217-224: Update the http.Transport configuration in the client
constructor to derive MaxIdleConnsPerHost from the configured MaxConcurrent
bound instead of keeping it fixed at 32, ensuring the idle pool scales with
allowed inspection concurrency while preserving the existing transport settings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dabc1632-199e-4b60-9219-153105916142

📥 Commits

Reviewing files that changed from the base of the PR and between dcb942a and 26f88d2.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (39)
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • management/internals/modules/reverseproxy/proxy/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/service.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • management/internals/shared/grpc/proxy.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_service_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/appsec/client.go
  • proxy/internal/appsec/client_test.go
  • proxy/internal/auth/appsec_test.go
  • proxy/internal/auth/header.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/oidc.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/internal/middleware/bodytap/request.go
  • proxy/internal/netutil/upgrade.go
  • proxy/internal/restrict/restrict.go
  • proxy/lifecycle.go
  • proxy/lifecycle_test.go
  • proxy/management_integration_test.go
  • proxy/server.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/proxy_service.proto
🚧 Files skipped from review as they are similar to previous changes (32)
  • management/internals/modules/reverseproxy/domain/manager/api.go
  • proxy/internal/auth/oidc.go
  • management/internals/modules/reverseproxy/domain/domain.go
  • management/server/store/store_mock.go
  • management/internals/modules/reverseproxy/domain/manager/manager_test.go
  • management/internals/modules/reverseproxy/proxy/manager.go
  • proxy/internal/netutil/upgrade.go
  • shared/management/proto/proxy_service.proto
  • proxy/internal/restrict/restrict.go
  • management/internals/modules/reverseproxy/proxy/proxy.go
  • proxy/internal/auth/header.go
  • management/internals/modules/reverseproxy/domain/manager/manager.go
  • shared/management/http/api/types.gen.go
  • management/internals/modules/reverseproxy/service/manager/api.go
  • infrastructure_files/getting-started.sh
  • management/internals/modules/reverseproxy/proxy/manager/manager.go
  • proxy/management_integration_test.go
  • proxy/internal/auth/tunnel_lookup_test.go
  • proxy/lifecycle.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/appsec/body.go
  • proxy/internal/middleware/bodytap/request.go
  • shared/management/http/api/openapi.yml
  • management/internals/modules/reverseproxy/service/service.go
  • management/server/store/sql_store.go
  • proxy/internal/auth/appsec_test.go
  • management/internals/modules/reverseproxy/service/service_test.go
  • proxy/server.go
  • management/internals/modules/reverseproxy/proxy/manager_mock.go
  • proxy/internal/auth/middleware_test.go
  • proxy/internal/auth/middleware.go
  • proxy/internal/appsec/client_test.go

@lixmal
lixmal force-pushed the reverse-proxy-crowdsec-appsec branch 3 times, most recently from 9770039 to 663468e Compare July 28, 2026 08:52
@lixmal
lixmal force-pushed the reverse-proxy-crowdsec-appsec branch from af1f94b to 5885fcc Compare August 19, 2026 10:05
@sonarqubecloud

Copy link
Copy Markdown

@lixmal

lixmal commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

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