Skip to content

Commit 1ab73a5

Browse files
authored
Merge pull request #64 from bunkerity/dev
Upgrade to BW 1.6.9 + add MCP support
2 parents 128927c + ea97c08 commit 1ab73a5

18 files changed

Lines changed: 3042 additions & 576 deletions

CLAUDE.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
BunkerWeb Helm chart — deploys BunkerWeb (open-source WAF/reverse proxy) on Kubernetes. Chart version 1.0.14, app version 1.6.9. Helm 3, API v2.
8+
9+
## Repository Layout
10+
11+
- `charts/bunkerweb/` — the Helm chart (Chart.yaml, values.yaml, templates/)
12+
- `scripts/``validate-chart.sh` (chart validation + lint + template rendering tests), `generate-docs.py` (auto-generate docs from values.yaml)
13+
- `examples/` — example values files (all-in-one, minimal, high-availability, logging, secrets)
14+
- `docs/` — user documentation (`values.md` is the comprehensive guide, `values-reference.md` is auto-generated)
15+
16+
## Common Commands
17+
18+
```bash
19+
# Validate chart (runs helm lint + template rendering with 20+ scenarios)
20+
./scripts/validate-chart.sh
21+
22+
# Lint only
23+
helm lint charts/bunkerweb/
24+
25+
# Render templates with default values
26+
helm template test charts/bunkerweb/
27+
28+
# Render with a specific values file
29+
helm template test charts/bunkerweb/ -f examples/all-in-one.yaml
30+
31+
# Render with value overrides (useful for testing specific template paths)
32+
helm template test charts/bunkerweb/ --set bunkerweb.kind=DaemonSet
33+
helm template test charts/bunkerweb/ --set bunkerweb.kind=StatefulSet
34+
helm template test charts/bunkerweb/ --set bunkerweb.hpa.enabled=true
35+
helm template test charts/bunkerweb/ --set controller.enabled=true
36+
37+
# Generate documentation from values.yaml
38+
python3 scripts/generate-docs.py
39+
```
40+
41+
## Architecture
42+
43+
BunkerWeb deploys as a multi-component system:
44+
45+
- **BunkerWeb** (`bunkerweb-*.yaml`) — the core WAF/reverse proxy. Supports three deployment kinds: Deployment (default), DaemonSet, or StatefulSet. Has both an external service (user traffic) and internal service (inter-component communication).
46+
- **Scheduler** (`scheduler-deployment.yaml`) — manages configuration distribution and coordination across BunkerWeb instances.
47+
- **Controller** (`controller-deployment.yaml`) — watches Kubernetes Ingress and Gateway API resources, translates them to BunkerWeb config. Optional, enabled via `controller.enabled`.
48+
- **UI** (`ui-*.yaml`) — web management interface with syslog sidecar for log collection. Optional, enabled via `ui.enabled`.
49+
- **API** (`api-*.yaml`) — external REST API. Optional, enabled via `api.enabled`.
50+
- **MariaDB/Redis/Prometheus/Grafana** — optional infrastructure components, each with their own deployment + service + PVC templates.
51+
52+
## Key Template Patterns
53+
54+
**`_helpers.tpl`** (829 lines) contains critical helper functions:
55+
- `bunkerweb.databaseUri` — builds the database connection string dynamically (internal MariaDB vs external)
56+
- `bunkerweb.featureEnvs` — generates environment variables from the large `scheduler.features` section in values.yaml. This is the most complex helper and maps feature config to BunkerWeb env vars.
57+
- `bunkerweb.redisEnv` — generates Redis connection env vars with auth support
58+
- `bunkerweb.syslogAddress` — resolves syslog address for UI log forwarding
59+
60+
**Conditional rendering**: Most components are gated by `.Values.<component>.enabled`. BunkerWeb kind selection uses three separate template files (`bunkerweb-deployment.yaml`, `bunkerweb-daemonset.yaml`, `bunkerweb-statefulset.yaml`) each guarded by `if eq .Values.bunkerweb.kind "<Kind>"`.
61+
62+
**Secret management**: The chart supports an `existingSecret` pattern — users reference a pre-created Kubernetes Secret rather than putting credentials in values.yaml. Sensitive values (DB URI, Redis password, admin creds, API keys) are injected via `secretKeyRef`.
63+
64+
## Values Structure
65+
66+
`values.yaml` (1,521 lines) has these major sections:
67+
- **`settings`** — BunkerWeb configuration: `kubernetes` (namespace, ingressClass), `misc` (databaseUri, DNS), `redis`, `ui`, `api`, `scheduler.features` (800+ lines of WAF feature toggles like ModSecurity, antibot, rate limiting, geo-blocking, etc.)
68+
- **`service`** — Kubernetes Service config (type, externalTrafficPolicy)
69+
- **`bunkerweb`** — core component config (kind, replicas, HPA, PDB, resources, probes, volumes, affinity)
70+
- **`scheduler`**, `controller`, `ui`, `api` — per-component deployment config
71+
- **`mariadb`**, `redis`, `prometheus`, `grafana` — optional infrastructure
72+
- **`ingressClass`**, `gatewayClass`** — Kubernetes integration resources
73+
- **`networkPolicy`** — network segmentation (disabled by default)
74+
75+
## CI/CD
76+
77+
Two GitHub Actions workflows:
78+
- `.github/workflows/dev.yml` — on push to `dev` branch (charts/** changes): validate → package → upload to dev Helm repo via SSH
79+
- `.github/workflows/prod.yml` — on push to `main` branch: same pipeline but uploads to production Helm repo
80+
81+
## Development Notes
82+
83+
- Gateway API support is experimental (disabled by default via `gatewayClass.enabled: false`)
84+
- The `KUBERNETES_REVERSE_PROXY_SUFFIX_START` env var is set to `"0"` (string) on the controller to avoid issues with UI/API templates
85+
- When adding new BunkerWeb features, update both `values.yaml` (under `scheduler.features`) and the `bunkerweb.featureEnvs` helper in `_helpers.tpl`
86+
- Always run `./scripts/validate-chart.sh` before committing — it tests template rendering across many configuration combinations

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Official [Helm chart](https://helm.sh/docs/) to deploy [BunkerWeb](https://www.b
1111
- **High Availability**: Support for DaemonSet and Deployment modes
1212
- **Monitoring**: Built-in Prometheus metrics and Grafana dashboards
1313
- **Management UI**: Web interface for configuration and monitoring
14+
- **AI Integration**: MCP server for AI assistants (Claude Code, etc.)
1415
- **Auto-scaling**: Kubernetes-native scaling capabilities
1516
- **Secret Management**: Integration with Kubernetes secrets
1617

@@ -55,6 +56,8 @@ helm install mybunkerweb bunkerweb/bunkerweb -n bunkerweb --create-namespace
5556
| **Scheduler** | Configuration management | Required |
5657
| **Controller** | Kubernetes integration | Enabled |
5758
| **UI** | Web management interface | Enabled |
59+
| **API** | External REST API for automation | Enabled |
60+
| **MCP** | Model Context Protocol server for AI assistants | Enabled |
5861
| **MariaDB** | Database backend | Enabled |
5962
| **Redis** | Caching and persistence | Enabled |
6063
| **Prometheus** | Metrics collection | Disabled |
@@ -109,6 +112,36 @@ service:
109112
externalTrafficPolicy: Local
110113
```
111114

115+
### MCP Server (AI Assistant Integration)
116+
117+
The MCP (Model Context Protocol) server enables AI assistants like Claude Code to manage BunkerWeb configuration.
118+
119+
```yaml
120+
mcp:
121+
enabled: true
122+
# API credentials (must match settings.api configuration)
123+
secrets:
124+
bunkerwebApiToken: "your-api-token"
125+
126+
# Expose via Ingress (legacy)
127+
ingress:
128+
enabled: true
129+
serverName: "mcp.example.com"
130+
annotations:
131+
bunkerweb.io/USE_WHITELIST: "yes"
132+
bunkerweb.io/WHITELIST_IP: "YOUR_IP/32"
133+
134+
# Or expose via Gateway API (modern)
135+
httpRoutes:
136+
enabled: true
137+
serverName: "mcp.example.com"
138+
extraAnnotations:
139+
bunkerweb.io/USE_WHITELIST: "yes"
140+
bunkerweb.io/WHITELIST_IP: "YOUR_IP/32"
141+
```
142+
143+
> **Security Warning**: The MCP server has no built-in authentication for the `/mcp` endpoint. Always use IP whitelisting or network policies to restrict access.
144+
112145
### Secret Management
113146

114147
```yaml
@@ -186,6 +219,7 @@ The chart includes pre-configured Grafana dashboards for:
186219
3. **Network Policies**: Enable network policies for production environments
187220
4. **Resource Limits**: Set appropriate CPU/memory limits
188221
5. **Pod Security**: Review and adjust security contexts
222+
6. **MCP Access Control**: Always configure IP whitelisting when exposing the MCP server
189223

190224
## Troubleshooting
191225

@@ -247,8 +281,10 @@ kubectl delete namespace bunkerweb
247281
### Key Configuration Areas
248282

249283
- **Global Settings**: Common configuration across all components
250-
- **BunkerWeb**: Main reverse proxy configuration
284+
- **BunkerWeb**: Main reverse proxy configuration
251285
- **UI**: Web interface settings
286+
- **API**: External REST API for automation and integrations
287+
- **MCP**: AI assistant integration (Claude Code, etc.)
252288
- **Database**: MariaDB configuration
253289
- **Monitoring**: Prometheus and Grafana setup
254290
- **Security**: Network policies and access control

charts/bunkerweb/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ type: application
1515
# This is the chart version. This version number should be incremented each time you make changes
1616
# to the chart and its templates, including the app version.
1717
# Versions are expected to follow Semantic Versioning (https://semver.org/)
18-
version: 1.0.14
18+
version: 1.0.15
1919

2020
# This is the version number of the application being deployed. This version number should be
2121
# incremented each time you make changes to the application. Versions are not expected to
2222
# follow Semantic Versioning. They should reflect the version the application is using.
2323
# It is recommended to use it with quotes.
24-
appVersion: "1.6.8"
24+
appVersion: "1.6.9"

charts/bunkerweb/templates/_helpers.tpl

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,66 @@ Generate BunkerWeb feature environment variables
151151
value: {{ .global.disableDefaultServerStrictSni | quote }}
152152
{{- end }}
153153

154+
# =============================================================================
155+
# NGINX TIMEOUTS
156+
# =============================================================================
157+
{{- if and .timeouts .timeouts.clientBodyTimeout (ne .timeouts.clientBodyTimeout "") }}
158+
- name: CLIENT_BODY_TIMEOUT
159+
value: {{ .timeouts.clientBodyTimeout | quote }}
160+
{{- end }}
161+
{{- if and .timeouts .timeouts.clientHeaderTimeout (ne .timeouts.clientHeaderTimeout "") }}
162+
- name: CLIENT_HEADER_TIMEOUT
163+
value: {{ .timeouts.clientHeaderTimeout | quote }}
164+
{{- end }}
165+
{{- if and .timeouts .timeouts.keepaliveTimeout (ne .timeouts.keepaliveTimeout "") }}
166+
- name: KEEPALIVE_TIMEOUT
167+
value: {{ .timeouts.keepaliveTimeout | quote }}
168+
{{- end }}
169+
{{- if and .timeouts .timeouts.sendTimeout (ne .timeouts.sendTimeout "") }}
170+
- name: SEND_TIMEOUT
171+
value: {{ .timeouts.sendTimeout | quote }}
172+
{{- end }}
173+
174+
# =============================================================================
175+
# DATABASE CONNECTION POOL
176+
# =============================================================================
177+
{{- if and .databasePool .databasePool.databasePoolSize (ne .databasePool.databasePoolSize "") }}
178+
- name: DATABASE_POOL_SIZE
179+
value: {{ .databasePool.databasePoolSize | quote }}
180+
{{- end }}
181+
{{- if and .databasePool .databasePool.databasePoolMaxOverflow (ne .databasePool.databasePoolMaxOverflow "") }}
182+
- name: DATABASE_POOL_MAX_OVERFLOW
183+
value: {{ .databasePool.databasePoolMaxOverflow | quote }}
184+
{{- end }}
185+
{{- if and .databasePool .databasePool.databasePoolTimeout (ne .databasePool.databasePoolTimeout "") }}
186+
- name: DATABASE_POOL_TIMEOUT
187+
value: {{ .databasePool.databasePoolTimeout | quote }}
188+
{{- end }}
189+
{{- if and .databasePool .databasePool.databasePoolRecycle (ne .databasePool.databasePoolRecycle "") }}
190+
- name: DATABASE_POOL_RECYCLE
191+
value: {{ .databasePool.databasePoolRecycle | quote }}
192+
{{- end }}
193+
{{- if and .databasePool .databasePool.databasePoolPrePing (ne .databasePool.databasePoolPrePing "") }}
194+
- name: DATABASE_POOL_PRE_PING
195+
value: {{ .databasePool.databasePoolPrePing | quote }}
196+
{{- end }}
197+
{{- if and .databasePool .databasePool.databasePoolResetOnReturn (ne .databasePool.databasePoolResetOnReturn "") }}
198+
- name: DATABASE_POOL_RESET_ON_RETURN
199+
value: {{ .databasePool.databasePoolResetOnReturn | quote }}
200+
{{- end }}
201+
{{- if and .databasePool .databasePool.databaseRetryTimeout (ne .databasePool.databaseRetryTimeout "") }}
202+
- name: DATABASE_RETRY_TIMEOUT
203+
value: {{ .databasePool.databaseRetryTimeout | quote }}
204+
{{- end }}
205+
{{- if and .databasePool .databasePool.databaseRequestRetryAttempts (ne .databasePool.databaseRequestRetryAttempts "") }}
206+
- name: DATABASE_REQUEST_RETRY_ATTEMPTS
207+
value: {{ .databasePool.databaseRequestRetryAttempts | quote }}
208+
{{- end }}
209+
{{- if and .databasePool .databasePool.databaseRequestRetryDelay (ne .databasePool.databaseRequestRetryDelay "") }}
210+
- name: DATABASE_REQUEST_RETRY_DELAY
211+
value: {{ .databasePool.databaseRequestRetryDelay | quote }}
212+
{{- end }}
213+
154214
# =============================================================================
155215
# MODSECURITY WAF
156216
# =============================================================================
@@ -206,6 +266,10 @@ Generate BunkerWeb feature environment variables
206266
- name: ANTIBOT_IGNORE_URI
207267
value: {{ .antibot.antibotIgnoreUri | quote }}
208268
{{- end }}
269+
{{- if and .antibot .antibot.antibotRecaptchaClassic (ne .antibot.antibotRecaptchaClassic "") }}
270+
- name: ANTIBOT_RECAPTCHA_CLASSIC
271+
value: {{ .antibot.antibotRecaptchaClassic | quote }}
272+
{{- end }}
209273

210274
# =============================================================================
211275
# RATE LIMITING
@@ -349,6 +413,38 @@ Generate BunkerWeb feature environment variables
349413
- name: USE_LETS_ENCRYPT_WILDCARD
350414
value: {{ .letsEncrypt.useLetsEncryptWildcard | quote }}
351415
{{- end }}
416+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptServer (ne .letsEncrypt.letsEncryptServer "") }}
417+
- name: LETS_ENCRYPT_SERVER
418+
value: {{ .letsEncrypt.letsEncryptServer | quote }}
419+
{{- end }}
420+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptProfile (ne .letsEncrypt.letsEncryptProfile "") }}
421+
- name: LETS_ENCRYPT_PROFILE
422+
value: {{ .letsEncrypt.letsEncryptProfile | quote }}
423+
{{- end }}
424+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptCustomProfile (ne .letsEncrypt.letsEncryptCustomProfile "") }}
425+
- name: LETS_ENCRYPT_CUSTOM_PROFILE
426+
value: {{ .letsEncrypt.letsEncryptCustomProfile | quote }}
427+
{{- end }}
428+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptZerosslApiKey (ne .letsEncrypt.letsEncryptZerosslApiKey "") }}
429+
- name: LETS_ENCRYPT_ZEROSSL_API_KEY
430+
value: {{ .letsEncrypt.letsEncryptZerosslApiKey | quote }}
431+
{{- end }}
432+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptZerosslApiConnectTimeout (ne .letsEncrypt.letsEncryptZerosslApiConnectTimeout "") }}
433+
- name: LETS_ENCRYPT_ZEROSSL_API_CONNECT_TIMEOUT
434+
value: {{ .letsEncrypt.letsEncryptZerosslApiConnectTimeout | quote }}
435+
{{- end }}
436+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptZerosslApiMaxTime (ne .letsEncrypt.letsEncryptZerosslApiMaxTime "") }}
437+
- name: LETS_ENCRYPT_ZEROSSL_API_MAX_TIME
438+
value: {{ .letsEncrypt.letsEncryptZerosslApiMaxTime | quote }}
439+
{{- end }}
440+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptZerosslApiRetry (ne .letsEncrypt.letsEncryptZerosslApiRetry "") }}
441+
- name: LETS_ENCRYPT_ZEROSSL_API_RETRY
442+
value: {{ .letsEncrypt.letsEncryptZerosslApiRetry | quote }}
443+
{{- end }}
444+
{{- if and .letsEncrypt .letsEncrypt.letsEncryptZerosslApiRetryDelay (ne .letsEncrypt.letsEncryptZerosslApiRetryDelay "") }}
445+
- name: LETS_ENCRYPT_ZEROSSL_API_RETRY_DELAY
446+
value: {{ .letsEncrypt.letsEncryptZerosslApiRetryDelay | quote }}
447+
{{- end }}
352448

353449
# Custom SSL certificate
354450
{{- if and .customSsl .customSsl.useCustomSsl (ne .customSsl.useCustomSsl "") }}
@@ -442,6 +538,78 @@ Generate BunkerWeb feature environment variables
442538
value: {{ .reverseProxy.reverseProxyReadTimeout | quote }}
443539
{{- end }}
444540

541+
# =============================================================================
542+
# GRPC REVERSE PROXY
543+
# =============================================================================
544+
{{- if and .grpc .grpc.useGrpc (ne .grpc.useGrpc "") }}
545+
- name: USE_GRPC
546+
value: {{ .grpc.useGrpc | quote }}
547+
{{- end }}
548+
{{- if and .grpc .grpc.grpcUrl (ne .grpc.grpcUrl "") }}
549+
- name: GRPC_URL
550+
value: {{ .grpc.grpcUrl | quote }}
551+
{{- end }}
552+
{{- if and .grpc .grpc.grpcHost (ne .grpc.grpcHost "") }}
553+
- name: GRPC_HOST
554+
value: {{ .grpc.grpcHost | quote }}
555+
{{- end }}
556+
{{- if and .grpc .grpc.grpcConnectTimeout (ne .grpc.grpcConnectTimeout "") }}
557+
- name: GRPC_CONNECT_TIMEOUT
558+
value: {{ .grpc.grpcConnectTimeout | quote }}
559+
{{- end }}
560+
{{- if and .grpc .grpc.grpcReadTimeout (ne .grpc.grpcReadTimeout "") }}
561+
- name: GRPC_READ_TIMEOUT
562+
value: {{ .grpc.grpcReadTimeout | quote }}
563+
{{- end }}
564+
{{- if and .grpc .grpc.grpcSendTimeout (ne .grpc.grpcSendTimeout "") }}
565+
- name: GRPC_SEND_TIMEOUT
566+
value: {{ .grpc.grpcSendTimeout | quote }}
567+
{{- end }}
568+
{{- if and .grpc .grpc.grpcCustomHost (ne .grpc.grpcCustomHost "") }}
569+
- name: GRPC_CUSTOM_HOST
570+
value: {{ .grpc.grpcCustomHost | quote }}
571+
{{- end }}
572+
{{- if and .grpc .grpc.grpcHeaders (ne .grpc.grpcHeaders "") }}
573+
- name: GRPC_HEADERS
574+
value: {{ .grpc.grpcHeaders | quote }}
575+
{{- end }}
576+
{{- if and .grpc .grpc.grpcHideHeaders (ne .grpc.grpcHideHeaders "") }}
577+
- name: GRPC_HIDE_HEADERS
578+
value: {{ .grpc.grpcHideHeaders | quote }}
579+
{{- end }}
580+
{{- if and .grpc .grpc.grpcInterceptErrors (ne .grpc.grpcInterceptErrors "") }}
581+
- name: GRPC_INTERCEPT_ERRORS
582+
value: {{ .grpc.grpcInterceptErrors | quote }}
583+
{{- end }}
584+
{{- if and .grpc .grpc.grpcSocketKeepalive (ne .grpc.grpcSocketKeepalive "") }}
585+
- name: GRPC_SOCKET_KEEPALIVE
586+
value: {{ .grpc.grpcSocketKeepalive | quote }}
587+
{{- end }}
588+
{{- if and .grpc .grpc.grpcSslSni (ne .grpc.grpcSslSni "") }}
589+
- name: GRPC_SSL_SNI
590+
value: {{ .grpc.grpcSslSni | quote }}
591+
{{- end }}
592+
{{- if and .grpc .grpc.grpcSslSniName (ne .grpc.grpcSslSniName "") }}
593+
- name: GRPC_SSL_SNI_NAME
594+
value: {{ .grpc.grpcSslSniName | quote }}
595+
{{- end }}
596+
{{- if and .grpc .grpc.grpcNextUpstream (ne .grpc.grpcNextUpstream "") }}
597+
- name: GRPC_NEXT_UPSTREAM
598+
value: {{ .grpc.grpcNextUpstream | quote }}
599+
{{- end }}
600+
{{- if and .grpc .grpc.grpcNextUpstreamTimeout (ne .grpc.grpcNextUpstreamTimeout "") }}
601+
- name: GRPC_NEXT_UPSTREAM_TIMEOUT
602+
value: {{ .grpc.grpcNextUpstreamTimeout | quote }}
603+
{{- end }}
604+
{{- if and .grpc .grpc.grpcNextUpstreamTries (ne .grpc.grpcNextUpstreamTries "") }}
605+
- name: GRPC_NEXT_UPSTREAM_TRIES
606+
value: {{ .grpc.grpcNextUpstreamTries | quote }}
607+
{{- end }}
608+
{{- if and .grpc .grpc.grpcIncludes (ne .grpc.grpcIncludes "") }}
609+
- name: GRPC_INCLUDES
610+
value: {{ .grpc.grpcIncludes | quote }}
611+
{{- end }}
612+
445613
# =============================================================================
446614
# REAL IP DETECTION
447615
# =============================================================================
@@ -813,6 +981,22 @@ Generate BunkerWeb feature environment variables
813981
- name: BACKUP_DIRECTORY
814982
value: {{ .backup.backupDirectory | quote }}
815983
{{- end }}
984+
985+
# =============================================================================
986+
# STREAM/PASSTHROUGH
987+
# =============================================================================
988+
{{- if and .stream .stream.listenStream (ne .stream.listenStream "") }}
989+
- name: LISTEN_STREAM
990+
value: {{ .stream.listenStream | quote }}
991+
{{- end }}
992+
{{- if and .stream .stream.listenStreamPort (ne .stream.listenStreamPort "") }}
993+
- name: LISTEN_STREAM_PORT
994+
value: {{ .stream.listenStreamPort | quote }}
995+
{{- end }}
996+
{{- if and .stream .stream.listenStreamPortSsl (ne .stream.listenStreamPortSsl "") }}
997+
- name: LISTEN_STREAM_PORT_SSL
998+
value: {{ .stream.listenStreamPortSsl | quote }}
999+
{{- end }}
8161000
{{- end }}
8171001
{{- end }}
8181002

0 commit comments

Comments
 (0)