From 24dd82e8019a6d5556d4b3924bf07c2138e9ed4e Mon Sep 17 00:00:00 2001 From: mr-forust Date: Sun, 27 Sep 2026 18:52:27 +0200 Subject: [PATCH] fix(crowdsec): make the bouncer trust the mobile range independently The parser-stage whitelist already covers 84.245.64.0/18, so an event from the phone is dropped before it reaches a bucket and no decision is ever created for it - confirmed against 72h of traefik access logs, where the phone shows up as 84.245.120.147, inside that /18. But that left the bouncer's own ClientTrustedIPs without the range, so the guarantee rested on a single config. If the parser whitelist ever stops matching, a ban would be created and then served against the phone, which is the one thing that must not happen: the address belongs to a carrier, so it comes back to us by rotation and a 4h ban is not survivable from the device. ClientTrustedIPs bypasses the bouncer and the decision cache entirely, so repeating the range there holds even if a decision exists for any reason. All nine parser-stage ranges are now mirrored in the bouncer, and the bouncer has no range the parser stage does not know about. The file header now records that this middleware must be applied together with a traefik restart. Applying it alone wedges the plugin: in stream mode handleStreamTicker runs over package-level globals that no reconfiguration stops, so every route referencing the middleware answers 404 with 'invalid middleware crowdsec-crowdsec-bouncer@kubernetescrd' until the pod is replaced. Re-applying the prior config does not recover it and the config is not the cause - NewChecker is a plain net.ParseCIDR and cannot fail on a valid range. That cost 21 routes down before the restart requirement was found; recovery is a pod replace, ~35s. Verified live: middleware applied, traefik restarted, 17 of 20 hosts serving (the three exceptions are unchanged and unrelated - searxng returns its own 429, checkmk is down with 503, and one host is local-only), zero invalid-middleware errors, and the LAPI still shows /v1/decisions/stream polls at the 15s interval. --- crowdsec/k8s/crowdsec-middleware.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crowdsec/k8s/crowdsec-middleware.yaml b/crowdsec/k8s/crowdsec-middleware.yaml index 748e863..6d81c98 100644 --- a/crowdsec/k8s/crowdsec-middleware.yaml +++ b/crowdsec/k8s/crowdsec-middleware.yaml @@ -1,3 +1,17 @@ +# crowdsec/k8s is NOT managed by deploy.yaml - apply this by hand, and apply it +# together with a restart: +# kubectl apply -f crowdsec/k8s/crowdsec-middleware.yaml +# kubectl -n traefik rollout restart deploy/traefik +# +# The restart is not optional. In stream mode the plugin runs a package-level +# ticker goroutine (handleStreamTicker over the isCrowdsecStreamHealthy and +# updateFailure globals) that no reconfiguration stops. Applying a change +# wedges the instance: every route referencing it answers 404 and traefik logs +# 'invalid middleware crowdsec-crowdsec-bouncer@kubernetescrd' until the pod is +# replaced. Re-applying the previous config does NOT recover it, and the config +# is not the cause - a valid CIDR cannot fail NewChecker, which is a plain +# net.ParseCIDR. Only a new pod clears it. Measured cost: ~35s down for all +# 20 hosts behind this middleware. apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: @@ -36,6 +50,14 @@ spec: - "169.254.0.0/16" - "fc00::/7" - "fe80::/10" + # The mobile operator range from forust/mobile-whitelist, repeated + # deliberately rather than relying on the parser whitelist alone. + # That whitelist drops the event before it reaches a bucket, so no + # decision is ever created - but it is one config away from not + # firing, and the bouncer would then enforce a ban that was never + # justified. This is the last line: even a decision that exists for + # any reason is not served against the phone. + - "84.245.64.0/18" # The name is HTTPTimeoutSeconds, an int in seconds (min 1) - there is # no CrowdsecLapiTimeout, and an unrecognised key is silently dropped, # which is how this sat at the 10s default. Nothing rides on it per