Massive Traefik Crowdsec Plugin/Middleware Kubernetes Issues

Practically begging for help here! I have been dealing with something that has been killing me for over a month now. I simply cannot get Crowdsec to work within my Kubernetes cluster. Which is where I usually deploy all my network infrastructure like Traefik for reverse proxying. It seems everything works okay, but when I apply the Crowdsec-Traefik plugin middleware to an Ingress route. It simply does not work whatsoever. I 100% have set up the Crowdsec-Traefik plugin correctly, with the correct LAPI key and everything. The Traefik dashboard also reports the middleware is recognized within Traefik and the cluster.

I will drop some info on my cluster for context: I am running a six node cluster with metallb as a load balancer for services. Everything has worked great so far. I run an HA 3 pod Traefik deployment within it. I primarily deploy everything with Helm charts.

I will drop some configuration down below:

Crowdsec’s values.yaml:

container_runtime: containerd
# Here you can specify your own custom configuration to be loaded in crowdsec agent or lapi
# Each config needs to be a multi-line using '|' in YAML specs
# for the agent those configs will be loaded : parsers, scenarios, postoverflows, simulation.yaml
# for the lapi those configs will be loaded : profiles.yaml, notifications, console.yaml

tls:
  enabled: true
  bouncer:
    reflector:
      namespaces: ["traefik"]
agent:
  # Specify each pod whose logs you want to process
  persistentVolume:
    config:
      enabled: false
    data:
      enabled: true
      storageClassName: "longhorn" 
  acquisition:
    # The namespace where the pod is located
    - namespace: traefik
      # The pod name
      podName: traefik-*
      # as in crowdsec configuration, we need to specify the program name to find a matching parser
      program: traefik
  env:
    - name: PARSERS
      value: "crowdsecurity/cri-logs crowdsecurity/whitelists crowdsecurity/nextcloud-whitelist"
    - name: COLLECTIONS
      value: "crowdsecurity/linux crowdsecurity/k8s-audit crowdsecurity/apache2 crowdsecurity/traefik crowdsecurity/home-assistant Dominic-Wagner/vaultwarden timokoessler/uptime-kuma firix/authentik LePresidente/jellyseerr LePresidente/jellyfin LePresidente/adguardhome crowdsecurity/nextcloud gauth-fr/immich"
    # When testing, allow bans on private networks
    #- name: DISABLE_PARSERS
    #  value: "crowdsecurity/whitelists"
  image:
    pullPolicy: Always
lapi:
  dashboard:
    enabled: false
    ingress:
      host: dashboard.local
      enabled: false
  persistentVolume:
    config:
      enabled: false
    data:
      enabled: true
      storageClassName: "longhorn"  
  resources:
    limits:
      memory: 200Mi
    requests:
      cpu: 250m
      memory: 200Mi
  env:
    # For an internal test, disable the Online API by setting 'DISABLE_ONLINE_API' to "true"
    - name: DISABLE_ONLINE_API
      value: "false"
    - name: ENROLL_KEY
      value: "placeholder-correct-key"
    - name: ENROLL_INSTANCE_NAME
      value: "k3s"
    - name: ENROLL_TAGS
      value: "homelab"

image:
  pullPolicy: Always

Here is my Traefik’s values.yaml:

globalArguments:
  - "--global.sendanonymoususage=false"
  - "--global.checknewversion=false"

additionalArguments:
  - "--serversTransport.insecureSkipVerify=true"
  - "--log.level=INFO"
  - "--providers.kubernetesingress.namespaces="
  - "--providers.kubernetescrd.namespaces="

deployment:
  enabled: true
  replicas: 3
  annotations: {}
  podAnnotations: {}
  additionalContainers: []
  initContainers: []

ports:
  web:
    redirectTo:
      port: websecure
  websecure:
    tls:
      enabled: true  
      
ingressRoute:
  dashboard:
    enabled: false

providers:
  kubernetesCRD:
    enabled: true
    ingressClass: traefik-external
    allowExternalNameServices: true
  kubernetesIngress:
    enabled: true
    allowExternalNameServices: true
    allowCrossNamespace: true
    publishedService:
      enabled: false

rbac:
  enabled: true

