Skip to content

Improve CrowdSec hardening and reverse proxy troubleshooting - #917

Open
TechHutTV wants to merge 2 commits into
mainfrom
docs/crowdsec-hardening
Open

Improve CrowdSec hardening and reverse proxy troubleshooting#917
TechHutTV wants to merge 2 commits into
mainfrom
docs/crowdsec-hardening

Conversation

@TechHutTV

@TechHutTV TechHutTV commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR expands the self-hosted CrowdSec and reverse proxy documentation with clearer setup, verification, monitoring, and recovery guidance. It documents observed CrowdSec behavior across access logs, community decisions, AppSec protection, and dashboard lockouts so operators can validate their deployment and recover safely when enforcement behaves unexpectedly.

Changes

  • Correct the dashboard location for reverse proxy access logs and clarify HTTP restriction and observe-mode fields.
  • Explain CrowdSec's lazy bouncer startup and how to verify that the proxy connected to LAPI.
  • Document initial CAPI synchronization, hidden community decisions, and per-label decision metrics.
  • Add end-to-end enforcement tests and safe allowlist and decision-removal workflows.
  • Improve Docker network guidance for the Traefik CrowdSec middleware.
  • Add middleware attachment probes and an optional Traefik health check.
  • Add dashboard lockout recovery steps for AppSec matches and IP decisions.
  • Warn about fail-closed behavior and deleting the entire community blocklist.
  • Clarify supported uses and visible effects of NB_PROXY_PRIVATE.

Summary by CodeRabbit

  • Documentation
    • Clarified where to view reverse-proxy access logs and how denial reasons, authentication metadata, and observe mode are reported.
    • Expanded CrowdSec setup, testing, troubleshooting, allowlisting, recovery, and dashboard lockout guidance.
    • Documented middleware verification, fail-closed behavior, bouncer startup, blocklist synchronization, local and CAPI decisions, metrics inspection, and enforcement timing.
    • Clarified manual enrollment, private-mode support, API security, and recovery from unwanted blocks in self-hosted deployments.

@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 12, 2026 3:14pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The documentation updates clarify reverse-proxy access-log fields and expand CrowdSec setup, verification, enforcement, allowlist, dashboard protection, recovery, and troubleshooting procedures for self-hosted deployments.

Changes

CrowdSec and reverse-proxy documentation

Layer / File(s) Summary
Access-log semantics
src/pages/manage/reverse-proxy/access-logs.mdx
Updates the dashboard path and documents HTTP denial fields, L4 behavior, CrowdSec metadata, and observe-mode behavior.
CrowdSec setup and operation
src/pages/selfhosted/maintenance/crowdsec.mdx, src/pages/selfhosted/migration/enable-reverse-proxy.mdx
Documents lazy bouncer startup, synchronization, decision inspection, enforcement tests, allowlists, recovery, and private proxy capability.
Dashboard protection and recovery
src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx
Documents generated network handling, middleware verification, dashboard lockout recovery, and unavailable-CrowdSec behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • netbirdio/docs#831: Expands the CrowdSec Dashboard Protection documentation covered by this change.
  • netbirdio/docs#863: Covers related reverse-proxy access-control and CrowdSec restriction behavior.
  • netbirdio/docs#889: Introduces related CrowdSec AppSec access-log and self-hosted setup documentation.

Suggested reviewers: lixmal

Poem

Poem

A rabbit checks logs in the soft evening light,
CrowdSec records each decision just right.
Networks connect, and lockouts unwind,
Clear fields and commands keep errors confined.
Hop by hop, the documentation guides.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the PR's main CrowdSec hardening and reverse proxy troubleshooting documentation changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docs/crowdsec-hardening

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: 5

🤖 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 `@src/pages/manage/reverse-proxy/access-logs.mdx`:
- Around line 79-83: Split the two top-level JSON examples in the access-log
documentation into separate json fences, keeping each object unchanged so every
fenced example is independently valid JSON.
- Line 12: Update the adjacent image alt text near the Access Logs description
to use the renamed “Access Logs” dashboard view instead of “Proxy Events table,”
while preserving the existing image and surrounding documentation.
- Line 62: Update the access-log field descriptions in the table around the
“Auth Method,” “Reason,” and “Denied requests” entries to match the HTTP API
contract: document that auth_method_used contains the restriction code, reason
contains “Authentication failed” for authentication failures and “Request
failed” for later request failures, and keep the descriptions consistent with
the existing HTTP note.

