fix(crowdsec): stop the 403 loop that banned our own VPS and runner
Chasing why apply-k8s kept dying mid-run turned up a self-inflicted
ban loop. 585 of 586 LePresidente/http-generic-403-bf alerts in the LAPI
came from 193.181.211.79 - our own VPS - POSTing
/management.ManagementService/GetServerKey, i.e. the NetBird client's
own management call. netbird-server had never seen a single one of them,
so the 403 was not NetBird's: the bouncer was rejecting the request
before it got there. A banned peer keeps retrying, each retry is another
403, and the scenario turns five 403s in ten seconds into a 4h ban, so
the loop kept re-arming the ban it was serving. The hourly janitor step
that deleted those decisions hourly was masking all of it.
* netbird/k8s/ingress.yaml - drop the bouncer from the mesh API routes
(gRPC-gateway management, signal, relay, /api, /oauth2). A ban there
locks a peer out of the network it needs to reach anything else, and
those endpoints authenticate by NetBird token, not by a login form.
The dashboard keeps the bouncer; it is a real login surface.
netbird-local was already exempt, so this makes prod match.
* crowdsec-middleware.yaml - CrowdsecMode stream instead of live. live
blocked on GET /v1/decisions per request, so a burst saturated the
LAPI and the plugin 403'd IPs that were never banned. v1.3.3 ignores
UpdateMaxFailure in live, so fail-open is only reachable in stream;
-1 now means an unreachable LAPI degrades to "no protection" rather
than "every site 403". 15s poll instead of the 60s default, because
the runner shares one public IP with the house.
Also corrects the key name: HTTPTimeoutSeconds, not
CrowdsecLapiTimeout, which never existed and was being silently
dropped, leaving the 10s default. Back at 10, not the 2s f7cd75d
guessed - a pull that times out leaves the ban cache frozen at its
startup contents, so new bans would silently never apply.
* crowdsec-values.yaml - CIDR allowlisting moves to parsers/s02-enrich,
where CrowdSec's docs put it: a parser whitelist drops the event before
it reaches a bucket, so those addresses never become a decision at
all. The old postoverflow LAN list was checked only after the ban
existed, which is the window the deploy kept landing in. Added
100.64.0.0/10, which the RFC 1918 blocks miss and where the
workstation, the k0s node and the VPS actually live. The DDNS
home-IP whitelist stays in postoverflows, because resolving a hostname
is the expensive check the docs reserve that stage for.
ClientTrustedIPs mirrors that list so the bouncer skips the LAPI
round-trip entirely for those addresses.
* janitor-cronjob.yaml - drop step 5. The bouncer can no longer
manufacture 403s, so the only remaining firings of that scenario are
real scanners, and deleting their decisions hourly was undoing a
working ban.
* Also lands the LAPI config.yaml.local (SQLite WAL, Central API off,
bounded flush) that f7cd75d's comments referenced but never included:
the LAPI was blocked in fsync on its rollback journal, and the CAPI
resolver held a write transaction while timing out against a host
this network cannot reach.
Verified in-cluster: no new 403-bf alerts in the 3.5min after applying,
the management endpoint answers 404 from the backend instead of 403 from
the bouncer in 0.17s, and the VPS client reports Management and Signal
connected with 2/2 relays.
This commit is contained in:
1 parent
f7cd75d65e
commit
892790822d
5 files changed
+141
-66
No files matched your search
@@ -16,15 +16,12 @@ spec:
|
||||
- name: gitea-service
|
||||
port: 3000
|
||||
# Registry route: NO crowdsec-bouncer.
|
||||
# The bouncer plugin does a blocking `GET /v1/decisions` to the LAPI on
|
||||
# *every* request. A deploy burst (runner Action API polls, `docker
|
||||
# manifest inspect` per own image, containerd pulls, smoke probes) fires
|
||||
# hundreds of parallel registry calls; LAPI saturation pushed the lookup
|
||||
# past the plugin timeout, and the bouncer fail-closed with 403 - which
|
||||
# containerd surfaces as ErrImagePull/ImagePullBackOff on the next pod.
|
||||
# This route only serves authenticated OCI traffic (registry tokens,
|
||||
# basic-auth already handled by gitea) and scanners get nothing useful
|
||||
# from /v2, so there is no bruteforce surface to protect here.
|
||||
# A deploy burst (runner Action API polls, `docker manifest inspect` per
|
||||
# own image, containerd pulls, smoke probes) fires hundreds of parallel
|
||||
# registry calls, and a ban on the runner breaks every later job. This
|
||||
# route only serves authenticated OCI traffic - registry tokens and
|
||||
# basic-auth are already handled by gitea - and scanners get nothing
|
||||
# useful from /v2, so there is no bruteforce surface to protect here.
|
||||
- match: Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)
|
||||
kind: Rule
|
||||
services:
|
||||
|
||||
Reference in new issue
Block a user