service:
  enabled: true
  type: LoadBalancer
  annotations: {}
  labels: {}
  spec:
    loadBalancerIP: 192.168.30.150 # this should be an IP in the MetalLB range
    externalTrafficPolicy: Local # for crowdsec
  loadBalancerSourceRanges: []
  externalIPs: []

logs:
  access:
    enabled: true

experimental:
  plugins:
    crowdsec-bouncer-traefik-plugin:
      moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
      version: "v1.3.0-dev2"

volumes:
  - name: my-crowdsec-bouncer-tls
    mountPath: /etc/traefik/certs/
    type: secret

image:
  pullPolicy: Always

Now for the middleware:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
    name: my-crowdsec-bouncer-traefik-plugin
    namespace: default
spec:
    plugin:
        crowdsec-bouncer-traefik-plugin:
            CrowdsecLapiKey: place-holder-correct-key
            Enabled: "true"

Now, here is an Ingress route format I’ve used with no avail:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: nextcloud-external
  namespace: default
  annotations: 
    kubernetes.io/ingress.class: traefik-external
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`www.nextcloud.xxx.xyz`)
    kind: Rule
    services:
      - name: nextcloud-external
        port: 80
    middlewares:
      - name: my-crowdsec-bouncer-traefik-plugin
  - match: Host(`nextcloud.xxx.xyz`)
    kind: Rule
    services:
    - name: nextcloud-external
      port: 80  
  tls:
    secretName: xxx-xyz-tls

I have also experimented with specifying the middleware on the "Host(nextcloud.xxx.xyz) portion such as this:

match: Host(`nextcloud.xxx.xyz`)
    kind: Rule
    services:
      - name: nextcloud-external
        port: 80
    middlewares:
      - name: my-crowdsec-bouncer-traefik-plugin

When I specify the middleware here, I simply get a blank webpage that does not work at all. When I specify the middleware at the Host(www.nextcloud.xxx.xyz) I can access the webpage, but it’s clear crowdsec will not work as I create a cscli decision to ban an IP I can VPN into, and the ban goes into place, yet when I VPN to the IP address, I can still access the webpage no problem.

I have troubleshooted this to no end and I have simply gotten nowhere.

In your middleware configuration I dont see where you have defined where the crowdsec LAPI is on our example we have

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: bouncer
  namespace: traefik
spec:
  plugin:
    bouncer:
      enabled: true
      crowdsecMode: none
      crowdsecLapiScheme: https
      crowdsecLapiHost: crowdsec-service.crowdsec:8080
      crowdsecLapiTLSCertificateAuthorityFile: /etc/traefik/crowdsec-certs/ca.crt
      crowdsecLapiTLSCertificateBouncerFile: /etc/traefik/crowdsec-certs/tls.crt
      crowdsecLapiTLSCertificateBouncerKeyFile: /etc/traefik/crowdsec-certs/tls.key

ref: https://www.crowdsec.net/blog/integrating-crowdsec-kubernetes-tls

You can know if this is working by execing into the LAPI pod and running cscli bouncers list if you dont see a ip address then there a communication problem between the nodes

Thanks for the middleware configuration! Went ahead and updated mine to reflect my configuration:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
    name: my-crowdsec-bouncer-traefik-plugin
    namespace: default
spec:
  plugin:
    crowdsec-bouncer-traefik-plugin:
      CrowdsecLapiKey: api-key
      enabled: "true"
      crowdsecMode: none
      crowdsecLapiScheme: https
      crowdsecLapiHost: crowdsec-service.crowdsec:8080
      crowdsecLapiTLSCertificateAuthorityFile: /etc/traefik/certs/ca.crt
      crowdsecLapiTLSCertificateBouncerFile: /etc/traefik/certs/tls.crt
      crowdsecLapiTLSCertificateBouncerKeyFile: /etc/traefik/certs/tls.key

However, this still doesn’t seem to be working at all. My LAPI is still connected to the bouncer (like the prior configuration as well):

ian@DESKTOP-0S14EUI:~/kube/traefik-cert/traefik$ kubectl exec -it -n crowdsec crowdsec-lapi-7d897997b4-cb4tz -- bin/bash
crowdsec-lapi-7d897997b4-cb4tz:/# cscli bouncers list
──────────────────────────────────────────────────────────────────────────────────
 Name      IP Address   Valid   Last API pull          Type   Version   Auth Type 
──────────────────────────────────────────────────────────────────────────────────
 traefik                ✔️       2024-04-08T20:39:46Z                    api-key   