In `@src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx`:
- Around line 397-399: Update the CrowdSec troubleshooting documentation to
distinguish AppSec outage behavior from NetBird Proxy’s initial-sync fail-closed
behavior. Keep the statement that crowdsecAppsecUnreachableBlock=true denies
dashboard requests when AppSec is unreachable, but remove the broader claim that
every later LAPI outage denies all reverse-proxy traffic unless that post-sync
behavior is explicitly verified.
- Around line 316-332: Remove the optional Traefik API health-check
configuration, including --api.insecure=true and the internal API router checks,
from the maintenance documentation. Direct the monitoring guidance to use the
external /.env probe instead, treating HTTP 403 as the healthy response; do not
recommend exposing the unauthenticated API on the shared Docker network.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e86d2b90-877a-4f54-901c-d1367701d645

📥 Commits

Reviewing files that changed from the base of the PR and between 204c279 and 7b8d77e.

📒 Files selected for processing (4)
  • src/pages/manage/reverse-proxy/access-logs.mdx
  • src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx
  • src/pages/selfhosted/maintenance/crowdsec.mdx
  • src/pages/selfhosted/migration/enable-reverse-proxy.mdx

## Viewing access logs

Access logs are available in the NetBird dashboard under **Activity** > **Proxy Events**. This view displays a table of all HTTP requests and L4 connections that have passed through your reverse proxy services, with filters to narrow down results by time range, status, or other fields.
Access logs are available in the NetBird dashboard under **Reverse Proxy** > **Access Logs**. This view displays a table of all HTTP requests and L4 connections that have passed through your reverse proxy services, with filters to narrow down results by time range, status, or other fields.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the screenshot alt text to match the renamed view.

Line 12 now names the page Access Logs, but the adjacent image at Line 15 still says Proxy Events table. Update the alt text so screen-reader users receive the current dashboard name.

Proposed fix
-    <img src="/docs-static/img/manage/reverse-proxy/access-logs/access-logs-table.png" alt="Proxy Events table showing reverse proxy access log entries" className="imagewrapper"/>
+    <img src="/docs-static/img/manage/reverse-proxy/access-logs/access-logs-table.png" alt="Access Logs table showing reverse proxy access log entries" className="imagewrapper"/>
🤖 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 `@src/pages/manage/reverse-proxy/access-logs.mdx` at line 12, Update the
adjacent image alt text near the Access Logs description to use the renamed
“Access Logs” dashboard view instead of “Proxy Events table,” while preserving
the existing image and surrounding documentation.

Comment thread src/pages/manage/reverse-proxy/access-logs.mdx
Comment thread src/pages/manage/reverse-proxy/access-logs.mdx
Comment thread src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx Outdated
Comment thread src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx Outdated

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx (1)

338-391: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Include Central API decisions in the lockout diagnosis.

cscli decisions list hides CAPI-sourced decisions by default. A community blocklist decision can therefore be mistaken for AppSec. Query the affected IP with --all and filter AppSec alerts by the same IP:

ip="<your-ip>"
docker compose exec -T crowdsec cscli decisions list --ip "$ip" --all
docker compose exec -T crowdsec cscli alerts list --ip "$ip" --kind waf --limit 0
🤖 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 `@src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx` around lines 338 -
391, Update the lockout diagnosis section around “cscli decisions list” to
include Central API decisions by querying the affected address with --all, and
distinguish those results from AppSec matches. Add the corresponding AppSec
alert query filtered by the same IP and kind waf, using the established shell
variable pattern, before the allowlisting guidance.

Source: MCP tools

🤖 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 `@src/pages/manage/reverse-proxy/access-logs.mdx`:
- Around line 40-42: Update the access-log field descriptions in the table
around “Auth Method,” “User,” and “Reason” to match the raw /api/events/proxy
fields: list auth methods as oidc, password, pin, and header; state that empty
auth_method_used values are omitted and credential failures retain only the
attempted method when available; clarify the exact reason values; and document
that denied L4 entries store the restriction code in auth_method_used while
omitting reason.

