|
| 1 | +# CrowdSec Manager — Helm Chart |
| 2 | + |
| 3 | +Deploy CrowdSec Manager on Kubernetes with a **Tailscale sidecar** for VPN-only access using Helm. |
| 4 | + |
| 5 | +For plain `kubectl` manifests (no Helm), see [`../k8s/`](../k8s/). |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Chart Location |
| 10 | + |
| 11 | +``` |
| 12 | +charts/ |
| 13 | +└── crowdsec-manager/ ← chart directory (pass this to helm install) |
| 14 | + ├── Chart.yaml |
| 15 | + ├── values.yaml ← all configurable defaults |
| 16 | + └── templates/ |
| 17 | + ├── _helpers.tpl |
| 18 | + ├── deployment.yaml ← Pod with Tailscale sidecar + app container |
| 19 | + ├── service.yaml |
| 20 | + ├── configmap.yaml |
| 21 | + ├── secret.yaml |
| 22 | + ├── pvc-data.yaml |
| 23 | + ├── pvc-config.yaml |
| 24 | + ├── pvc-backups.yaml |
| 25 | + ├── pvc-logs.yaml |
| 26 | + ├── pvc-tailscale.yaml |
| 27 | + └── NOTES.txt |
| 28 | +``` |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## Prerequisites |
| 33 | + |
| 34 | +| Requirement | Notes | |
| 35 | +|---|---| |
| 36 | +| Kubernetes 1.24+ | Tested on k3s, k0s, microk8s | |
| 37 | +| Helm 3.10+ | `helm version` to check | |
| 38 | +| Tailscale account | Auth key from [admin console](https://login.tailscale.com/admin/settings/keys) | |
| 39 | +| Node with Docker daemon | The node that runs CrowdSec + Traefik | |
| 40 | +| `/dev/net/tun` + WireGuard | Linux 5.6+ has both built-in; older: `modprobe tun wireguard` | |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## Quick Start |
| 45 | + |
| 46 | +### 1. Label the target node |
| 47 | + |
| 48 | +```bash |
| 49 | +kubectl label node <your-node-name> crowdsec-manager/host=true |
| 50 | +``` |
| 51 | + |
| 52 | +> Skip this step on single-node clusters — set `nodeSelector: {}` in your values instead. |
| 53 | +
|
| 54 | +### 2. Create the namespace |
| 55 | + |
| 56 | +```bash |
| 57 | +kubectl create namespace crowdsec |
| 58 | +``` |
| 59 | + |
| 60 | +### 3. Create the Tailscale secret (recommended — keeps the key out of Helm values) |
| 61 | + |
| 62 | +Generate a **reusable** auth key at [Tailscale Admin → Settings → Keys](https://login.tailscale.com/admin/settings/keys). |
| 63 | + |
| 64 | +```bash |
| 65 | +kubectl create secret generic crowdsec-manager-tailscale \ |
| 66 | + --namespace crowdsec \ |
| 67 | + --from-literal=TS_AUTHKEY="tskey-auth-XXXXXXXXXXXX-YYYYYYY" |
| 68 | +``` |
| 69 | + |
| 70 | +### 4. Install the chart |
| 71 | + |
| 72 | +Using the pre-created secret: |
| 73 | + |
| 74 | +```bash |
| 75 | +helm install crowdsec-manager ./charts/crowdsec-manager \ |
| 76 | + --namespace crowdsec \ |
| 77 | + --set tailscaleSecret.existingSecret=crowdsec-manager-tailscale |
| 78 | +``` |
| 79 | + |
| 80 | +Or supply the key inline (not recommended for GitOps — it ends up in Helm history): |
| 81 | + |
| 82 | +```bash |
| 83 | +helm install crowdsec-manager ./charts/crowdsec-manager \ |
| 84 | + --namespace crowdsec \ |
| 85 | + --set tailscaleSecret.authKey="tskey-auth-XXXXXXXXXXXX-YYYYYYY" |
| 86 | +``` |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +## Configuration |
| 91 | + |
| 92 | +All defaults are in `values.yaml`. Override with `--set key=value` or a custom values file. |
| 93 | + |
| 94 | +### Common overrides |
| 95 | + |
| 96 | +```yaml |
| 97 | +# my-values.yaml |
| 98 | + |
| 99 | +# Single-node cluster — disable node label requirement |
| 100 | +nodeSelector: {} |
| 101 | + |
| 102 | +# Adjust container names to match your Docker setup |
| 103 | +app: |
| 104 | + crowdsec: |
| 105 | + containerName: crowdsec |
| 106 | + metricsUrl: http://crowdsec:6060/metrics |
| 107 | + traefik: |
| 108 | + containerName: traefik |
| 109 | + |
| 110 | +# Disable Pangolin/Gerbil if not in use |
| 111 | +app: |
| 112 | + pangolin: |
| 113 | + enabled: "false" |
| 114 | + gerbil: |
| 115 | + enabled: "false" |
| 116 | + |
| 117 | +# Tailscale hostname in the admin console |
| 118 | +tailscale: |
| 119 | + hostname: my-crowdsec-server |
| 120 | + |
| 121 | +# StorageClass for PVCs (leave empty for cluster default) |
| 122 | +persistence: |
| 123 | + data: |
| 124 | + storageClass: local-path |
| 125 | +``` |
| 126 | +
|
| 127 | +Apply: |
| 128 | +
|
| 129 | +```bash |
| 130 | +helm install crowdsec-manager ./charts/crowdsec-manager \ |
| 131 | + --namespace crowdsec \ |
| 132 | + --set tailscaleSecret.existingSecret=crowdsec-manager-tailscale \ |
| 133 | + -f my-values.yaml |
| 134 | +``` |
| 135 | + |
| 136 | +### Injecting a docker-compose.yml |
| 137 | + |
| 138 | +To let the app manage Docker Compose services, inject your compose file as a ConfigMap: |
| 139 | + |
| 140 | +```bash |
| 141 | +helm upgrade crowdsec-manager ./charts/crowdsec-manager \ |
| 142 | + --namespace crowdsec \ |
| 143 | + --reuse-values \ |
| 144 | + --set dockerCompose.enabled=true \ |
| 145 | + --set-file dockerCompose.content=/path/to/your/docker-compose.yml |
| 146 | +``` |
| 147 | + |
| 148 | +### Userspace Tailscale (no kernel WireGuard) |
| 149 | + |
| 150 | +If your node's kernel lacks WireGuard support (Linux < 5.6 without backport): |
| 151 | + |
| 152 | +```bash |
| 153 | +helm install crowdsec-manager ./charts/crowdsec-manager \ |
| 154 | + --namespace crowdsec \ |
| 155 | + --set tailscale.userspace=true \ |
| 156 | + --set tailscaleSecret.existingSecret=crowdsec-manager-tailscale |
| 157 | +``` |
| 158 | + |
| 159 | +### NATS messaging (optional) |
| 160 | + |
| 161 | +```bash |
| 162 | +kubectl create secret generic crowdsec-manager-nats \ |
| 163 | + --namespace crowdsec \ |
| 164 | + --from-literal=NATS_TOKEN="my-nats-token" |
| 165 | + |
| 166 | +helm install crowdsec-manager ./charts/crowdsec-manager \ |
| 167 | + --namespace crowdsec \ |
| 168 | + --set app.nats.enabled=true \ |
| 169 | + --set app.nats.url=nats://nats-server:4222 \ |
| 170 | + --set natsSecret.existingSecret=crowdsec-manager-nats \ |
| 171 | + --set tailscaleSecret.existingSecret=crowdsec-manager-tailscale |
| 172 | +``` |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +## Verify the Deployment |
| 177 | + |
| 178 | +```bash |
| 179 | +# Pod status — both containers should show Running |
| 180 | +kubectl get pods -n crowdsec |
| 181 | + |
| 182 | +# Find the Tailscale IP |
| 183 | +kubectl logs -n crowdsec deployment/crowdsec-manager -c tailscale | grep -E "100\.[0-9]" |
| 184 | + |
| 185 | +# Health check from a Tailscale-connected device |
| 186 | +curl http://<tailscale-ip>:8080/health |
| 187 | +# Expected: {"status":"ok"} |
| 188 | + |
| 189 | +# All 5 PVCs should be Bound |
| 190 | +kubectl get pvc -n crowdsec |
| 191 | +``` |
| 192 | + |
| 193 | +--- |
| 194 | + |
| 195 | +## Upgrading |
| 196 | + |
| 197 | +```bash |
| 198 | +# Pull latest app image and apply changed values |
| 199 | +helm upgrade crowdsec-manager ./charts/crowdsec-manager \ |
| 200 | + --namespace crowdsec \ |
| 201 | + --reuse-values |
| 202 | +``` |
| 203 | + |
| 204 | +Deployments use `strategy.type: Recreate` — there is brief downtime while the old Pod terminates and the new one starts. This is required because SQLite PVCs use `ReadWriteOnce`. |
| 205 | + |
| 206 | +--- |
| 207 | + |
| 208 | +## Uninstalling |
| 209 | + |
| 210 | +```bash |
| 211 | +# Removes the Deployment, Service, ConfigMap, and non-keep PVCs |
| 212 | +helm uninstall crowdsec-manager --namespace crowdsec |
| 213 | + |
| 214 | +# PVCs annotated with helm.sh/resource-policy=keep must be deleted manually: |
| 215 | +kubectl delete pvc \ |
| 216 | + crowdsec-manager-data \ |
| 217 | + crowdsec-manager-config \ |
| 218 | + crowdsec-manager-tailscalestate \ |
| 219 | + -n crowdsec |
| 220 | + |
| 221 | +# Remove the Tailscale auth secret if you created it separately |
| 222 | +kubectl delete secret crowdsec-manager-tailscale -n crowdsec |
| 223 | + |
| 224 | +# Remove the namespace |
| 225 | +kubectl delete namespace crowdsec |
| 226 | +``` |
| 227 | + |
| 228 | +--- |
| 229 | + |
| 230 | +## Key Design Notes |
| 231 | + |
| 232 | +### Tailscale Sidecar = `network_mode: service:tailscale` |
| 233 | + |
| 234 | +In Docker Compose, `network_mode: service:tailscale` makes the app share the Tailscale container's network namespace. In Kubernetes, all containers in the same Pod share a network namespace automatically — no special configuration required. The sidecar pattern is the direct equivalent. |
| 235 | + |
| 236 | +### Deployment Strategy: Recreate |
| 237 | + |
| 238 | +`ReadWriteOnce` PVCs can only be mounted by one node at a time. `RollingUpdate` would deadlock waiting for the old Pod to release the PVC. `Recreate` terminates the old Pod first. |
| 239 | + |
| 240 | +### SQLite StorageClass |
| 241 | + |
| 242 | +Use a **node-local** StorageClass (`local-path`, `openebs-hostpath`) for the `data` PVC. SQLite over NFS causes corruption due to POSIX lock semantics. |
| 243 | + |
| 244 | +### Docker Socket Security |
| 245 | + |
| 246 | +Mounting `/var/run/docker.sock` grants the container effective root on the host node. Mitigations: |
| 247 | +- `nodeSelector` pins the Pod to a trusted node |
| 248 | +- Add a `NetworkPolicy` to restrict cluster-wide egress from this Pod |
| 249 | +- Keep `replicas: 1` — never run two instances of this app |
| 250 | + |
| 251 | +### Auth Key Persistence |
| 252 | + |
| 253 | +After first boot, the Tailscale WireGuard identity is saved to the `tailscaleState` PVC. The auth key is only needed for the initial registration. Use a **reusable** key so the Pod can re-authenticate if the state PVC is ever lost. |
0 commit comments