diff --git a/helm/huly/README.md b/helm/huly/README.md index ae742cb..da8ea0a 100644 --- a/helm/huly/README.md +++ b/helm/huly/README.md @@ -104,6 +104,50 @@ helm install huly ./helm/huly \ --set external.elastic=https://es.example.com:9200 ``` +## Externally Managed Secrets (GitOps) + +By default the chart generates its own Secret and preserves the values across `helm upgrade` +by looking up the Secret already in the cluster. + +That lookup only works when Helm has a live cluster connection. Tools that render manifests +without one — ArgoCD, Flux, `helm template`, `--dry-run` — always see an empty lookup, so every +render regenerates `SERVER_SECRET`, `COCKROACH_PASSWORD` and the rest, which invalidates all +sessions on each sync. + +Set `secrets.existingSecret` to a Secret you manage yourself. The chart then renders no Secret +of its own and points every service at the named one, so it can be produced by External Secrets +Operator, Sealed Secrets, Vault, or `kubectl create secret`. + +```bash +kubectl create secret generic huly-secrets -n \ + --from-literal=SERVER_SECRET="$(openssl rand -hex 16)" \ + --from-literal=COCKROACH_PASSWORD="$(openssl rand -hex 12)" \ + --from-literal=REDPANDA_SUPERUSER_PASSWORD="$(openssl rand -hex 12)" \ + --from-literal=AIBOT_PASSWORD="$(openssl rand -hex 12)" \ + --from-literal=CR_DB_URL='postgres://selfhost:@cockroach:26257/defaultdb' \ + --from-literal=STORAGE_CONFIG='minio|minio?accessKey=&secretKey=' + +helm install huly ./helm/huly \ + --set domain=huly.mysite.com \ + --set secrets.existingSecret=huly-secrets +``` + +| Key | Required when | +|-----|---------------| +| `SERVER_SECRET` | always | +| `CR_DB_URL` | always | +| `STORAGE_CONFIG` | always | +| `COCKROACH_PASSWORD` | `cockroach.enabled=true` | +| `REDPANDA_SUPERUSER_PASSWORD` | `redpanda.enabled=true` | +| `AIBOT_PASSWORD`, `OPENAI_API_KEY` | `aibot.enabled=true` | +| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | `auth.google.clientId` is set | +| `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | `auth.github.clientId` is set | +| `OPENID_CLIENT_ID`, `OPENID_CLIENT_SECRET`, `OPENID_ISSUER` | `auth.oidc.clientId` is set | +| `GITHUB_APP_ID`, `GITHUB_APP_CLIENT_ID`, `GITHUB_APP_CLIENT_SECRET`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_APP_WEBHOOK_SECRET` | `githubIntegration.enabled=true` | + +The `auth.*.clientId` values still gate whether a provider is wired into the account service, so +keep setting them in values even though the credentials come from the external Secret. + ## GitHub Integration Bidirectional sync of issues, PRs, and comments between Huly and GitHub. Requires a [GitHub App](https://docs.github.com/en/apps/creating-github-apps). @@ -244,10 +288,13 @@ kubectl logs deployment/ -n --tail=20 ### Secrets -All secrets are auto-generated if left empty. They persist across `helm upgrade` via Kubernetes secret lookup. +All secrets are auto-generated if left empty. They persist across `helm upgrade` via Kubernetes +secret lookup, which requires a live cluster connection — see +[Externally Managed Secrets (GitOps)](#externally-managed-secrets-gitops) for ArgoCD and Flux. | Key | Description | Default | |-----|-------------|---------| +| `secrets.existingSecret` | Use a Secret you manage; disables the chart-managed Secret | `""` | | `secrets.serverSecret` | Shared JWT signing secret | auto | | `secrets.storageConfig` | Full storage connection string override | auto | | `secrets.cockroachPassword` | CockroachDB password | auto | diff --git a/helm/huly/templates/_helpers.tpl b/helm/huly/templates/_helpers.tpl index ec20b0b..ea6c969 100644 --- a/helm/huly/templates/_helpers.tpl +++ b/helm/huly/templates/_helpers.tpl @@ -25,7 +25,11 @@ Create a default fully qualified app name. Secret resource name. */}} {{- define "huly.secretName" -}} -{{- printf "%s-secret" (include "huly.fullname" .) }} +{{- if .Values.secrets.existingSecret -}} +{{- .Values.secrets.existingSecret -}} +{{- else -}} +{{- printf "%s-secret" (include "huly.fullname" .) -}} +{{- end -}} {{- end }} {{/* diff --git a/helm/huly/templates/secret.yaml b/helm/huly/templates/secret.yaml index 1c8ba53..1815e98 100644 --- a/helm/huly/templates/secret.yaml +++ b/helm/huly/templates/secret.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.secrets.existingSecret }} {{- $secretName := include "huly.secretName" . -}} {{- $existing := lookup "v1" "Secret" .Release.Namespace $secretName -}} {{- $hasExisting := not (empty $existing) -}} @@ -122,3 +123,4 @@ data: GITHUB_APP_PRIVATE_KEY: {{ .Values.githubIntegration.privateKey | b64enc | quote }} GITHUB_APP_WEBHOOK_SECRET: {{ .Values.githubIntegration.webhookSecret | b64enc | quote }} {{- end }} +{{- end }} diff --git a/helm/huly/values.yaml b/helm/huly/values.yaml index c562bad..8febf06 100644 --- a/helm/huly/values.yaml +++ b/helm/huly/values.yaml @@ -21,6 +21,21 @@ hulyRegistry: hardcoreeng # Secrets — leave empty to auto-generate (persists across upgrades) # ────────────────────────────────────────────────────────────── secrets: + # Use a Secret you manage instead of the chart-managed one. + # When set, the chart renders no Secret of its own and every service reads + # from the named Secret — required for GitOps tools (ArgoCD, Flux) that + # render manifests without cluster access, and for External Secrets + # Operator / Sealed Secrets / Vault. + # + # Required keys: SERVER_SECRET, CR_DB_URL, STORAGE_CONFIG, + # COCKROACH_PASSWORD (when cockroach.enabled=true), + # REDPANDA_SUPERUSER_PASSWORD (when redpanda.enabled=true), + # AIBOT_PASSWORD and OPENAI_API_KEY (when aibot.enabled=true), + # GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID/ + # GITHUB_CLIENT_SECRET, OPENID_CLIENT_ID/OPENID_CLIENT_SECRET/ + # OPENID_ISSUER (per configured auth provider), + # GITHUB_APP_* (when githubIntegration.enabled=true) + existingSecret: "" # Shared JWT signing secret (auto-generated if empty) serverSecret: "" # Full storage config string override.