diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..3d47e46a --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,78 @@ +name: test + +on: + push: + branches-ignore: + - master + paths: + - 'charts/mini-runtime/**' + pull_request: + branches: + - master + paths: + - 'charts/mini-runtime/**' + +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Helm + uses: azure/setup-helm@v3 + + - name: Add Strimzi Helm repo + run: helm repo add strimzi https://strimzi.io/charts/ && helm repo update + + - name: Update chart dependencies + run: helm dependency update charts/mini-runtime + + - name: Install helm-unittest plugin + run: helm plugin install https://github.com/helm-unittest/helm-unittest --version 1.0.3 + + - name: Lint (default values) + run: helm lint charts/mini-runtime + + - name: Lint (SASL enabled) + run: | + helm lint charts/mini-runtime \ + --set kafkaCluster.sasl.enabled=true \ + --set kafkaCluster.sasl.password=secret + + - name: Lint (TLS enabled) + run: helm lint charts/mini-runtime --set kafkaCluster.tls=true + + - name: Lint (SASL + TLS) + run: | + helm lint charts/mini-runtime \ + --set kafkaCluster.sasl.enabled=true \ + --set kafkaCluster.sasl.password=secret \ + --set kafkaCluster.tls=true + + - name: Lint (external Kafka) + run: | + helm lint charts/mini-runtime \ + --set kafkaCluster.enabled=false \ + --set mini_runtime.useExternalKafka=true \ + --set mini_runtime.externalKafka.brokerUrl=kafka1:9092 + + - name: Lint (external Kafka with SASL_SSL) + run: | + helm lint charts/mini-runtime \ + --set kafkaCluster.enabled=false \ + --set mini_runtime.useExternalKafka=true \ + --set mini_runtime.externalKafka.brokerUrl=kafka1:9092 \ + --set mini_runtime.externalKafka.username=user \ + --set mini_runtime.externalKafka.password=pass \ + --set mini_runtime.externalKafka.saslMechanism=SCRAM-SHA-256 \ + --set mini_runtime.externalKafka.useSsl=true \ + --set mini_runtime.externalKafka.securityProtocol=SASL_SSL + + - name: Lint (port override) + run: helm lint charts/mini-runtime --set kafkaCluster.ports.plain=9094 + + - name: Run unit tests + run: helm unittest charts/mini-runtime diff --git a/charts/akto-k8s-agent/templates/daemonset.yaml b/charts/akto-k8s-agent/templates/daemonset.yaml index 86b8e828..361c093d 100644 --- a/charts/akto-k8s-agent/templates/daemonset.yaml +++ b/charts/akto-k8s-agent/templates/daemonset.yaml @@ -1,4 +1,20 @@ {{- if .Values.env.DEPLOY_DAEMONSET }} +{{- $caSecretName := "" }} +{{- if .Values.tls.enabled }} +{{- if .Values.tls.autoDiscover }} +{{- $caSecretName = printf "%s-cluster-ca-cert" .Values.tls.kafkaClusterName }} +{{- else }} +{{- $caSecretName = .Values.tls.caSecretName }} +{{- end }} +{{- end }} +{{- $saslSecretName := "" }} +{{- if .Values.sasl.enabled }} +{{- if .Values.sasl.existingSecret }} +{{- $saslSecretName = .Values.sasl.existingSecret }} +{{- else if .Values.sasl.autoDiscover }} +{{- $saslSecretName = printf "%s-sasl-credentials" .Values.sasl.kafkaClusterName }} +{{- end }} +{{- end }} apiVersion: apps/v1 kind: DaemonSet metadata: @@ -47,18 +63,63 @@ spec: - name: AKTO_INFRA_MIRRORING_MODE value: {{ .Values.env.AKTO_INFRA_MIRRORING_MODE | quote }} - name: AKTO_KAFKA_BROKER_MAL - value: {{ (.Values.env.AKTO_KAFKA_BROKER_MAL | default (printf "%s.%s.svc.cluster.local:9092" (default (printf "%s-mini-runtime" .Values.miniRuntimeReleaseName) .Values.miniRuntimeServiceName) .Release.Namespace)) | quote }} + {{- if and .Values.tls.enabled .Values.tls.autoDiscover }} + value: {{ printf "%s-kafka-bootstrap.%s.svc.cluster.local:%v" .Values.tls.kafkaClusterName .Release.Namespace .Values.tls.kafkaPort | quote }} + {{- else if and .Values.sasl.enabled .Values.sasl.autoDiscover }} + value: {{ printf "%s-kafka-bootstrap.%s.svc.cluster.local:%v" .Values.sasl.kafkaClusterName .Release.Namespace .Values.sasl.kafkaPort | quote }} + {{- else if .Values.env.AKTO_KAFKA_BROKER_MAL }} + value: {{ .Values.env.AKTO_KAFKA_BROKER_MAL | quote }} + {{- else }} + value: {{ printf "%s.%s.svc.cluster.local:9092" (default (printf "%s-mini-runtime" .Values.miniRuntimeReleaseName) .Values.miniRuntimeServiceName) .Release.Namespace | quote }} + {{- end }} - name: AKTO_MONGO_CONN value: {{ .Values.env.AKTO_MONGO_CONN | quote }} - name: IS_AUTH_IMPLEMENTED - value: {{ .Values.env.IS_AUTH_IMPLEMENTED | quote }} + value: {{ .Values.sasl.enabled | quote }} + {{- if .Values.sasl.enabled }} + {{- if $saslSecretName }} - name: KAFKA_USERNAME - value: {{ .Values.env.KAFKA_USERNAME | quote }} + valueFrom: + secretKeyRef: + name: {{ $saslSecretName | quote }} + key: {{ .Values.sasl.usernameKey | quote }} - name: KAFKA_PASSWORD - value: {{ .Values.env.KAFKA_PASSWORD | quote }} + valueFrom: + secretKeyRef: + name: {{ $saslSecretName | quote }} + key: {{ .Values.sasl.passwordKey | quote }} + {{- else if .Values.sasl.username }} + - name: KAFKA_USERNAME + value: {{ .Values.sasl.username | quote }} + - name: KAFKA_PASSWORD + value: {{ .Values.sasl.password | quote }} + {{- end }} + - name: AKTO_KAFKA_SASL_MECHANISM + value: {{ .Values.sasl.saslMechanism | quote }} + {{- end }} - name: AKTO_THREAT_ENABLED value: {{ .Values.env.AKTO_THREAT_ENABLED | quote }} + {{- if .Values.tls.enabled }} + - name: USE_TLS + value: "true" + - name: TLS_CA_CERT_PATH + value: {{ printf "%s/%s" .Values.tls.mountPath .Values.tls.caCertKey | quote }} + - name: INSECURE_SKIP_VERIFY + value: {{ .Values.tls.insecureSkipVerify | quote }} + {{- end }} + {{- if .Values.tls.enabled }} + volumeMounts: + - name: kafka-tls-ca + mountPath: {{ .Values.tls.mountPath | quote }} + readOnly: true + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.tls.enabled }} + volumes: + - name: kafka-tls-ca + secret: + secretName: {{ $caSecretName | quote }} + {{- end }} {{- end }} diff --git a/charts/akto-k8s-agent/values.yaml b/charts/akto-k8s-agent/values.yaml index cc7ec350..2c2eb8fb 100644 --- a/charts/akto-k8s-agent/values.yaml +++ b/charts/akto-k8s-agent/values.yaml @@ -1,6 +1,6 @@ image: repository: aktosecurity/mirror-api-logging - tag: k8s_agent + tag: strimzi-kafka-tcp pullPolicy: Always podAnnotations: {} @@ -30,3 +30,32 @@ affinity: {} miniRuntimeServiceName: "" miniRuntimeReleaseName: "akto-mini-runtime" +# SASL credentials — set existingSecret to reference a pre-existing Secret +# (e.g. the "-sasl-credentials" Secret created by the mini-runtime chart) +sasl: + enabled: true + autoDiscover: false # derive secret from kafkaClusterName: "-sasl-credentials" + saslMechanism: "" # don't change if using akto-kafka, it only supports 512. + kafkaClusterName: "akto-kafka" # matches kafkaCluster.clusterName in mini-runtime chart + kafkaPort: 9092 # Kafka port for SASL (plain listener port) + existingSecret: "" # option 1: explicit secret name (overrides autoDiscover) + usernameKey: "username" # key inside the Secret for the Kafka username + passwordKey: "password" # key inside the Secret for the Kafka password + username: "test" # option 2: inline credentials + password: "test" + +# TLS configuration for Kafka connection +# Mode 1 (default): auto-discover Strimzi CA cert secret +# Set tls.enabled=true — secret name derived as "-cluster-ca-cert" +# Mode 2: bring your own secret +# Set tls.enabled=true, tls.autoDiscover=false, tls.caSecretName= +tls: + enabled: false + autoDiscover: true # derive CA secret from kafkaClusterName (Strimzi default) + kafkaClusterName: "akto-kafka" # matches kafkaCluster.clusterName in mini-runtime chart + kafkaPort: 9093 # TLS listener port — used when autoDiscover=true and AKTO_KAFKA_BROKER_MAL is not overridden + caSecretName: "" # used when autoDiscover=false + caCertKey: "ca.crt" # key inside the secret containing the CA cert + mountPath: "/etc/kafka/tls" + insecureSkipVerify: false + diff --git a/charts/mini-runtime/CLAUDE.md b/charts/mini-runtime/CLAUDE.md new file mode 100644 index 00000000..b0f94824 --- /dev/null +++ b/charts/mini-runtime/CLAUDE.md @@ -0,0 +1,378 @@ +# akto-mini-runtime Helm Chart + +## Overview + +Deploys the Akto hybrid runtime stack into a Kubernetes cluster. The chart installs: +- **mini-runtime** deployment — API security runtime container +- **Strimzi Kafka cluster** (default) — Strimzi operator + KafkaNodePool, Kafka, KafkaTopic CRDs via Helm subchart dependency +- **Keel** deployment (auto-updater) +- **Threat detection client** deployment +- **Redis** deployment (when threat client + aggregation rules enabled) + +**Two Kafka modes:** +- **Strimzi mode** (default, `kafkaCluster.enabled: true`) — Strimzi operator and Kafka cluster deployed automatically by this chart +- **External mode** (`mini_runtime.useExternalKafka: true`) — bring your own Kafka; disables all Strimzi resources + +**Current install command:** +```sh +helm install akto-mini-runtime akto/akto-mini-runtime -n dev \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="" +``` + +**External Kafka install command:** +```sh +helm install akto-mini-runtime akto/akto-mini-runtime -n dev \ + --set kafkaCluster.enabled=false \ + --set mini_runtime.useExternalKafka=true \ + --set mini_runtime.externalKafka.brokerUrl="kafka1:9092" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="" +``` + +--- + +## Required Values + +| Value | Description | +|-------|-------------| +| `mongo.aktoMongoConn` | MongoDB connection string. Injected as `AKTO_MONGO_CONN` into the runtime container. | +| `mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken` | Auth token for database abstractor service (cyborg.akto.io). Injected as `DATABASE_ABSTRACTOR_SERVICE_TOKEN` into runtime and as `AKTO_THREAT_PROTECTION_BACKEND_TOKEN` / `DATABASE_ABSTRACTOR_SERVICE_TOKEN` into the threat client. Can alternatively be provided via Kubernetes secret (see Secret Management). | + +--- + +## Strimzi Kafka Cluster (`kafkaCluster`) + +### Quick start + +```sh +helm install akto-mini-runtime akto/akto-mini-runtime -n dev \ + --set kafkaCluster.enabled=true \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="" +``` + +This will: +1. Install the Strimzi operator via the bundled Helm subchart dependency +2. Deploy a 1-broker + 1-controller KRaft Kafka cluster named `akto-kafka` +3. Create 4 KafkaTopics (`akto.api.logs`, `akto.api.logs2`, `akto.api.producer.logs`, `akto.daemonset.producer.heartbeats`) +4. Auto-configure the runtime and threat client to connect to `akto-kafka-kafka-bootstrap..svc.cluster.local:9092` + +### Strimzi operator + +The Strimzi operator is bundled as a Helm subchart (`strimzi/strimzi-kafka-operator 0.51.0`). It is installed automatically when `kafkaCluster.enabled: true` and skipped when `kafkaCluster.enabled: false` (external Kafka mode). Helm handles idempotency — re-installing or upgrading is safe even if the operator is already present. + +### Cluster topology + +```yaml +kafkaCluster: + clusterName: "akto-kafka" + broker: + replicas: 3 + storage: + size: "10Gi" + storageClass: "" # empty = default StorageClass + deleteClaim: true + controller: + replicas: 1 + storage: + size: "10Gi" + storageClass: "" + deleteClaim: true +``` + +### Retention / replication + +```yaml +kafkaCluster: + config: + retentionMs: "604800000" # 7 days + retentionBytes: "5368709120" # 5GB per partition + cleanupPolicy: "delete" + defaultReplicationFactor: 3 + minInsyncReplicas: 2 + offsetsTopicReplicationFactor: 3 + transactionStateLogReplicationFactor: 3 + transactionStateLogMinIsr: 2 +``` + +### Port override + +```yaml +kafkaCluster: + ports: + plain: 9092 # plain (or SASL) listener port — also drives bootstrap URL in runtime/threat-client + tls: 9093 # TLS listener port (only used when tls: true) +``` + +Changing `ports.plain` changes both the Strimzi listener port and the bootstrap URL injected into all containers. + +### Topics + +```yaml +kafkaCluster: + topics: + - name: "akto.api.logs" + partitions: 3 + replicas: 3 + # add more as needed +``` + +Each entry renders a `KafkaTopic` CRD managed by the Strimzi topic operator. + +### Service annotations + +Annotations are forwarded to Strimzi-generated Kafka Services via `spec.kafka.template`: + +```yaml +kafkaCluster: + serviceAnnotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" +``` + +This sets annotations on both `-kafka-bootstrap` and `-kafka-brokers` Services. + +--- + +## Kafka Authentication (Strimzi) + +### No auth (default) + +```yaml +kafkaCluster: + sasl: + enabled: false + tls: false +``` + +### SASL only (SCRAM) + +```yaml +kafkaCluster: + sasl: + enabled: true + mechanism: "SCRAM-SHA-512" # SCRAM-SHA-256 or SCRAM-SHA-512 + username: "akto-runtime" + password: "secret" + # OR reference an existing Secret (must have keys: username, password): + useExistingSecret: true + existingSecret: "my-sasl-secret" +``` + +Strimzi creates a `KafkaUser` CRD and its user operator provisions the SCRAM credentials automatically — no JAAS config or broker-side init containers needed. The chart creates a `Secret` named `-sasl-credentials` (unless `useExistingSecret: true`). + +SASL env vars (`KAFKA_AUTH_ENABLED`, `AKTO_KAFKA_SASL_ENABLED`, `AKTO_KAFKA_SASL_MECHANISM`, `KAFKA_USERNAME`, `KAFKA_PASSWORD`) are injected into both the runtime and threat client containers. + +The threat client also receives `AKTO_KAFKA_USERNAME` / `AKTO_KAFKA_PASSWORD` from the same secret. + +### TLS only + +```yaml +kafkaCluster: + tls: true + sasl: + enabled: false +``` + +Strimzi auto-generates broker TLS certificates via its internal cluster CA. The CA cert is stored in a Secret named `-cluster-ca-cert` (created by Strimzi, not this chart). The chart mounts this Secret into the runtime and threat client containers at `/etc/kafka/tls/ca.crt` and sets: +- `AKTO_KAFKA_TLS_ENABLED: "true"` +- `AKTO_KAFKA_SECURITY_PROTOCOL: "SSL"` +- `KAFKA_SSL_TRUSTSTORE_LOCATION: "/etc/kafka/tls/ca.crt"` + +### SASL + TLS + +```yaml +kafkaCluster: + tls: true + sasl: + enabled: true + mechanism: "SCRAM-SHA-512" + username: "akto-runtime" + password: "secret" +``` + +Sets `AKTO_KAFKA_SECURITY_PROTOCOL: "SASL_SSL"` in containers. + +--- + +## External Kafka (`mini_runtime.useExternalKafka`) + +When `kafkaCluster.enabled: false` and `useExternalKafka: true`, point at an existing Kafka cluster: + +```yaml +mini_runtime: + useExternalKafka: true + externalKafka: + brokerUrl: "kafka1:9092,kafka2:9092" + username: "" + password: "" + saslMechanism: "SCRAM-SHA-256" # PLAIN | SCRAM-SHA-256 | SCRAM-SHA-512 + useSsl: false + securityProtocol: "SASL_SSL" # PLAINTEXT | SASL_PLAINTEXT | SASL_SSL | SSL +``` + +No Strimzi CRDs or Kafka Service are created. SASL auth env vars are injected when `username` is non-empty. + +--- + +## Secret Management + +### Database Abstractor Token + +Option 1 — inline (default): +```yaml +mini_runtime: + aktoApiSecurityRuntime: + env: + databaseAbstractorToken: "" +``` + +Option 2 — create a new Kubernetes Secret: +```yaml +mini_runtime: + aktoApiSecurityRuntime: + env: + useSecretsForDatabaseAbstractorToken: true + databaseAbstractorTokenSecrets: + token: "" +``` + +Option 3 — reference an existing Secret: +```yaml +mini_runtime: + aktoApiSecurityRuntime: + env: + useSecretsForDatabaseAbstractorToken: true + databaseAbstractorTokenSecrets: + existingSecret: "my-existing-secret" +``` + +### Kafka SASL Credentials (Strimzi) + +Use `kafkaCluster.sasl.password` (inline) or `kafkaCluster.sasl.useExistingSecret: true` + `kafkaCluster.sasl.existingSecret` (pre-existing Secret with keys `username` and `password`). + +--- + +## Annotations + +Annotation injection points — **not in default values.yaml**, add as needed: + +| Value | Applied to | +|-------|-----------| +| `mini_runtime.deploymentAnnotations` | mini-runtime Deployment metadata | +| `mini_runtime.podAnnotations` | mini-runtime Pod template metadata | +| `kafkaCluster.serviceAnnotations` | Strimzi-generated Kafka Services (bootstrap + brokers) | +| `keel.deploymentAnnotations` | Keel Deployment metadata | +| `keel.podAnnotations` | Keel Pod template metadata | +| `threat_client.deploymentAnnotations` | Threat client Deployment metadata | +| `threat_client.podAnnotations` | Threat client Pod template metadata | + +--- + +## Conditional Components + +| Component | Enabled when | Default | +|-----------|-------------|---------| +| Strimzi operator (subchart) + Strimzi CRDs (Kafka, NodePools, Topics, User) | `kafkaCluster.enabled: true` | **enabled** | +| Kafka SASL Secret + KafkaUser | `kafkaCluster.enabled: true` AND `kafkaCluster.sasl.enabled: true` AND `useExistingSecret: false` | disabled | +| Strimzi CA cert volume mount | `kafkaCluster.enabled: true` AND `kafkaCluster.tls: true` | disabled | +| Keel deployment + RBAC | `keel.keel.enabled: true` | **enabled** | +| Threat client deployment | `threat_client.aktoApiSecurityThreatClient.env.enabled: true` | **enabled** | +| Redis deployment + Service | Threat client enabled AND `aggregationRulesEnabled: true` | disabled | +| Fluent Bit sidecar | `fluent_bit.enabled: true` | disabled | +| Database abstractor Secret | `useSecretsForDatabaseAbstractorToken: true` AND no existing secret | disabled | + +--- + +## Component Reference + +### Runtime Container (`mini_runtime.aktoApiSecurityRuntime`) +- Image: `public.ecr.aws/aktosecurity/akto-api-security-mini-runtime` +- Default resources: 1-2 CPU, 2-4Gi RAM +- HPA: 1–2 replicas at 80% CPU + +### Strimzi Kafka +- Kafka version: 4.1.1 (KRaft mode, no Zookeeper) +- 1 controller node + 3 broker nodes (separate pods, managed by Strimzi) +- Bootstrap Service: `-kafka-bootstrap..svc.cluster.local` +- Topic operator and user operator enabled + +### Keel (`keel.keel`) +- Image: `public.ecr.aws/aktosecurity/keelhq-keel` +- Polls for image updates every 60 minutes +- Exposed via LoadBalancer on port 9300 +- Disable with `keel.keel.enabled: false` + +### Threat Client (`threat_client.aktoApiSecurityThreatClient`) +- Image: `public.ecr.aws/aktosecurity/akto-threat-detection` +- Requires Postgres connection (`postgresUrl`, `postgresUser`, `postgresPassword`) +- Disable with `threat_client.aktoApiSecurityThreatClient.env.enabled: false` + +### Redis (`redis`) +- Image: `public.ecr.aws/aktosecurity/redis:latestV8.6.2` +- Only deployed when threat client + aggregation rules are enabled +- Optional persistence: `redis.persistence.enabled: true` + +### Fluent Bit (`fluent_bit`) +- Optional logging sidecar, disabled by default +- Ships logs to `observability.akto.io` authenticated with the database abstractor token + +--- + +## Pod Scheduling + +```yaml +nodeSelector: {} # e.g. kubernetes.io/arch: amd64 +tolerations: [] +affinity: {} +``` +Applied to all deployments and the Strimzi install Job. + +--- + +## Common Overrides + +**Deploy with Strimzi Kafka (first install, Strimzi not yet installed):** +```sh +helm install akto-mini-runtime akto/akto-mini-runtime -n dev \ + --set kafkaCluster.enabled=true \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="" +``` + +**Strimzi Kafka with SASL (SCRAM-SHA-512):** +```sh +--set kafkaCluster.enabled=true \ +--set kafkaCluster.sasl.enabled=true \ +--set kafkaCluster.sasl.mechanism="SCRAM-SHA-512" \ +--set kafkaCluster.sasl.username="akto-runtime" \ +--set kafkaCluster.sasl.password="secret" +``` + +**Strimzi Kafka with TLS:** +```sh +--set kafkaCluster.enabled=true \ +--set kafkaCluster.tls=true +``` + +**Use external Kafka with SASL_SSL:** +```sh +--set mini_runtime.useExternalKafka=true \ +--set mini_runtime.externalKafka.brokerUrl="b1:9092,b2:9092" \ +--set mini_runtime.externalKafka.username="user" \ +--set mini_runtime.externalKafka.password="pass" \ +--set mini_runtime.externalKafka.saslMechanism="SCRAM-SHA-256" \ +--set mini_runtime.externalKafka.useSsl=true \ +--set mini_runtime.externalKafka.securityProtocol="SASL_SSL" +``` + +**Disable Keel:** +```sh +--set keel.keel.enabled=false +``` + +**Disable threat client (skip Redis/Postgres dependency):** +```sh +--set threat_client.aktoApiSecurityThreatClient.env.enabled=false +``` + +**Override Kafka Service port:** +```sh +--set kafkaCluster.ports.plain=9094 +``` diff --git a/charts/mini-runtime/Chart.yaml b/charts/mini-runtime/Chart.yaml index 4b7b9fe0..596f3365 100644 --- a/charts/mini-runtime/Chart.yaml +++ b/charts/mini-runtime/Chart.yaml @@ -16,3 +16,8 @@ keywords: maintainers: - name: Ankush Jain email: ankush@akto.io +dependencies: + - name: strimzi-kafka-operator + version: "0.51.0" + repository: "https://strimzi.io/charts/" + condition: kafkaCluster.enabled diff --git a/charts/mini-runtime/templates/deployment.yaml b/charts/mini-runtime/templates/deployment.yaml index 16ed6dc1..9b94fe21 100644 --- a/charts/mini-runtime/templates/deployment.yaml +++ b/charts/mini-runtime/templates/deployment.yaml @@ -43,54 +43,23 @@ spec: affinity: {{- toYaml . | nindent 8 }} {{- end }} - {{- if and (not .Values.mini_runtime.useExternalKafka) .Values.mini_runtime.kafka1.useSasl }} + {{- if .Values.kafkaCluster.enabled }} initContainers: - - name: generate-jaas-config - image: {{ .Values.busybox.image.repository }}@{{ .Values.busybox.image.digest }} - command: ["/bin/sh", "-c"] - args: + - name: wait-for-kafka + image: busybox:latest + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c - | - mkdir -p /etc/kafka/secrets - {{- if eq .Values.mini_runtime.kafka1.env.saslMechanism "PLAIN" }} - cat > /etc/kafka/secrets/kafka_server_jaas.conf < /etc/kafka/secrets/kafka_server_jaas.conf <- - {{- if .Values.mini_runtime.kafka1.env.kafkaAdvertisedListeners }} - {{ .Values.mini_runtime.kafka1.env.kafkaAdvertisedListeners }} - {{- else if and .Values.mini_runtime.kafka1.useSasl .Values.mini_runtime.kafka1.useTls }} - LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL://{{ .Release.Name }}-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.sasl 1).port }}, LISTENER_DOCKER_EXTERNAL_LOCALHOST_SASL://localhost:29092 - {{- else if .Values.mini_runtime.kafka1.useSasl }} - LISTENER_DOCKER_EXTERNAL_LOCALHOST_SASL://localhost:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL://{{ .Release.Name }}-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.sasl 0).port }} - {{- else if .Values.mini_runtime.kafka1.useTls }} - LISTENER_DOCKER_EXTERNAL_LOCALHOST://localhost:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST://{{ .Release.Name }}-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.tls 0).port }},LISTENER_DOCKER_EXTERNAL_DIFFHOST_ENCRYPTED://{{ .Release.Name }}-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.tls 1).port }} - {{- else }} - LISTENER_DOCKER_EXTERNAL_LOCALHOST://localhost:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST://{{ .Release.Name }}-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.default 0).port }} - {{- end }} - - name: KAFKA_BROKER_ID - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaBrokerId }} - - name: KAFKA_CLEANUP_POLICY - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaCleanupPolicy }} - - name: KAFKA_CREATE_TOPICS - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaCreateTopics }} - - name: KAFKA_INTER_BROKER_LISTENER_NAME - value: >- - {{- if .Values.mini_runtime.kafka1.env.kafkaAdvertisedListeners }} - {{ .Values.mini_runtime.kafka1.env.kafkaInterBrokerListenerName }} - {{- else if .Values.mini_runtime.kafka1.useSasl }} - LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL - {{- else if .Values.mini_runtime.kafka1.useTls }} - LISTENER_DOCKER_EXTERNAL_DIFFHOST - {{- else }} - {{ .Values.mini_runtime.kafka1.env.kafkaInterBrokerListenerName }} - {{- end }} - - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP - value: >- - {{- if and .Values.mini_runtime.kafka1.useSasl .Values.mini_runtime.kafka1.useTls }} - {{ .Values.mini_runtime.kafka1.env.kafkaListenerSecurityProtocolMapSaslTls }} - {{- else if .Values.mini_runtime.kafka1.useSasl }} - {{ .Values.mini_runtime.kafka1.env.kafkaListenerSecurityProtocolMapSasl }} - {{- else if .Values.mini_runtime.kafka1.useTls }} - {{ .Values.mini_runtime.kafka1.env.kafkaListenerSecurityProtocolMapSsl }} - {{- else }} - {{ .Values.mini_runtime.kafka1.env.kafkaListenerSecurityProtocolMap }} - {{- end }} - - name: KAFKA_PROCESS_ROLES - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaProcessRoles }} - - name: KAFKA_NODE_ID - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaNodeId }} - - name: KAFKA_CONTROLLER_QUORUM_VOTERS - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaControllerQuorumVoters }} - - name: KAFKA_CONTROLLER_LISTENER_NAMES - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaControllerListenerNames }} - - name: KAFKA_LISTENERS - value: >- - {{- if .Values.mini_runtime.kafka1.env.kafkaListeners }} - {{ .Values.mini_runtime.kafka1.env.kafkaListeners }} - {{- else if and .Values.mini_runtime.kafka1.useSasl .Values.mini_runtime.kafka1.useTls }} - CONTROLLER://0.0.0.0:9094,LISTENER_DOCKER_EXTERNAL_LOCALHOST_SASL://0.0.0.0:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL://0.0.0.0:9093 - {{- else if .Values.mini_runtime.kafka1.useSasl }} - CONTROLLER://0.0.0.0:9094,LISTENER_DOCKER_EXTERNAL_LOCALHOST_SASL://0.0.0.0:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL://0.0.0.0:9092 - {{- else if .Values.mini_runtime.kafka1.useTls }} - CONTROLLER://0.0.0.0:9094,LISTENER_DOCKER_EXTERNAL_LOCALHOST://0.0.0.0:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST_ENCRYPTED://0.0.0.0:9093 - {{- else }} - CONTROLLER://0.0.0.0:9094,LISTENER_DOCKER_EXTERNAL_LOCALHOST://0.0.0.0:29092,LISTENER_DOCKER_EXTERNAL_DIFFHOST://0.0.0.0:9092 - {{- end }} - - - name: KAFKA_LOG_CLEANER_ENABLE - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaLogCleanerEnable }} - - name: KAFKA_LOG_RETENTION_BYTES - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaLogRetentionBytes }} - - name: KAFKA_LOG_RETENTION_CHECK_INTERVAL_MS - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaLogRetentionCheckIntervalMs }} - - name: KAFKA_LOG_RETENTION_HOURS - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaLogRetentionHours }} - - name: KAFKA_LOG_SEGMENT_BYTES - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaLogSegmentBytes }} - - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaOffsetsTopicReplicationFactor - }} - - name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaTransactionStateLogMinIsr }} - - name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaTransactionStateLogReplicationFactor - }} - - name: CLUSTER_ID - value: {{ quote .Values.mini_runtime.kafka1.env.kafkaClusterId }} - - {{- if .Values.mini_runtime.kafka1.useTls }} - - name: KAFKA_SSL_KEYSTORE_LOCATION - value: {{ quote .Values.mini_runtime.kafka1.env.sslKeystoreLocation }} - - name: KAFKA_SSL_KEYSTORE_PASSWORD - value: {{ quote .Values.mini_runtime.kafka1.env.sslKeystorePassword }} - - name: KAFKA_SSL_KEY_PASSWORD - value: {{ quote .Values.mini_runtime.kafka1.env.sslKeyPassword }} - - name: KAFKA_SSL_TRUSTSTORE_LOCATION - value: {{ quote .Values.mini_runtime.kafka1.env.sslTruststoreLocation }} - - name: KAFKA_SSL_TRUSTSTORE_PASSWORD - value: {{ quote .Values.mini_runtime.kafka1.env.sslTruststorePassword }} - {{- end }} - - {{- if .Values.mini_runtime.kafka1.useSasl }} - - name: KAFKA_OPTS - value: "-Djava.security.auth.login.config=/etc/kafka/secrets/kafka_server_jaas.conf" - - name: KAFKA_SASL_ENABLED_MECHANISMS - value: {{ quote .Values.mini_runtime.kafka1.env.saslMechanism }} - - name: KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL - value: {{ quote .Values.mini_runtime.kafka1.env.saslMechanism }} - {{- end }} - - - name: KUBERNETES_CLUSTER_DOMAIN - value: {{ quote .Values.kubernetesClusterDomain }} - {{- if and .Values.mini_runtime.kafka1.useSasl (or (eq .Values.mini_runtime.kafka1.env.saslMechanism "SCRAM-SHA-256") (eq .Values.mini_runtime.kafka1.env.saslMechanism "SCRAM-SHA-512")) }} - - name: KAFKA_SCRAM_USERNAME - {{- if .Values.mini_runtime.kafka1.env.useSecretsForSaslCredentials }} - valueFrom: - secretKeyRef: - name: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret | default (printf "%s-mini-runtime-sasl" (include "akto.fullname" .)) }} - key: username - {{- else }} - value: {{ quote .Values.mini_runtime.kafka1.env.saslUsername }} - {{- end }} - - name: KAFKA_SCRAM_PASSWORD - {{- if .Values.mini_runtime.kafka1.env.useSecretsForSaslCredentials }} - valueFrom: - secretKeyRef: - name: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret | default (printf "%s-mini-runtime-sasl" (include "akto.fullname" .)) }} - key: password - {{- else }} - value: {{ quote .Values.mini_runtime.kafka1.env.saslPassword }} - {{- end }} - - name: KAFKA_SCRAM_MECHANISM - value: {{ quote .Values.mini_runtime.kafka1.env.saslMechanism }} - {{- end }} - image: {{ .Values.mini_runtime.kafka1.image.repository }}{{ if .Values.mini_runtime.kafka1.image.digest }}@{{ .Values.mini_runtime.kafka1.image.digest }}{{ else }}:{{ .Values.mini_runtime.kafka1.image.tag | default .Chart.AppVersion }}{{ end }} - {{- if .Values.mini_runtime.kafka1.autoFormatWithScram }} - command: - - bash - - -c - - | - # Auto-format storage with SCRAM credentials on first deployment - if [ ! -f /var/lib/kafka/data/.kafka-formatted ]; then - echo "========================================================================" - echo "FIRST TIME DEPLOYMENT - Formatting Kafka storage with SCRAM user" - echo "========================================================================" - echo "Cluster ID: ${CLUSTER_ID}" - echo "SCRAM Username: ${KAFKA_SCRAM_USERNAME}" - echo "SCRAM Mechanism: ${KAFKA_SCRAM_MECHANISM}" - echo "" - - # Generate server.properties using Confluent's configure script - echo "Generating Kafka configuration..." - /etc/confluent/docker/configure - - # Format storage with SCRAM credentials - echo "Formatting storage with SCRAM user..." - kafka-storage format \ - --config /etc/kafka/kafka.properties \ - --cluster-id "${CLUSTER_ID}" \ - --add-scram "${KAFKA_SCRAM_MECHANISM}=[name=${KAFKA_SCRAM_USERNAME},password=${KAFKA_SCRAM_PASSWORD}]" || { - echo "ERROR: Failed to format storage with SCRAM" - exit 1 - } - - # Mark as formatted - touch /var/lib/kafka/data/.kafka-formatted - echo "" - echo "SUCCESS: Storage formatted with SCRAM user!" - echo "========================================================================" - else - echo "========================================================================" - echo "Storage already formatted - skipping format step" - echo "========================================================================" - fi - - echo "" - echo "Starting Kafka broker with SASL authentication..." - echo "========================================================================" - - # Start Kafka using the default entrypoint - exec /etc/confluent/docker/run - {{- end }} - volumeMounts: - {{- if .Values.mini_runtime.kafka1.useSasl }} - - name: kafka-jaas-config - mountPath: /etc/kafka/secrets - {{- end }} - {{- if .Values.mini_runtime.kafka1.useTls }} - - name: kafka-certs - mountPath: {{ quote .Values.mini_runtime.kafka1.env.sslBaseMountPath }} - {{- end }} - - name: kafka1 - ports: - - containerPort: 9092 - - containerPort: 9093 - - containerPort: 29092 - - containerPort: 9999 - resources: {{- toYaml .Values.mini_runtime.kafka1.resources | nindent 10 }} - {{- end }} - restartPolicy: Always volumes: {{- if .Values.fluent_bit.enabled }} @@ -442,14 +221,10 @@ spec: emptyDir: sizeLimit: {{ .Values.fluent_bit.stateStorage.sizeLimit }} {{- end }} - {{- if and (not .Values.mini_runtime.useExternalKafka) .Values.mini_runtime.kafka1.useSasl }} - - name: kafka-jaas-config - emptyDir: {} - {{- end }} - {{- if and (not .Values.mini_runtime.useExternalKafka) .Values.mini_runtime.kafka1.useTls }} - - name: kafka-certs + {{- if and .Values.kafkaCluster.enabled .Values.kafkaCluster.tls }} + - name: kafka-tls-ca secret: - secretName: {{ quote .Values.mini_runtime.kafka1.env.sslSecretName }} + secretName: {{ printf "%s-cluster-ca-cert" .Values.kafkaCluster.clusterName | quote }} {{- end }} --- @@ -543,7 +318,7 @@ spec: resources: {{- toYaml .Values.keel.keel.resources | nindent 10 }} serviceAccountName: {{ include "akto.fullname" . }}-keel {{ end }} - +--- {{ if .Values.threat_client.aktoApiSecurityThreatClient.env.enabled }} apiVersion: apps/v1 kind: Deployment @@ -589,6 +364,24 @@ spec: affinity: {{- toYaml . | nindent 8 }} {{- end }} + {{- if .Values.kafkaCluster.enabled }} + initContainers: + - name: wait-for-kafka + image: busybox:latest + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - | + KAFKA_HOST="{{ .Values.kafkaCluster.clusterName }}-kafka-bootstrap.{{ .Release.Namespace }}.svc.cluster.local" + KAFKA_PORT="{{ ternary .Values.kafkaCluster.ports.tls .Values.kafkaCluster.ports.plain .Values.kafkaCluster.tls }}" + echo "Waiting for Kafka at ${KAFKA_HOST}:${KAFKA_PORT}..." + until nc -z "${KAFKA_HOST}" "${KAFKA_PORT}"; do + echo "Kafka not ready yet, retrying in 5s..." + sleep 5 + done + echo "Kafka is ready." + {{- end }} containers: - env: - name: POD_NAME @@ -609,9 +402,9 @@ spec: value: {{ quote .Values.threat_client.aktoApiSecurityThreatClient.env.postgresPassword }} - name: AKTO_TRAFFIC_KAFKA_BOOTSTRAP_SERVER - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.brokerUrl }}{{ else }}akto-mini-runtime-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.default 0).port }}{{ end }} + value: {{ if .Values.kafkaCluster.enabled }}{{ printf "%s-kafka-bootstrap.%s.svc.cluster.local:%v" .Values.kafkaCluster.clusterName .Release.Namespace (ternary .Values.kafkaCluster.ports.tls .Values.kafkaCluster.ports.plain .Values.kafkaCluster.tls) | quote }}{{ else if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.brokerUrl }}{{ else }}{{ quote .Values.mini_runtime.aktoApiSecurityRuntime.env.aktoKafkaBrokerUrl }}{{ end }} - name: AKTO_INTERNAL_KAFKA_BOOTSTRAP_SERVER - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.brokerUrl }}{{ else }}akto-mini-runtime-mini-runtime.{{ .Release.Namespace }}.svc.cluster.local:{{ (index .Values.ports.default 0).port }}{{ end }} + value: {{ if .Values.kafkaCluster.enabled }}{{ printf "%s-kafka-bootstrap.%s.svc.cluster.local:%v" .Values.kafkaCluster.clusterName .Release.Namespace (ternary .Values.kafkaCluster.ports.tls .Values.kafkaCluster.ports.plain .Values.kafkaCluster.tls) | quote }}{{ else if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.brokerUrl }}{{ else }}{{ quote .Values.mini_runtime.aktoApiSecurityRuntime.env.aktoKafkaBrokerUrl }}{{ end }} - name: AKTO_THREAT_DETECTION_LOCAL_REDIS_URI value: "redis://{{ include "akto.fullname" . }}-redis:6379" @@ -642,52 +435,72 @@ spec: value: {{ quote .Values.threat_client.aktoApiSecurityThreatClient.env.aggregationRulesEnabled }} - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} - {{- if or .Values.mini_runtime.kafka1.useSasl (and .Values.mini_runtime.useExternalKafka .Values.mini_runtime.externalKafka.username) }} + {{- if or (and .Values.kafkaCluster.enabled .Values.kafkaCluster.sasl.enabled) (and .Values.mini_runtime.useExternalKafka .Values.mini_runtime.externalKafka.username) }} - name: AKTO_KAFKA_SASL_ENABLED value: "true" - name: AKTO_KAFKA_SASL_MECHANISM - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.saslMechanism }}{{ else }}{{ quote .Values.mini_runtime.kafka1.env.saslMechanism }}{{ end }} + value: {{ if .Values.kafkaCluster.enabled }}{{ quote .Values.kafkaCluster.sasl.mechanism }}{{ else }}{{ quote .Values.mini_runtime.externalKafka.saslMechanism }}{{ end }} - name: KAFKA_AUTH_ENABLED value: "true" {{- if .Values.mini_runtime.useExternalKafka }} - name: AKTO_KAFKA_SECURITY_PROTOCOL value: {{ quote .Values.mini_runtime.externalKafka.securityProtocol }} {{- end }} - {{- if and .Values.mini_runtime.kafka1.useSasl .Values.mini_runtime.kafka1.env.useSecretsForSaslCredentials }} + {{- if and .Values.kafkaCluster.enabled .Values.kafkaCluster.sasl.enabled }} - name: KAFKA_USERNAME valueFrom: secretKeyRef: - name: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret | default (printf "%s-mini-runtime-sasl" (include "akto.fullname" .)) }} + name: {{ if .Values.kafkaCluster.sasl.useExistingSecret }}{{ .Values.kafkaCluster.sasl.existingSecret | quote }}{{ else }}{{ printf "%s-sasl-credentials" .Values.kafkaCluster.clusterName | quote }}{{ end }} key: username - name: KAFKA_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret | default (printf "%s-mini-runtime-sasl" (include "akto.fullname" .)) }} + name: {{ if .Values.kafkaCluster.sasl.useExistingSecret }}{{ .Values.kafkaCluster.sasl.existingSecret | quote }}{{ else }}{{ printf "%s-sasl-credentials" .Values.kafkaCluster.clusterName | quote }}{{ end }} key: password - name: AKTO_KAFKA_USERNAME valueFrom: secretKeyRef: - name: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret | default (printf "%s-mini-runtime-sasl" (include "akto.fullname" .)) }} + name: {{ if .Values.kafkaCluster.sasl.useExistingSecret }}{{ .Values.kafkaCluster.sasl.existingSecret | quote }}{{ else }}{{ printf "%s-sasl-credentials" .Values.kafkaCluster.clusterName | quote }}{{ end }} key: username - name: AKTO_KAFKA_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret | default (printf "%s-mini-runtime-sasl" (include "akto.fullname" .)) }} + name: {{ if .Values.kafkaCluster.sasl.useExistingSecret }}{{ .Values.kafkaCluster.sasl.existingSecret | quote }}{{ else }}{{ printf "%s-sasl-credentials" .Values.kafkaCluster.clusterName | quote }}{{ end }} key: password {{- else }} - name: KAFKA_USERNAME - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.username }}{{ else }}{{ quote .Values.mini_runtime.kafka1.env.saslUsername }}{{ end }} + value: {{ quote .Values.mini_runtime.externalKafka.username }} - name: KAFKA_PASSWORD - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.password }}{{ else }}{{ quote .Values.mini_runtime.kafka1.env.saslPassword }}{{ end }} + value: {{ quote .Values.mini_runtime.externalKafka.password }} - name: AKTO_KAFKA_USERNAME - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.username }}{{ else }}{{ quote .Values.mini_runtime.kafka1.env.saslUsername }}{{ end }} + value: {{ quote .Values.mini_runtime.externalKafka.username }} - name: AKTO_KAFKA_PASSWORD - value: {{ if .Values.mini_runtime.useExternalKafka }}{{ quote .Values.mini_runtime.externalKafka.password }}{{ else }}{{ quote .Values.mini_runtime.kafka1.env.saslPassword }}{{ end }} + value: {{ quote .Values.mini_runtime.externalKafka.password }} + {{- end }} + {{- end }} + {{- if and .Values.kafkaCluster.enabled .Values.kafkaCluster.tls }} + - name: AKTO_KAFKA_TLS_ENABLED + value: "true" + - name: AKTO_KAFKA_SECURITY_PROTOCOL + value: {{ if .Values.kafkaCluster.sasl.enabled }}"SASL_SSL"{{ else }}"SSL"{{ end }} + - name: KAFKA_SSL_TRUSTSTORE_LOCATION + value: "/etc/kafka/tls/ca.crt" {{- end }} - {{- end}} image: {{ .Values.threat_client.aktoApiSecurityThreatClient.image.repository }}{{ if .Values.threat_client.aktoApiSecurityThreatClient.image.digest }}@{{ .Values.threat_client.aktoApiSecurityThreatClient.image.digest }}{{ else }}:{{ .Values.threat_client.aktoApiSecurityThreatClient.image.tag | default .Chart.AppVersion }}{{ end }} imagePullPolicy: {{ .Values.threat_client.aktoApiSecurityThreatClient.imagePullPolicy }} name: akto-api-security-threat-client resources: {{- toYaml .Values.threat_client.aktoApiSecurityThreatClient.resources | nindent 10 }} + {{- if and .Values.kafkaCluster.enabled .Values.kafkaCluster.tls }} + volumeMounts: + - name: kafka-tls-ca + mountPath: /etc/kafka/tls + readOnly: true + {{- end }} restartPolicy: Always -{{ end }} \ No newline at end of file + {{- if and .Values.kafkaCluster.enabled .Values.kafkaCluster.tls }} + volumes: + - name: kafka-tls-ca + secret: + secretName: {{ printf "%s-cluster-ca-cert" .Values.kafkaCluster.clusterName | quote }} + {{- end }} +{{ end }} diff --git a/charts/mini-runtime/templates/kafka-cluster.yaml b/charts/mini-runtime/templates/kafka-cluster.yaml new file mode 100644 index 00000000..4fd92a9b --- /dev/null +++ b/charts/mini-runtime/templates/kafka-cluster.yaml @@ -0,0 +1,146 @@ +{{- if .Values.kafkaCluster.enabled }} +--- +apiVersion: kafka.strimzi.io/v1 +kind: KafkaNodePool +metadata: + name: controller + labels: + strimzi.io/cluster: {{ .Values.kafkaCluster.clusterName | quote }} +spec: + replicas: {{ .Values.kafkaCluster.controller.replicas }} + roles: + - controller + resources: + {{- toYaml .Values.kafkaCluster.controller.resources | nindent 4 }} + storage: + type: jbod + volumes: + - id: 0 + type: persistent-claim + size: {{ .Values.kafkaCluster.controller.storage.size | quote }} + {{- if .Values.kafkaCluster.controller.storage.storageClass }} + class: {{ .Values.kafkaCluster.controller.storage.storageClass | quote }} + {{- end }} + kraftMetadata: shared + deleteClaim: {{ .Values.kafkaCluster.controller.storage.deleteClaim }} +--- +apiVersion: kafka.strimzi.io/v1 +kind: KafkaNodePool +metadata: + name: broker + labels: + strimzi.io/cluster: {{ .Values.kafkaCluster.clusterName | quote }} +spec: + replicas: {{ .Values.kafkaCluster.broker.replicas }} + roles: + - broker + resources: + {{- toYaml .Values.kafkaCluster.broker.resources | nindent 4 }} + storage: + type: jbod + volumes: + - id: 0 + type: persistent-claim + size: {{ .Values.kafkaCluster.broker.storage.size | quote }} + {{- if .Values.kafkaCluster.broker.storage.storageClass }} + class: {{ .Values.kafkaCluster.broker.storage.storageClass | quote }} + {{- end }} + kraftMetadata: shared + deleteClaim: {{ .Values.kafkaCluster.broker.storage.deleteClaim }} +--- +apiVersion: kafka.strimzi.io/v1 +kind: Kafka +metadata: + name: {{ .Values.kafkaCluster.clusterName | quote }} + annotations: + strimzi.io/node-pools: enabled + strimzi.io/kraft: enabled +spec: + kafka: + version: 4.1.1 + metadataVersion: 4.1-IV1 + listeners: + - name: plain + port: {{ .Values.kafkaCluster.ports.plain }} + type: internal + tls: false + {{- if .Values.kafkaCluster.sasl.enabled }} + authentication: + type: {{ .Values.kafkaCluster.sasl.mechanism | lower }} + {{- end }} + {{- if .Values.kafkaCluster.tls }} + - name: tls + port: {{ .Values.kafkaCluster.ports.tls }} + type: internal + tls: true + {{- if .Values.kafkaCluster.sasl.enabled }} + authentication: + type: {{ .Values.kafkaCluster.sasl.mechanism | lower }} + {{- end }} + {{- end }} + config: + offsets.topic.replication.factor: {{ .Values.kafkaCluster.config.offsetsTopicReplicationFactor }} + transaction.state.log.replication.factor: {{ .Values.kafkaCluster.config.transactionStateLogReplicationFactor }} + transaction.state.log.min.isr: {{ .Values.kafkaCluster.config.transactionStateLogMinIsr }} + default.replication.factor: {{ .Values.kafkaCluster.config.defaultReplicationFactor }} + min.insync.replicas: {{ .Values.kafkaCluster.config.minInsyncReplicas }} + log.retention.ms: {{ .Values.kafkaCluster.config.retentionMs | quote }} + log.retention.bytes: {{ .Values.kafkaCluster.config.retentionBytes | quote }} + log.cleanup.policy: {{ .Values.kafkaCluster.config.cleanupPolicy | quote }} + {{- if .Values.kafkaCluster.serviceAnnotations }} + template: + bootstrapService: + metadata: + annotations: + {{- toYaml .Values.kafkaCluster.serviceAnnotations | nindent 12 }} + brokersService: + metadata: + annotations: + {{- toYaml .Values.kafkaCluster.serviceAnnotations | nindent 12 }} + {{- end }} + entityOperator: + topicOperator: {} + userOperator: {} +--- +{{- if .Values.kafkaCluster.sasl.enabled }} +{{- if not .Values.kafkaCluster.sasl.useExistingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-sasl-credentials" .Values.kafkaCluster.clusterName }} + labels: + {{- include "akto.labels" . | nindent 4 }} +type: Opaque +data: + username: {{ .Values.kafkaCluster.sasl.username | b64enc | quote }} + password: {{ .Values.kafkaCluster.sasl.password | b64enc | quote }} +--- +{{- end }} +apiVersion: kafka.strimzi.io/v1 +kind: KafkaUser +metadata: + name: {{ .Values.kafkaCluster.sasl.username | quote }} + labels: + strimzi.io/cluster: {{ .Values.kafkaCluster.clusterName | quote }} +spec: + authentication: + type: {{ .Values.kafkaCluster.sasl.mechanism | lower }} + password: + valueFrom: + secretKeyRef: + name: {{ if .Values.kafkaCluster.sasl.useExistingSecret }}{{ .Values.kafkaCluster.sasl.existingSecret | quote }}{{ else }}{{ printf "%s-sasl-credentials" .Values.kafkaCluster.clusterName | quote }}{{ end }} + key: password +{{- end }} +{{- range .Values.kafkaCluster.topics }} +--- +apiVersion: kafka.strimzi.io/v1 +kind: KafkaTopic +metadata: + name: {{ .name | quote }} + labels: + strimzi.io/cluster: {{ $.Values.kafkaCluster.clusterName | quote }} +spec: + partitions: {{ .partitions }} + replicas: {{ .replicas }} +{{- end }} +{{- end }} diff --git a/charts/mini-runtime/templates/mini-runtime.yaml b/charts/mini-runtime/templates/mini-runtime.yaml deleted file mode 100644 index 4ee018ae..00000000 --- a/charts/mini-runtime/templates/mini-runtime.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if not .Values.mini_runtime.useExternalKafka }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "akto.fullname" . }}-mini-runtime - labels: - {{- include "akto.labels" . | nindent 4 }} - annotations: - {{- with .Values.mini_runtime.serviceAnnotations }} - {{- toYaml . | nindent 4 }} - {{- else }} - {{- with .Values.mini_runtime.annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} - {{- end }} -spec: - type: {{ .Values.mini_runtime.type }} - selector: - app: {{ include "akto.fullname" . }}-mini-runtime - {{- include "akto.selectorLabels" . | nindent 4 }} - ports: - {{- if .Values.mini_runtime.kafka1.useSasl }} - {{- .Values.ports.sasl | toYaml | nindent 4 -}} - {{- else if .Values.mini_runtime.kafka1.useTls }} - {{- .Values.ports.tls | toYaml | nindent 4 -}} - {{- else }} - {{- .Values.ports.default | toYaml | nindent 4 -}} - {{- end }} -{{- end }} diff --git a/charts/mini-runtime/templates/sasl-secret.yaml b/charts/mini-runtime/templates/sasl-secret.yaml deleted file mode 100644 index 314e1087..00000000 --- a/charts/mini-runtime/templates/sasl-secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if and .Values.mini_runtime.kafka1.useSasl .Values.mini_runtime.kafka1.env.useSecretsForSaslCredentials (not .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.existingSecret) .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.username .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.password}} -apiVersion: v1 -kind: Secret -metadata: - name: {{ printf "%s-mini-runtime-sasl" (include "akto.fullname" .) }} - labels: - {{- include "akto.labels" . | nindent 4 }} -type: Opaque -data: - username: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.username | b64enc }} - password: {{ .Values.mini_runtime.kafka1.env.saslCredentialsSecrets.password | b64enc }} -{{- end }} - diff --git a/charts/mini-runtime/tests/annotations_test.yaml b/charts/mini-runtime/tests/annotations_test.yaml new file mode 100644 index 00000000..2b50fed6 --- /dev/null +++ b/charts/mini-runtime/tests/annotations_test.yaml @@ -0,0 +1,116 @@ +suite: Deployment and Pod Annotations +templates: + - templates/deployment.yaml + +tests: + # ── mini-runtime deployment annotations ─────────────────────────────────── + - it: applies deploymentAnnotations to mini-runtime Deployment metadata + set: + mini_runtime.deploymentAnnotations: + my-annotation: foo + team: platform + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - equal: + path: metadata.annotations["my-annotation"] + value: foo + - equal: + path: metadata.annotations.team + value: platform + + # ── mini-runtime pod annotations ────────────────────────────────────────── + - it: applies podAnnotations to mini-runtime Pod template metadata + set: + mini_runtime.podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - equal: + path: spec.template.metadata.annotations["prometheus.io/scrape"] + value: "true" + - equal: + path: spec.template.metadata.annotations["prometheus.io/port"] + value: "8080" + + # ── threat client deployment annotations ────────────────────────────────── + - it: applies deploymentAnnotations to threat-client Deployment metadata + set: + threat_client.deploymentAnnotations: + my-annotation: bar + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - equal: + path: metadata.annotations["my-annotation"] + value: bar + + # ── threat client pod annotations ───────────────────────────────────────── + - it: applies podAnnotations to threat-client Pod template metadata + set: + threat_client.podAnnotations: + sidecar.istio.io/inject: "false" + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - equal: + path: spec.template.metadata.annotations["sidecar.istio.io/inject"] + value: "false" + + # ── keel deployment annotations ─────────────────────────────────────────── + - it: applies deploymentAnnotations to keel Deployment metadata + set: + keel.deploymentAnnotations: + keel-custom: annotation + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-keel + skipEmptyTemplates: true + asserts: + - equal: + path: metadata.annotations["keel-custom"] + value: annotation + + # ── keel pod annotations ────────────────────────────────────────────────── + - it: applies podAnnotations to keel Pod template metadata + set: + keel.podAnnotations: + custom: value + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-keel + skipEmptyTemplates: true + asserts: + - equal: + path: spec.template.metadata.annotations.custom + value: value + + # ── No annotation cross-leaking between deployments ─────────────────────── + - it: runtime deploymentAnnotations do not appear on threat-client Deployment + set: + mini_runtime.deploymentAnnotations: + only-for-runtime: "true" + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - notExists: + path: metadata.annotations["only-for-runtime"] diff --git a/charts/mini-runtime/tests/external_kafka_test.yaml b/charts/mini-runtime/tests/external_kafka_test.yaml new file mode 100644 index 00000000..ecda6f4f --- /dev/null +++ b/charts/mini-runtime/tests/external_kafka_test.yaml @@ -0,0 +1,154 @@ +suite: External Kafka Mode +templates: + - templates/kafka-cluster.yaml + - templates/deployment.yaml + +tests: + # ── No documents when kafkaCluster disabled ─────────────────────────────── + - it: renders no documents when kafkaCluster disabled + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: kafka1:9092 + template: templates/kafka-cluster.yaml + asserts: + - hasDocuments: + count: 0 + + # ── Runtime uses external broker URL ────────────────────────────────────── + - it: runtime AKTO_KAFKA_BROKER_URL uses external brokerUrl + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: "kafka1:9092,kafka2:9092" + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_BROKER_URL + value: "kafka1:9092,kafka2:9092" + + # ── No wait-for-kafka init container in external mode ───────────────────── + - it: does not render wait-for-kafka init container when kafkaCluster disabled + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: kafka1:9092 + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - notExists: + path: spec.template.spec.initContainers + + # ── External Kafka with SASL: runtime gets auth env vars ────────────────── + - it: injects SASL env vars into runtime when external Kafka has username set + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: kafka1:9092 + mini_runtime.externalKafka.username: myuser + mini_runtime.externalKafka.password: mypass + mini_runtime.externalKafka.saslMechanism: SCRAM-SHA-256 + mini_runtime.externalKafka.securityProtocol: SASL_SSL + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_AUTH_ENABLED + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SASL_MECHANISM + value: SCRAM-SHA-256 + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SECURITY_PROTOCOL + value: SASL_SSL + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_USERNAME + value: myuser + + # ── External Kafka with SASL: threat client gets auth env vars ──────────── + - it: injects SASL env vars into threat client when external Kafka has username set + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: kafka1:9092 + mini_runtime.externalKafka.username: myuser + mini_runtime.externalKafka.password: mypass + mini_runtime.externalKafka.saslMechanism: SCRAM-SHA-256 + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_USERNAME + value: myuser + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SASL_MECHANISM + value: SCRAM-SHA-256 + + # ── Threat client uses external broker URL ──────────────────────────────── + - it: threat client bootstrap servers use external brokerUrl + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: "kafka1:9092,kafka2:9092" + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_TRAFFIC_KAFKA_BOOTSTRAP_SERVER + value: "kafka1:9092,kafka2:9092" + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_INTERNAL_KAFKA_BOOTSTRAP_SERVER + value: "kafka1:9092,kafka2:9092" + + # ── No SASL env vars when external Kafka has no username ────────────────── + - it: does not inject SASL env vars when external Kafka username is empty + set: + kafkaCluster.enabled: false + mini_runtime.useExternalKafka: true + mini_runtime.externalKafka.brokerUrl: kafka1:9092 + mini_runtime.externalKafka.username: "" + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_AUTH_ENABLED + value: "true" diff --git a/charts/mini-runtime/tests/integration_test.sh b/charts/mini-runtime/tests/integration_test.sh new file mode 100755 index 00000000..8e83c189 --- /dev/null +++ b/charts/mini-runtime/tests/integration_test.sh @@ -0,0 +1,559 @@ +#!/usr/bin/env bash +# Integration tests for akto-mini-runtime Helm chart. +# Requires: helm, kubectl pointed at a live cluster with sufficient resources. +# +# Usage: +# ./charts/mini-runtime/tests/integration_test.sh --token # run all scenarios +# ./charts/mini-runtime/tests/integration_test.sh --token 1 # run scenario 1 only +# ./charts/mini-runtime/tests/integration_test.sh --token 1 3 6 # run scenarios 1, 3, and 6 +# +# Each scenario installs the chart into its own namespace, runs assertions, +# then cleans up. Cleanup also runs on script exit/interrupt via trap. +# Final exit code is 1 if any assertion failed. + +set -euo pipefail + +CHART_DIR="$(cd "$(dirname "$0")/.." && pwd)" +PASS=0 +FAIL=0 + +# ── Parse arguments ─────────────────────────────────────────────────────────── +# Usage: integration_test.sh [--token ] [scenario numbers...] +DB_TOKEN="" +SCENARIOS=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --token) + DB_TOKEN="$2" + shift 2 + ;; + *) + SCENARIOS+=("$1") + shift + ;; + esac +done + +if [[ -z "$DB_TOKEN" ]]; then + echo "Error: --token is required" + echo "Usage: $0 [--token ] [scenario numbers...]" + exit 1 +fi + +run_scenario() { + local n="$1" + if [[ ${#SCENARIOS[@]} -eq 0 ]]; then + return 0 + fi + for s in "${SCENARIOS[@]}"; do + [[ "$s" == "$n" ]] && return 0 + done + return 1 +} + +# ── Colour codes ────────────────────────────────────────────────────────────── +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# ── Helper: print section header ────────────────────────────────────────────── +header() { + echo "" + echo -e "${YELLOW}══════════════════════════════════════════${NC}" + echo -e "${YELLOW} $1${NC}" + echo -e "${YELLOW}══════════════════════════════════════════${NC}" +} + +# ── Helper: assert two values are equal ─────────────────────────────────────── +assert_eq() { + local label="$1" + local actual="$2" + local expected="$3" + if [[ "$actual" == "$expected" ]]; then + echo -e " ${GREEN}✓ PASS${NC} $label" + (( PASS++ )) || true + else + echo -e " ${RED}✗ FAIL${NC} $label" + echo -e " expected: ${expected}" + echo -e " actual: ${actual}" + (( FAIL++ )) || true + fi +} + +# ── Helper: assert a count is zero ──────────────────────────────────────────── +assert_zero() { + local label="$1" + local count="$2" + if [[ "$count" -eq 0 ]]; then + echo -e " ${GREEN}✓ PASS${NC} $label" + (( PASS++ )) || true + else + echo -e " ${RED}✗ FAIL${NC} $label (count=$count, expected 0)" + (( FAIL++ )) || true + fi +} + +# ── Helper: assert a string matches a pattern ───────────────────────────────── +assert_match() { + local label="$1" + local value="$2" + local pattern="$3" + if echo "$value" | grep -q "$pattern"; then + echo -e " ${GREEN}✓ PASS${NC} $label" + (( PASS++ )) || true + else + echo -e " ${RED}✗ FAIL${NC} $label" + echo -e " value: ${value}" + echo -e " pattern: ${pattern}" + (( FAIL++ )) || true + fi +} + +# ── Helper: assert a resource exists ───────────────────────────────────────── +assert_exists() { + local label="$1" + shift + if kubectl get "$@" &>/dev/null; then + echo -e " ${GREEN}✓ PASS${NC} $label" + (( PASS++ )) || true + else + echo -e " ${RED}✗ FAIL${NC} $label (resource not found)" + (( FAIL++ )) || true + fi +} + +# ── Helper: kubectl wait with descriptive output ────────────────────────────── +run_wait() { + local label="$1" + local timeout="$2" + shift 2 + local deadline=$(( $(date +%s) + timeout )) + # kubectl wait exits immediately with "no matching resources found" if the + # resource doesn't exist yet. Retry in a loop until the deadline. + while true; do + if kubectl wait "$@" --timeout=5s 2>/dev/null; then + echo -e " ${GREEN}✓ PASS${NC} $label" + (( PASS++ )) || true + return + fi + if [[ $(date +%s) -ge $deadline ]]; then + echo -e " ${RED}✗ FAIL${NC} $label (timed out after ${timeout}s)" + (( FAIL++ )) || true + return + fi + sleep 5 + done +} + +# ── Cleanup: track all releases+namespaces to remove ───────────────────────── +CLEANUP_RELEASES=() +CLEANUP_NAMESPACES=() + +cleanup() { + if [[ ${#CLEANUP_RELEASES[@]} -gt 0 ]]; then + echo "" + echo "── Cleaning up ──────────────────────────────────────────" + for i in "${!CLEANUP_RELEASES[@]}"; do + local release="${CLEANUP_RELEASES[$i]}" + local ns="${CLEANUP_NAMESPACES[$i]}" + echo " Uninstalling $release from $ns..." + #helm uninstall "$release" -n "$ns" --ignore-not-found 2>/dev/null || true + # Delete PVCs explicitly — helm uninstall does not remove PVCs created by + # Strimzi StatefulSets (they are outside Helm's ownership). Leaving them + # behind causes "Invalid cluster.id" errors on the next install. + #kubectl delete pvc --all -n "$ns" --ignore-not-found 2>/dev/null || true + #kubectl delete namespace "$ns" --ignore-not-found 2>/dev/null || true + done + fi +} +trap cleanup EXIT + +# ── Minimal resource overrides for test environments (e.g. minikube) ───────── +MINIKUBE_RESOURCES=( + --set kafkaCluster.broker.resources.requests.cpu=200m + --set kafkaCluster.broker.resources.requests.memory=512Mi + --set kafkaCluster.broker.resources.limits.cpu=500m + --set kafkaCluster.broker.resources.limits.memory=1Gi + --set kafkaCluster.controller.resources.requests.cpu=100m + --set kafkaCluster.controller.resources.requests.memory=256Mi + --set kafkaCluster.controller.resources.limits.cpu=200m + --set kafkaCluster.controller.resources.limits.memory=512Mi + --set mini_runtime.aktoApiSecurityRuntime.resources.requests.cpu=100m + --set mini_runtime.aktoApiSecurityRuntime.resources.requests.memory=256Mi + --set mini_runtime.aktoApiSecurityRuntime.resources.limits.cpu=500m + --set mini_runtime.aktoApiSecurityRuntime.resources.limits.memory=512Mi +) + +# ── Pre-flight ──────────────────────────────────────────────────────────────── +header "Pre-flight" +echo " Chart: $CHART_DIR" +echo " Cluster: $(kubectl config current-context)" +if [[ ${#SCENARIOS[@]} -gt 0 ]]; then + echo " Running scenarios: ${SCENARIOS[*]}" +else + echo " Running scenarios: all" +fi +echo "" + +echo " Updating chart dependencies..." +helm repo add strimzi https://strimzi.io/charts/ --force-update >/dev/null 2>&1 +helm dependency update "$CHART_DIR" >/dev/null 2>&1 +echo -e " ${GREEN}✓${NC} Dependencies updated" + + +# ══════════════════════════════════════════════════════════════════════════════ +# SCENARIO 1: Default install — Strimzi Kafka mode +# ══════════════════════════════════════════════════════════════════════════════ +if run_scenario 1; then +header "Scenario 1: Default install (Strimzi Kafka mode)" + +NS1="default" +REL1="akto-mini-runtime" +CLEANUP_RELEASES+=("$REL1") +CLEANUP_NAMESPACES+=("$NS1") + +kubectl create namespace "$NS1" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +helm install "$REL1" "$CHART_DIR" -n "$NS1" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="$DB_TOKEN" \ + # "${MINIKUBE_RESOURCES[@]}" \ + --wait=false 2>&1 | tail -1 + +run_wait "[S1] Strimzi operator available" 300 \ + deployment --selector app.kubernetes.io/name=strimzi-kafka-operator \ + -n "$NS1" --for=condition=available + +run_wait "[S1] Kafka controller+broker pods ready" 360 \ + pod -l strimzi.io/cluster=akto-kafka \ + -n "$NS1" --for=condition=ready + +assert_exists "[S1] akto-kafka-kafka-bootstrap Service exists" \ + service akto-kafka-kafka-bootstrap -n "$NS1" + +for topic in akto.api.logs akto.api.logs2 akto.api.producer.logs akto.daemonset.producer.heartbeats; do + run_wait "[S1] KafkaTopic $topic ready" 120 \ + kafkatopic "$topic" -n "$NS1" --for=condition=ready +done + +run_wait "[S1] Redis available" 120 \ + deployment/${REL1}-akto-mini-runtime-redis \ + -n "$NS1" --for=condition=available + +run_wait "[S1] Keel available (validates /healthz liveness)" 180 \ + deployment/${REL1}-akto-mini-runtime-keel \ + -n "$NS1" --for=condition=available + +run_wait "[S1] mini-runtime init container passed (Kafka TCP verified)" 240 \ + pod -l app=${REL1}-akto-mini-runtime-mini-runtime \ + -n "$NS1" --for=condition=initialized + +PHASE=$(kubectl get pod -l app=${REL1}-akto-mini-runtime-mini-runtime \ + -n "$NS1" -o jsonpath='{.items[0].status.phase}' 2>/dev/null || echo "Unknown") +if [[ "$PHASE" == "Pending" ]]; then + echo -e " ${RED}✗ FAIL${NC} [S1] mini-runtime pod stuck in Pending" + kubectl describe pod -l app=${REL1}-akto-mini-runtime-mini-runtime -n "$NS1" | tail -20 + (( FAIL++ )) || true +else + echo -e " ${GREEN}✓ PASS${NC} [S1] mini-runtime pod phase=$PHASE (Running or CrashLoopBackOff OK)" + (( PASS++ )) || true +fi + +run_wait "[S1] threat-client init container passed" 240 \ + pod -l app=${REL1}-akto-mini-runtime-threat-client \ + -n "$NS1" --for=condition=initialized + +fi # end scenario 1 + + +# ══════════════════════════════════════════════════════════════════════════════ +# SCENARIO 2: External Kafka mode +# ══════════════════════════════════════════════════════════════════════════════ +if run_scenario 2; then +header "Scenario 2: External Kafka mode" + +NS2="akto-int-ext" +REL2="akto-ext" +CLEANUP_RELEASES+=("$REL2") +CLEANUP_NAMESPACES+=("$NS2") + +kubectl create namespace "$NS2" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +EXTERNAL_BROKER="kafka-stub.${NS2}.svc.cluster.local:9092" + +helm install "$REL2" "$CHART_DIR" -n "$NS2" \ + --set kafkaCluster.enabled=false \ + --set mini_runtime.useExternalKafka=true \ + --set mini_runtime.externalKafka.brokerUrl="$EXTERNAL_BROKER" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="$DB_TOKEN" \ + --set keel.keel.enabled=false \ + --wait=false 2>&1 | tail -1 + +KAFKA_COUNT=$(kubectl get kafka -n "$NS2" 2>/dev/null | grep -c "akto-kafka" || true) +assert_zero "[S2] No Kafka CRD resources in namespace" "$KAFKA_COUNT" + +SVC_COUNT=$(kubectl get service -n "$NS2" 2>/dev/null | grep -c "akto-kafka" || true) +assert_zero "[S2] No akto-kafka-* Services" "$SVC_COUNT" + +BROKER_URL=$(kubectl get deployment ${REL2}-akto-mini-runtime-mini-runtime \ + -n "$NS2" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AKTO_KAFKA_BROKER_URL")].value}' \ + 2>/dev/null || echo "") +assert_eq "[S2] AKTO_KAFKA_BROKER_URL == external broker" "$BROKER_URL" "$EXTERNAL_BROKER" + +INIT_CONTAINERS=$(kubectl get deployment ${REL2}-akto-mini-runtime-mini-runtime \ + -n "$NS2" \ + -o jsonpath='{.spec.template.spec.initContainers}' \ + 2>/dev/null || echo "") +assert_eq "[S2] No wait-for-kafka init container" "$INIT_CONTAINERS" "" + +fi # end scenario 2 + + +# ══════════════════════════════════════════════════════════════════════════════ +# SCENARIO 3: TLS enabled +# ══════════════════════════════════════════════════════════════════════════════ +if run_scenario 3; then +header "Scenario 3: TLS enabled" + +NS3="default" +REL3="akto-tls" +CLEANUP_RELEASES+=("$REL3") +CLEANUP_NAMESPACES+=("$NS3") + +kubectl create namespace "$NS3" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +helm install "$REL3" "$CHART_DIR" -n "$NS3" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="$DB_TOKEN" \ + --set kafkaCluster.tls=true \ + --set keel.keel.enabled=false \ + "${MINIKUBE_RESOURCES[@]}" \ + --wait=false 2>&1 | tail -1 + +run_wait "[S3] Strimzi operator available" 300 \ + deployment --selector app.kubernetes.io/name=strimzi-kafka-operator \ + -n "$NS3" --for=condition=available + +run_wait "[S3] Kafka pods ready" 360 \ + pod -l strimzi.io/cluster=akto-kafka \ + -n "$NS3" --for=condition=ready + +assert_exists "[S3] Strimzi CA cert Secret exists" \ + secret akto-kafka-cluster-ca-cert -n "$NS3" + +TLS_LISTENER_NAME=$(kubectl get kafka akto-kafka -n "$NS3" \ + -o jsonpath='{.spec.kafka.listeners[1].name}' 2>/dev/null || echo "") +assert_eq "[S3] Kafka CR has TLS listener named 'tls'" "$TLS_LISTENER_NAME" "tls" + +TLS_LISTENER_TLS=$(kubectl get kafka akto-kafka -n "$NS3" \ + -o jsonpath='{.spec.kafka.listeners[1].tls}' 2>/dev/null || echo "") +assert_eq "[S3] Kafka TLS listener has tls=true" "$TLS_LISTENER_TLS" "true" + +TLS_ENABLED=$(kubectl get deployment ${REL3}-akto-mini-runtime-mini-runtime \ + -n "$NS3" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AKTO_KAFKA_TLS_ENABLED")].value}' \ + 2>/dev/null || echo "") +assert_eq "[S3] mini-runtime has AKTO_KAFKA_TLS_ENABLED=true" "$TLS_ENABLED" "true" + +TRUSTSTORE=$(kubectl get deployment ${REL3}-akto-mini-runtime-mini-runtime \ + -n "$NS3" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="KAFKA_SSL_TRUSTSTORE_LOCATION")].value}' \ + 2>/dev/null || echo "") +assert_eq "[S3] mini-runtime has KAFKA_SSL_TRUSTSTORE_LOCATION=/etc/kafka/tls/ca.crt" \ + "$TRUSTSTORE" "/etc/kafka/tls/ca.crt" + +TLS_VOLUME_SECRET=$(kubectl get deployment ${REL3}-akto-mini-runtime-mini-runtime \ + -n "$NS3" \ + -o jsonpath='{.spec.template.spec.volumes[?(@.name=="kafka-tls-ca")].secret.secretName}' \ + 2>/dev/null || echo "") +assert_eq "[S3] mini-runtime kafka-tls-ca volume references akto-kafka-cluster-ca-cert" \ + "$TLS_VOLUME_SECRET" "akto-kafka-cluster-ca-cert" + +fi # end scenario 3 + + +# ══════════════════════════════════════════════════════════════════════════════ +# SCENARIO 4: Port override (plain port 9094) +# ══════════════════════════════════════════════════════════════════════════════ +if run_scenario 4; then +header "Scenario 4: Port override (kafkaCluster.ports.plain=9094)" + +NS4="akto-int-port" +REL4="akto-port" +CLEANUP_RELEASES+=("$REL4") +CLEANUP_NAMESPACES+=("$NS4") + +kubectl create namespace "$NS4" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +helm install "$REL4" "$CHART_DIR" -n "$NS4" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="$DB_TOKEN" \ + --set kafkaCluster.ports.plain=9094 \ + --set keel.keel.enabled=false \ + "${MINIKUBE_RESOURCES[@]}" \ + --wait=false 2>&1 | tail -1 + +run_wait "[S4] Strimzi operator available" 300 \ + deployment --selector app.kubernetes.io/name=strimzi-kafka-operator \ + -n "$NS4" --for=condition=available + +LISTENER_PORT=$(kubectl get kafka akto-kafka -n "$NS4" \ + -o jsonpath='{.spec.kafka.listeners[0].port}' 2>/dev/null || echo "") +assert_eq "[S4] Kafka CR plain listener port == 9094" "$LISTENER_PORT" "9094" + +assert_exists "[S4] akto-kafka-kafka-bootstrap Service exists" \ + service akto-kafka-kafka-bootstrap -n "$NS4" + +BROKER_URL=$(kubectl get deployment ${REL4}-akto-mini-runtime-mini-runtime \ + -n "$NS4" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AKTO_KAFKA_BROKER_URL")].value}' \ + 2>/dev/null || echo "") +assert_match "[S4] AKTO_KAFKA_BROKER_URL contains :9094" "$BROKER_URL" ":9094" + +BROKER_MAL=$(kubectl get deployment ${REL4}-akto-mini-runtime-mini-runtime \ + -n "$NS4" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AKTO_KAFKA_BROKER_MAL")].value}' \ + 2>/dev/null || echo "") +assert_match "[S4] AKTO_KAFKA_BROKER_MAL contains :9094" "$BROKER_MAL" ":9094" + +INIT_CMD=$(kubectl get deployment ${REL4}-akto-mini-runtime-mini-runtime \ + -n "$NS4" \ + -o jsonpath='{.spec.template.spec.initContainers[0].command[2]}' \ + 2>/dev/null || echo "") +assert_match "[S4] wait-for-kafka init container uses KAFKA_PORT=9094" "$INIT_CMD" 'KAFKA_PORT="9094"' + +fi # end scenario 4 + + +# ══════════════════════════════════════════════════════════════════════════════ +# SCENARIO 5: Service annotations + pod/deployment annotations +# ══════════════════════════════════════════════════════════════════════════════ +if run_scenario 5; then +header "Scenario 5: Service annotations and pod/deployment annotations" + +NS5="akto-int-ann" +REL5="akto-ann" +CLEANUP_RELEASES+=("$REL5") +CLEANUP_NAMESPACES+=("$NS5") + +kubectl create namespace "$NS5" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +helm install "$REL5" "$CHART_DIR" -n "$NS5" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="$DB_TOKEN" \ + --set "kafkaCluster.serviceAnnotations.test\.akto\.io/ci-marker=integration-test" \ + --set "mini_runtime.podAnnotations.prometheus\.io/scrape=true" \ + --set "mini_runtime.deploymentAnnotations.team=platform" \ + --set keel.keel.enabled=false \ + "${MINIKUBE_RESOURCES[@]}" \ + --wait=false 2>&1 | tail -1 + +BOOTSTRAP_ANN=$(kubectl get kafka akto-kafka -n "$NS5" \ + -o jsonpath='{.spec.kafka.template.bootstrapService.metadata.annotations.test\.akto\.io/ci-marker}' \ + 2>/dev/null || echo "") +assert_eq "[S5] Kafka CR bootstrapService annotation set" \ + "$BOOTSTRAP_ANN" "integration-test" + +BROKERS_ANN=$(kubectl get kafka akto-kafka -n "$NS5" \ + -o jsonpath='{.spec.kafka.template.brokersService.metadata.annotations.test\.akto\.io/ci-marker}' \ + 2>/dev/null || echo "") +assert_eq "[S5] Kafka CR brokersService annotation set" \ + "$BROKERS_ANN" "integration-test" + +DEPLOY_ANN=$(kubectl get deployment ${REL5}-akto-mini-runtime-mini-runtime \ + -n "$NS5" \ + -o jsonpath='{.metadata.annotations.team}' \ + 2>/dev/null || echo "") +assert_eq "[S5] mini-runtime Deployment has annotation team=platform" "$DEPLOY_ANN" "platform" + +POD_ANN=$(kubectl get deployment ${REL5}-akto-mini-runtime-mini-runtime \ + -n "$NS5" \ + -o jsonpath='{.spec.template.metadata.annotations.prometheus\.io/scrape}' \ + 2>/dev/null || echo "") +assert_eq "[S5] mini-runtime pod template has prometheus.io/scrape=true" "$POD_ANN" "true" + +run_wait "[S5] Strimzi operator available (for Service annotation reconciliation)" 300 \ + deployment --selector app.kubernetes.io/name=strimzi-kafka-operator \ + -n "$NS5" --for=condition=available + +echo " Waiting 30s for Strimzi to reconcile Services..." +sleep 30 + +LIVE_SVC_ANN=$(kubectl get service akto-kafka-kafka-bootstrap -n "$NS5" \ + -o jsonpath='{.metadata.annotations.test\.akto\.io/ci-marker}' \ + 2>/dev/null || echo "") +assert_eq "[S5] Live akto-kafka-kafka-bootstrap Service has annotation" \ + "$LIVE_SVC_ANN" "integration-test" + +fi # end scenario 5 + + +# ══════════════════════════════════════════════════════════════════════════════ +# SCENARIO 6: SASL / SCRAM-SHA-512 +# ══════════════════════════════════════════════════════════════════════════════ +if run_scenario 6; then +header "Scenario 6: SASL (SCRAM-SHA-512)" + +NS6="default" +REL6="akto-mini-runtime" +CLEANUP_RELEASES+=("$REL6") +CLEANUP_NAMESPACES+=("$NS6") + +kubectl create namespace "$NS6" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +helm install "$REL6" "$CHART_DIR" -n "$NS6" \ + --set mini_runtime.aktoApiSecurityRuntime.env.databaseAbstractorToken="$DB_TOKEN" \ + --set kafkaCluster.sasl.enabled=true \ + --set kafkaCluster.sasl.username=akto-runtime \ + --set kafkaCluster.sasl.password=ci-sasl-secret \ + # "${MINIKUBE_RESOURCES[@]}" \ + --wait=false 2>&1 | tail -1 + +run_wait "[S6] Strimzi operator available" 300 \ + deployment --selector app.kubernetes.io/name=strimzi-kafka-operator \ + -n "$NS6" --for=condition=available + +run_wait "[S6] KafkaUser akto-runtime ready (user operator provisioned credentials)" 180 \ + kafkauser/akto-runtime -n "$NS6" --for=condition=ready + +assert_exists "[S6] akto-kafka-sasl-credentials Secret exists" \ + secret akto-kafka-sasl-credentials -n "$NS6" + +SASL_USERNAME=$(kubectl get secret akto-kafka-sasl-credentials -n "$NS6" \ + -o jsonpath='{.data.username}' 2>/dev/null | base64 -d || echo "") +assert_eq "[S6] SASL Secret username == akto-runtime" "$SASL_USERNAME" "akto-runtime" + +AUTH_ENABLED=$(kubectl get deployment ${REL6}-akto-mini-runtime-mini-runtime \ + -n "$NS6" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="KAFKA_AUTH_ENABLED")].value}' \ + 2>/dev/null || echo "") +assert_eq "[S6] mini-runtime has KAFKA_AUTH_ENABLED=true" "$AUTH_ENABLED" "true" + +SECRET_REF=$(kubectl get deployment ${REL6}-akto-mini-runtime-mini-runtime \ + -n "$NS6" \ + -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="KAFKA_USERNAME")].valueFrom.secretKeyRef.name}' \ + 2>/dev/null || echo "") +assert_eq "[S6] mini-runtime KAFKA_USERNAME reads from akto-kafka-sasl-credentials" \ + "$SECRET_REF" "akto-kafka-sasl-credentials" + +LISTENER_AUTH=$(kubectl get kafka akto-kafka -n "$NS6" \ + -o jsonpath='{.spec.kafka.listeners[0].authentication.type}' \ + 2>/dev/null || echo "") +assert_eq "[S6] Kafka listener authentication.type == scram-sha-512" \ + "$LISTENER_AUTH" "scram-sha-512" + +fi # end scenario 6 + + +# ══════════════════════════════════════════════════════════════════════════════ +# Results +# ══════════════════════════════════════════════════════════════════════════════ +echo "" +echo -e "${YELLOW}══════════════════════════════════════════${NC}" +if [[ $FAIL -eq 0 ]]; then + echo -e "${GREEN} Results: $PASS passed, $FAIL failed${NC}" +else + echo -e "${RED} Results: $PASS passed, $FAIL failed${NC}" +fi +echo -e "${YELLOW}══════════════════════════════════════════${NC}" + +[[ $FAIL -eq 0 ]] diff --git a/charts/mini-runtime/tests/port_override_test.yaml b/charts/mini-runtime/tests/port_override_test.yaml new file mode 100644 index 00000000..1a534775 --- /dev/null +++ b/charts/mini-runtime/tests/port_override_test.yaml @@ -0,0 +1,117 @@ +suite: Port Override +templates: + - templates/kafka-cluster.yaml + - templates/deployment.yaml + +# Note: helm-unittest renders with .Release.Namespace = "NAMESPACE" + +tests: + # ── Kafka CR plain listener uses overridden port ─────────────────────────── + - it: sets plain listener port to overridden value in Kafka CR + set: + kafkaCluster.ports.plain: 9094 + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.listeners[0].port + value: 9094 + + # ── TLS listener uses overridden tls port ───────────────────────────────── + - it: sets tls listener port to overridden value in Kafka CR + set: + kafkaCluster.tls: true + kafkaCluster.ports.tls: 9095 + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.listeners[1].port + value: 9095 + + # ── Runtime AKTO_KAFKA_BROKER_URL reflects overridden port ──────────────── + - it: runtime AKTO_KAFKA_BROKER_URL reflects overridden plain port + set: + kafkaCluster.ports.plain: 9094 + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_BROKER_URL + value: akto-kafka-kafka-bootstrap.NAMESPACE.svc.cluster.local:9094 + + # ── Runtime AKTO_KAFKA_BROKER_MAL reflects overridden port ──────────────── + - it: runtime AKTO_KAFKA_BROKER_MAL reflects overridden plain port + set: + kafkaCluster.ports.plain: 9094 + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_BROKER_MAL + value: akto-kafka-kafka-bootstrap.NAMESPACE.svc.cluster.local:9094 + + # ── Threat client bootstrap servers reflect overridden port ─────────────── + - it: threat client bootstrap server env vars reflect overridden port + set: + kafkaCluster.ports.plain: 9094 + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_TRAFFIC_KAFKA_BOOTSTRAP_SERVER + value: akto-kafka-kafka-bootstrap.NAMESPACE.svc.cluster.local:9094 + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_INTERNAL_KAFKA_BOOTSTRAP_SERVER + value: akto-kafka-kafka-bootstrap.NAMESPACE.svc.cluster.local:9094 + + # ── Init container uses overridden port ─────────────────────────────────── + - it: wait-for-kafka init container command contains overridden port + set: + kafkaCluster.ports.plain: 9094 + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - matchRegex: + path: spec.template.spec.initContainers[0].command[2] + pattern: 'KAFKA_PORT="9094"' + + # ── Default port 9092 used when no override ─────────────────────────────── + - it: uses default port 9092 when no port override set + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_BROKER_URL + value: akto-kafka-kafka-bootstrap.NAMESPACE.svc.cluster.local:9092 diff --git a/charts/mini-runtime/tests/run_tests.sh b/charts/mini-runtime/tests/run_tests.sh new file mode 100755 index 00000000..31046bbb --- /dev/null +++ b/charts/mini-runtime/tests/run_tests.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Run all automated tests for the mini-runtime chart. +# Requires: helm, helm-unittest plugin (helm plugin install https://github.com/helm-unittest/helm-unittest) +# +# Usage: +# ./charts/mini-runtime/tests/run_tests.sh + +set -euo pipefail + +CHART_DIR="$(cd "$(dirname "$0")/.." && pwd)" + +echo "=== helm lint ===" +helm lint "$CHART_DIR" + +echo "" +echo "=== helm lint: SASL enabled ===" +helm lint "$CHART_DIR" \ + --set kafkaCluster.sasl.enabled=true \ + --set kafkaCluster.sasl.password=secret + +echo "" +echo "=== helm lint: TLS enabled ===" +helm lint "$CHART_DIR" \ + --set kafkaCluster.tls=true + +echo "" +echo "=== helm lint: SASL + TLS ===" +helm lint "$CHART_DIR" \ + --set kafkaCluster.sasl.enabled=true \ + --set kafkaCluster.sasl.password=secret \ + --set kafkaCluster.tls=true + +echo "" +echo "=== helm lint: external Kafka ===" +helm lint "$CHART_DIR" \ + --set kafkaCluster.enabled=false \ + --set mini_runtime.useExternalKafka=true \ + --set mini_runtime.externalKafka.brokerUrl=kafka1:9092 + +echo "" +echo "=== helm lint: external Kafka with SASL_SSL ===" +helm lint "$CHART_DIR" \ + --set kafkaCluster.enabled=false \ + --set mini_runtime.useExternalKafka=true \ + --set mini_runtime.externalKafka.brokerUrl=kafka1:9092 \ + --set mini_runtime.externalKafka.username=user \ + --set mini_runtime.externalKafka.password=pass \ + --set mini_runtime.externalKafka.saslMechanism=SCRAM-SHA-256 \ + --set mini_runtime.externalKafka.useSsl=true \ + --set mini_runtime.externalKafka.securityProtocol=SASL_SSL + +echo "" +echo "=== helm lint: port override ===" +helm lint "$CHART_DIR" \ + --set kafkaCluster.ports.plain=9094 + +echo "" +echo "=== helm unittest ===" +helm unittest "$CHART_DIR" + +echo "" +echo "All tests passed." diff --git a/charts/mini-runtime/tests/sasl_test.yaml b/charts/mini-runtime/tests/sasl_test.yaml new file mode 100644 index 00000000..4f170464 --- /dev/null +++ b/charts/mini-runtime/tests/sasl_test.yaml @@ -0,0 +1,209 @@ +suite: SASL / SCRAM Authentication +templates: + - templates/kafka-cluster.yaml + - templates/deployment.yaml + +tests: + # ── KafkaUser rendered with scram-sha-512 ───────────────────────────────── + - it: renders KafkaUser with scram-sha-512 when sasl enabled + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.mechanism: SCRAM-SHA-512 + kafkaCluster.sasl.username: akto-runtime + kafkaCluster.sasl.password: mysecret + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: KafkaUser + skipEmptyTemplates: true + asserts: + - isKind: + of: KafkaUser + - equal: + path: spec.authentication.type + value: scram-sha-512 + + # ── KafkaUser rendered with scram-sha-256 ───────────────────────────────── + - it: renders KafkaUser with scram-sha-256 when mechanism is SCRAM-SHA-256 + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.mechanism: SCRAM-SHA-256 + kafkaCluster.sasl.username: akto-runtime + kafkaCluster.sasl.password: mysecret + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: KafkaUser + skipEmptyTemplates: true + asserts: + - isKind: + of: KafkaUser + - equal: + path: spec.authentication.type + value: scram-sha-256 + + # ── Kafka listener has authentication block when sasl enabled ────────────── + - it: adds authentication to plain listener when sasl enabled + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.mechanism: SCRAM-SHA-512 + kafkaCluster.sasl.password: mysecret + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.listeners[0].authentication.type + value: scram-sha-512 + + # ── No authentication block on listener when sasl disabled (default) ─────── + - it: does not add authentication to listener when sasl disabled + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - notExists: + path: spec.kafka.listeners[0].authentication + + # ── SASL credentials Secret created ─────────────────────────────────────── + - it: creates sasl-credentials Secret when sasl enabled and not useExistingSecret + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.username: akto-runtime + kafkaCluster.sasl.password: mysecret + template: templates/kafka-cluster.yaml + documentSelector: + path: metadata.name + value: akto-kafka-sasl-credentials + skipEmptyTemplates: true + asserts: + - isKind: + of: Secret + - equal: + path: metadata.name + value: akto-kafka-sasl-credentials + + # ── KafkaUser references existingSecret when useExistingSecret true ──────── + - it: KafkaUser references existingSecret when useExistingSecret is true + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.useExistingSecret: true + kafkaCluster.sasl.existingSecret: my-sasl-secret + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: KafkaUser + skipEmptyTemplates: true + asserts: + - equal: + path: spec.authentication.password.valueFrom.secretKeyRef.name + value: my-sasl-secret + + # ── Runtime: KAFKA_AUTH_ENABLED injected ────────────────────────────────── + - it: injects KAFKA_AUTH_ENABLED and SASL env vars into runtime container + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.mechanism: SCRAM-SHA-512 + kafkaCluster.sasl.password: mysecret + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_AUTH_ENABLED + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SASL_ENABLED + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SASL_MECHANISM + value: SCRAM-SHA-512 + + # ── Runtime reads KAFKA_USERNAME from secret ────────────────────────────── + - it: runtime container reads KAFKA_USERNAME from sasl-credentials secret + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.password: mysecret + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_USERNAME + valueFrom: + secretKeyRef: + name: akto-kafka-sasl-credentials + key: username + + # ── Threat client: SASL env vars injected ───────────────────────────────── + - it: injects SASL env vars into threat client container + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.mechanism: SCRAM-SHA-512 + kafkaCluster.sasl.password: mysecret + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SASL_ENABLED + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_AUTH_ENABLED + value: "true" + + # ── Threat client reads AKTO_KAFKA_USERNAME from secret ─────────────────── + - it: threat client reads AKTO_KAFKA_USERNAME from sasl-credentials secret + set: + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.password: mysecret + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_USERNAME + valueFrom: + secretKeyRef: + name: akto-kafka-sasl-credentials + key: username + + # ── No SASL env vars when sasl disabled (default) ───────────────────────── + - it: does not inject SASL env vars when sasl disabled + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_AUTH_ENABLED + value: "true" diff --git a/charts/mini-runtime/tests/service_annotations_test.yaml b/charts/mini-runtime/tests/service_annotations_test.yaml new file mode 100644 index 00000000..e4df6e9b --- /dev/null +++ b/charts/mini-runtime/tests/service_annotations_test.yaml @@ -0,0 +1,55 @@ +suite: Service Annotations +templates: + - templates/kafka-cluster.yaml + +tests: + # ── template block rendered with annotations on both services ───────────── + - it: renders spec.kafka.template with annotations on bootstrap and brokers services + set: + kafkaCluster.serviceAnnotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" + service.beta.kubernetes.io/aws-load-balancer-type: nlb + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.template.bootstrapService.metadata.annotations["service.beta.kubernetes.io/aws-load-balancer-internal"] + value: "true" + - equal: + path: spec.kafka.template.bootstrapService.metadata.annotations["service.beta.kubernetes.io/aws-load-balancer-type"] + value: nlb + - equal: + path: spec.kafka.template.brokersService.metadata.annotations["service.beta.kubernetes.io/aws-load-balancer-internal"] + value: "true" + + # ── No template block when serviceAnnotations empty (default) ───────────── + - it: does not render spec.kafka.template block when serviceAnnotations is empty + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - notExists: + path: spec.kafka.template + + # ── Single annotation forwarded to both services ────────────────────────── + - it: forwards a single annotation to both bootstrapService and brokersService + set: + kafkaCluster.serviceAnnotations: + custom.io/annotation: my-value + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.template.bootstrapService.metadata.annotations["custom.io/annotation"] + value: my-value + - equal: + path: spec.kafka.template.brokersService.metadata.annotations["custom.io/annotation"] + value: my-value diff --git a/charts/mini-runtime/tests/tls_test.yaml b/charts/mini-runtime/tests/tls_test.yaml new file mode 100644 index 00000000..c9bd7471 --- /dev/null +++ b/charts/mini-runtime/tests/tls_test.yaml @@ -0,0 +1,181 @@ +suite: TLS +templates: + - templates/kafka-cluster.yaml + - templates/deployment.yaml + +tests: + # ── TLS listener added to Kafka CR ──────────────────────────────────────── + - it: adds tls listener to Kafka CR when tls enabled + set: + kafkaCluster.tls: true + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - contains: + path: spec.kafka.listeners + content: + name: tls + port: 9093 + type: internal + tls: true + + # ── No TLS listener by default ──────────────────────────────────────────── + - it: does not add tls listener when tls disabled (default) + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.listeners + value: + - name: plain + port: 9092 + type: internal + tls: false + + # ── TLS listener has SASL authentication when both enabled ──────────────── + - it: tls listener has authentication when sasl and tls both enabled + set: + kafkaCluster.tls: true + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.mechanism: SCRAM-SHA-512 + kafkaCluster.sasl.password: secret + template: templates/kafka-cluster.yaml + documentSelector: + path: kind + value: Kafka + skipEmptyTemplates: true + asserts: + - equal: + path: spec.kafka.listeners[1].authentication.type + value: scram-sha-512 + + # ── Runtime: TLS env vars injected ──────────────────────────────────────── + - it: injects TLS env vars into runtime container when tls enabled + set: + kafkaCluster.tls: true + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_TLS_ENABLED + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_SSL_TRUSTSTORE_LOCATION + value: /etc/kafka/tls/ca.crt + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SECURITY_PROTOCOL + value: SSL + + # ── SASL_SSL when both sasl and tls enabled ─────────────────────────────── + - it: sets AKTO_KAFKA_SECURITY_PROTOCOL to SASL_SSL when sasl and tls both enabled + set: + kafkaCluster.tls: true + kafkaCluster.sasl.enabled: true + kafkaCluster.sasl.password: secret + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_SECURITY_PROTOCOL + value: SASL_SSL + + # ── Runtime: CA cert volume and mount when tls enabled ──────────────────── + - it: mounts kafka-tls-ca volume in runtime container when tls enabled + set: + kafkaCluster.tls: true + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: kafka-tls-ca + mountPath: /etc/kafka/tls + readOnly: true + - contains: + path: spec.template.spec.volumes + content: + name: kafka-tls-ca + secret: + secretName: akto-kafka-cluster-ca-cert + + # ── Runtime: no TLS env var when tls disabled ───────────────────────────── + - it: does not inject TLS env vars when tls disabled + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-mini-runtime + skipEmptyTemplates: true + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_TLS_ENABLED + value: "true" + + # ── Threat client: TLS env vars injected ────────────────────────────────── + - it: injects TLS env vars into threat client container when tls enabled + set: + kafkaCluster.tls: true + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AKTO_KAFKA_TLS_ENABLED + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: KAFKA_SSL_TRUSTSTORE_LOCATION + value: /etc/kafka/tls/ca.crt + + # ── Threat client: CA cert volume mounted ───────────────────────────────── + - it: mounts kafka-tls-ca volume in threat client when tls enabled + set: + kafkaCluster.tls: true + template: templates/deployment.yaml + documentSelector: + path: metadata.name + value: RELEASE-NAME-threat-client + skipEmptyTemplates: true + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: kafka-tls-ca + mountPath: /etc/kafka/tls + readOnly: true + - contains: + path: spec.template.spec.volumes + content: + name: kafka-tls-ca + secret: + secretName: akto-kafka-cluster-ca-cert diff --git a/charts/mini-runtime/values.yaml b/charts/mini-runtime/values.yaml index 3c3980a3..baeafa42 100644 --- a/charts/mini-runtime/values.yaml +++ b/charts/mini-runtime/values.yaml @@ -3,12 +3,6 @@ mongo: kubernetesClusterDomain: cluster.local -# Busybox image for init containers -busybox: - image: - repository: busybox - digest: sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f - # Node scheduling configuration nodeSelector: {} # Example usage: @@ -34,8 +28,7 @@ affinity: {} mini_runtime: # External Kafka Configuration - # Set useExternalKafka to true to use an existing Kafka cluster - # When enabled, the internal Kafka container will not be deployed + # Set useExternalKafka to true to use an existing Kafka cluster outside of this chart useExternalKafka: false externalKafka: # Kafka broker URL (e.g., kafka.example.com:9092 or kafka1:9092,kafka2:9092,kafka3:9092) @@ -56,8 +49,9 @@ mini_runtime: aktoAccountName: Helios aktoConfigName: staging aktoInstanceType: DASHBOARD - aktoKafkaBrokerMal: localhost:29092 - aktoKafkaBrokerUrl: 127.0.0.1:29092 + # Broker URL used when useExternalKafka: true (ignored when kafkaCluster.enabled: true) + aktoKafkaBrokerMal: "" + aktoKafkaBrokerUrl: "" aktoKafkaGroupIdConfig: asdf aktoKafkaMaxPollRecordsConfig: "100" aktoKafkaTopicName: akto.api.logs @@ -75,14 +69,14 @@ mini_runtime: # If not using secrets, place token here. databaseAbstractorToken: "" # If using secrets, place token here. - databaseAbstractorTokenSecrets: + databaseAbstractorTokenSecrets: # Name of an existing secret containing the Database Abstractor Token existingSecret: "" # Token to use if existingSecret is not provided token: "" image: - repository: public.ecr.aws/aktosecurity/akto-api-security-mini-runtime - digest: sha256:f2aec6045c82ab6f4c1ca22748c09e5a346ec9f0d4ce5c908704fd35d4eed6f2 + repository: aktosecurity/mini-runtime + tag: a-feat-mr-kafka-scram imagePullPolicy: Always resources: requests: @@ -91,71 +85,89 @@ mini_runtime: limits: cpu: 2 memory: "4Gi" - kafka1: - env: - # add advertised listeners if you want to override the default listeners - kafkaAdvertisedListeners: "" - kafkaBrokerId: "1" - kafkaCleanupPolicy: delete - kafkaCreateTopics: akto.api.logs:3:3 - kafkaInterBrokerListenerName: LISTENER_DOCKER_EXTERNAL_LOCALHOST - kafkaListenerSecurityProtocolMap: CONTROLLER:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_LOCALHOST:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_DIFFHOST:PLAINTEXT - kafkaListenerSecurityProtocolMapSsl: CONTROLLER:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_LOCALHOST:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_DIFFHOST:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_DIFFHOST_ENCRYPTED:SSL - kafkaListenerSecurityProtocolMapSasl: CONTROLLER:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL:SASL_PLAINTEXT,LISTENER_DOCKER_EXTERNAL_LOCALHOST_SASL:SASL_PLAINTEXT,LISTENER_DOCKER_EXTERNAL_LOCALHOST:SASL_PLAINTEXT,LISTENER_DOCKER_EXTERNAL_DIFFHOST:SASL_PLAINTEXT - kafkaListenerSecurityProtocolMapSaslTls: CONTROLLER:PLAINTEXT,LISTENER_DOCKER_EXTERNAL_DIFFHOST_SASL:SASL_SSL,LISTENER_DOCKER_EXTERNAL_LOCALHOST_SASL:SASL_PLAINTEXT - kafkaProcessRoles: "broker,controller" - kafkaNodeId: "1" - kafkaControllerQuorumVoters: "1@localhost:9094" - kafkaControllerListenerNames: "CONTROLLER" - kafkaListeners: "" - kafkaLogCleanerEnable: "true" - kafkaLogRetentionBytes: "10737418240" - kafkaLogRetentionCheckIntervalMs: "60000" - kafkaLogRetentionHours: "5" - kafkaLogSegmentBytes: "104857600" - kafkaOffsetsTopicReplicationFactor: "1" - kafkaTransactionStateLogMinIsr: "1" - kafkaTransactionStateLogReplicationFactor: "1" - kafkaClusterId: "c6a1b8e2-4f2a-4b2a-9c3f-1a2b3c4d5e6f" - sslKeystoreLocation: "/etc/kafka/ssl/secrets/server.keystore.jks" - sslKeystorePassword: "password" - sslKeyPassword: "password" - sslTruststoreLocation: "/etc/kafka/ssl/secrets/server.truststore.jks" - sslTruststorePassword: "password" - sslBaseMountPath: "/etc/kafka/ssl/secrets" - sslSecretName: "kafka-certs" - useSecretsForSaslCredentials: false - saslCredentialsSecrets: - existingSecret: "" - username: "" - password: "" - saslUsername: "" - saslPassword: "" - # SASL mechanism: PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512 - saslMechanism: "SCRAM-SHA-512" - useTls: false - useSasl: false - # Auto-format storage with SCRAM on first deployment (single helm install) - # When enabled, Kafka will format storage with SCRAM user on first run - # Requires persistence.enabled: true - autoFormatWithScram: false - image: - repository: public.ecr.aws/aktosecurity/confluentinc-cp-kafka - digest: sha256:d20bd62f01826c454e88530efc6d73654cb4697182f37206742b96c7b947236e + + replicas: 1 + +# Strimzi-based Kafka cluster (default mode) +# Set mini_runtime.useExternalKafka: true to disable and use your own Kafka instead +kafkaCluster: + enabled: true + clusterName: "akto-kafka" # used in labels and bootstrap DNS + + broker: + replicas: 1 # increase to 3 for HA (also bump replication factors below) resources: requests: - cpu: 1.5 + cpu: "1.5" memory: "4Gi" - ephemeral-storage: "500Mi" limits: - cpu: 1.5 + cpu: "1.5" memory: "4Gi" - ephemeral-storage: "11Gi" + storage: + size: "10Gi" + storageClass: "" # leave empty for default StorageClass + deleteClaim: true - replicas: 1 - type: ClusterIP - annotations: - service.beta.kubernetes.io/aws-load-balancer-internal: "true" + controller: + replicas: 1 + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "500m" + memory: "1Gi" + storage: + size: "10Gi" + storageClass: "" + deleteClaim: true + + config: + retentionMs: "604800000" # 7 days + retentionBytes: "5368709120" # 5GB per partition + cleanupPolicy: "delete" + # set to 3 when broker.replicas: 3 + defaultReplicationFactor: 1 + minInsyncReplicas: 1 + offsetsTopicReplicationFactor: 1 + transactionStateLogReplicationFactor: 1 + transactionStateLogMinIsr: 1 + + # Listener port overrides — also controls the bootstrap URL injected into runtime/threat-client + ports: + plain: 9092 # plain (or SASL) listener port + tls: 9093 # TLS listener port (only used when tls: true) + + tls: false # enable TLS listener; Strimzi auto-generates broker certs via cluster CA + # CA cert stored in Secret: -cluster-ca-cert + + sasl: + enabled: false + mechanism: "SCRAM-SHA-512" # don't change is using akto-kafka + username: "akto-runtime" + password: "" + useExistingSecret: false + existingSecret: "" # existing Secret name; must have keys: username, password + + # Annotations forwarded to Strimzi-generated Kafka Services via spec.kafka.template + # Replaces the old mini_runtime.serviceAnnotations / mini_runtime.annotations + serviceAnnotations: {} + # Example: + # service.beta.kubernetes.io/aws-load-balancer-internal: "true" + + topics: + - name: "akto.api.logs" + partitions: 1 # increase to 3 when broker.replicas: 3 + replicas: 1 # increase to 3 when broker.replicas: 3 + - name: "akto.api.logs2" + partitions: 1 + replicas: 1 + - name: "akto.api.producer.logs" + partitions: 1 + replicas: 1 + - name: "akto.daemonset.producer.heartbeats" + partitions: 1 + replicas: 1 keel: keel: @@ -196,25 +208,6 @@ keel: annotations: {} type: LoadBalancer -ports: - default: - - port: 9092 # Over-ride this to change kafka service exposed port - targetPort: 9092 - tls: - - name: plaintext - port: 9092 - targetPort: 9092 - - name: ssl - port: 9093 - targetPort: 9093 - sasl: - - name: sasl-plaintext - port: 9092 - targetPort: 9092 - - name: sasl-ssl - port: 9093 - targetPort: 9093 - threat_client: replicas: 1 aktoApiSecurityThreatClient: @@ -231,8 +224,8 @@ threat_client: aktoLogLevel: INFO useHostName: true image: - repository: public.ecr.aws/aktosecurity/akto-threat-detection - digest: sha256:1bd73a6606258800324bf96bd3425e371506ebe058efd34962b690e0400436a4 + repository: aktosecurity/akto-threat-detection + tag: a-fix-threat-use-scram-sasl imagePullPolicy: Always resources: requests: @@ -261,7 +254,7 @@ redis: fluent_bit: enabled: false - + image: repository: public.ecr.aws/aktosecurity/akto-logging-sidecar digest: sha256:7d727245767ae632eb296c2ff4d206bf2e205b5f244c1f37b8fdd61f9fb33985 @@ -278,7 +271,7 @@ fluent_bit: flush: 5 metricsPort: 2020 logLevel: info - # default values + # default values logStorage: sizeLimit: "1Gi" stateStorage: diff --git a/kafka-cluster/kafka-persistent.yaml b/kafka-cluster/kafka-persistent.yaml new file mode 100644 index 00000000..a8711e2c --- /dev/null +++ b/kafka-cluster/kafka-persistent.yaml @@ -0,0 +1,113 @@ +apiVersion: kafka.strimzi.io/v1 +kind: KafkaNodePool +metadata: + name: controller + labels: + strimzi.io/cluster: my-cluster +spec: + replicas: 1 + roles: + - controller + storage: + type: jbod + volumes: + - id: 0 + type: persistent-claim + size: 10Gi + kraftMetadata: shared + deleteClaim: true +--- + +apiVersion: kafka.strimzi.io/v1 +kind: KafkaNodePool +metadata: + name: broker + labels: + strimzi.io/cluster: my-cluster +spec: + replicas: 3 + roles: + - broker + storage: + type: jbod + volumes: + - id: 0 + type: persistent-claim + size: 10Gi + kraftMetadata: shared + deleteClaim: true +--- + +apiVersion: kafka.strimzi.io/v1 +kind: Kafka +metadata: + name: my-cluster +spec: + kafka: + version: 4.1.1 + metadataVersion: 4.1-IV1 + listeners: + - name: plain + port: 9092 + type: internal + tls: false + - name: tls + port: 9093 + type: internal + tls: true + config: + offsets.topic.replication.factor: 3 + transaction.state.log.replication.factor: 3 + transaction.state.log.min.isr: 2 + default.replication.factor: 3 + min.insync.replicas: 2 + log.retention.ms: 604800000 + log.retention.bytes: 5368709120 + log.cleanup.policy: delete + entityOperator: + topicOperator: {} + userOperator: {} +--- + +apiVersion: kafka.strimzi.io/v1 +kind: KafkaTopic +metadata: + name: akto.api.logs + labels: + strimzi.io/cluster: my-cluster +spec: + partitions: 3 + replicas: 3 +--- + +apiVersion: kafka.strimzi.io/v1 +kind: KafkaTopic +metadata: + name: akto.api.logs2 + labels: + strimzi.io/cluster: my-cluster +spec: + partitions: 3 + replicas: 3 +--- + +apiVersion: kafka.strimzi.io/v1 +kind: KafkaTopic +metadata: + name: akto.api.producer.logs + labels: + strimzi.io/cluster: my-cluster +spec: + partitions: 3 + replicas: 3 +--- + +apiVersion: kafka.strimzi.io/v1 +kind: KafkaTopic +metadata: + name: akto.daemonset.producer.heartbeats + labels: + strimzi.io/cluster: my-cluster +spec: + partitions: 3 + replicas: 3 \ No newline at end of file diff --git a/kafka-cluster/strimzi.sh b/kafka-cluster/strimzi.sh new file mode 100644 index 00000000..375d726b --- /dev/null +++ b/kafka-cluster/strimzi.sh @@ -0,0 +1 @@ +kubectl create -f 'https://strimzi.io/install/latest?namespace=default'