diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8517412..b27a70f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,6 +142,14 @@ jobs: > /tmp/helm-cosi-custom-rbac.yaml ! grep -q "cosi-driver-clusterrole" /tmp/helm-cosi-custom-rbac.yaml + # Existing console Secret: chart must not create its own Secret and the + # Deployment must reference the supplied name. + helm template test deploy/rustfs-operator \ + --set console.existingSecret=my-console-jwt \ + > /tmp/helm-console-existing-secret.yaml + ! grep -q "console-secret" /tmp/helm-console-existing-secret.yaml + grep -q 'name: "my-console-jwt"' /tmp/helm-console-existing-secret.yaml + - name: Check release metadata run: make release-metadata-check diff --git a/deploy/rustfs-operator/README.md b/deploy/rustfs-operator/README.md index 2bae013..1167e1f 100755 --- a/deploy/rustfs-operator/README.md +++ b/deploy/rustfs-operator/README.md @@ -662,6 +662,30 @@ Before rolling back to a release without server-side sessions, scale the Console Deployment to zero, perform the rollback, then restore one replica so the two cookie formats never overlap. +### Console JWT secret + +The Console encrypts in-process session data with a `jwt-secret`. By default the +chart generates a random secret on first install and stores it in +`-console-secret` in the operator namespace, reusing it across upgrades. +To manage the secret value outside the chart, either set `console.jwtSecret` or +reference an existing Secret: + +```yaml +console: + existingSecret: my-console-jwt +``` + +The referenced Secret must live in the operator namespace and contain a +`jwt-secret` key (generate one with `openssl rand -base64 32`). When +`console.existingSecret` is set, the chart does not create its own Console Secret +and the Console Deployment reads `JWT_SECRET` from the existing Secret. +`console.existingSecret` and `console.jwtSecret` are mutually exclusive. + +> Note: externally managed Secrets do not automatically trigger a Console +> restart when the value of that Secret changes. Rotate the Secret value and +> restart the Console Deployment manually (e.g. `kubectl rollout restart`) +> for the new `jwt-secret` to take effect. + ### Backend CORS (when frontend is on a different host) If the frontend is served from another host (e.g. `https://ui.example.com`) and the API at `https://api.example.com`, set allowed origins on the console backend: diff --git a/deploy/rustfs-operator/templates/_helpers.tpl b/deploy/rustfs-operator/templates/_helpers.tpl index c7dfdb8..3e7cb84 100755 --- a/deploy/rustfs-operator/templates/_helpers.tpl +++ b/deploy/rustfs-operator/templates/_helpers.tpl @@ -113,3 +113,16 @@ Create the name of the COSI driver service account to use {{- default "default" .Values.cosiDriver.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Checksum of the Console JWT secret source, used to force a rollout when the +secret changes. When console.existingSecret is set, checksum the referenced +Secret name; otherwise checksum the chart-managed Console Secret template. +*/}} +{{- define "rustfs-operator.consoleSecretChecksum" -}} +{{- if .Values.console.existingSecret -}} +{{- .Values.console.existingSecret | sha256sum -}} +{{- else -}} +{{- include (print $.Template.BasePath "/console-secret.yaml") . | sha256sum -}} +{{- end -}} +{{- end }} diff --git a/deploy/rustfs-operator/templates/console-deployment.yaml b/deploy/rustfs-operator/templates/console-deployment.yaml index 9de4cf3..73a674f 100755 --- a/deploy/rustfs-operator/templates/console-deployment.yaml +++ b/deploy/rustfs-operator/templates/console-deployment.yaml @@ -17,6 +17,10 @@ {{- if and .Values.console.enabled (ne (toString .Values.console.replicas) "1") -}} {{- fail "console.replicas must be 1 because Console sessions are stored in process" -}} {{- end -}} +{{- if and .Values.console.enabled .Values.console.existingSecret .Values.console.jwtSecret -}} +{{- fail "console.existingSecret and console.jwtSecret are mutually exclusive; set only one" -}} +{{- end -}} +{{- $consoleSecretName := .Values.console.existingSecret | default (printf "%s-console-secret" (include "rustfs-operator.fullname" .)) -}} {{- if .Values.console.enabled -}} apiVersion: apps/v1 kind: Deployment @@ -46,7 +50,7 @@ spec: app.kubernetes.io/component: console annotations: # Force reload on secret changes - checksum/secret: {{ include (print $.Template.BasePath "/console-secret.yaml") . | sha256sum }} + checksum/secret: {{ include "rustfs-operator.consoleSecretChecksum" . }} spec: serviceAccountName: {{ include "rustfs-operator.consoleServiceAccountName" . }} {{- if $openShiftEnabled }} @@ -73,7 +77,7 @@ spec: - name: JWT_SECRET valueFrom: secretKeyRef: - name: {{ include "rustfs-operator.fullname" . }}-console-secret + name: {{ $consoleSecretName | quote }} key: jwt-secret {{- with .Values.console.bindAddress }} - name: CONSOLE_BIND_ADDRESS diff --git a/deploy/rustfs-operator/templates/console-secret.yaml b/deploy/rustfs-operator/templates/console-secret.yaml index 8186c0d..1e1325c 100755 --- a/deploy/rustfs-operator/templates/console-secret.yaml +++ b/deploy/rustfs-operator/templates/console-secret.yaml @@ -1,4 +1,4 @@ -{{- if .Values.console.enabled -}} +{{- if and .Values.console.enabled (not .Values.console.existingSecret) -}} {{- $secretName := printf "%s-console-secret" (include "rustfs-operator.fullname" .) -}} {{- $namespace := include "rustfs-operator.namespace" . -}} {{- $existingSecret := lookup "v1" "Secret" $namespace $secretName -}} diff --git a/deploy/rustfs-operator/values.schema.json b/deploy/rustfs-operator/values.schema.json index ead0aaf..db3959a 100644 --- a/deploy/rustfs-operator/values.schema.json +++ b/deploy/rustfs-operator/values.schema.json @@ -68,6 +68,12 @@ "console": { "type": "object", "properties": { + "existingSecret": { + "type": "string", + "maxLength": 253, + "pattern": "^$|^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?)*$", + "description": "Name of a pre-existing Secret in the operator namespace containing the key jwt-secret; when set the chart does not create its own Console Secret." + }, "loginAdmission": { "$ref": "#/definitions/admission" } diff --git a/deploy/rustfs-operator/values.yaml b/deploy/rustfs-operator/values.yaml index 11002a9..80a35a3 100755 --- a/deploy/rustfs-operator/values.yaml +++ b/deploy/rustfs-operator/values.yaml @@ -222,6 +222,12 @@ console: # Generate with: openssl rand -base64 32 jwtSecret: "" + # Name of a pre-existing Secret in the operator namespace that contains the key + # "jwt-secret" used to encrypt in-process Console session data. When set, the chart + # does not create its own Console Secret and the Console Deployment references this + # Secret directly. Mutually exclusive with console.jwtSecret. + existingSecret: "" + image: # Console uses the same image as operator repository: rustfs/operator