Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
900756d
feat(helm): add Redis auth support with password bootstrap
sebastianiv21 Jun 4, 2026
3b44511
Merge branch 'release' into claude/strange-colden-2a0830
sebastianiv21 Jun 4, 2026
86564b9
Merge branch 'release' into claude/strange-colden-2a0830
sebastianiv21 Jun 4, 2026
964a971
chore: bump helm chart version to v3.8.2
sebastianiv21 Jun 9, 2026
812db5c
chore(client): batch resolution upgrades to fix 22 Dependabot alerts …
subrata71 Jun 5, 2026
a1dfc27
chore(plugins): upgrade mssql-jdbc to fix Dependabot alert (#41880)
subrata71 Jun 8, 2026
f334e14
chore(widgets): upgrade tinymce from 6.8.5 to 7.9.3 to fix 8 XSS aler…
subrata71 Jun 9, 2026
b649f1d
chore(client): add shell-quote 1.8.4 resolution to fix critical alert…
subrata71 Jun 9, 2026
dfca3cd
chore(helm): replace README with helm-docs generated file to keep val…
sebastianiv21 Jun 9, 2026
2c81821
chore(deps): bump golang from 1.26.3-alpine to 1.26.4-alpine in /depl…
dependabot[bot] Jun 9, 2026
ff71415
feat(helm): add Redis auth support with password bootstrap
sebastianiv21 Jun 9, 2026
58ffcdc
Merge branch 'release' into claude/strange-colden-2a0830
sebastianiv21 Jun 9, 2026
7788231
chore: generate README
sebastianiv21 Jun 9, 2026
4e79144
chore: add documentation comments for Redis auth parameters
sebastianiv21 Jun 9, 2026
ddf0389
fix(helm): guard against unsupported redis.auth.password configurations
wyattwalter Jun 24, 2026
319b4c7
fix(helm): skip redis-init-container REDISCLI_AUTH on self-managed pa…
wyattwalter Jun 24, 2026
d279bea
docs(helm): record redis.auth.password self-managed-path invariants i…
wyattwalter Jun 24, 2026
f175f91
Revert "docs(helm): record redis.auth.password self-managed-path inva…
wyattwalter Jun 25, 2026
cf9fa0d
Merge pull request #41924 from appsmithorg/fm/redis-pw-guard-v3
wyattwalter Jun 25, 2026
c659b7b
fix(helm): derive redis master host from subchart fullname + tests
sebastianiv21 Jun 26, 2026
c354051
fix(helm): make redisMasterHost self-contained so unittest CI passes
sebastianiv21 Jun 26, 2026
bc002b8
Revert "fix(helm): make redisMasterHost self-contained so unittest CI…
sebastianiv21 Jun 26, 2026
9921ef3
Merge branch 'release' into claude/strange-colden-2a0830
sebastianiv21 Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deploy/helm/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ sources:
- https://github.com/appsmithorg/appsmith
home: https://www.appsmith.com/
icon: https://assets.appsmith.com/appsmith-icon.png
version: 3.8.1
version: 3.8.2
dependencies:
- condition: redis.enabled
name: redis
Expand Down
13 changes: 12 additions & 1 deletion deploy/helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ for prerequisites, step-by-step setup, and platform-specific instructions (EKS,
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| redis.enabled | bool | `true` | Deploy the bundled Bitnami Redis subchart |
| redis.auth.enabled | bool | `false` | Enable Redis authentication |
| redis.auth.enabled | bool | `true` | Enable Redis authentication |
| redis.auth.existingSecret | string | `"appsmith-redis-secret"` | Name of the Secret holding the Redis password. Bootstrapped by the redisAuth.passwordInit Job when absent (see redisAuth below) |
| redis.auth.existingSecretPasswordKey | string | `"redis-password"` | Key within existingSecret that holds the Redis password |
| redis.master.nodeSelector | object | `{}` | Node selector for Redis master pods |
| redis.master.disableCommands | list | `[]` | Commands to disable on Redis master |
| redis.master.affinity | object | `{}` | Affinity rules for Redis master pods |
Expand All @@ -78,6 +80,15 @@ for prerequisites, step-by-step setup, and platform-specific instructions (EKS,
| redis.image.repository | string | `"redis"` | Redis image repository |
| redis.image.tag | string | `"7.4.9"` | Redis image tag |

### Redis Auth

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| redisAuth.passwordInit.image.registry | string | `"docker.io"` | Registry for the kubectl image |
| redisAuth.passwordInit.image.repository | string | `"alpine/kubectl"` | Repository path for the kubectl image |
| redisAuth.passwordInit.image.tag | string | `"latest"` | Image tag. "latest" is used by default to sidestep upstream tag retention; pin for reproducibility. |
| redisAuth.passwordInit.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy for the Job |

### MongoDB (Bitnami subchart)

| Key | Type | Default | Description |
Expand Down
51 changes: 51 additions & 0 deletions deploy/helm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,57 @@ Uses existing secret if provided, otherwise derives from the CR name
{{- end -}}
{{- end -}}

{{/*
Redis: password secret name
Uses existing secret if provided, otherwise derives "{release}-redis-secret"
*/}}
{{- define "appsmith.redisSecretName" -}}
{{- .Values.redis.auth.existingSecret | default (printf "%s-redis-secret" .Release.Name) -}}
{{- end -}}

{{/*
Redis: validate the redis.auth.password configuration.

redis.auth.password is a Bitnami subchart passthrough that the Appsmith
templates never read on their own. There is exactly ONE supported way to use
it: the fully self-managed path, where the operator also disables the chart's
bootstrap secret (existingSecret: "") and hands the app a matching connection
string via applicationConfig.APPSMITH_REDIS_URL. Any other use silently splits
the password between Redis and the app, so we fail fast instead.

Invoked from a template that always renders (configMap.yaml) so it evaluates on
every `helm template`/install/upgrade.
*/}}
{{- define "appsmith.validateRedisAuth" -}}
{{- if .Values.redis.auth.password -}}
{{- if or .Values.redis.auth.existingSecret (not .Values.applicationConfig.APPSMITH_REDIS_URL) -}}
{{ fail (printf "redis.auth.password is set, which is only supported on the self-managed path. Choose one of:\n 1. Leave redis.auth.password unset and let the chart bootstrap a password (default), or supply your own secret via redis.auth.existingSecret / redis.auth.existingSecretPasswordKey.\n 2. Self-manage the password: set redis.auth.password, set redis.auth.existingSecret: \"\", and set applicationConfig.APPSMITH_REDIS_URL=redis://:<password>@%s-redis-master:6379 so the app uses the same credential." .Release.Name) }}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Redis: master service hostname (FQDN inside the cluster).
Reuses the bundled `common.names.fullname` helper (the one the subchart's master
Service uses), evaluated in the redis subchart's context (.Subcharts.redis), so the
host always matches the Service it renders — including the edge cases where the
release name contains "redis" (the subchart collapses its fullname to just the
release name) or redis.nameOverride / redis.fullnameOverride is set.
Only valid when redis.enabled (the subchart context exists); all callers gate on it.
*/}}
{{- define "appsmith.redisMasterHost" -}}
{{- printf "%s-master.%s.svc.cluster.local" (include "common.names.fullname" .Subcharts.redis) (include "appsmith.namespace" .) -}}
{{- end -}}
Comment thread
sebastianiv21 marked this conversation as resolved.

{{/*
Redis: kubectl image used by the password-init Job.
Kept independent from the MongoDB equivalent so the two bootstraps don't share config.
*/}}
{{- define "appsmith.redisPasswordInitImage" -}}
{{- $img := .Values.redisAuth.passwordInit.image -}}
{{- printf "%s/%s:%s" $img.registry $img.repository $img.tag -}}
{{- end -}}

{{/*
Renders a value that contains template.
*/}}
Expand Down
5 changes: 3 additions & 2 deletions deploy/helm/templates/configMap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
{{- $postgresqlPassword := .Values.postgresql.auth.password -}}
{{- $postgresqlDatabase := .Values.postgresql.auth.database -}}
{{- $releaseName := .Release.Name -}}
{{- include "appsmith.validateRedisAuth" . -}}
apiVersion: v1
kind: ConfigMap
metadata:
Expand Down Expand Up @@ -37,8 +38,8 @@ data:
{{- end }}

{{- if and (eq "APPSMITH_REDIS_URL" $key) ( not $value) }}
{{- if $.Values.redis.enabled }}
{{ $key }}: redis://{{ $releaseName }}-redis-master.{{ $nameSpace }}.svc.cluster.local:6379
{{- if and $.Values.redis.enabled (not $.Values.redis.auth.enabled) }}
{{ $key }}: redis://{{ include "appsmith.redisMasterHost" $ }}:6379
{{- end }}
{{- end }}

Expand Down
31 changes: 30 additions & 1 deletion deploy/helm/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,21 @@ spec:
{{- else }}
image: "{{ .Values.redis.image.registry }}/{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}"
{{- end }}
command: ['sh', '-c', "until redis-cli -h {{.Release.Name}}-redis-master.{{.Release.Namespace}}.svc.cluster.local ping ; do echo waiting for redis; sleep 2; done"]
command: ['sh', '-c', "until redis-cli -h {{ include "appsmith.redisMasterHost" . }} ping ; do echo waiting for redis; sleep 2; done"]
{{- if and .Values.redis.auth.enabled (not .Values.applicationConfig.APPSMITH_REDIS_URL) }}
# Pull the password from the chart-managed Secret so the readiness ping can
# authenticate. Skipped when the operator supplies their own APPSMITH_REDIS_URL
# (the self-managed redis.auth.password path), because then no chart Secret exists
# to reference and an unresolvable secretKeyRef would wedge the pod in
# CreateContainerConfigError. The wait still works unauthenticated: `redis-cli ping`
# against an auth-required server replies NOAUTH but exits 0, satisfying the loop.
env:
- name: REDISCLI_AUTH
valueFrom:
secretKeyRef:
name: {{ include "appsmith.redisSecretName" . }}
key: {{ .Values.redis.auth.existingSecretPasswordKey }}
{{- end }}
{{- end }}
{{- if .Values.mongodb.enabled }}
- name: mongo-init-container
Expand Down Expand Up @@ -175,6 +189,21 @@ spec:
name: {{ include "appsmith.mongoOperatorSecretName" . }}
key: connectionString.standardSrv
{{- end }}
{{- if and .Values.redis.enabled .Values.redis.auth.enabled (not .Values.applicationConfig.APPSMITH_REDIS_URL) }}
# Assemble the authenticated Redis URL for the bundled subchart. Skipped when the
# user supplied applicationConfig.APPSMITH_REDIS_URL (which is honored via the
# ConfigMap) — an explicit env entry here would otherwise take precedence over
# envFrom and silently override it. The password is pulled from the Secret and
# injected by reference, so the cleartext never lands in the ConfigMap; order
# matters — the password var must precede the URL that references it via $(VAR).
- name: APPSMITH_REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "appsmith.redisSecretName" . }}
key: {{ .Values.redis.auth.existingSecretPasswordKey }}
- name: APPSMITH_REDIS_URL
value: "redis://:$(APPSMITH_REDIS_PASSWORD)@{{ include "appsmith.redisMasterHost" . }}:6379"
{{- end }}
envFrom:
- configMapRef:
name: {{ include "appsmith.fullname" . }}
Expand Down
139 changes: 139 additions & 0 deletions deploy/helm/templates/hooks/redis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
{{/*
Creates the Redis password secret via a pre-install/pre-upgrade Job when the
bundled redis subchart is enabled with auth on.

The Job is idempotent: if the secret already exists, it exits without change.
This keeps the password stable across Helm upgrades and ArgoCD re-syncs —
unlike a template-based `randAlphaNum` approach, which changes on every render
and would either lock clients out or force an ArgoCD ignoreDifferences rule.

The runtime "secret already exists -> exit 0" check is also the migration and
bring-your-own-secret guard: an upgrade of an install that already has the
secret (or a user who pre-created their own) is left untouched.

The resulting Secret has no Helm release labels/annotations and no
ownerReferences, so ArgoCD does not track or diff it.
*/}}
{{/*
Skip the bootstrap entirely when redis.auth.password is set: on that path the
operator self-manages the credential (Bitnami uses redis.auth.password directly)
and there is no chart secret to create. Safe ONLY because appsmith.validateRedisAuth
(see _helpers.tpl, invoked from configMap.yaml) rejects every redis.auth.password
configuration except the self-managed one (existingSecret: "" + a matching
APPSMITH_REDIS_URL) — so this can no longer leave a non-empty existingSecret
pointing at a secret the hook never creates.
*/}}
{{- if and .Values.redis.enabled .Values.redis.auth.enabled (not .Values.redis.auth.password) }}
{{- $secretName := include "appsmith.redisSecretName" . -}}
{{- $passwordKey := .Values.redis.auth.existingSecretPasswordKey -}}
{{- $jobName := printf "%s-redis-password-init" (include "appsmith.fullname" .) | trunc 63 | trimSuffix "-" -}}
{{- $namespace := include "appsmith.namespace" . -}}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ $jobName }}
namespace: {{ $namespace }}
labels:
{{- include "appsmith.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-5"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ $jobName }}
namespace: {{ $namespace }}
labels:
{{- include "appsmith.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-5"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
rules:
# Check whether the password secret already exists (scoped to one name).
- apiGroups: [""]
resources: ["secrets"]
resourceNames: [{{ $secretName | quote }}]
verbs: ["get"]
# Create it if it doesn't. resourceNames can't scope `create` (the name
# is in the request body, not the URL), so this is namespace-scoped.
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ $jobName }}
namespace: {{ $namespace }}
labels:
{{- include "appsmith.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-5"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
subjects:
- kind: ServiceAccount
name: {{ $jobName }}
namespace: {{ $namespace }}
roleRef:
kind: Role
name: {{ $jobName }}
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ $jobName }}
namespace: {{ $namespace }}
labels:
{{- include "appsmith.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
ttlSecondsAfterFinished: 60
backoffLimit: 3
template:
metadata:
labels:
{{- include "appsmith.labels" . | nindent 8 }}
spec:
serviceAccountName: {{ $jobName }}
restartPolicy: Never
{{- if .Values.image.pullSecrets }}
imagePullSecrets:
- name: {{ .Values.image.pullSecrets }}
{{- end }}
containers:
- name: create-password
image: {{ include "appsmith.redisPasswordInitImage" . | quote }}
imagePullPolicy: {{ .Values.redisAuth.passwordInit.image.pullPolicy }}
command:
- /bin/sh
- -c
- |
set -eu
SECRET_NAME={{ $secretName | quote }}
NAMESPACE={{ $namespace | quote }}
PASSWORD_KEY={{ $passwordKey | quote }}
if kubectl -n "$NAMESPACE" get secret "$SECRET_NAME" >/dev/null 2>&1; then
echo "Secret $SECRET_NAME already exists; leaving it untouched."
exit 0
fi
PASSWORD=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 24)
kubectl -n "$NAMESPACE" create secret generic "$SECRET_NAME" \
--from-literal="$PASSWORD_KEY"="$PASSWORD"
echo "Created secret $SECRET_NAME."
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop: ["ALL"]
{{- end }}
Loading
Loading