From 97934ae579dd570320dd4d1153554fae36350b93 Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 7 Aug 2025 15:16:42 +0200 Subject: [PATCH 01/15] move Traefik doc --- .../unversioned/bouncers/traefik.mdx | 58 ++++++++++++++++ .../installation/kubernetes.mdx | 66 ------------------- 2 files changed, 58 insertions(+), 66 deletions(-) create mode 100644 crowdsec-docs/unversioned/bouncers/traefik.mdx diff --git a/crowdsec-docs/unversioned/bouncers/traefik.mdx b/crowdsec-docs/unversioned/bouncers/traefik.mdx new file mode 100644 index 000000000..056072928 --- /dev/null +++ b/crowdsec-docs/unversioned/bouncers/traefik.mdx @@ -0,0 +1,58 @@ +--- +id: traefik +title: Traefix +sidebar_position: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import RemediationSupportBadges from '@site/src/components/remediation-support-badge'; + + +

+CrowdSec +

+

+ + +

+

+📚 Documentation +💠 Hub +💬 Discourse +

+ + + +### Traefik on kubernetes + +Traefik expects a resource of "Middleware" type named "bouncer", which we will create now. + +Here is bouncer-middleware.yaml: + +```yaml +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: bouncer + namespace: traefik +spec: + plugin: + bouncer: + enabled: true + crowdsecMode: stream + crowdsecLapiScheme: https + crowdsecLapiHost: crowdsec-service.crowdsec:8080 + crowdsecLapiKey: mysecretkey12345 +``` + +You can see all the configuration options in the [bouncer documentation](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin). + +Now, you can install the remediation component: + +```bash +kubectl apply -f bouncer-middleware.yaml +``` diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 2f101a593..2107a9468 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -122,73 +122,7 @@ lapi: Then, you can install the remediation component with the following command: -#### Traefik -Traefik expects a resource of "Middleware" type named "bouncer", which we will create now. - -Here is bouncer-middleware.yaml: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: bouncer - namespace: traefik -spec: - plugin: - bouncer: - enabled: true - crowdsecMode: stream - crowdsecLapiScheme: https - crowdsecLapiHost: crowdsec-service.crowdsec:8080 - crowdsecLapiKey: mysecretkey12345 -``` - -You can see all the configuration options in the [bouncer documentation](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin). - -Now, you can install the remediation component: - -```bash -kubectl apply -f bouncer-middleware.yaml -``` - -#### Nginx - -:::info -We supposed that you have already installed the Nginx ingress controller using this [helm chart](https://artifacthub.io/packages/helm/ingress-nginx/ingress-nginx). -::: - -We need to patch ingress-nginx helm chart to add and enable [the crowdsec lua plugin](https://github.com/crowdsecurity/cs-openresty-bouncer). -You can put this configuration example in a file `crowdsec-ingress-nginx.yaml`: - -```yaml -controller: - extraVolumes: - - name: crowdsec-bouncer-plugin - emptyDir: {} - extraInitContainers: - - name: init-clone-crowdsec-bouncer - image: crowdsecurity/lua-bouncer-plugin - imagePullPolicy: IfNotPresent - env: - - name: API_URL - value: "http://crowdsec-service.crowdsec.svc.cluster.local:8080" - - name: API_KEY - value: "mysecretkey12345" - - name: BOUNCER_CONFIG - value: "/crowdsec/crowdsec-bouncer.conf" - command: ['sh', '-c', "sh /docker_start.sh; mkdir -p /lua_plugins/crowdsec/; cp -R /crowdsec/* /lua_plugins/crowdsec/"] - volumeMounts: - - name: crowdsec-bouncer-plugin - mountPath: /lua_plugins - extraVolumeMounts: - - name: crowdsec-bouncer-plugin - mountPath: /etc/nginx/lua/plugins/crowdsec - subPath: crowdsec - config: - plugins: "crowdsec" - lua-shared-dicts: "crowdsec_cache: 50m" -``` Once we have this patch we can upgrade the ingress-nginx chart From 7eae9e08dca50613f6bcff94df40f2fa0dbb6ceb Mon Sep 17 00:00:00 2001 From: sabban Date: Mon, 18 Aug 2025 15:19:11 +0200 Subject: [PATCH 02/15] ingress nginx send metrics --- crowdsec-docs/unversioned/bouncers/ingress-nginx.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crowdsec-docs/unversioned/bouncers/ingress-nginx.mdx b/crowdsec-docs/unversioned/bouncers/ingress-nginx.mdx index 4df587bb0..735f3e914 100644 --- a/crowdsec-docs/unversioned/bouncers/ingress-nginx.mdx +++ b/crowdsec-docs/unversioned/bouncers/ingress-nginx.mdx @@ -24,7 +24,8 @@ import RemediationSupportBadges from '@site/src/components/remediation-support-b A lua Remediation Component for Ingress Nginx Controller. @@ -429,4 +430,4 @@ The timeout to send data from the Remediation Component to the AppSec Component. APPSEC_PROCESS_TIMEOUT=500 # default ``` -The timeout to process the request from the Remediation Component to the AppSec Component. \ No newline at end of file +The timeout to process the request from the Remediation Component to the AppSec Component. From 516650a097e0ae4d236a757d3319d7cbc90fb1d8 Mon Sep 17 00:00:00 2001 From: sabban Date: Mon, 18 Aug 2025 17:48:44 +0200 Subject: [PATCH 03/15] kubernetes documentation revamp --- .../installation/kubernetes.mdx | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 2107a9468..1a9c4da71 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -80,18 +80,17 @@ crowdsec-agent-kf9fr 1/1 Running 0 34s crowdsec-lapi-777c469947-jbk9q 1/1 Running 0 34s ``` -### Install Remediation Component +### A Word About Remediation Component -:::info -Depends which ingress controller you are using, you can install the remediation component. -::: - -First you need to already have an ingress controller installed in your cluster (we consider that you installed it using helm). +Installing the CrowdSec Engine as a local API and log processors is very useful +to detect aggressive behaviors, but no remediation action will be taken upon it. +To get remediation actions, one has to install remediation component. As of now +remediation can only happen at ingress level. For now, we support: -* Traefik -* Nginx +* [Ingress Nginx](u/bouncers/ingress-nginx/) +* [Traefik Kubernetes Ingress (Third party development)](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) Before installing the remediation component, you need to generate API key to communicate with the LAPI. @@ -102,7 +101,8 @@ If you **have persistentVolumes enabled** in `values.yaml`, you can generate the kubectl -n crowdsec exec -it crowdsec-lapi- -- cscli bouncers add my-bouncer-name ``` -Else you **don't have persistentVolumes enabled**, you need to specify your key in the `values.yaml` file: +Else you **don't have persistentVolumes enabled**, you need to specify your key +in the crowdsec helm `values.yaml` file: ```yaml lapi: @@ -119,18 +119,9 @@ lapi: ``` ::: -Then, you can install the remediation component with the following command: - - - - -Once we have this patch we can upgrade the ingress-nginx chart - -```bash -helm -n ingress-nginx upgrade -f ingress-nginx-values.yaml -f crowdsec-ingress-bouncer.yaml ingress-nginx ingress-nginx/ingress-nginx -``` - ## Next Steps? -Great, you now have CrowdSec installed on your system. Within the [post installation steps](/getting_started/next_steps.mdx) you will find the next steps to configure and optimize your installation. +Great, you now have CrowdSec installed on your system. Within the [post +installation steps](/getting_started/next_steps.mdx) you will find the next +steps to configure and optimize your installation. From 017e2b5935f69b21bd00db297a4eda6c67ce65bf Mon Sep 17 00:00:00 2001 From: sabban Date: Wed, 20 Aug 2025 11:56:25 +0200 Subject: [PATCH 04/15] docs(installation): clarify Helm requirement in Kubernetes setup - Added note specifying Helm is required for installation as non-Helm installations are not supported currently. --- .../unversioned/getting_started/installation/kubernetes.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 1a9c4da71..cfca0220e 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -19,6 +19,8 @@ Before getting started, it is advised to read the [introduction](/unversioned/ge - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) - [Helm](https://helm.sh/docs/intro/install/) +Even if an installation could be possible without Helm, it's not supported for now. + ## Helm Repository Installation Add the CrowdSec helm repository to your Helm installation: From b42e1c6b8e9f758adb9901e84b739a6019cf2908 Mon Sep 17 00:00:00 2001 From: sabban Date: Wed, 20 Aug 2025 16:25:04 +0200 Subject: [PATCH 05/15] addd a word about database --- .../installation/kubernetes.mdx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index cfca0220e..408e6347b 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -121,6 +121,28 @@ lapi: ``` ::: +### A word about databases + +By default, CrowdSec uses a SQLite database, which does not support replication. +In a Kubernetes environment, this limitation prevents the Local API from being +replicated. + +For production deployments on Kubernetes, we recommend using a database engine +that can be deployed in a replicated or highly available way, such as MariaDB or +PostgreSQL. You can leverage existing operators to manage these databases: +* [mariadb operator](https://mariadb.com/resources/blog/get-started-with-mariadb-in-kubernetes-and-mariadb-operator/) +* [postgresql operator](https://github.com/cloudnative-pg/cloudnative-pg) + +Configuration those databases is out of scope of this documentation. + + +:::warning + +SQLite may be suitable for testing or low traffic clusters, but it is not +recommended for Kubernetes production deployments. Besides the lack of +replication, SQLite can also become a performance bottleneck under heavy load. + +::: ## Next Steps? From ca9c1d6f697c91cd645cbb74184ed6a2a58bacd7 Mon Sep 17 00:00:00 2001 From: sabban Date: Wed, 20 Aug 2025 17:30:56 +0200 Subject: [PATCH 06/15] adjust traefik bouncer configuration --- crowdsec-docs/sidebarsUnversioned.ts | 5 +++++ crowdsec-docs/unversioned/bouncers/traefik.mdx | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/crowdsec-docs/sidebarsUnversioned.ts b/crowdsec-docs/sidebarsUnversioned.ts index fc945a1f0..140950cda 100644 --- a/crowdsec-docs/sidebarsUnversioned.ts +++ b/crowdsec-docs/sidebarsUnversioned.ts @@ -515,6 +515,11 @@ const sidebarsUnversionedConfig: SidebarConfig = { label: "Wordpress", id: "bouncers/wordpress", }, + { + type: "doc", + label: "Traefik", + id: "bouncers/traefik", + }, { type: "link", label: "Third Party", diff --git a/crowdsec-docs/unversioned/bouncers/traefik.mdx b/crowdsec-docs/unversioned/bouncers/traefik.mdx index 056072928..085a79b7c 100644 --- a/crowdsec-docs/unversioned/bouncers/traefik.mdx +++ b/crowdsec-docs/unversioned/bouncers/traefik.mdx @@ -11,7 +11,7 @@ import RemediationSupportBadges from '@site/src/components/remediation-support-b

-CrowdSec +CrowdSec

@@ -50,6 +50,7 @@ spec: ``` You can see all the configuration options in the [bouncer documentation](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin). +You can also refer to a [full traefik and CrowdSec stack on kubernetes](https://raw.githubusercontent.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/main/examples/kubernetes/README.md) Now, you can install the remediation component: From 04061929d71f5bc94debc5f6f0edf2f0b3cfa0b1 Mon Sep 17 00:00:00 2001 From: sabban Date: Wed, 20 Aug 2025 18:13:39 +0200 Subject: [PATCH 07/15] add trafik logo --- crowdsec-docs/static/img/traefik.logo.png | Bin 0 -> 35056 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 crowdsec-docs/static/img/traefik.logo.png diff --git a/crowdsec-docs/static/img/traefik.logo.png b/crowdsec-docs/static/img/traefik.logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4778d0f5bedb0c41247dd369162c09d6638071e4 GIT binary patch literal 35056 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 meeHLrd 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 Date: Wed, 20 Aug 2025 20:19:09 +0200 Subject: [PATCH 08/15] indentation --- crowdsec-docs/sidebarsUnversioned.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crowdsec-docs/sidebarsUnversioned.ts b/crowdsec-docs/sidebarsUnversioned.ts index 140950cda..a0ef740e3 100644 --- a/crowdsec-docs/sidebarsUnversioned.ts +++ b/crowdsec-docs/sidebarsUnversioned.ts @@ -515,11 +515,11 @@ const sidebarsUnversionedConfig: SidebarConfig = { label: "Wordpress", id: "bouncers/wordpress", }, - { - type: "doc", - label: "Traefik", - id: "bouncers/traefik", - }, + { + type: "doc", + label: "Traefik", + id: "bouncers/traefik", + }, { type: "link", label: "Third Party", From 2d357ef8b342a301021f035b274e182588f2b4e7 Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 15:15:53 +0200 Subject: [PATCH 09/15] improved docs --- .../installation/kubernetes.mdx | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 408e6347b..e3fa2aa93 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -12,14 +12,16 @@ import CodeBlock from '@theme/CodeBlock'; # Kubernetes Deployment -Before getting started, it is advised to read the [introduction](/unversioned/getting_started/introduction.mdx) page to understand the prerequisites and concepts for running CrowdSec. +Before getting started, it is advised to read the +[introduction](/unversioned/getting_started/introduction.mdx) page to understand +the prerequisites and concepts for running CrowdSec. ## Requirements - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) - [Helm](https://helm.sh/docs/intro/install/) -Even if an installation could be possible without Helm, it's not supported for now. +Even if an installation could be possible without Helm, it's not documented for now. ## Helm Repository Installation @@ -60,7 +62,32 @@ lapi: value: "k8s linux test" ``` -If you want more information about the configuration, you can check the default [values.yaml](https://artifacthub.io/packages/helm/crowdsec/crowdsec#values) +Acquisition is done by reading logs directly from pods. You select which pods to +watch thanks to `namespace` and `podName`, and you have to tag the logs with a +program so CrowdSec knows which parser should handle them. For example, if you +set program: nginx, the nginx parser will pick them up. CrowdSec will +automatically attach to the right pods and feed the logs into the right parsers. + + +

Why `program` and not `type` ? +In standard standalone setups documentation states that the labels should be +name `type` with the type being the parsed log program (eg nginx, traefik). A +transformation from `type` to `program` is done by the first stage parser +`crowdsecurity/syslog-logs` which is not relevant in a Kubernetes context. + + + + How collection fit in kubernetes environment? + +Collections are "recipes" for understanding logs; they don’t find pods on their +own. You choose which pods to read, and you tag those logs with a program (like +nginx or traefik). When the tag matches what a collection expects, its rules +run; if it doesn’t, they stay idle. One log stream can match several collections +if the tags fit. + + +If you want more information about the configuration, you can check the default +[values.yaml](https://artifacthub.io/packages/helm/crowdsec/crowdsec#values) Then, you can install the Security Engine with the following command: @@ -82,6 +109,18 @@ crowdsec-agent-kf9fr 1/1 Running 0 34s crowdsec-lapi-777c469947-jbk9q 1/1 Running 0 34s ``` +### A word About Source IPs + +For CrowdSec to do its job in Kubernetes, it needs to see the real client IP. If +not, every request will just look like it’s coming from your ingress controller +or load balancer, and CrowdSec won’t know who the actual attacker is. To fix +this, you need to make sure the original IP gets passed through. Depending on +your setup, that could mean turning on the proxy-protocol in your ingress, +setting externalTrafficPolicy: Local on Services, or tweaking things like +real_ip_header and set_real_ip_from if you’re using NGINX. The exact steps +depend on your stack, but the main idea is simple: CrowdSec needs the real IP, +not the proxy’s. + ### A Word About Remediation Component Installing the CrowdSec Engine as a local API and log processors is very useful @@ -91,8 +130,12 @@ remediation can only happen at ingress level. For now, we support: -* [Ingress Nginx](u/bouncers/ingress-nginx/) -* [Traefik Kubernetes Ingress (Third party development)](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) +* [Ingress Nginx](/u/bouncers/ingress-nginx/) +* [Traefik Ingress](/u/bouncers/traefik/) + +Please note that the [Traefik Kubernetes Ingress (Third party +development)](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin)) +is maintained outside CrowdSec Before installing the remediation component, you need to generate API key to communicate with the LAPI. From a82fac743f088ebcd182c0570d3c4ee70ccca7bb Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 15:25:59 +0200 Subject: [PATCH 10/15] typo --- .../unversioned/getting_started/installation/kubernetes.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index e3fa2aa93..a78fdf27d 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -68,7 +68,7 @@ program so CrowdSec knows which parser should handle them. For example, if you set program: nginx, the nginx parser will pick them up. CrowdSec will automatically attach to the right pods and feed the logs into the right parsers. - +
Why `program` and not `type` ? In standard standalone setups documentation states that the labels should be name `type` with the type being the parsed log program (eg nginx, traefik). A @@ -76,7 +76,7 @@ transformation from `type` to `program` is done by the first stage parser `crowdsecurity/syslog-logs` which is not relevant in a Kubernetes context.
- +
How collection fit in kubernetes environment? Collections are "recipes" for understanding logs; they don’t find pods on their From 55a3dcd3ef5933680450ffbf48869bb3a8196e4d Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 15:36:18 +0200 Subject: [PATCH 11/15] fix links --- .../unversioned/getting_started/installation/kubernetes.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index a78fdf27d..74ebe684d 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -130,8 +130,8 @@ remediation can only happen at ingress level. For now, we support: -* [Ingress Nginx](/u/bouncers/ingress-nginx/) -* [Traefik Ingress](/u/bouncers/traefik/) +* [Ingress Nginx](/bouncers/ingress-nginx.mdx) +* [Traefik Ingress](/bouncers/traefik.mdx) Please note that the [Traefik Kubernetes Ingress (Third party development)](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin)) From fe63beea66ca9fa9af11fe009eca78edfec35a83 Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 15:40:59 +0200 Subject: [PATCH 12/15] add newline for details --- .../unversioned/getting_started/installation/kubernetes.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 74ebe684d..0b7e74042 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -70,6 +70,7 @@ automatically attach to the right pods and feed the logs into the right parsers.
Why `program` and not `type` ? + In standard standalone setups documentation states that the labels should be name `type` with the type being the parsed log program (eg nginx, traefik). A transformation from `type` to `program` is done by the first stage parser From abc6b300167d0a0301adce092bc88635de721007 Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 15:51:48 +0200 Subject: [PATCH 13/15] typo --- .../unversioned/getting_started/installation/kubernetes.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 0b7e74042..5a137db5e 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -69,7 +69,7 @@ set program: nginx, the nginx parser will pick them up. CrowdSec will automatically attach to the right pods and feed the logs into the right parsers.
- Why `program` and not `type` ? + Why "program" and not "type" ? In standard standalone setups documentation states that the labels should be name `type` with the type being the parsed log program (eg nginx, traefik). A From 1b7656caba5ce469e0b041a113f03c148c34520e Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 15:55:29 +0200 Subject: [PATCH 14/15] rollback --- .../unversioned/getting_started/installation/kubernetes.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 5a137db5e..0b7e74042 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -69,7 +69,7 @@ set program: nginx, the nginx parser will pick them up. CrowdSec will automatically attach to the right pods and feed the logs into the right parsers.
- Why "program" and not "type" ? + Why `program` and not `type` ? In standard standalone setups documentation states that the labels should be name `type` with the type being the parsed log program (eg nginx, traefik). A From 5c060bb5f0d808505aa0d83cb4031d4c9c71623b Mon Sep 17 00:00:00 2001 From: sabban Date: Thu, 21 Aug 2025 16:08:51 +0200 Subject: [PATCH 15/15] typo --- .../unversioned/getting_started/installation/kubernetes.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx index 0b7e74042..8f834d18b 100644 --- a/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx +++ b/crowdsec-docs/unversioned/getting_started/installation/kubernetes.mdx @@ -71,14 +71,14 @@ automatically attach to the right pods and feed the logs into the right parsers.
Why `program` and not `type` ? -In standard standalone setups documentation states that the labels should be +In standard standalone setups, documentation states that the labels should be name `type` with the type being the parsed log program (eg nginx, traefik). A transformation from `type` to `program` is done by the first stage parser `crowdsecurity/syslog-logs` which is not relevant in a Kubernetes context.
- How collection fit in kubernetes environment? + How collections fit in kubernetes environment? Collections are "recipes" for understanding logs; they don’t find pods on their own. You choose which pods to read, and you tag those logs with a program (like