diff --git a/docs/self-hosting/kubernetes.mdx b/docs/self-hosting/kubernetes.mdx index e2498c56ca..8932607ec8 100644 --- a/docs/self-hosting/kubernetes.mdx +++ b/docs/self-hosting/kubernetes.mdx @@ -198,19 +198,152 @@ webapp: ### External services -You can disable the built-in services and use external services instead. For example: +You can disable the built-in services and use external services instead. The chart supports both direct configuration and existing Kubernetes secrets for secure credential management. +#### PostgreSQL + +**Direct configuration:** +```yaml +postgres: + deploy: false + external: + databaseUrl: "postgresql://user:password@host:5432/database?schema=public" + directUrl: "" # Optional, defaults to databaseUrl +``` + +**Using existing secrets (recommended):** ```yaml postgres: deploy: false external: - host: "my-postgres.example.com" - port: 5432 - database: "my-database" + existingSecret: "postgres-credentials" + # Optional: Use secretKeys to specify the key names in the secret + # secretKeys: + # databaseUrlKey: "postgres-database-url" # default + # directUrlKey: "postgres-direct-url" # default +``` + +#### Redis + +**Direct configuration:** +```yaml +redis: + deploy: false + external: + host: "my-redis.example.com" + port: 6379 + password: "my-password" + tls: + enabled: true +``` + +**Using existing secrets (recommended):** +```yaml +redis: + deploy: false + external: + host: "my-redis.example.com" + port: 6379 + existingSecret: "redis-credentials" + # existingSecretPasswordKey: "redis-password" # default (optional) + tls: + enabled: true +``` + +#### ClickHouse + +**Direct configuration:** +```yaml +clickhouse: + deploy: false + external: + host: "my-clickhouse.example.com" + port: 8123 username: "my-username" password: "my-password" ``` +**Using existing secrets (recommended):** +```yaml +clickhouse: + deploy: false + external: + host: "my-clickhouse.example.com" + port: 8123 + username: "my-username" + existingSecret: "clickhouse-credentials" + # existingSecretKey: "clickhouse-password" # default (optional) +``` + +#### S3 Object Storage + +**Direct configuration:** +```yaml +minio: + deploy: false +s3: + external: + endpoint: "https://s3.amazonaws.com" + accessKeyId: "my-access-key" + secretAccessKey: "my-secret-key" +``` + +**Using existing secrets (recommended):** +```yaml +minio: + deploy: false +s3: + external: + endpoint: "https://s3.amazonaws.com" + existingSecret: "s3-credentials" + # Optional: Use secretKeys to specify the key names in the secret + # secretKeys: + # accessKeyIdKey: "access-key-id" # default + # secretAccessKeyKey: "secret-access-key" # default +``` + +### PostgreSQL SSL with custom CA certificates + +When connecting to PostgreSQL instances that require custom CA certificates (such as AWS RDS with SSL verification), you can mount the CA certificate as a volume and configure the webapp to use it: + +```yaml +postgres: + deploy: false + external: + databaseUrl: "postgresql://user:password@mydb.example.com:5432/triggerdb?schema=public&sslmode=require" + # Alternatively, use an existing secret + existingSecret: "postgres-credentials" + # secretKeys: + # databaseUrlKey: "postgres-database-url" # default + connection: + sslMode: "require" + +# Webapp configuration with SSL CA certificate +webapp: + extraEnvVars: + - name: NODE_EXTRA_CA_CERTS + value: "/etc/ssl/certs/postgres-ca.crt" + + extraVolumes: + - name: postgres-ca-cert + secret: + secretName: postgres-ca-secret + items: + - key: ca.crt + path: postgres-ca.crt + + extraVolumeMounts: + - name: postgres-ca-cert + mountPath: /etc/ssl/certs + readOnly: true +``` + +**Benefits:** +- No plaintext credentials in `values.yaml` or Helm releases +- Complete `DATABASE_URL` stored securely in Kubernetes secrets +- Compatible with secret management tools (External Secrets Operator, etc.) +- Follows Kubernetes security best practices + ## Worker token When using the default bootstrap configuration, worker creation and authentication is handled automatically. The webapp generates a worker token and makes it available to the supervisor via a shared volume. diff --git a/hosting/k8s/helm/Chart.yaml b/hosting/k8s/helm/Chart.yaml index 4975353390..a0b80a62c0 100644 --- a/hosting/k8s/helm/Chart.yaml +++ b/hosting/k8s/helm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: trigger description: The official Trigger.dev Helm chart type: application -version: 4.0.0-beta.16 +version: 4.0.0-beta.17 appVersion: v4.0.0-v4-beta.22 home: https://trigger.dev sources: diff --git a/hosting/k8s/helm/templates/_helpers.tpl b/hosting/k8s/helm/templates/_helpers.tpl index ebd5890587..6d2f5fdb57 100644 --- a/hosting/k8s/helm/templates/_helpers.tpl +++ b/hosting/k8s/helm/templates/_helpers.tpl @@ -96,33 +96,40 @@ Get the full image name for supervisor {{- end }} {{/* -PostgreSQL hostname +PostgreSQL hostname (deprecated - used only for legacy DATABASE_HOST env var) */}} {{- define "trigger-v4.postgres.hostname" -}} -{{- if .Values.postgres.host }} -{{- .Values.postgres.host }} -{{- else if .Values.postgres.deploy }} +{{- if .Values.postgres.deploy }} {{- printf "%s-postgres" .Release.Name }} +{{- else }} +{{- "external-postgres" }} {{- end }} {{- end }} {{/* -PostgreSQL connection string +PostgreSQL connection string (fallback when not using secrets) */}} {{- define "trigger-v4.postgres.connectionString" -}} -{{- if .Values.postgres.host -}} -postgresql://{{ .Values.postgres.username }}:{{ .Values.postgres.password }}@{{ .Values.postgres.host }}:{{ .Values.postgres.port | default 5432 }}/{{ .Values.postgres.database }}?schema={{ .Values.postgres.schema | default "public" }}&sslmode={{ .Values.postgres.sslMode | default "prefer" }} +{{- if .Values.postgres.external.databaseUrl -}} +{{ .Values.postgres.external.databaseUrl }} {{- else if .Values.postgres.deploy -}} postgresql://{{ .Values.postgres.auth.username }}:{{ .Values.postgres.auth.password }}@{{ include "trigger-v4.postgres.hostname" . }}:5432/{{ .Values.postgres.auth.database }}?schema={{ .Values.postgres.connection.schema | default "public" }}&sslmode={{ .Values.postgres.connection.sslMode | default "prefer" }} {{- end -}} {{- end }} +{{/* +Check if we should use DATABASE_URL from secret +*/}} +{{- define "trigger-v4.postgres.useSecretUrl" -}} +{{- and .Values.postgres.external.existingSecret -}} +{{- end }} + {{/* Redis hostname */}} {{- define "trigger-v4.redis.hostname" -}} -{{- if .Values.redis.host }} -{{- .Values.redis.host }} +{{- if .Values.redis.external.host }} +{{- .Values.redis.external.host }} {{- else if .Values.redis.deploy }} {{- printf "%s-redis-master" .Release.Name }} {{- end }} @@ -136,13 +143,211 @@ Redis connection details {{- end }} {{- define "trigger-v4.redis.port" -}} -{{- if .Values.redis.host -}} -{{ .Values.redis.port | default 6379 }} +{{- if .Values.redis.external.host -}} +{{ .Values.redis.external.port | default 6379 }} {{- else if .Values.redis.deploy -}} 6379 {{- end -}} {{- end }} +{{/* +Redis password +*/}} +{{- define "trigger-v4.redis.password" -}} +{{- if .Values.redis.external.host -}} +{{ .Values.redis.external.password }} +{{- else if .Values.redis.deploy -}} +{{ .Values.redis.auth.password }} +{{- end -}} +{{- end }} + +{{/* +Redis TLS disabled setting +*/}} +{{- define "trigger-v4.redis.tlsDisabled" -}} +{{- if .Values.redis.external.host -}} +{{ not (.Values.redis.external.tls.enabled | default false) }} +{{- else -}} +{{- true -}} +{{- end -}} +{{- end }} + +{{/* +PostgreSQL external secret name +*/}} +{{- define "trigger-v4.postgres.external.secretName" -}} +{{- if .Values.postgres.external.existingSecret -}} +{{ .Values.postgres.external.existingSecret }} +{{- else -}} +{{ include "trigger-v4.secretsName" . }} +{{- end -}} +{{- end }} + +{{/* +PostgreSQL external secret database URL key +*/}} +{{- define "trigger-v4.postgres.external.databaseUrlKey" -}} +{{- if .Values.postgres.external.existingSecret -}} +{{ .Values.postgres.external.secretKeys.databaseUrlKey }} +{{- else -}} +postgres-database-url +{{- end -}} +{{- end }} + +{{/* +PostgreSQL external secret direct URL key +*/}} +{{- define "trigger-v4.postgres.external.directUrlKey" -}} +{{- if .Values.postgres.external.existingSecret -}} +{{ .Values.postgres.external.secretKeys.directUrlKey | default .Values.postgres.external.secretKeys.databaseUrlKey }} +{{- else -}} +postgres-direct-url +{{- end -}} +{{- end }} + +{{/* +PostgreSQL direct URL (fallback to database URL if not set) +*/}} +{{- define "trigger-v4.postgres.directUrl" -}} +{{- if .Values.postgres.external.directUrl -}} +{{ .Values.postgres.external.directUrl }} +{{- else -}} +{{ include "trigger-v4.postgres.connectionString" . }} +{{- end -}} +{{- end }} + +{{/* +Redis external secret name +*/}} +{{- define "trigger-v4.redis.external.secretName" -}} +{{- if .Values.redis.external.existingSecret -}} +{{ .Values.redis.external.existingSecret }} +{{- else -}} +{{ include "trigger-v4.secretsName" . }} +{{- end -}} +{{- end }} + +{{/* +Redis external secret password key +*/}} +{{- define "trigger-v4.redis.external.passwordKey" -}} +{{- if .Values.redis.external.existingSecret -}} +{{ .Values.redis.external.existingSecretPasswordKey }} +{{- else -}} +redis-password +{{- end -}} +{{- end }} + +{{/* +ClickHouse external secret name +*/}} +{{- define "trigger-v4.clickhouse.external.secretName" -}} +{{- if .Values.clickhouse.external.existingSecret -}} +{{ .Values.clickhouse.external.existingSecret }} +{{- else -}} +{{ include "trigger-v4.secretsName" . }} +{{- end -}} +{{- end }} + +{{/* +ClickHouse external secret password key +*/}} +{{- define "trigger-v4.clickhouse.external.passwordKey" -}} +{{- if .Values.clickhouse.external.existingSecret -}} +{{ .Values.clickhouse.external.existingSecretKey }} +{{- else -}} +clickhouse-password +{{- end -}} +{{- end }} + +{{/* +S3 external secret name +*/}} +{{- define "trigger-v4.s3.external.secretName" -}} +{{- if .Values.s3.external.existingSecret -}} +{{ .Values.s3.external.existingSecret }} +{{- else -}} +{{ include "trigger-v4.secretsName" . }} +{{- end -}} +{{- end }} + +{{/* +S3 external secret access key ID key +*/}} +{{- define "trigger-v4.s3.external.accessKeyIdKey" -}} +{{- if .Values.s3.external.existingSecret -}} +{{ .Values.s3.external.existingSecretAccessKeyIdKey }} +{{- else -}} +s3-access-key-id +{{- end -}} +{{- end }} + +{{/* +S3 external secret secret access key key +*/}} +{{- define "trigger-v4.s3.external.secretAccessKeyKey" -}} +{{- if .Values.s3.external.existingSecret -}} +{{ .Values.s3.external.existingSecretSecretAccessKeyKey }} +{{- else -}} +s3-secret-access-key +{{- end -}} +{{- end }} + +{{/* +S3 auth secret name +*/}} +{{- define "trigger-v4.s3.auth.secretName" -}} +{{- if .Values.s3.auth.existingSecret -}} +{{ .Values.s3.auth.existingSecret }} +{{- else -}} +{{ include "trigger-v4.secretsName" . }} +{{- end -}} +{{- end }} + +{{/* +S3 auth secret access key ID key +*/}} +{{- define "trigger-v4.s3.auth.accessKeyIdKey" -}} +{{- if .Values.s3.auth.existingSecret -}} +{{ .Values.s3.auth.accessKeyIdSecretKey }} +{{- else -}} +s3-auth-access-key-id +{{- end -}} +{{- end }} + +{{/* +S3 auth secret secret access key key +*/}} +{{- define "trigger-v4.s3.auth.secretAccessKeyKey" -}} +{{- if .Values.s3.auth.existingSecret -}} +{{ .Values.s3.auth.secretAccessKeySecretKey }} +{{- else -}} +s3-auth-secret-access-key +{{- end -}} +{{- end }} + +{{/* +S3 auth effective access key ID (with fallback to rootUser) +*/}} +{{- define "trigger-v4.s3.auth.effectiveAccessKeyId" -}} +{{- if .Values.s3.auth.accessKeyId -}} +{{ .Values.s3.auth.accessKeyId }} +{{- else -}} +{{ .Values.s3.auth.rootUser }} +{{- end -}} +{{- end }} + +{{/* +S3 auth effective secret access key (with fallback to rootPassword) +*/}} +{{- define "trigger-v4.s3.auth.effectiveSecretAccessKey" -}} +{{- if .Values.s3.auth.secretAccessKey -}} +{{ .Values.s3.auth.secretAccessKey }} +{{- else -}} +{{ .Values.s3.auth.rootPassword }} +{{- end -}} +{{- end }} + {{/* Electric service URL */}} @@ -176,8 +381,12 @@ ClickHouse URL for application (with secure parameter) {{- else if .Values.clickhouse.external.host -}} {{- $protocol := ternary "https" "http" .Values.clickhouse.external.secure -}} {{- $secure := ternary "true" "false" .Values.clickhouse.external.secure -}} +{{- if .Values.clickhouse.external.existingSecret -}} +{{ $protocol }}://{{ .Values.clickhouse.external.username }}:${CLICKHOUSE_PASSWORD}@{{ .Values.clickhouse.external.host }}:{{ .Values.clickhouse.external.httpPort | default 8123 }}?secure={{ $secure }} +{{- else -}} {{ $protocol }}://{{ .Values.clickhouse.external.username }}:{{ .Values.clickhouse.external.password }}@{{ .Values.clickhouse.external.host }}:{{ .Values.clickhouse.external.httpPort | default 8123 }}?secure={{ $secure }} {{- end -}} +{{- end -}} {{- end }} {{/* @@ -189,8 +398,12 @@ ClickHouse URL for replication (without secure parameter) {{ $protocol }}://{{ .Values.clickhouse.auth.username }}:{{ .Values.clickhouse.auth.password }}@{{ include "trigger-v4.clickhouse.hostname" . }}:8123 {{- else if .Values.clickhouse.external.host -}} {{- $protocol := ternary "https" "http" .Values.clickhouse.external.secure -}} +{{- if .Values.clickhouse.external.existingSecret -}} +{{ $protocol }}://{{ .Values.clickhouse.external.username }}:${CLICKHOUSE_PASSWORD}@{{ .Values.clickhouse.external.host }}:{{ .Values.clickhouse.external.httpPort | default 8123 }} +{{- else -}} {{ $protocol }}://{{ .Values.clickhouse.external.username }}:{{ .Values.clickhouse.external.password }}@{{ .Values.clickhouse.external.host }}:{{ .Values.clickhouse.external.httpPort | default 8123 }} {{- end -}} +{{- end -}} {{- end }} {{/* @@ -244,14 +457,27 @@ Registry connection details {{- end -}} {{- end }} +{{/* +Webapp connectivity check enabled +*/}} +{{- define "trigger-v4.webapp.connectivityCheckEnabled" -}} +{{- $connectivityCheckEnabled := true -}} +{{- if hasKey .Values.webapp "connectivityCheck" -}} +{{- if hasKey .Values.webapp.connectivityCheck "postgres" -}} +{{- $connectivityCheckEnabled = .Values.webapp.connectivityCheck.postgres -}} +{{- end -}} +{{- end -}} +{{- $connectivityCheckEnabled -}} +{{- end }} + {{/* PostgreSQL host (for wait-for-it script) */}} {{- define "trigger-v4.postgres.host" -}} -{{- if .Values.postgres.host -}} -{{ .Values.postgres.host }}:{{ .Values.postgres.port | default 5432 }} -{{- else if .Values.postgres.deploy -}} +{{- if .Values.postgres.deploy -}} {{ include "trigger-v4.postgres.hostname" . }}:5432 +{{- else if .Values.postgres.external.connectivityCheck.host -}} +{{ .Values.postgres.external.connectivityCheck.host }} {{- end -}} {{- end }} diff --git a/hosting/k8s/helm/templates/secrets.yaml b/hosting/k8s/helm/templates/secrets.yaml index 3f88eaab99..c122f89d0b 100644 --- a/hosting/k8s/helm/templates/secrets.yaml +++ b/hosting/k8s/helm/templates/secrets.yaml @@ -11,8 +11,28 @@ data: MAGIC_LINK_SECRET: {{ .Values.secrets.magicLinkSecret | b64enc | quote }} ENCRYPTION_KEY: {{ .Values.secrets.encryptionKey | b64enc | quote }} MANAGED_WORKER_SECRET: {{ .Values.secrets.managedWorkerSecret | b64enc | quote }} - OBJECT_STORE_ACCESS_KEY_ID: {{ .Values.secrets.objectStore.accessKeyId | b64enc | quote }} - OBJECT_STORE_SECRET_ACCESS_KEY: {{ .Values.secrets.objectStore.secretAccessKey | b64enc | quote }} + {{- if and .Values.s3.external.accessKeyId (not .Values.s3.external.existingSecret) }} + s3-access-key-id: {{ .Values.s3.external.accessKeyId | b64enc | quote }} + s3-secret-access-key: {{ .Values.s3.external.secretAccessKey | b64enc | quote }} + {{- end }} + {{- if and .Values.s3.deploy (not .Values.s3.auth.existingSecret) }} + s3-auth-access-key-id: {{ include "trigger-v4.s3.auth.effectiveAccessKeyId" . | b64enc | quote }} + s3-auth-secret-access-key: {{ include "trigger-v4.s3.auth.effectiveSecretAccessKey" . | b64enc | quote }} + {{- end }} + {{- if and .Values.postgres.external.databaseUrl (not .Values.postgres.external.existingSecret) }} + postgres-database-url: {{ .Values.postgres.external.databaseUrl | b64enc | quote }} + {{- if .Values.postgres.external.directUrl }} + postgres-direct-url: {{ .Values.postgres.external.directUrl | b64enc | quote }} + {{- else }} + postgres-direct-url: {{ .Values.postgres.external.databaseUrl | b64enc | quote }} + {{- end }} + {{- end }} + {{- if and .Values.redis.external.host (not .Values.redis.external.existingSecret) .Values.redis.external.password }} + redis-password: {{ .Values.redis.external.password | b64enc | quote }} + {{- end }} + {{- if and .Values.clickhouse.external.host (not .Values.clickhouse.external.existingSecret) .Values.clickhouse.external.password }} + clickhouse-password: {{ .Values.clickhouse.external.password | b64enc | quote }} + {{- end }} {{- end }} --- {{- if and .Values.registry.deploy .Values.registry.auth.enabled }} diff --git a/hosting/k8s/helm/templates/validate-external-config.yaml b/hosting/k8s/helm/templates/validate-external-config.yaml index 47998f7d23..5147c87684 100644 --- a/hosting/k8s/helm/templates/validate-external-config.yaml +++ b/hosting/k8s/helm/templates/validate-external-config.yaml @@ -3,8 +3,8 @@ Validation template to ensure external service configurations are provided when This template will fail the Helm deployment if external config is missing for required services */}} {{- if not .Values.postgres.deploy }} -{{- if or (not .Values.postgres.external.host) (not .Values.postgres.external.database) (not .Values.postgres.external.username) }} -{{- fail "PostgreSQL external configuration is required when postgres.deploy=false. Please provide postgres.external.host, postgres.external.database, and postgres.external.username" }} +{{- if and (not .Values.postgres.external.databaseUrl) (not .Values.postgres.external.existingSecret) }} +{{- fail "PostgreSQL external configuration is required when postgres.deploy=false. Please provide either postgres.external.databaseUrl or postgres.external.existingSecret" }} {{- end }} {{- end }} @@ -20,9 +20,16 @@ This template will fail the Helm deployment if external config is missing for re {{- end }} {{- end }} -{{- if not .Values.s3.deploy }} -{{- if or (not .Values.s3.external.endpoint) (not .Values.s3.external.accessKeyId) }} -{{- fail "S3 external configuration is required when s3.deploy=false. Please provide s3.external.endpoint and s3.external.accessKeyId" }} +{{- if .Values.s3.deploy }} +{{- if and (not .Values.s3.auth.existingSecret) (not .Values.s3.auth.accessKeyId) (not .Values.s3.auth.rootUser) }} +{{- fail "S3 auth credentials are required when s3.deploy=true. Please provide either s3.auth.accessKeyId, s3.auth.existingSecret, or s3.auth.rootUser" }} +{{- end }} +{{- else }} +{{- if not .Values.s3.external.endpoint }} +{{- fail "S3 external configuration is required when s3.deploy=false. Please provide s3.external.endpoint" }} +{{- end }} +{{- if and (not .Values.s3.external.existingSecret) (or (not .Values.s3.external.accessKeyId) (not .Values.s3.external.secretAccessKey)) }} +{{- fail "S3 credentials are required when s3.deploy=false. Please provide either s3.external.existingSecret or both s3.external.accessKeyId and s3.external.secretAccessKey" }} {{- end }} {{- end }} diff --git a/hosting/k8s/helm/templates/webapp.yaml b/hosting/k8s/helm/templates/webapp.yaml index aec47bc51f..9a0794ce69 100644 --- a/hosting/k8s/helm/templates/webapp.yaml +++ b/hosting/k8s/helm/templates/webapp.yaml @@ -180,18 +180,43 @@ spec: value: {{ .Values.webapp.apiOrigin | quote }} - name: ELECTRIC_ORIGIN value: {{ include "trigger-v4.electric.url" . | quote }} + {{- if include "trigger-v4.postgres.useSecretUrl" . }} - name: DATABASE_URL - value: {{ include "trigger-v4.postgres.connectionString" . | quote }} + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.postgres.external.secretName" . }} + key: {{ include "trigger-v4.postgres.external.databaseUrlKey" . }} - name: DIRECT_URL + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.postgres.external.secretName" . }} + key: {{ include "trigger-v4.postgres.external.directUrlKey" . }} + {{- else }} + - name: DATABASE_URL value: {{ include "trigger-v4.postgres.connectionString" . | quote }} + - name: DIRECT_URL + value: {{ include "trigger-v4.postgres.directUrl" . | quote }} + {{- end }} + {{- if and (include "trigger-v4.webapp.connectivityCheckEnabled" .) (include "trigger-v4.postgres.host" .) }} - name: DATABASE_HOST value: {{ include "trigger-v4.postgres.host" . | quote }} + {{- end }} - name: REDIS_HOST value: {{ include "trigger-v4.redis.host" . | quote }} - name: REDIS_PORT value: {{ include "trigger-v4.redis.port" . | quote }} + {{- if and .Values.redis.external.host .Values.redis.external.existingSecret }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.redis.external.secretName" . }} + key: {{ include "trigger-v4.redis.external.passwordKey" . }} + {{- else if include "trigger-v4.redis.password" . }} + - name: REDIS_PASSWORD + value: {{ include "trigger-v4.redis.password" . | quote }} + {{- end }} - name: REDIS_TLS_DISABLED - value: "true" + value: {{ include "trigger-v4.redis.tlsDisabled" . | quote }} - name: APP_LOG_LEVEL value: {{ .Values.webapp.logLevel | quote }} - name: DEV_OTEL_EXPORTER_OTLP_ENDPOINT @@ -259,16 +284,55 @@ spec: secretKeyRef: name: {{ include "trigger-v4.secretsName" . }} key: MANAGED_WORKER_SECRET + {{- if .Values.s3.deploy }} + {{- if .Values.s3.auth.existingSecret }} + - name: OBJECT_STORE_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.s3.auth.secretName" . }} + key: {{ include "trigger-v4.s3.auth.accessKeyIdKey" . }} + - name: OBJECT_STORE_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.s3.auth.secretName" . }} + key: {{ include "trigger-v4.s3.auth.secretAccessKeyKey" . }} + {{- else }} + - name: OBJECT_STORE_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.secretsName" . }} + key: s3-auth-access-key-id + - name: OBJECT_STORE_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.secretsName" . }} + key: s3-auth-secret-access-key + {{- end }} + {{- else }} + {{- if .Values.s3.external.existingSecret }} + - name: OBJECT_STORE_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.s3.external.secretName" . }} + key: {{ include "trigger-v4.s3.external.accessKeyIdKey" . }} + - name: OBJECT_STORE_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.s3.external.secretName" . }} + key: {{ include "trigger-v4.s3.external.secretAccessKeyKey" . }} + {{- else if .Values.s3.external.accessKeyId }} - name: OBJECT_STORE_ACCESS_KEY_ID valueFrom: secretKeyRef: name: {{ include "trigger-v4.secretsName" . }} - key: OBJECT_STORE_ACCESS_KEY_ID + key: s3-access-key-id - name: OBJECT_STORE_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: {{ include "trigger-v4.secretsName" . }} - key: OBJECT_STORE_SECRET_ACCESS_KEY + key: s3-secret-access-key + {{- end }} + {{- end }} {{- end }} {{- if .Values.webapp.observability }} {{- if .Values.webapp.observability.tracing.exporterUrl }} @@ -304,6 +368,13 @@ spec: - name: INTERNAL_OTEL_METRIC_EXPORTER_INTERVAL_MS value: {{ .Values.webapp.observability.metrics.exporterIntervalMs | quote }} {{- end }} + {{- if and .Values.clickhouse.external.host .Values.clickhouse.external.existingSecret }} + - name: CLICKHOUSE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "trigger-v4.clickhouse.external.secretName" . }} + key: {{ include "trigger-v4.clickhouse.external.passwordKey" . }} + {{- end }} - name: CLICKHOUSE_URL value: {{ include "trigger-v4.clickhouse.url" . | quote }} - name: CLICKHOUSE_LOG_LEVEL diff --git a/hosting/k8s/helm/values.yaml b/hosting/k8s/helm/values.yaml index e1699c4dde..d55fe4154b 100644 --- a/hosting/k8s/helm/values.yaml +++ b/hosting/k8s/helm/values.yaml @@ -25,8 +25,6 @@ secrets: # - MAGIC_LINK_SECRET # - ENCRYPTION_KEY # - MANAGED_WORKER_SECRET - # - OBJECT_STORE_ACCESS_KEY_ID - # - OBJECT_STORE_SECRET_ACCESS_KEY existingSecret: "" # Session secret for user authentication (32 hex chars) @@ -37,10 +35,7 @@ secrets: encryptionKey: "f686147ab967943ebbe9ed3b496e465a" # Worker secret for managed worker authentication (32 hex chars) managedWorkerSecret: "447c29678f9eaf289e9c4b70d3dd8a7f" - # Object store credentials (change for production) - objectStore: - accessKeyId: "admin" - secretAccessKey: "very-safe-password" + # Object store credentials moved to s3.auth and s3.external section # Webapp configuration webapp: @@ -104,6 +99,10 @@ webapp: # cpu: 500m # memory: 1Gi + # Connectivity check configuration + connectivityCheck: + postgres: true # Set to false to disable DATABASE_HOST env var (overrides postgres.external.connectivityCheck) + # Extra environment variables for webapp extraEnvVars: [] @@ -114,6 +113,10 @@ webapp: # secretKeyRef: # name: my-secret # key: secret-key + # + # Example: PostgreSQL SSL with custom CA certificate + # - name: NODE_EXTRA_CA_CERTS + # value: "/etc/ssl/certs/postgres-ca.crt" # Extra volumes for the webapp pod extraVolumes: @@ -124,6 +127,14 @@ webapp: # - name: secret-volume # secret: # secretName: my-secret + # + # Example: PostgreSQL SSL CA certificate volume + # - name: postgres-ca-cert + # secret: + # secretName: postgres-ca-secret + # items: + # - key: ca.crt + # path: postgres-ca.crt # Extra volume mounts for the webapp container extraVolumeMounts: @@ -134,6 +145,11 @@ webapp: # - name: secret-volume # mountPath: /etc/secrets # readOnly: true + # + # Example: PostgreSQL SSL CA certificate mount + # - name: postgres-ca-cert + # mountPath: /etc/ssl/certs + # readOnly: true # ServiceMonitor for Prometheus monitoring serviceMonitor: @@ -339,6 +355,7 @@ supervisor: affinity: {} # PostgreSQL configuration +# Subchart: https://github.com/bitnami/charts/tree/main/bitnami/postgresql postgres: deploy: true @@ -363,17 +380,32 @@ postgres: # Custom connection settings connection: schema: "public" - sslMode: "disable" # Use "require" or "verify-full" for production + sslMode: "disable" # Use "require" or "verify-full" for production with custom CA # External PostgreSQL connection (when deploy: false) external: - host: "" - port: 5432 - database: "" - username: "" - password: "" + # Database URL configuration - simplified approach using URLs instead of individual parameters + databaseUrl: "" # Full PostgreSQL connection URL (e.g., postgresql://user:pass@host:port/db?schema=public&sslmode=prefer) + directUrl: "" # Optional: Direct URL for migrations (if not set, databaseUrl will be used) + # + # Optional: Connectivity check configuration during webapp startup + connectivityCheck: + host: "" # Optional: hostname:port for wait-for-it script (e.g., "postgres.example.com:5432") + # + # Secure credential management + existingSecret: "" # Name of existing secret containing DATABASE_URL + secretKeys: + databaseUrlKey: "postgres-database-url" # Key in existing secret containing complete DATABASE_URL + directUrlKey: "postgres-direct-url" # Key in existing secret containing direct URL (optional) + # + # Example: For SSL connections with custom CA (e.g., AWS RDS): + # 1. Set connection.sslMode to "require" or "verify-full" + # 2. Create a secret with your CA certificate: + # kubectl create secret generic postgres-ca-secret --from-file=ca.crt=/path/to/rds-ca-cert.pem + # 3. Configure extraVolumes, extraVolumeMounts, and extraEnvVars (see webapp section above) # Redis configuration +# Subchart: https://github.com/bitnami/charts/tree/main/bitnami/redis redis: deploy: true @@ -394,7 +426,13 @@ redis: external: host: "" port: 6379 - password: "" + password: "" # Optional - ignored if existingSecret is set + tls: + enabled: false # Set to true for Redis instances requiring TLS (e.g., AWS ElastiCache) + # + # Secure credential management + existingSecret: "" # Name of existing secret containing password + existingSecretPasswordKey: "redis-password" # Key in existing secret containing password # Electric configuration electric: @@ -457,6 +495,7 @@ electric: # value: "custom-value" # ClickHouse configuration +# Subchart: https://github.com/bitnami/charts/tree/main/bitnami/clickhouse clickhouse: deploy: true @@ -486,8 +525,12 @@ clickhouse: httpPort: 8123 nativePort: 9000 username: "" - password: "" + password: "" # Optional - ignored if existingSecret is set secure: false # Set to true for external secure connections + # + # Secure credential management + existingSecret: "" # Name of existing secret containing password + existingSecretKey: "clickhouse-password" # Key in existing secret containing password # ClickHouse configuration override # These defaults are based on official recommendations for systems with <16GB RAM: @@ -512,6 +555,7 @@ clickhouse: # S3-compatible object storage configuration +# Subchart: https://github.com/bitnami/charts/tree/main/bitnami/minio s3: # Set to false to use external S3-compatible storage # Set to true to deploy internal MinIO (default) @@ -522,6 +566,13 @@ s3: auth: rootUser: "admin" rootPassword: "very-safe-password" + # Webapp credentials for S3 access (defaults to root credentials if not specified) + accessKeyId: "" # Defaults to rootUser if empty + secretAccessKey: "" # Defaults to rootPassword if empty + # Existing secret support for webapp credentials + existingSecret: "" # If set, accessKeyId/secretAccessKey will be ignored + accessKeyIdSecretKey: "access-key-id" # Key in existingSecret containing access key ID + secretAccessKeySecretKey: "secret-access-key" # Key in existingSecret containing secret access key # The required "packets" bucket is created by default. defaultBuckets: "packets" @@ -534,8 +585,13 @@ s3: # External S3 connection (when deploy: false) external: endpoint: "" # e.g., "https://s3.amazonaws.com" or "https://your-minio.com:9000" - accessKeyId: "" - secretAccessKey: "" + accessKeyId: "admin" # Default for internal MinIO - change for production + secretAccessKey: "very-safe-password" # Default for internal MinIO - change for production + # + # Secure credential management + existingSecret: "" # Name of existing secret containing S3 credentials + existingSecretAccessKeyIdKey: "access-key-id" # Key in existing secret containing access key ID + existingSecretSecretAccessKeyKey: "secret-access-key" # Key in existing secret containing secret access key # Docker Registry configuration registry: