improvement(auth): added ability to inject secrets to kubernetes, server-side ff to disable email registration (#2728)

* improvement(auth): added ability to inject secrets to kubernetes, server-side ff to disable email registration

* consolidated telemetry events

* comments cleanup

* ack PR comment

* refactor to use createEnvMock helper instead of local mocks
This commit is contained in:
Waleed
2026-01-08 11:09:35 -08:00
committed by GitHub
parent 05904a73b2
commit a54fcbc094
48 changed files with 1632 additions and 251 deletions

View File

@@ -39,6 +39,8 @@ The chart includes several pre-configured values files for different scenarios:
| `values-azure.yaml` | Azure AKS optimized | Azure Kubernetes Service |
| `values-aws.yaml` | AWS EKS optimized | Amazon Elastic Kubernetes Service |
| `values-gcp.yaml` | GCP GKE optimized | Google Kubernetes Engine |
| `values-external-secrets.yaml` | External Secrets Operator integration | Using Azure Key Vault, AWS Secrets Manager, Vault |
| `values-existing-secret.yaml` | Pre-existing Kubernetes secrets | GitOps, Sealed Secrets, manual secret management |
### Development Environment
@@ -623,6 +625,111 @@ To uninstall/delete the release:
helm uninstall sim
```
## External Secret Management
The chart supports integration with external secret management systems for production-grade secret handling. This enables you to store secrets in secure vaults and have them automatically synced to Kubernetes.
### Option 1: External Secrets Operator (Recommended)
[External Secrets Operator](https://external-secrets.io/) is the industry-standard solution for syncing secrets from external stores like Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, and GCP Secret Manager.
**Prerequisites:**
```bash
# Install External Secrets Operator
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
-n external-secrets --create-namespace
```
**Configuration:**
```yaml
externalSecrets:
enabled: true
refreshInterval: "1h"
secretStoreRef:
name: "my-secret-store"
kind: "ClusterSecretStore"
remoteRefs:
app:
BETTER_AUTH_SECRET: "sim/app/better-auth-secret"
ENCRYPTION_KEY: "sim/app/encryption-key"
INTERNAL_API_SECRET: "sim/app/internal-api-secret"
postgresql:
password: "sim/postgresql/password"
```
See `examples/values-external-secrets.yaml` for complete examples including SecretStore configurations for Azure, AWS, GCP, and Vault.
### Option 2: Pre-Existing Kubernetes Secrets
Reference secrets you've created manually, via GitOps (Sealed Secrets, SOPS), or through other automation.
**Configuration:**
```yaml
app:
secrets:
existingSecret:
enabled: true
name: "my-app-secrets"
postgresql:
auth:
existingSecret:
enabled: true
name: "my-postgresql-secret"
passwordKey: "POSTGRES_PASSWORD"
externalDatabase:
existingSecret:
enabled: true
name: "my-external-db-secret"
passwordKey: "password"
```
**Create secrets manually:**
```bash
# Generate secure values
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
INTERNAL_API_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=')
# Create app secrets
kubectl create secret generic my-app-secrets \
--namespace sim \
--from-literal=BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
--from-literal=ENCRYPTION_KEY="$ENCRYPTION_KEY" \
--from-literal=INTERNAL_API_SECRET="$INTERNAL_API_SECRET"
# Create PostgreSQL secret
kubectl create secret generic my-postgresql-secret \
--namespace sim \
--from-literal=POSTGRES_PASSWORD="$POSTGRES_PASSWORD"
```
See `examples/values-existing-secret.yaml` for more details.
### External Secrets Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| `app.secrets.existingSecret.enabled` | Use existing secret for app credentials | `false` |
| `app.secrets.existingSecret.name` | Name of existing secret | `""` |
| `app.secrets.existingSecret.keys` | Key name mappings | See values.yaml |
| `postgresql.auth.existingSecret.enabled` | Use existing secret for PostgreSQL | `false` |
| `postgresql.auth.existingSecret.name` | Name of existing secret | `""` |
| `postgresql.auth.existingSecret.passwordKey` | Key containing password | `"POSTGRES_PASSWORD"` |
| `externalDatabase.existingSecret.enabled` | Use existing secret for external DB | `false` |
| `externalDatabase.existingSecret.name` | Name of existing secret | `""` |
| `externalDatabase.existingSecret.passwordKey` | Key containing password | `"EXTERNAL_DB_PASSWORD"` |
| `externalSecrets.enabled` | Enable External Secrets Operator integration | `false` |
| `externalSecrets.refreshInterval` | How often to sync secrets | `"1h"` |
| `externalSecrets.secretStoreRef.name` | Name of SecretStore/ClusterSecretStore | `""` |
| `externalSecrets.secretStoreRef.kind` | Kind of store | `"ClusterSecretStore"` |
| `externalSecrets.remoteRefs.app.*` | Remote paths for app secrets | See values.yaml |
| `externalSecrets.remoteRefs.postgresql.password` | Remote path for PostgreSQL password | `""` |
| `externalSecrets.remoteRefs.externalDatabase.password` | Remote path for external DB password | `""` |
## Security Considerations
### Production Secrets

View File

@@ -0,0 +1,54 @@
# Using pre-existing Kubernetes secrets for Sim
# For GitOps, Sealed Secrets, or manual secret management
# Prerequisites:
# Create your secrets before installing (see examples at bottom of file)
app:
enabled: true
replicaCount: 2
secrets:
existingSecret:
enabled: true
name: "sim-app-secrets"
env:
NEXT_PUBLIC_APP_URL: "https://sim.example.com"
BETTER_AUTH_URL: "https://sim.example.com"
NEXT_PUBLIC_SOCKET_URL: "wss://sim-ws.example.com"
NODE_ENV: "production"
realtime:
enabled: true
replicaCount: 2
env:
NEXT_PUBLIC_APP_URL: "https://sim.example.com"
BETTER_AUTH_URL: "https://sim.example.com"
NEXT_PUBLIC_SOCKET_URL: "wss://sim-ws.example.com"
ALLOWED_ORIGINS: "https://sim.example.com"
NODE_ENV: "production"
postgresql:
enabled: true
auth:
username: postgres
database: sim
existingSecret:
enabled: true
name: "sim-postgresql-secret"
passwordKey: "POSTGRES_PASSWORD"
# ---
# Create secrets before installing:
# ---
# kubectl create secret generic sim-app-secrets \
# --namespace sim \
# --from-literal=BETTER_AUTH_SECRET="$(openssl rand -hex 32)" \
# --from-literal=ENCRYPTION_KEY="$(openssl rand -hex 32)" \
# --from-literal=INTERNAL_API_SECRET="$(openssl rand -hex 32)" \
# --from-literal=CRON_SECRET="$(openssl rand -hex 32)" \
# --from-literal=API_ENCRYPTION_KEY="$(openssl rand -hex 32)"
# kubectl create secret generic sim-postgresql-secret \
# --namespace sim \
# --from-literal=POSTGRES_PASSWORD="$(openssl rand -base64 16 | tr -d '/+=')"

View File

@@ -0,0 +1,94 @@
# External Secrets Operator integration for Sim
# Syncs secrets from Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, etc.
# Prerequisites:
# 1. Install ESO: helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace
# 2. Create a SecretStore/ClusterSecretStore for your provider (see examples at bottom of file)
externalSecrets:
enabled: true
apiVersion: "v1"
refreshInterval: "1h"
secretStoreRef:
name: "sim-secret-store"
kind: "ClusterSecretStore"
remoteRefs:
app:
BETTER_AUTH_SECRET: "sim/app/better-auth-secret"
ENCRYPTION_KEY: "sim/app/encryption-key"
INTERNAL_API_SECRET: "sim/app/internal-api-secret"
CRON_SECRET: "sim/app/cron-secret"
API_ENCRYPTION_KEY: "sim/app/api-encryption-key"
postgresql:
password: "sim/postgresql/password"
app:
enabled: true
replicaCount: 2
env:
NEXT_PUBLIC_APP_URL: "https://sim.example.com"
BETTER_AUTH_URL: "https://sim.example.com"
NEXT_PUBLIC_SOCKET_URL: "wss://sim-ws.example.com"
NODE_ENV: "production"
realtime:
enabled: true
replicaCount: 2
env:
NEXT_PUBLIC_APP_URL: "https://sim.example.com"
BETTER_AUTH_URL: "https://sim.example.com"
NEXT_PUBLIC_SOCKET_URL: "wss://sim-ws.example.com"
ALLOWED_ORIGINS: "https://sim.example.com"
NODE_ENV: "production"
postgresql:
enabled: true
auth:
username: postgres
database: sim
# ---
# SecretStore Examples (apply one of these to your cluster before installing)
# ---
# Azure Key Vault (Workload Identity):
# apiVersion: external-secrets.io/v1beta1
# kind: ClusterSecretStore
# metadata:
# name: sim-secret-store
# spec:
# provider:
# azurekv:
# authType: WorkloadIdentity
# vaultUrl: "https://your-keyvault.vault.azure.net"
# serviceAccountRef:
# name: external-secrets-sa
# namespace: external-secrets
# AWS Secrets Manager (IRSA):
# apiVersion: external-secrets.io/v1beta1
# kind: ClusterSecretStore
# metadata:
# name: sim-secret-store
# spec:
# provider:
# aws:
# service: SecretsManager
# region: us-east-1
# role: arn:aws:iam::123456789012:role/external-secrets-role
# HashiCorp Vault (Kubernetes Auth):
# apiVersion: external-secrets.io/v1beta1
# kind: ClusterSecretStore
# metadata:
# name: sim-secret-store
# spec:
# provider:
# vault:
# server: "https://vault.example.com"
# path: "secret"
# version: "v2"
# auth:
# kubernetes:
# mountPath: "kubernetes"
# role: "external-secrets"

View File

@@ -181,8 +181,15 @@ Database URL for internal PostgreSQL
{{/*
Validate required secrets and reject default placeholder values
Skip validation when using existing secrets or External Secrets Operator
*/}}
{{- define "sim.validateSecrets" -}}
{{- $useExistingAppSecret := and .Values.app.secrets .Values.app.secrets.existingSecret .Values.app.secrets.existingSecret.enabled }}
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
{{- $useExistingPostgresSecret := and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled }}
{{- $useExistingExternalDbSecret := and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled }}
{{- /* App secrets validation - skip if using existing secret or ESO */ -}}
{{- if not (or $useExistingAppSecret $useExternalSecrets) }}
{{- if and .Values.app.enabled (not .Values.app.env.BETTER_AUTH_SECRET) }}
{{- fail "app.env.BETTER_AUTH_SECRET is required for production deployment" }}
{{- end }}
@@ -198,15 +205,21 @@ Validate required secrets and reject default placeholder values
{{- if and .Values.realtime.enabled (eq .Values.realtime.env.BETTER_AUTH_SECRET "CHANGE-ME-32-CHAR-SECRET-FOR-PRODUCTION-USE") }}
{{- fail "realtime.env.BETTER_AUTH_SECRET must not use the default placeholder value. Generate a secure secret with: openssl rand -hex 32" }}
{{- end }}
{{- end }}
{{- /* PostgreSQL password validation - skip if using existing secret or ESO */ -}}
{{- if not (or $useExistingPostgresSecret $useExternalSecrets) }}
{{- if and .Values.postgresql.enabled (not .Values.postgresql.auth.password) }}
{{- fail "postgresql.auth.password is required when using internal PostgreSQL" }}
{{- end }}
{{- if and .Values.postgresql.enabled (eq .Values.postgresql.auth.password "CHANGE-ME-SECURE-PASSWORD") }}
{{- fail "postgresql.auth.password must not use the default placeholder value. Set a secure password for production" }}
{{- end }}
{{- if and .Values.postgresql.enabled (not (regexMatch "^[a-zA-Z0-9._-]+$" .Values.postgresql.auth.password)) }}
{{- if and .Values.postgresql.enabled .Values.postgresql.auth.password (not (regexMatch "^[a-zA-Z0-9._-]+$" .Values.postgresql.auth.password)) }}
{{- fail "postgresql.auth.password must only contain alphanumeric characters, hyphens, underscores, or periods to ensure DATABASE_URL compatibility. Generate with: openssl rand -base64 16 | tr -d '/+='" }}
{{- end }}
{{- end }}
{{- /* External database password validation - skip if using existing secret or ESO */ -}}
{{- if not (or $useExistingExternalDbSecret $useExternalSecrets) }}
{{- if and .Values.externalDatabase.enabled (not .Values.externalDatabase.password) }}
{{- fail "externalDatabase.password is required when using external database" }}
{{- end }}
@@ -214,6 +227,103 @@ Validate required secrets and reject default placeholder values
{{- fail "externalDatabase.password must only contain alphanumeric characters, hyphens, underscores, or periods to ensure DATABASE_URL compatibility." }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Get the app secrets name
Returns the name of the secret containing app credentials (auth, encryption keys)
*/}}
{{- define "sim.appSecretName" -}}
{{- if and .Values.app.secrets .Values.app.secrets.existingSecret .Values.app.secrets.existingSecret.enabled -}}
{{- .Values.app.secrets.existingSecret.name -}}
{{- else -}}
{{- printf "%s-app-secrets" (include "sim.fullname" .) -}}
{{- end -}}
{{- end }}
{{/*
Get the PostgreSQL secret name
Returns the name of the secret containing PostgreSQL password
*/}}
{{- define "sim.postgresqlSecretName" -}}
{{- if and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled -}}
{{- .Values.postgresql.auth.existingSecret.name -}}
{{- else -}}
{{- printf "%s-postgresql-secret" (include "sim.fullname" .) -}}
{{- end -}}
{{- end }}
{{/*
Get the PostgreSQL password key name
Returns the key name in the secret that contains the password
*/}}
{{- define "sim.postgresqlPasswordKey" -}}
{{- if and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled -}}
{{- .Values.postgresql.auth.existingSecret.passwordKey | default "POSTGRES_PASSWORD" -}}
{{- else -}}
{{- print "POSTGRES_PASSWORD" -}}
{{- end -}}
{{- end }}
{{/*
Get the external database secret name
Returns the name of the secret containing external database password
*/}}
{{- define "sim.externalDbSecretName" -}}
{{- if and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled -}}
{{- .Values.externalDatabase.existingSecret.name -}}
{{- else -}}
{{- printf "%s-external-db-secret" (include "sim.fullname" .) -}}
{{- end -}}
{{- end }}
{{/*
Get the external database password key name
Returns the key name in the secret that contains the password
*/}}
{{- define "sim.externalDbPasswordKey" -}}
{{- if and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled -}}
{{- .Values.externalDatabase.existingSecret.passwordKey | default "EXTERNAL_DB_PASSWORD" -}}
{{- else -}}
{{- print "EXTERNAL_DB_PASSWORD" -}}
{{- end -}}
{{- end }}
{{/*
Check if app secrets should be created by the chart
Returns true if we should create the app secrets (not using existing or ESO)
*/}}
{{- define "sim.createAppSecrets" -}}
{{- $useExistingAppSecret := and .Values.app.secrets .Values.app.secrets.existingSecret .Values.app.secrets.existingSecret.enabled }}
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
{{- if not (or $useExistingAppSecret $useExternalSecrets) -}}
true
{{- end -}}
{{- end }}
{{/*
Check if PostgreSQL secret should be created by the chart
Returns true if we should create the PostgreSQL secret (not using existing or ESO)
*/}}
{{- define "sim.createPostgresqlSecret" -}}
{{- $useExistingSecret := and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled }}
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
{{- if not (or $useExistingSecret $useExternalSecrets) -}}
true
{{- end -}}
{{- end }}
{{/*
Check if external database secret should be created by the chart
Returns true if we should create the external database secret (not using existing or ESO)
*/}}
{{- define "sim.createExternalDbSecret" -}}
{{- $useExistingSecret := and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled }}
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
{{- if not (or $useExistingSecret $useExternalSecrets) -}}
true
{{- end -}}
{{- end }}
{{/*
Ollama URL

View File

@@ -44,15 +44,14 @@ spec:
cd /app/packages/db
export DATABASE_URL="{{ include "sim.databaseUrl" . }}"
bun run db:migrate
{{- if .Values.postgresql.enabled }}
envFrom:
{{- if .Values.postgresql.enabled }}
- secretRef:
name: {{ include "sim.fullname" . }}-postgresql-secret
{{- else if .Values.externalDatabase.enabled }}
envFrom:
name: {{ include "sim.postgresqlSecretName" . }}
{{- else if .Values.externalDatabase.enabled }}
- secretRef:
name: {{ include "sim.fullname" . }}-external-db-secret
{{- end }}
name: {{ include "sim.externalDbSecretName" . }}
{{- end }}
{{- include "sim.resources" .Values.migrations | nindent 10 }}
{{- include "sim.securityContext" .Values.migrations | nindent 10 }}
{{- end }}
@@ -89,15 +88,18 @@ spec:
{{- with .Values.extraEnvVars }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.postgresql.enabled }}
envFrom:
# App secrets (authentication, encryption keys)
- secretRef:
name: {{ include "sim.fullname" . }}-postgresql-secret
{{- else if .Values.externalDatabase.enabled }}
envFrom:
name: {{ include "sim.appSecretName" . }}
# Database secrets
{{- if .Values.postgresql.enabled }}
- secretRef:
name: {{ include "sim.fullname" . }}-external-db-secret
{{- end }}
name: {{ include "sim.postgresqlSecretName" . }}
{{- else if .Values.externalDatabase.enabled }}
- secretRef:
name: {{ include "sim.externalDbSecretName" . }}
{{- end }}
{{- if .Values.app.livenessProbe }}
livenessProbe:
{{- toYaml .Values.app.livenessProbe | nindent 12 }}

View File

@@ -62,15 +62,18 @@ spec:
{{- with .Values.extraEnvVars }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.postgresql.enabled }}
envFrom:
# App secrets (authentication keys shared with main app)
- secretRef:
name: {{ include "sim.fullname" . }}-postgresql-secret
{{- else if .Values.externalDatabase.enabled }}
envFrom:
name: {{ include "sim.appSecretName" . }}
# Database secrets
{{- if .Values.postgresql.enabled }}
- secretRef:
name: {{ include "sim.fullname" . }}-external-db-secret
{{- end }}
name: {{ include "sim.postgresqlSecretName" . }}
{{- else if .Values.externalDatabase.enabled }}
- secretRef:
name: {{ include "sim.externalDbSecretName" . }}
{{- end }}
{{- if .Values.realtime.livenessProbe }}
livenessProbe:
{{- toYaml .Values.realtime.livenessProbe | nindent 12 }}

View File

@@ -1,4 +1,4 @@
{{- if .Values.externalDatabase.enabled }}
{{- if and .Values.externalDatabase.enabled (include "sim.createExternalDbSecret" .) }}
---
# Secret for external database credentials
apiVersion: v1

View File

@@ -0,0 +1,44 @@
{{- if and .Values.externalSecrets.enabled .Values.app.enabled }}
# ExternalSecret for app credentials (syncs from external secret managers)
apiVersion: external-secrets.io/{{ .Values.externalSecrets.apiVersion | default "v1" }}
kind: ExternalSecret
metadata:
name: {{ include "sim.fullname" . }}-app-secrets
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.app.labels" . | nindent 4 }}
spec:
refreshInterval: {{ .Values.externalSecrets.refreshInterval | quote }}
secretStoreRef:
name: {{ required "externalSecrets.secretStoreRef.name is required when externalSecrets.enabled=true" .Values.externalSecrets.secretStoreRef.name }}
kind: {{ .Values.externalSecrets.secretStoreRef.kind | default "ClusterSecretStore" }}
target:
name: {{ include "sim.fullname" . }}-app-secrets
creationPolicy: Owner
data:
{{- if .Values.externalSecrets.remoteRefs.app.BETTER_AUTH_SECRET }}
- secretKey: BETTER_AUTH_SECRET
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.app.BETTER_AUTH_SECRET }}
{{- end }}
{{- if .Values.externalSecrets.remoteRefs.app.ENCRYPTION_KEY }}
- secretKey: ENCRYPTION_KEY
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.app.ENCRYPTION_KEY }}
{{- end }}
{{- if .Values.externalSecrets.remoteRefs.app.INTERNAL_API_SECRET }}
- secretKey: INTERNAL_API_SECRET
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.app.INTERNAL_API_SECRET }}
{{- end }}
{{- if .Values.externalSecrets.remoteRefs.app.CRON_SECRET }}
- secretKey: CRON_SECRET
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.app.CRON_SECRET }}
{{- end }}
{{- if .Values.externalSecrets.remoteRefs.app.API_ENCRYPTION_KEY }}
- secretKey: API_ENCRYPTION_KEY
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.app.API_ENCRYPTION_KEY }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,23 @@
{{- if and .Values.externalSecrets.enabled .Values.externalDatabase.enabled .Values.externalSecrets.remoteRefs.externalDatabase.password }}
# ExternalSecret for external database password (syncs from external secret managers)
apiVersion: external-secrets.io/{{ .Values.externalSecrets.apiVersion | default "v1" }}
kind: ExternalSecret
metadata:
name: {{ include "sim.fullname" . }}-external-db-secret
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: external-database
spec:
refreshInterval: {{ .Values.externalSecrets.refreshInterval | quote }}
secretStoreRef:
name: {{ required "externalSecrets.secretStoreRef.name is required when externalSecrets.enabled=true" .Values.externalSecrets.secretStoreRef.name }}
kind: {{ .Values.externalSecrets.secretStoreRef.kind | default "ClusterSecretStore" }}
target:
name: {{ include "sim.fullname" . }}-external-db-secret
creationPolicy: Owner
data:
- secretKey: EXTERNAL_DB_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.externalDatabase.password }}
{{- end }}

View File

@@ -0,0 +1,22 @@
{{- if and .Values.externalSecrets.enabled .Values.postgresql.enabled .Values.externalSecrets.remoteRefs.postgresql.password }}
# ExternalSecret for PostgreSQL password (syncs from external secret managers)
apiVersion: external-secrets.io/{{ .Values.externalSecrets.apiVersion | default "v1" }}
kind: ExternalSecret
metadata:
name: {{ include "sim.fullname" . }}-postgresql-secret
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.postgresql.labels" . | nindent 4 }}
spec:
refreshInterval: {{ .Values.externalSecrets.refreshInterval | quote }}
secretStoreRef:
name: {{ required "externalSecrets.secretStoreRef.name is required when externalSecrets.enabled=true" .Values.externalSecrets.secretStoreRef.name }}
kind: {{ .Values.externalSecrets.secretStoreRef.kind | default "ClusterSecretStore" }}
target:
name: {{ include "sim.fullname" . }}-postgresql-secret
creationPolicy: Owner
data:
- secretKey: POSTGRES_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.remoteRefs.postgresql.password }}
{{- end }}

View File

@@ -0,0 +1,27 @@
{{- if and .Values.app.enabled (include "sim.createAppSecrets" .) }}
# Secret for app credentials (authentication, encryption keys)
apiVersion: v1
kind: Secret
metadata:
name: {{ include "sim.fullname" . }}-app-secrets
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.app.labels" . | nindent 4 }}
type: Opaque
stringData:
{{- if .Values.app.env.BETTER_AUTH_SECRET }}
BETTER_AUTH_SECRET: {{ .Values.app.env.BETTER_AUTH_SECRET | quote }}
{{- end }}
{{- if .Values.app.env.ENCRYPTION_KEY }}
ENCRYPTION_KEY: {{ .Values.app.env.ENCRYPTION_KEY | quote }}
{{- end }}
{{- if .Values.app.env.INTERNAL_API_SECRET }}
INTERNAL_API_SECRET: {{ .Values.app.env.INTERNAL_API_SECRET | quote }}
{{- end }}
{{- if .Values.app.env.CRON_SECRET }}
CRON_SECRET: {{ .Values.app.env.CRON_SECRET | quote }}
{{- end }}
{{- if .Values.app.env.API_ENCRYPTION_KEY }}
API_ENCRYPTION_KEY: {{ .Values.app.env.API_ENCRYPTION_KEY | quote }}
{{- end }}
{{- end }}

View File

@@ -65,6 +65,7 @@ data:
POSTGRES_USER: {{ .Values.postgresql.auth.username | quote }}
PGDATA: "/var/lib/postgresql/data/pgdata"
{{- if (include "sim.createPostgresqlSecret" .) }}
---
# Secret for PostgreSQL password
apiVersion: v1
@@ -77,6 +78,7 @@ metadata:
type: Opaque
data:
POSTGRES_PASSWORD: {{ .Values.postgresql.auth.password | b64enc }}
{{- end }}
---
# StatefulSet for PostgreSQL
@@ -128,7 +130,7 @@ spec:
- configMapRef:
name: {{ include "sim.fullname" . }}-postgresql-env
- secretRef:
name: {{ include "sim.fullname" . }}-postgresql-secret
name: {{ include "sim.postgresqlSecretName" . }}
{{- if .Values.postgresql.livenessProbe }}
livenessProbe:
{{- toYaml .Values.postgresql.livenessProbe | nindent 12 }}

View File

@@ -81,18 +81,39 @@
}
}
},
"secrets": {
"type": "object",
"description": "Secret management configuration",
"properties": {
"existingSecret": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use an existing secret instead of creating one"
},
"name": {
"type": "string",
"description": "Name of the existing Kubernetes secret"
},
"keys": {
"type": "object",
"description": "Key name mappings in the existing secret"
}
}
}
}
},
"env": {
"type": "object",
"properties": {
"BETTER_AUTH_SECRET": {
"type": "string",
"minLength": 32,
"description": "Auth secret (minimum 32 characters required)"
"description": "Auth secret (minimum 32 characters required when not using existingSecret)"
},
"ENCRYPTION_KEY": {
"type": "string",
"minLength": 32,
"description": "Encryption key (minimum 32 characters required)"
"description": "Encryption key (minimum 32 characters required when not using existingSecret)"
},
"NEXT_PUBLIC_APP_URL": {
"type": "string",
@@ -329,8 +350,7 @@
"properties": {
"BETTER_AUTH_SECRET": {
"type": "string",
"minLength": 32,
"description": "Auth secret (minimum 32 characters required)"
"description": "Auth secret (minimum 32 characters required when not using existingSecret)"
},
"NEXT_PUBLIC_APP_URL": {
"type": "string",
@@ -431,11 +451,25 @@
},
"password": {
"type": "string",
"minLength": 8,
"not": {
"const": "CHANGE-ME-SECURE-PASSWORD"
},
"description": "PostgreSQL password (minimum 8 characters, must not be default placeholder)"
"description": "PostgreSQL password (minimum 8 characters when not using existingSecret)"
},
"existingSecret": {
"type": "object",
"description": "Use an existing secret for PostgreSQL credentials",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use an existing secret instead of creating one"
},
"name": {
"type": "string",
"description": "Name of the existing Kubernetes secret"
},
"passwordKey": {
"type": "string",
"description": "Key in the secret containing the password"
}
}
}
}
}
@@ -475,6 +509,24 @@
"type": "string",
"enum": ["disable", "allow", "prefer", "require", "verify-ca", "verify-full"],
"description": "SSL mode for database connection"
},
"existingSecret": {
"type": "object",
"description": "Use an existing secret for external database credentials",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use an existing secret instead of creating one"
},
"name": {
"type": "string",
"description": "Name of the existing Kubernetes secret"
},
"passwordKey": {
"type": "string",
"description": "Key in the secret containing the password"
}
}
}
},
"if": {
@@ -821,6 +873,61 @@
}
}
},
"externalSecrets": {
"type": "object",
"description": "External Secrets Operator integration",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable External Secrets Operator integration"
},
"apiVersion": {
"type": "string",
"enum": ["v1", "v1beta1"],
"description": "ESO API version - use v1 for ESO v0.17+ (recommended), v1beta1 for older versions"
},
"refreshInterval": {
"type": "string",
"description": "How often to sync secrets from external store"
},
"secretStoreRef": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the SecretStore or ClusterSecretStore"
},
"kind": {
"type": "string",
"enum": ["SecretStore", "ClusterSecretStore"],
"description": "Kind of the store"
}
}
},
"remoteRefs": {
"type": "object",
"description": "Remote key paths in external secret store",
"properties": {
"app": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"postgresql": {
"type": "object",
"properties": {
"password": { "type": "string" }
}
},
"externalDatabase": {
"type": "object",
"properties": {
"password": { "type": "string" }
}
}
}
}
}
},
"ingress": {
"type": "object",
"properties": {

View File

@@ -16,16 +16,16 @@ global:
app:
# Enable/disable the main application
enabled: true
# Image configuration
image:
repository: simstudioai/simstudio
tag: latest
pullPolicy: Always
# Number of replicas
replicaCount: 1
# Resource limits and requests
resources:
limits:
@@ -34,19 +34,37 @@ app:
requests:
memory: "2Gi"
cpu: "1000m"
# Node selector for pod scheduling (leave empty to allow scheduling on any node)
nodeSelector: {}
# Pod security context
podSecurityContext:
fsGroup: 1001
# Container security context
securityContext:
runAsNonRoot: true
runAsUser: 1001
# Secret management configuration
# Use this to reference pre-existing Kubernetes secrets instead of defining values directly
# This enables integration with External Secrets Operator, HashiCorp Vault, Azure Key Vault, etc.
secrets:
existingSecret:
# Set to true to use an existing secret instead of creating one from values
enabled: false
# Name of the existing Kubernetes secret containing app credentials
name: ""
# Key mappings - specify the key names in your existing secret
# Only needed if your secret uses different key names than the defaults
keys:
BETTER_AUTH_SECRET: "BETTER_AUTH_SECRET"
ENCRYPTION_KEY: "ENCRYPTION_KEY"
INTERNAL_API_SECRET: "INTERNAL_API_SECRET"
CRON_SECRET: "CRON_SECRET"
API_ENCRYPTION_KEY: "API_ENCRYPTION_KEY"
# Environment variables
env:
# Application URLs
@@ -118,7 +136,9 @@ app:
# Registration Control
DISABLE_REGISTRATION: "" # Set to "true" to disable new user signups
EMAIL_PASSWORD_SIGNUP_ENABLED: "" # Set to "false" to disable email/password login (SSO-only mode, server-side enforcement)
NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED: "" # Set to "false" to hide email/password login form (UI-side)
# Access Control (leave empty if not restricting login)
ALLOWED_LOGIN_EMAILS: "" # Comma-separated list of allowed email addresses for login
ALLOWED_LOGIN_DOMAINS: "" # Comma-separated list of allowed email domains for login
@@ -305,6 +325,12 @@ postgresql:
username: postgres
password: "" # REQUIRED - set via --set flag or external secret manager
database: sim
# Use an existing secret for PostgreSQL credentials
# This enables integration with External Secrets Operator, HashiCorp Vault, etc.
existingSecret:
enabled: false
name: "" # Name of existing Kubernetes secret
passwordKey: "POSTGRES_PASSWORD" # Key in the secret containing the password
# Node selector for database pod scheduling (leave empty to allow scheduling on any node)
nodeSelector: {}
@@ -387,17 +413,24 @@ postgresql:
externalDatabase:
# Enable to use an external database instead of the internal PostgreSQL instance
enabled: false
# Database connection details
host: "external-db.example.com"
port: 5432
username: postgres
password: ""
database: sim
# SSL configuration
sslMode: require
# Use an existing secret for external database credentials
# This enables integration with External Secrets Operator, HashiCorp Vault, etc.
existingSecret:
enabled: false
name: "" # Name of existing Kubernetes secret
passwordKey: "EXTERNAL_DB_PASSWORD" # Key in the secret containing the password
# Ollama local AI models configuration
ollama:
# Enable/disable Ollama deployment
@@ -1013,4 +1046,51 @@ copilot:
# Job configuration
backoffLimit: 3
restartPolicy: OnFailure
restartPolicy: OnFailure
# External Secrets Operator integration
# Use this to automatically sync secrets from external secret managers (Azure Key Vault, AWS Secrets Manager, etc.)
# Prerequisites: Install External Secrets Operator in your cluster first
# See: https://external-secrets.io/latest/introduction/getting-started/
externalSecrets:
# Enable External Secrets Operator integration
enabled: false
# ESO API version - use "v1" for ESO v0.17+ (recommended), "v1beta1" for older versions
apiVersion: "v1"
# How often to sync secrets from the external store
refreshInterval: "1h"
# Reference to the SecretStore or ClusterSecretStore
secretStoreRef:
# Name of the SecretStore or ClusterSecretStore resource
name: ""
# Kind of the store: "SecretStore" (namespaced) or "ClusterSecretStore" (cluster-wide)
kind: "ClusterSecretStore"
# Remote references - paths/keys in your external secret store
# These map to the secrets that will be created in Kubernetes
remoteRefs:
# App secrets (authentication, encryption keys)
app:
# Path to BETTER_AUTH_SECRET in external store (e.g., "sim/app/better-auth-secret")
BETTER_AUTH_SECRET: ""
# Path to ENCRYPTION_KEY in external store
ENCRYPTION_KEY: ""
# Path to INTERNAL_API_SECRET in external store
INTERNAL_API_SECRET: ""
# Path to CRON_SECRET in external store (optional)
CRON_SECRET: ""
# Path to API_ENCRYPTION_KEY in external store (optional)
API_ENCRYPTION_KEY: ""
# PostgreSQL password (for internal PostgreSQL)
postgresql:
# Path to PostgreSQL password in external store (e.g., "sim/postgresql/password")
password: ""
# External database password (when using managed database services)
externalDatabase:
# Path to external database password in external store
password: ""