diff --git a/charts/retool/Chart.yaml b/charts/retool/Chart.yaml index b14c54f..d3ced8c 100644 --- a/charts/retool/Chart.yaml +++ b/charts/retool/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: retool description: A Helm chart for Kubernetes type: application -version: 6.11.2 +version: 6.11.3 maintainers: - name: Retool Engineering email: engineering+helm@retool.com diff --git a/charts/retool/ci/test-agent-sandbox-enabled-option.yaml b/charts/retool/ci/test-agent-sandbox-enabled-option.yaml index 32ba7f6..792c46c 100644 --- a/charts/retool/ci/test-agent-sandbox-enabled-option.yaml +++ b/charts/retool/ci/test-agent-sandbox-enabled-option.yaml @@ -7,9 +7,10 @@ rr: # the image-prepuller + seccomp DaemonSets, the smarter-device-manager device # plugin DaemonSet, the NetworkPolicies, and both PDBs. # - # Secret/Postgres sourcing here uses externalSecret.name (Postgres OPTION 4: - # the secret's postgres-url key). The other secret/Postgres precedence paths and - # the same-origin (no-ingress) proxy mode are covered by sibling files: + # Secrets come from externalSecret.name (JWT/encryption); Postgres reads its DSN + # from that same Secret via postgres.urlSecretName (Option 3). The other + # secret/Postgres precedence paths and the same-origin (no-ingress) proxy mode + # are covered by sibling files: # - test-agent-sandbox-inline-secrets-option.yaml (inline secrets, plaintext DSN, same-origin/no ingress, hostPath tun) # - test-agent-sandbox-postgres-fields-option.yaml (assemble DSN from fields + PGPASSWORD secret) # - test-agent-sandbox-postgres-url-secret-option.yaml (full DSN from an existing secret) @@ -31,6 +32,11 @@ rr: name: agent-sandbox-secrets postgres: + # Option 3: read the DSN from the same Secret that holds the JWT/encryption + # keys by pointing urlSecretName at it. externalSecret.name itself never + # sources Postgres. + urlSecretName: agent-sandbox-secrets + urlSecretKey: postgres-url schema: agent_executor poolMax: 10 diff --git a/charts/retool/ci/test-agent-sandbox-inherit-postgres-option.yaml b/charts/retool/ci/test-agent-sandbox-inherit-postgres-option.yaml index c5d09a4..0dc2c47 100644 --- a/charts/retool/ci/test-agent-sandbox-inherit-postgres-option.yaml +++ b/charts/retool/ci/test-agent-sandbox-inherit-postgres-option.yaml @@ -20,3 +20,5 @@ rr: jwtPublicKey: '-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEljtqa2nhBwe/PqNhWgPHhj0jv8AI\nY+QUCicYtfv9wLGcEGPQuXoBQtuoIuOwXOdbEWgrQyLdIEb0YjegAW3miA==\n-----END PUBLIC KEY-----' jwtPrivateKey: '-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMFXLiN/YsJv89D2YkEZ6/Dj5fujghENmYTOilwdChU3oAoGCCqGSM49\nAwEHoUQDQgAEljtqa2nhBwe/PqNhWgPHhj0jv8AIY+QUCicYtfv9wLGcEGPQuXoB\nQtuoIuOwXOdbEWgrQyLdIEb0YjegAW3miA==\n-----END EC PRIVATE KEY-----' + # encryption key is required (proxy derives the asset-token HMAC key from it) + encryptionKey: a12b01429fe0fe69a80da94e9e837ab2f1e9bda378ed8a25905a238f6fea6b7a diff --git a/charts/retool/ci/test-agent-sandbox-postgres-fields-option.yaml b/charts/retool/ci/test-agent-sandbox-postgres-fields-option.yaml index 2b4e25b..3e9c12a 100644 --- a/charts/retool/ci/test-agent-sandbox-postgres-fields-option.yaml +++ b/charts/retool/ci/test-agent-sandbox-postgres-fields-option.yaml @@ -21,6 +21,8 @@ rr: jwtPublicKey: '-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEljtqa2nhBwe/PqNhWgPHhj0jv8AI\nY+QUCicYtfv9wLGcEGPQuXoBQtuoIuOwXOdbEWgrQyLdIEb0YjegAW3miA==\n-----END PUBLIC KEY-----' jwtPrivateKey: '-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMFXLiN/YsJv89D2YkEZ6/Dj5fujghENmYTOilwdChU3oAoGCCqGSM49\nAwEHoUQDQgAEljtqa2nhBwe/PqNhWgPHhj0jv8AIY+QUCicYtfv9wLGcEGPQuXoB\nQtuoIuOwXOdbEWgrQyLdIEb0YjegAW3miA==\n-----END EC PRIVATE KEY-----' + # encryption key is required (proxy derives the asset-token HMAC key from it) + encryptionKey: a12b01429fe0fe69a80da94e9e837ab2f1e9bda378ed8a25905a238f6fea6b7a # Option 2: host + user + database, password via PGPASSWORD secretKeyRef. postgres: diff --git a/charts/retool/ci/test-rr-enabled-option.yaml b/charts/retool/ci/test-rr-enabled-option.yaml index aae0c9d..b61ecc7 100644 --- a/charts/retool/ci/test-rr-enabled-option.yaml +++ b/charts/retool/ci/test-rr-enabled-option.yaml @@ -18,6 +18,8 @@ rr: # be single-line (\n-escaped) or templating breaks. jwtPublicKey: '-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEljtqa2nhBwe/PqNhWgPHhj0jv8AIY+QUCicYtfv9wLGcEGPQuXoBQtuoIuOwXOdbEWgrQyLdIEb0YjegAW3miA==\n-----END PUBLIC KEY-----' jwtPrivateKey: '-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMFXLiN/YsJv89D2YkEZ6/Dj5fujghENmYTOilwdChU3oAoGCCqGSM49AwEHoUQDQgAEljtqa2nhBwe/PqNhWgPHhj0jv8AIY+QUCicYtfv9wLGcEGPQuXoBQtuoIuOwXOdbEWgrQyLdIEb0YjegAW3miA==\n-----END EC PRIVATE KEY-----' + # encryption key is required (proxy derives the asset-token HMAC key from it) + encryptionKey: a12b01429fe0fe69a80da94e9e837ab2f1e9bda378ed8a25905a238f6fea6b7a postgres: url: postgres://retool:retool@agent-sandbox-db.example.internal:5432/agent_sandbox schema: agent_executor diff --git a/charts/retool/templates/_helpers.tpl b/charts/retool/templates/_helpers.tpl index 2989dc3..60521e9 100644 --- a/charts/retool/templates/_helpers.tpl +++ b/charts/retool/templates/_helpers.tpl @@ -676,14 +676,14 @@ or the catch-all externalSecret.name. No-op when agentSandbox is disabled. {{- if eq (include "retool.rr.componentEnabled" (dict "root" . "component" "agentSandbox")) "1" -}} {{- $as := .Values.rr.agentSandbox -}} {{- $ext := $as.externalSecret.name -}} -{{- $explicitPg := or $as.postgres.url $as.postgres.urlSecretName $as.postgres.host $ext -}} +{{- $explicitPg := or $as.postgres.url $as.postgres.urlSecretName $as.postgres.host -}} {{- if not $explicitPg -}} {{- /* No explicit source: inherit the backend's Postgres connection. */ -}} {{- if not (include "retool.postgresql.host" . | trimAll "\"") -}} -{{- fail "agentSandbox.enabled defaults to reusing the backend's Postgres connection, but config.postgresql resolved no host. Set agentSandbox.postgres.url / .host / .urlSecretName / externalSecret.name, or configure config.postgresql." -}} +{{- fail "agentSandbox.enabled defaults to reusing the backend's Postgres connection, but config.postgresql resolved no host. Set agentSandbox.postgres.url / .host / .urlSecretName (point .urlSecretName at your externalSecret to reuse its postgres-url key; externalSecret.name alone only covers the JWT/encryption keys), or configure config.postgresql." -}} {{- end -}} {{- if not (or .Values.postgresql.enabled .Values.config.postgresql.passwordSecretName (eq (include "shouldIncludeConfigSecretsEnvVars" . | trim) "1")) -}} -{{- fail "agentSandbox.postgres is unset so it would inherit the backend's Postgres password, but that password is supplied via external secrets (envFrom) and cannot be referenced from a separate pod. Set agentSandbox.postgres.url / .urlSecretName / .host (+ passwordSecretName), or agentSandbox.externalSecret.name." -}} +{{- fail "agentSandbox.postgres is unset so it would inherit the backend's Postgres password, but that password is supplied via external secrets (envFrom) and cannot be referenced from a separate pod. Set agentSandbox.postgres.url / .urlSecretName / .host (+ passwordSecretName) -- .urlSecretName can point at your externalSecret's postgres-url key (externalSecret.name alone only covers the JWT/encryption keys)." -}} {{- end -}} {{- end -}} {{- if $as.postgres.host -}} @@ -714,6 +714,9 @@ or the catch-all externalSecret.name. No-op when agentSandbox is disabled. {{- if not (or $as.jwtPrivateKey $ext) -}} {{- fail "agentSandbox.enabled requires a JWT private key (the backend signs sandbox tokens with it). Set agentSandbox.jwtPrivateKey or agentSandbox.externalSecret.name." -}} {{- end -}} +{{- if not (or $as.encryptionKey $ext) -}} +{{- fail "agentSandbox.enabled requires an encryption key: the proxy derives the sandbox-iframe asset-token HMAC key from it and throws when serving a sandbox without it, and the backend must use the same value. Set agentSandbox.encryptionKey (64 hex chars, openssl rand -hex 32) or agentSandbox.externalSecret.name (with an encryption-key entry)." -}} +{{- end -}} {{- end -}} {{- end -}} @@ -721,8 +724,10 @@ or the catch-all externalSecret.name. No-op when agentSandbox is disabled. Render the AGENT_SANDBOX_POSTGRES_URL env entry for the controller/proxy (plus a PGPASSWORD entry when assembling from fields). validateSecrets guarantees one of these applies, in order: postgres.url -> postgres.host -> postgres.urlSecretName --> externalSecret.name -> inherit the backend's config.postgresql connection -(the default when nothing agent-specific is set). +-> inherit the backend's config.postgresql connection (the default when nothing +agent-specific is set). externalSecret.name covers only the JWT/encryption keys +-- it never sources Postgres. To read a DSN from that same secret, point +postgres.urlSecretName at it (its postgres-url key is the urlSecretKey default). For the host path the password is passed via PGPASSWORD rather than embedded in the URL: node-postgres reads PGPASSWORD when the connection string omits the @@ -737,7 +742,6 @@ Usage: {{- include "retool.agentSandbox.postgresUrlEnv" . | nindent 12 }} */}} {{- define "retool.agentSandbox.postgresUrlEnv" -}} {{- $pg := .Values.rr.agentSandbox.postgres -}} -{{- $ext := .Values.rr.agentSandbox.externalSecret.name -}} {{- if $pg.url }} - name: AGENT_SANDBOX_POSTGRES_URL value: {{ $pg.url | quote }} @@ -761,12 +765,18 @@ Usage: {{- include "retool.agentSandbox.postgresUrlEnv" . | nindent 12 }} secretKeyRef: name: {{ $pg.urlSecretName }} key: {{ $pg.urlSecretKey | default "postgres-url" }} -{{- else if $ext }} -- name: AGENT_SANDBOX_POSTGRES_URL +{{- /* + The DSN may omit the password; supply it separately via passwordSecretName so + an auto-rotated password (e.g. the backend's RDS secret) isn't duplicated into + the DSN secret. node-postgres reads PGPASSWORD when the URL omits the password. +*/}} +{{- if $pg.passwordSecretName }} +- name: PGPASSWORD valueFrom: secretKeyRef: - name: {{ $ext }} - key: postgres-url + name: {{ $pg.passwordSecretName }} + key: {{ $pg.passwordSecretKey | default "password" }} +{{- end }} {{- else }} {{- /* Default: inherit the backend's Postgres connection (config.postgresql or the diff --git a/charts/retool/templates/_workers.tpl b/charts/retool/templates/_workers.tpl index 36c1e7e..b82b439 100644 --- a/charts/retool/templates/_workers.tpl +++ b/charts/retool/templates/_workers.tpl @@ -230,6 +230,17 @@ spec: value: http://{{ template "retool.jsExecutor.name" $ }} {{- end }} {{- include "retool.agentSandbox.backendEnvVars" $ | nindent 10 }} + {{- if $.Values.rr.gitServer.enabled }} + {{- /* + Snapshot blob storage: the agentExecutor / snapshotRetention temporal + activities run on this worker and read RR_SNAPSHOTS_* with an + RR_DEFAULT_* fallback (backend getBlobStoreForSnapshots). Render the + same blobStorage env the backend and git-server get, so the fallback + resolves here too. No git-server host/port split is needed -- the + worker is a blob-storage client, not the git server itself. + */}} + {{- include "retool.gitServer.commonEnv" $ | nindent 10 }} + {{- end }} {{- include "retool.telemetry.includeEnvVars" $ | nindent 10 }} diff --git a/charts/retool/values.yaml b/charts/retool/values.yaml index f1efd1e..74934fd 100644 --- a/charts/retool/values.yaml +++ b/charts/retool/values.yaml @@ -887,14 +887,16 @@ rr: # === Secrets ============================================================ # Provide each secret as a plaintext value below, OR set externalSecret.name # to a pre-existing Secret with keys jwt-public-key, jwt-private-key, - # encryption-key, api-secret, postgres-url. A plaintext value always wins over - # the external secret for that key. + # encryption-key, api-secret. A plaintext value always wins over the external + # secret for that key. externalSecret.name covers ONLY these app secrets -- + # it does not source Postgres. To read a DSN from that same Secret, point + # postgres.urlSecretName at it (see Postgres Option 3 below). externalSecret: - name: '' # optional: existing Secret holding all keys below + name: '' # optional: existing Secret holding the keys below jwtPublicKey: '' # REQUIRED (ES256) unless provided via externalSecret jwtPrivateKey: '' # REQUIRED (ES256) unless provided via externalSecret - encryptionKey: '' # optional: hex 256-bit; must match backend AGENT_SANDBOX_ENCRYPTION_KEY + encryptionKey: '' # REQUIRED: 64 hex chars (openssl rand -hex 32, NOT base64). Proxy throws when serving a sandbox without it; must match the backend's AGENT_SANDBOX_ENCRYPTION_KEY. May instead be supplied via externalSecret.name (encryption-key entry). apiSecret: '' # optional: admin/test endpoints # === Postgres state backend ============================================= @@ -923,15 +925,16 @@ rr: passwordSecretName: '' passwordSecretKey: 'password' - # -- Option 3: existing Secret holding the full DSN -- + # -- Option 3: DSN from an existing Secret -- + # Set urlSecretName (urlSecretKey defaults to postgres-url). To reuse the + # Secret in rr.agentSandbox.externalSecret.name, just point urlSecretName at + # it -- externalSecret.name itself never sources Postgres. + # The DSN may omit the password; supply it separately via passwordSecretName + # (above) so an auto-rotated password isn't duplicated into the DSN secret. urlSecretName: '' urlSecretKey: 'postgres-url' - # -- Option 4: reuse externalSecret.name (its postgres-url key) -- - # Selected by setting rr.agentSandbox.externalSecret.name (in the Secrets - # section above), not by anything here. Used when options 1-3 are blank. - # - # If options 1-4 are ALL unset, the default (inherit config.postgresql) + # If options 1-3 are ALL unset, the default (inherit config.postgresql) # applies -- see the note at the top of this block. # -- Optional tuning (defaults shown) -- diff --git a/values.yaml b/values.yaml index f1efd1e..74934fd 100644 --- a/values.yaml +++ b/values.yaml @@ -887,14 +887,16 @@ rr: # === Secrets ============================================================ # Provide each secret as a plaintext value below, OR set externalSecret.name # to a pre-existing Secret with keys jwt-public-key, jwt-private-key, - # encryption-key, api-secret, postgres-url. A plaintext value always wins over - # the external secret for that key. + # encryption-key, api-secret. A plaintext value always wins over the external + # secret for that key. externalSecret.name covers ONLY these app secrets -- + # it does not source Postgres. To read a DSN from that same Secret, point + # postgres.urlSecretName at it (see Postgres Option 3 below). externalSecret: - name: '' # optional: existing Secret holding all keys below + name: '' # optional: existing Secret holding the keys below jwtPublicKey: '' # REQUIRED (ES256) unless provided via externalSecret jwtPrivateKey: '' # REQUIRED (ES256) unless provided via externalSecret - encryptionKey: '' # optional: hex 256-bit; must match backend AGENT_SANDBOX_ENCRYPTION_KEY + encryptionKey: '' # REQUIRED: 64 hex chars (openssl rand -hex 32, NOT base64). Proxy throws when serving a sandbox without it; must match the backend's AGENT_SANDBOX_ENCRYPTION_KEY. May instead be supplied via externalSecret.name (encryption-key entry). apiSecret: '' # optional: admin/test endpoints # === Postgres state backend ============================================= @@ -923,15 +925,16 @@ rr: passwordSecretName: '' passwordSecretKey: 'password' - # -- Option 3: existing Secret holding the full DSN -- + # -- Option 3: DSN from an existing Secret -- + # Set urlSecretName (urlSecretKey defaults to postgres-url). To reuse the + # Secret in rr.agentSandbox.externalSecret.name, just point urlSecretName at + # it -- externalSecret.name itself never sources Postgres. + # The DSN may omit the password; supply it separately via passwordSecretName + # (above) so an auto-rotated password isn't duplicated into the DSN secret. urlSecretName: '' urlSecretKey: 'postgres-url' - # -- Option 4: reuse externalSecret.name (its postgres-url key) -- - # Selected by setting rr.agentSandbox.externalSecret.name (in the Secrets - # section above), not by anything here. Used when options 1-3 are blank. - # - # If options 1-4 are ALL unset, the default (inherit config.postgresql) + # If options 1-3 are ALL unset, the default (inherit config.postgresql) # applies -- see the note at the top of this block. # -- Optional tuning (defaults shown) --