In `@src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx`:
- Around line 316-322: Update the external monitoring guidance around the
`https://<your-domain>/.env` probe to also cover the protected `/api` router by
adding `https://<your-domain>/api/.env`, or explicitly state that the existing
check covers only the dashboard router. Preserve the instruction to keep the
unauthenticated Traefik API disabled.

---

Outside diff comments:
In `@src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx`:
- Around line 338-391: Update the lockout diagnosis section around “cscli
decisions list” to include Central API decisions by querying the affected
address with --all, and distinguish those results from AppSec matches. Add the
corresponding AppSec alert query filtered by the same IP and kind waf, using the
established shell variable pattern, before the allowlisting guidance.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ec487e1-2236-4ed4-87d3-dc8e22bd71c8

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8d77e and c5d45c3.

📒 Files selected for processing (2)
  • src/pages/manage/reverse-proxy/access-logs.mdx
  • src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx

Comment on lines +40 to +42
| **Auth Method** | Authentication method used (SSO, password, PIN, header, or none). For denied requests, carries the restriction code instead (e.g. `ip_restricted`, `crowdsec_ban`) | Yes | N/A |
| **User** | The authenticated user's ID (if SSO was used) | Yes | N/A |
| **Reason** | Reason for denial, if applicable | Yes | Yes |
| **Reason** | `Authentication failed` when authentication or an access restriction rejected the request, or `Request failed` when an authenticated request returned `4xx`/`5xx`. Not a specific denial code: see the note under [Deny reasons](#deny-reasons) | Yes | No |

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ref="${NETBIRD_REF:?Set NETBIRD_REF to the matching NetBird server commit or release}"
base="https://raw.githubusercontent.com/netbirdio/netbird/${ref}"

for path in \
  proxy/auth/auth.go \
  proxy/internal/auth/middleware.go \
  proxy/internal/accesslog/logger.go \
  management/internals/modules/reverseproxy/accesslogs/accesslogentry.go
do
  printf '\n--- %s ---\n' "$path"
  curl -fsSL "$base/$path" |
    rg -n -C 4 \
      'MethodPassword|MethodPIN|MethodOIDC|MethodHeader|attemptedMethod|SetAuthMethod|DenyReason|AuthMechanism|AuthMethodUsed|Authentication failed|Request failed'
done

Repository: netbirdio/docs

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- changed file status ---'
git status --short
printf '%s\n' '--- relevant file lines ---'
cat -n src/pages/manage/reverse-proxy/access-logs.mdx | sed -n '1,180p'
printf '%s\n' '--- recent repository references ---'
rg -n 'NETBIRD_REF|/api/events/proxy|auth_method_used|Denied L4|Deny reasons' . -g '*.mdx' -g '*.md' -g '*.yml' -g '*.yaml' -g '*.json' | head -200

Repository: netbirdio/docs

Length of output: 13427


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- changed file status ---'
git status --short
printf '%s\n' '--- relevant file lines ---'
cat -n src/pages/manage/reverse-proxy/access-logs.mdx | sed -n '1,180p'
printf '%s\n' '--- repository references ---'
rg -n 'NETBIRD_REF|/api/events/proxy|auth_method_used|Denied L4|Deny reasons' . -g '*.mdx' -g '*.md' -g '*.yml' -g '*.yaml' -g '*.json' | head -200

Repository: netbirdio/docs

Length of output: 13420


🏁 Script executed:

#!/bin/bash
set -euo pipefail

fetch() {
  local ref="$1" path="$2"
  printf '\n--- %s %s ---\n' "$ref" "$path"
  curl -fsSL "https://raw.githubusercontent.com/netbirdio/netbird/${ref}/${path}" |
    nl -ba |
    rg -n -C 8 \
      'MethodPassword|MethodPIN|MethodOIDC|MethodHeader|attemptedMethod|SetAuthMethod|DenyReason|AuthMechanism|AuthMethodUsed|Authentication failed|Request failed|reason|auth_method_used|metadata|AccessLog'
}

for ref in main 1e7b16db; do
  fetch "$ref" proxy/auth/auth.go
  fetch "$ref" proxy/internal/auth/middleware.go
  fetch "$ref" proxy/internal/accesslog/logger.go
  fetch "$ref" management/internals/modules/reverseproxy/accesslogs/accesslogentry.go
done

Repository: netbirdio/docs

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

fetch() {
  local ref="$1" path="$2" tmp
  tmp="$(mktemp)"
  curl -fsSL "https://raw.githubusercontent.com/netbirdio/netbird/${ref}/${path}" >"$tmp"
  printf '\n--- %s %s ---\n' "$ref" "$path"
  awk '{printf "%6d\t%s\n", NR, $0}' "$tmp" |
    rg -n -C 8 \
      'MethodPassword|MethodPIN|MethodOIDC|MethodHeader|attemptedMethod|SetAuthMethod|DenyReason|AuthMechanism|AuthMethodUsed|Authentication failed|Request failed|reason|auth_method_used|metadata|AccessLog'
  rm -f "$tmp"
}

for ref in main 1e7b16db; do
  fetch "$ref" proxy/auth/auth.go
  fetch "$ref" proxy/internal/auth/middleware.go
  fetch "$ref" proxy/internal/accesslog/logger.go
  fetch "$ref" management/internals/modules/reverseproxy/accesslogs/accesslogentry.go
done

Repository: netbirdio/docs

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ref="1e7b16db"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

for spec in \
  "proxy/internal/auth/middleware.go middleware.go" \
  "proxy/internal/accesslog/logger.go logger.go" \
  "management/internals/modules/reverseproxy/accesslogs/accesslogentry.go accesslogentry.go"
do
  path="${spec% *}"
  file="${spec##* }"
  curl -fsSL "https://raw.githubusercontent.com/netbirdio/netbird/${ref}/${path}" > "$tmp/$file"
done

python3 - "$tmp" <<'PY'
import re
import sys
from pathlib import Path

root = Path(sys.argv[1])
auth = (root / "middleware.go").read_text()
logger = (root / "logger.go").read_text()
entry = (root / "accesslogentry.go").read_text()

checks = {
    "raw auth methods": all(
        f'Method{name} Method = "{value}"' in auth.replace("auth.", "")
        for name, value in (
            ("Password", "password"),
            ("PIN", "pin"),
            ("OIDC", "oidc"),
            ("Header", "header"),
        )
    ),
    "failed credentials retain attempted method": (
        "attemptedMethod = scheme.Type().String()" in auth
        and "cd.SetAuthMethod(attemptedMethod)" in auth
    ),
    "restriction denial stores restriction code": (
        "cd.SetAuthMethod(reason)" in auth
    ),
    "L4 denial maps to auth mechanism": (
        "le.AuthMechanism = entry.DenyReason" in logger
        and "le.AuthSuccess = false" in logger
    ),
    "L4 denial does not set reason": (
        "if a.Protocol == \"\" || a.Protocol == AccessLogProtocolHTTP" in entry
    ),
    "reason classification": all(
        text in entry
        for text in (
            'a.Reason = "Authentication failed"',
            'a.Reason = "Request failed"',
            'if !serviceLog.GetAuthSuccess()',
            'else if serviceLog.GetResponseCode() >= 400',
        )
    ),
    "empty auth method omitted": (
        "if a.AuthMethodUsed != \"\"" in entry
        and "authMethod = &a.AuthMethodUsed" in entry
    ),
}

for name, passed in checks.items():
    print(f"{'PASS' if passed else 'FAIL'}: {name}")

if not all(checks.values()):
    raise SystemExit(1)
PY

Repository: netbirdio/docs

Length of output: 408


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ref="1e7b16db"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

for spec in \
  "proxy/internal/auth/middleware.go middleware.go" \
  "proxy/internal/accesslog/logger.go logger.go" \
  "management/internals/modules/reverseproxy/accesslogs/accesslogentry.go accesslogentry.go"
do
  path="${spec% *}"
  file="${spec##* }"
  curl -fsSL "https://raw.githubusercontent.com/netbirdio/netbird/${ref}/${path}" > "$tmp/$file"
done

python3 - "$tmp" <<'PY'
import sys
from pathlib import Path

root = Path(sys.argv[1])
auth = (root / "middleware.go").read_text()
logger = (root / "logger.go").read_text()
entry = (root / "accesslogentry.go").read_text()

checks = {
    "raw auth methods": all(
        f'Method{name} Method = "{value}"' in auth.replace("auth.", "")
        for name, value in (
            ("Password", "password"),
            ("PIN", "pin"),
            ("OIDC", "oidc"),
            ("Header", "header"),
        )
    ),
    "failed credentials retain attempted method": (
        "attemptedMethod = scheme.Type().String()" in auth
        and "cd.SetAuthMethod(attemptedMethod)" in auth
    ),
    "restriction denial stores restriction code": (
        "cd.SetAuthMethod(reason)" in auth
    ),
    "L4 denial maps to auth mechanism": (
        "le.AuthMechanism = entry.DenyReason" in logger
        and "le.AuthSuccess = false" in logger
    ),
    "L4 denial does not set reason": (
        "if a.Protocol == \"\" || a.Protocol == AccessLogProtocolHTTP" in entry
    ),
    "reason classification": all(
        text in entry
        for text in (
            'a.Reason = "Authentication failed"',
            'a.Reason = "Request failed"',
            'if !serviceLog.GetAuthSuccess()',
            'else if serviceLog.GetResponseCode() >= 400',
        )
    ),
    "empty auth method omitted": (
        "if a.AuthMethodUsed != \"\"" in entry
        and "authMethod = &a.AuthMethodUsed" in entry
    ),
}

for name, passed in checks.items():
    print(f"{'PASS' if passed else 'FAIL'}: {name}")

if not all(checks.values()):
    raise SystemExit(1)
PY

Repository: netbirdio/docs

Length of output: 408


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ref="1e7b16db"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

for spec in \
  "proxy/auth/auth.go auth.go" \
  "proxy/internal/auth/middleware.go middleware.go" \
  "proxy/internal/accesslog/logger.go logger.go" \
  "management/internals/modules/reverseproxy/accesslogs/accesslogentry.go accesslogentry.go"
do
  path="${spec% *}"
  file="${spec##* }"
  curl -fsSL "https://raw.githubusercontent.com/netbirdio/netbird/${ref}/${path}" > "$tmp/$file"
done

python3 - "$tmp" <<'PY'
import sys
from pathlib import Path

root = Path(sys.argv[1])
auth_types = (root / "auth.go").read_text()
middleware = (root / "middleware.go").read_text()
logger = (root / "logger.go").read_text()
entry = (root / "accesslogentry.go").read_text()

checks = {
    "raw auth methods": all(
        f'Method{name} Method = "{value}"' in auth_types
        for name, value in (
            ("Password", "password"),
            ("PIN", "pin"),
            ("OIDC", "oidc"),
            ("Header", "header"),
        )
    ),
    "failed credentials retain attempted method": (
        "attemptedMethod = scheme.Type().String()" in middleware
        and "cd.SetAuthMethod(attemptedMethod)" in middleware
    ),
    "restriction denial stores restriction code": (
        "cd.SetAuthMethod(reason)" in middleware
    ),
    "L4 denial maps to auth mechanism": (
        "le.AuthMechanism = entry.DenyReason" in logger
        and "le.AuthSuccess = false" in logger
    ),
    "L4 denial does not set reason": (
        "if a.Protocol == \"\" || a.Protocol == AccessLogProtocolHTTP" in entry
    ),
    "reason classification": all(
        text in entry
        for text in (
            'a.Reason = "Authentication failed"',
            'a.Reason = "Request failed"',
            'if !serviceLog.GetAuthSuccess()',
            'else if serviceLog.GetResponseCode() >= 400',
        )
    ),
    "empty auth method omitted": (
        "if a.AuthMethodUsed != \"\"" in entry
        and "authMethod = &a.AuthMethodUsed" in entry
    ),
}

for name, passed in checks.items():
    print(f"{'PASS' if passed else 'FAIL'}: {name}")

if not all(checks.values()):
    raise SystemExit(1)
PY

Repository: netbirdio/docs

Length of output: 408


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
curl -fsSL 'https://raw.githubusercontent.com/netbirdio/netbird/1e7b16db/proxy/auth/auth.go' > "$tmp"

printf '%s\n' '--- declaration lines ---'
sed -n '12,24p' "$tmp"

python3 - "$tmp" <<'PY'
import sys
from pathlib import Path

text = Path(sys.argv[1]).read_text()
for line in text.splitlines():
    if "Method" in line and ("password" in line or "pin" in line or "oidc" in line or "header" in line):
        print(repr(line))
PY

Repository: netbirdio/docs

Length of output: 542


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
curl -fsSL 'https://raw.githubusercontent.com/netbirdio/netbird/1e7b16db/proxy/internal/accesslog/logger.go' > "$tmp"

printf '%s\n' '--- AuthSuccess assignments and log construction ---'
awk '{printf "%6d\t%s\n", NR, $0}' "$tmp" |
  rg -n -C 10 'AuthSuccess\s*:|AuthSuccess|LogHTTP|LogL4|logEntry\{'

printf '%s\n' '--- public/auth middleware references ---'
for path in \
  proxy/internal/auth/middleware.go \
  proxy/internal/proxy/proxy.go \
  proxy/internal/proxy/handler.go
do
  url="https://raw.githubusercontent.com/netbirdio/netbird/1e7b16db/$path"
  if curl -fsSL "$url" > "$tmp"; then
    printf '\n--- %s ---\n' "$path"
    awk '{printf "%6d\t%s\n", NR, $0}' "$tmp" |
      rg -n -C 8 'AuthSuccess|LogHTTP|AuthMechanism|public|authenticate' || true
  fi
done

Repository: netbirdio/docs

Length of output: 12851


Align access-log documentation with the raw /api/events/proxy fields.

  • Use oidc, password, pin, and header. Empty auth_method_used values are omitted.
  • For credential failures, auth_method_used stores the attempted method when available. It does not store detailed causes.
  • reason is Authentication failed for authentication failures and Request failed for other HTTP 4xx/5xx responses.
  • Denied L4 entries store the restriction code in auth_method_used and omit reason.
🤖 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 `@src/pages/manage/reverse-proxy/access-logs.mdx` around lines 40 - 42, Update
the access-log field descriptions in the table around “Auth Method,” “User,” and
“Reason” to match the raw /api/events/proxy fields: list auth methods as oidc,
password, pin, and header; state that empty auth_method_used values are omitted
and credential failures retain only the attempted method when available; clarify
the exact reason values; and document that denied L4 entries store the
restriction code in auth_method_used while omitting reason.

Source: MCP tools

Comment on lines +316 to +322
To surface this automatically, monitor the same probe from outside the deployment and treat `403` as the healthy response. Point an uptime monitor or a cron job at:

```text
https://<your-domain>/.env
```

Alert when the response is anything other than `403`. This is an inverted check, alerting on the absence of a block rather than on an outage, but it is the only signal that confirms the protection layer is still in the request path.

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

base="https://<your-domain>"
for path in /.env /api/.env; do
  code="$(curl -ks -o /dev/null -w '%{http_code}' "${base}${path}")"
  test "$code" = 403 || exit 1
done

Repository: netbirdio/docs

Length of output: 152


🏁 Script executed:

#!/bin/bash
set -eu

file="src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx"
printf '%s\n' '--- target section ---'
sed -n '300,345p' "$file"

printf '%s\n' '--- route and middleware references ---'
rg -n -C 3 'middleware|/api|/oauth2|\\.env|router|Traefik' "$file"

Repository: netbirdio/docs

Length of output: 16088


🏁 Script executed:

#!/bin/bash
set -eu

file="src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx"

printf '%s\n' '--- complete route configuration ---'
sed -n '115,165p' "$file"

printf '%s\n' '--- probes and router labels ---'
rg -n -C 2 'https?://.*(\\.env|api)|routers\\.|middlewares=' "$file"

printf '%s\n' '--- repository references to the same router names ---'
rg -n -C 2 'netbird-dashboard-api|netbird-dashboard-crowdsec|PathPrefix\\(`/api`\\)|PathPrefix\\(`/oauth2`\\)' .

Repository: netbirdio/docs

Length of output: 7790


Probe both protected routers. The /.env check covers only the dashboard router. The guide also protects /api and /oauth2 through a separate router.

Add /api/.env to the monitor, or state that the check covers only the dashboard router. Keep the unauthenticated Traefik API disabled.

🤖 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 `@src/pages/selfhosted/maintenance/crowdsec-dashboard.mdx` around lines 316 -
322, Update the external monitoring guidance around the
`https://<your-domain>/.env` probe to also cover the protected `/api` router by
adding `https://<your-domain>/api/.env`, or explicitly state that the existing
check covers only the dashboard router. Preserve the instruction to keep the
unauthenticated Traefik API disabled.

Source: MCP tools

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