──────────────────────────────────────────────────────────────────────────────────
crowdsec-lapi-7d897997b4-cb4tz:/# 

But I still can access my webpages from an IP I panned via exec’ing into one of my agent pods:

crowdsec-agent-62j75:/# cscli decisions add --ip 193.37.254.73
INFO[2024-04-08T22:38:32Z] Decision successfully added                  
crowdsec-agent-62j75:/# cscli decisions list
╭────────┬────────┬──────────────────┬──────────────────────┬────────┬─────────┬────┬────────┬────────────────────┬──────────╮
│   ID   │ Source │   Scope:Value    │        Reason        │ Action │ Country │ AS │ Events │     expiration     │ Alert ID │
├────────┼────────┼──────────────────┼──────────────────────┼────────┼─────────┼────┼────────┼────────────────────┼──────────┤
│ 836732 │ cscli  │ Ip:193.37.254.73 │ manual 'ban' from '' │ ban    │         │    │ 1      │ 3h59m47.305148694s │ 128      │
╰────────┴────────┴──────────────────┴──────────────────────┴────────┴─────────┴────┴────────┴────────────────────┴──────────╯
crowdsec-agent-62j75:/# exit

For IP decision bans, do I need to enter the command into all three agent pods? (I have three.) Rather than just a single agent pod? Cannot see as to why this isn’t working right.

It not working correctly because the bouncer isnt connecting either because a misconfiguration or a networking issue.

My recommendation is to walk through our TLS guide again and see what went wrong as clearly there is something just not right

https://www.crowdsec.net/blog/integrating-crowdsec-kubernetes-tls

It not working correctly because the bouncer isnt connecting either because a misconfiguration or a networking issue.

Could you check my configuration I listed above to see if anything is glaringly wrong? I have scoured it and reconfigured about 3-4 times now. With the TLS guide as well. Nothing is working whatsoever.

There’s a couple things with the guide though that may be outdated around the current Traefik helm chart.

I was getting errors when trying to deploy Traefik with middleware configuration in the values.yaml with the additional arguments:

I was only able to get it to work with:

experimental:
plugins:
crowdsec-bouncer-traefik-plugin:
moduleName: “GitHub - maxlerebourg/crowdsec-bouncer-traefik-plugin: Traefik plugin for Crowdsec - WAF and IP protection
version: “v1.2.1”

I think the helm chart has updated it’s default configuration since this tutorial was written.

Aside from that, I am running into issues with specifying the crowdsec-traefik bouncer in the Traefik namespace as stated in the TLS guide. Even with RBAC and crossname space configuration in the Traefik’s values.yaml, the middleware cannot work with services deployed in the default namespace or others.

Okay, following back as I was able to get Crowdsec to properly work as global middleware. Primarily through the flags on:

"--entrypoints.web.http.middlewares=traefik-bouncer@kubernetescrd"
"--entrypoints.websecure.http.middlewares=traefik-bouncer@kubernetescrd"

I had to remove any reference of middleware on my IngressRoutes, then Crowdsec was able to work perfectly fine and block traffic.

However, I really want to avoid using Crowdsec globally. As I have IngressRoutes that are used specifically on services that are only resolvable via local network. Of which, there’s no need for the overhead of Crowdsec as they are behind my firewall. I would like to be able to finely tune where I am using the Crowdsec/Traefik bouncer plugin and when I go to IngressRoute UptimeKuma specifically, while removing Crowdsec as global middleware I get errors. Even though this should work:

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: uptime-kuma
  namespace: default
  annotations: 
    kubernetes.io/ingress.class: traefik-external
spec:
  entryPoints:
    - websecure
  routes:    
    - match: Host(`uptimekuma.xxx.xyz`)
      kind: Rule
      middlewares:
        - name: bouncer
          namespace: traefik
      services:
        - name: uptime-kuma-external
          port: 3001
  tls:
    secretName: xxx-xyz-tls

Unsure as to why, in configuring the bouncer plugin via middleware doesn’t work, but it works globally. I am using Traefik 2.11.1, and the Traefik-Crowdsec Plugin v1.1.16, any possibility specifying middleware specifically is only workable on a later plugin version?

Might be something to raise with the original maintainers of the plugin, as myself don’t run traefik and havent configured it as your use case.