diff --git a/.agents/architecture.md b/.agents/architecture.md index 92acbb2..44f11e2 100644 --- a/.agents/architecture.md +++ b/.agents/architecture.md @@ -91,6 +91,13 @@ editing templates or values. `authup.tplvalues.render`, so umbrella charts can inject template expressions (the PrivateAIM lesson: their untemplatable `existingSecret` forced a hardcoded-names table). + `server.route.enabled` / `adminConsole.route.enabled` extend this to a + BOOLEAN, read through `authup.flag`. That reader is strict by necessity: + the schema is widened to `[boolean, string]` so it no longer rejects + garbage, and a rendered `"false"` is a non-empty (truthy) string, so a + plain `if` would create the route exactly when the parent switched it off. + All six read sites (2 HTTPRoutes, 2 validations, 2 NOTES) convert together + or the sub-path catch-all guard of rule 18 stops covering umbrella users. 17. **`global` must stay open in the schema.** helm copies a parent chart's ENTIRE `global` map into every subchart before validating that subchart's schema, so `additionalProperties: false` there makes the chart diff --git a/.agents/references/authup.md b/.agents/references/authup.md index 6e9639d..59b5118 100644 --- a/.agents/references/authup.md +++ b/.agents/references/authup.md @@ -3,7 +3,7 @@ Repo: https://github.com/authup/authup (local checkout commonly at `/opt/projects/authup/authup`). The chart encodes facts about the app; verify against these sources when authup releases change behavior. Pinned against the -v1.0.0-beta.62 line (chart `appVersion`). +v1.0.0-beta.63 line (chart `appVersion`). ## Image / entrypoint contract @@ -11,7 +11,7 @@ v1.0.0-beta.62 line (chart `appVersion`). |---|---|---| | One image `authup/authup`, arg-dispatched entrypoint (`server/core start`, `client/admin-console start`, `server/core migration run`, `server/core healthcheck`) | `Dockerfile`, `entrypoint.sh` (repo root) | `args` in `templates/{server,admin-console}/deployment.yaml`, `server/migration-job.yaml` | | Entrypoint force-exports `PORT=3000` / `NUXT_PORT=3000` (chart-set PORT is dead) | `entrypoint.sh` | containerPort pinned 3000 everywhere | -| Image runs as root; writable paths `/usr/src/app/writable` + npm cache | `Dockerfile` (`WRITABLE_DIRECTORY_PATH`, no `USER`) | emptyDir mounts + `npm_config_cache=/tmp/.npm-cache`; root securityContext default | +| Image runs as root; writable directory `/var/lib/authup` (moved there from `/usr/src/app/writable` in v1.0.0-beta.63, authup/authup#3474) + npm cache. The CODE default is still `/writable`, so only the image sets the FHS path | `Dockerfile` (`WRITABLE_DIRECTORY_PATH`, no `USER`), `apps/server-core/src/app/modules/config/read/env.ts` | emptyDir mounts + `npm_config_cache=/tmp/.npm-cache`; root securityContext default. The chart SETS `WRITABLE_DIRECTORY_PATH` to the path it mounts instead of inheriting the image default, so it works on either side of that bump and with a pinned older `image.tag` | | `latest`/``/`beta`/`next` tags | `.github/workflows/release.yml`, `docker-nightly.yml` | `image.tag` defaults to `Chart.AppVersion` | | `authup` CLI supervisor NOT routable through the entrypoint | `entrypoint.sh` case statement | chart never offers a combined pod | | An unknown service arg EXITS 1 since beta.59 (it used to exit 0 and start nothing); `client/web` was renamed `client/admin-console` with no alias | `entrypoint.sh` `*)` branch | chart already passes `client/admin-console` | diff --git a/.agents/testing.md b/.agents/testing.md index dbe2769..8ffaf33 100644 --- a/.agents/testing.md +++ b/.agents/testing.md @@ -46,6 +46,9 @@ helm template t charts/authup --set server.publicUrl=auth.example.com # s helm template t charts/authup --set postgresql.enabled=false --set externalDatabase.host=db # extdb w/o password helm template t charts/authup --set server.ingress.enabled=true # ingress w/o hostname helm template t charts/authup --set server.config.PUBLIC_URL=http://x # first-class collision +helm template t charts/authup --set server.config.WRITABLE_DIRECTORY_PATH=/x # ditto; the chart pins this one to the path it mounts +helm template t charts/authup --set 'server.route.enabled=yes' # flag that is neither true nor false +helm template t charts/authup --set adminConsole.enabled=false --set adminConsole.route.enabled=yes # ditto: validated even with the component off helm template t charts/authup --set server.theme.enabled=true # theme with no carrier helm template t charts/authup --set server.theme.enabled=true --set server.theme.title=X --set server.theme.existingConfigMap=cm # manifest + existing CM helm template t charts/authup --set server.theme.enabled=true --set server.theme.logo=logo.svg # asset outside assets/ @@ -62,6 +65,24 @@ case needs its own line: only the origin reaches the HTTPRoute hostname, and the dropped path is exactly what turns the rule into a catch-all. Adding `--set 'server.route.matches[0].path.value=/auth'` must make both RENDER. +`server.route.enabled` / `adminConsole.route.enabled` accept a tpl-rendered +string, so an umbrella can drive them from one of its own switches. `--set-string` +cannot carry `{{ }}` (helm fails parsing on the closing brace), so both directions +go through a values file, and BOTH are needed: a rendered `"false"` is a non-empty +string, which a Go template `if` reads as true. + +```bash +printf 'global:\n gw:\n enabled: false\nserver:\n route:\n enabled: "{{ .Values.global.gw.enabled }}"\n' \ + | helm template t charts/authup -f - | grep -c 'kind: HTTPRoute' # must be 0 +printf 'global:\n gw:\n enabled: true\nserver:\n publicUrl: https://auth.example.com\n route:\n enabled: "{{ .Values.global.gw.enabled }}"\n parentRefs:\n - name: gw\n' \ + | helm template t charts/authup -f - | grep -c 'kind: HTTPRoute' # must be 1 +``` + +The sub-path catch-all guard and the NOTES path-prefix warning read the same +flag, so all six read sites convert together: leave one raw and an umbrella-driven +route renders unguarded. `ci/default-values.yaml` carries the false direction as +the in-repo regression guard. + Umbrella use is part of the contract: `global` must stay open. Render a throwaway parent chart with authup in `charts/` and an unrelated global (`global.myOrgKey`) whenever the schema generation changes; `ci/default-values.yaml` carries a stray diff --git a/DESIGN.md b/DESIGN.md index f9dfbe7..0632940 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -349,7 +349,7 @@ fail-closed semantics (an invalid file aborts boot) and camelCase keys. - Hardened `podSecurityContext` / `containerSecurityContext` defaults (runAsNonRoot 1000, readOnlyRootFilesystem, drop ALL, seccompRuntimeDefault) — with the caveat that the upstream image runs as root and `npm` wants a writable - HOME: the chart mounts emptyDirs at `/usr/src/app/writable` and `/tmp`, sets + HOME: the chart mounts emptyDirs at `/var/lib/authup` and `/tmp`, sets `npm_config_cache=/tmp/.npm-cache`, and documents that full hardening is best-effort until upstream ships a non-root image (tracked as an upstream issue). - `/metrics` is unauthenticated: `metrics.serviceMonitor` targets the Service diff --git a/charts/authup/BREAKING.md b/charts/authup/BREAKING.md index a505ac0..3bd34a2 100644 --- a/charts/authup/BREAKING.md +++ b/charts/authup/BREAKING.md @@ -3,6 +3,24 @@ This chart uses `0.major.minor` versioning while below 1.0.0: breaking changes land on the middle digit. Every entry lists the value migrations required. +## Next release (unreleased) + +- The writable directory moves from `/usr/src/app/writable` to `/var/lib/authup`, + following the image (authup/authup#3474, shipped in v1.0.0-beta.63). The chart + mounts an emptyDir there, so nothing persists across the change; only a + `server.extraVolumeMounts` / `server.extraVolumes` entry aimed at the old path + needs updating, along with anything reading the container's log files by path. +- The chart now SETS `WRITABLE_DIRECTORY_PATH` to the path it mounts instead of + inheriting the image default, so it works with a pinned older `image.tag` too. + As a consequence `server.config.WRITABLE_DIRECTORY_PATH` now fails the render: + it would have emitted a duplicate ConfigMap key and pointed the server at a + path the chart mounts nothing at, which fails silently (production logs on the + container layer, file provisioning scanning a directory that does not exist). + To move the directory anyway, set it through `server.extraEnvVars` and mount + the same path with `server.extraVolumeMounts`; `server.provisioning` then needs + its source (ConfigMap or Secret) re-mounted at `/provisioning` by + hand, because the chart's own provisioning mount stays where the chart puts it. + ## 0.2.0 (unreleased) Follows the upstream rename of the admin UI app (authup/authup#3370) and its diff --git a/charts/authup/Chart.yaml b/charts/authup/Chart.yaml index 6b29fe2..f37db84 100644 --- a/charts/authup/Chart.yaml +++ b/charts/authup/Chart.yaml @@ -5,7 +5,7 @@ description: Authup is an authentication & authorization system. This chart with optional built-in PostgreSQL, MySQL and Valkey instances. type: application version: 0.2.2 -appVersion: "1.0.0-beta.62" +appVersion: "1.0.0-beta.63" kubeVersion: ">=1.25.0-0" home: https://authup.org icon: https://raw.githubusercontent.com/authup/helm/master/assets/icon.svg @@ -32,8 +32,8 @@ annotations: url: https://github.com/authup/helm artifacthub.io/changes: | - kind: fixed - description: A parent chart's own global.* keys no longer fail schema validation + description: The writable directory moves to /var/lib/authup and WRITABLE_DIRECTORY_PATH is set to the path the chart mounts, so an image default change cannot silently stop file provisioning + - kind: changed + description: Track authup v1.0.0-beta.63, whose image writes to /var/lib/authup - kind: added - description: server.route / adminConsole.route accept matches and filters - - kind: added - description: An HTTPRoute for a sub-path public URL without matches now fails the render + description: server.route.enabled / adminConsole.route.enabled accept a tpl-rendered string, so an umbrella chart can drive them from a global diff --git a/charts/authup/README.md b/charts/authup/README.md index 16673d3..d5e6879 100644 --- a/charts/authup/README.md +++ b/charts/authup/README.md @@ -7,7 +7,7 @@ # authup ![Version](https://img.shields.io/badge/Version-0.2.2?style=flat-square&color=informational) -![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0-beta.62](https://img.shields.io/badge/AppVersion-1.0.0--beta.62-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0-beta.63](https://img.shields.io/badge/AppVersion-1.0.0--beta.63-informational?style=flat-square) Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-admin-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. It deploys: @@ -230,7 +230,7 @@ Kubernetes: `>=1.25.0-0` | adminConsole.resources | object | `{"limits":{"memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | UI container resources | | adminConsole.revisionHistoryLimit | int | `3` | Deployment revision history limit | | adminConsole.route.annotations | object | `{}` | HTTPRoute annotations | -| adminConsole.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for the UI | +| adminConsole.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for the UI (tpl-rendered: a string rendering to "true" enables it, so an umbrella chart can drive this from one of its own switches; "false" and "" disable it, anything else fails the render) | | adminConsole.route.filters | list | `[]` | Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix | | adminConsole.route.hostnames | list | `[]` | Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname; only the host is kept, a public URL path is dropped and needs its own matches entry) | | adminConsole.route.matches | list | `[]` | Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix "/" | @@ -349,7 +349,7 @@ Kubernetes: `>=1.25.0-0` | server.command | list | `[]` | Override the container command | | server.config | object | `{}` | Extra environment variables rendered literally into the env ConfigMap (map of NAME: value) for options without first-class values, e.g. AUTH_CONSOLE_PATH / ACCOUNT_CONSOLE_PATH, which replace a served console with your own build (pair them with extraVolumes; the substituted package owns the login flow, so use server.theme for branding instead) | | server.configuration | string | `""` | Content of an authup.server.core.conf mounted into the working directory for file-only options (middleware objects, per-field SMTP, CORS allowlist). Environment variables always win over file values. | -| server.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":false,"runAsNonRoot":false,"runAsUser":0,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context. The upstream image runs as root and needs a writable npm cache; the chart mounts emptyDirs at /usr/src/app/writable and /tmp to keep readOnlyRootFilesystem viable. | +| server.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":false,"runAsNonRoot":false,"runAsUser":0,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context. The upstream image runs as root and needs a writable npm cache; the chart mounts emptyDirs at /var/lib/authup and /tmp to keep readOnlyRootFilesystem viable. | | server.customLivenessProbe | object | `{}` | Custom liveness probe | | server.customReadinessProbe | object | `{}` | Custom readiness probe | | server.customStartupProbe | object | `{}` | Custom startup probe overriding the structured one | @@ -434,7 +434,7 @@ Kubernetes: `>=1.25.0-0` | server.resources | object | `{"limits":{"memory":"2Gi"},"requests":{"cpu":"250m","memory":"512Mi"}}` | Server container resources | | server.revisionHistoryLimit | int | `3` | Deployment revision history limit | | server.route.annotations | object | `{}` | HTTPRoute annotations | -| server.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for server-core | +| server.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for server-core (tpl-rendered: a string rendering to "true" enables it, so an umbrella chart can drive this from one of its own switches; "false" and "" disable it, anything else fails the render) | | server.route.filters | list | `[]` | Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix | | server.route.hostnames | list | `[]` | Route hostnames ([] = derived from server.publicUrl / ingress hostname; only the host is kept, a public URL path is dropped and needs its own matches entry) | | server.route.matches | list | `[]` | Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix "/" | diff --git a/charts/authup/ci/default-values.yaml b/charts/authup/ci/default-values.yaml index ec449d4..f77f99a 100644 --- a/charts/authup/ci/default-values.yaml +++ b/charts/authup/ci/default-values.yaml @@ -4,6 +4,10 @@ # reject globals this chart does not declare. global: umbrellaProbe: parent-injected + # A templated route.enabled rendering to "false" is the second umbrella guard: + # the schema must accept the string, and the render must produce no HTTPRoute + # (a rendered "false" is a non-empty, i.e. truthy, string to a Go template). + gatewayApiProbe: false postgresql: persistence: enabled: false @@ -17,3 +21,5 @@ adminConsole: requests: cpu: 25m memory: 128Mi + route: + enabled: '{{ .Values.global.gatewayApiProbe }}' diff --git a/charts/authup/templates/NOTES.txt b/charts/authup/templates/NOTES.txt index 19a529e..1ee7dd2 100644 --- a/charts/authup/templates/NOTES.txt +++ b/charts/authup/templates/NOTES.txt @@ -99,10 +99,10 @@ small deployments. For production, consider an external database {{- end }} {{- $prefixed := list }} -{{- if and .Values.server.enabled .Values.server.route.enabled (urlParse $serverUrl).path (ne (urlParse $serverUrl).path "/") }} +{{- if and .Values.server.enabled (include "authup.flag" (dict "value" .Values.server.route.enabled "context" $ "key" "server.route.enabled")) (urlParse $serverUrl).path (ne (urlParse $serverUrl).path "/") }} {{- $prefixed = append $prefixed (printf "server.route %s" (urlParse $serverUrl).path) }} {{- end }} -{{- if and .Values.adminConsole.enabled .Values.adminConsole.route.enabled (urlParse $uiUrl).path (ne (urlParse $uiUrl).path "/") }} +{{- if and .Values.adminConsole.enabled (include "authup.flag" (dict "value" .Values.adminConsole.route.enabled "context" $ "key" "adminConsole.route.enabled")) (urlParse $uiUrl).path (ne (urlParse $uiUrl).path "/") }} {{- $prefixed = append $prefixed (printf "adminConsole.route %s" (urlParse $uiUrl).path) }} {{- end }} {{- if $prefixed }} diff --git a/charts/authup/templates/_helpers.tpl b/charts/authup/templates/_helpers.tpl index 9b362b4..ad125b4 100644 --- a/charts/authup/templates/_helpers.tpl +++ b/charts/authup/templates/_helpers.tpl @@ -73,6 +73,26 @@ Usage: {{ include "authup.tplvalues.render" (dict "value" .Values. "contex {{- end -}} {{- end -}} +{{/* +Boolean that may arrive as a tpl-rendered string, so an umbrella chart can drive +it from one of its own switches. Returns "true" (truthy) or "" (falsy). + +Strict on purpose. The schema no longer rejects a string here, and a rendered +"false" is a NON-EMPTY string, i.e. truthy to a Go template `if`: a lenient +reader would create the resource exactly when the parent switched it off. +Anything that is not true/false/"" fails the render instead. +Usage: {{ if (include "authup.flag" (dict "value" .Values.server.route.enabled "context" $ "key" "server.route.enabled")) }} +*/}} +{{- define "authup.flag" -}} +{{- $rendered := include "authup.tplvalues.render" (dict "value" .value "context" .context) | trim -}} +{{- $value := lower $rendered -}} +{{- if eq $value "true" -}} +true +{{- else if not (or (eq $value "false") (eq $value "")) -}} +{{- fail (printf "authup: %s must be true or false (or a template rendering to one of them), got %q." .key $rendered) -}} +{{- end -}} +{{- end -}} + {{/* Standard labels. Usage: {{ include "authup.labels" (dict "context" $ "component" "server") }} diff --git a/charts/authup/templates/_server-env.tpl b/charts/authup/templates/_server-env.tpl index 38ce47c..615628f 100644 --- a/charts/authup/templates/_server-env.tpl +++ b/charts/authup/templates/_server-env.tpl @@ -38,10 +38,15 @@ CLIENT_SYSTEM_ENABLED: "true" CLIENT_SYSTEM_SECRET_RESET: "true" {{- end }} {{- end }} -{{- $reserved := list "DB_TYPE" "DB_HOST" "DB_PORT" "DB_USERNAME" "DB_DATABASE" "DB_PASSWORD" "PUBLIC_URL" "TRUSTED_ORIGINS" "TRUST_PROXY" "REGISTRATION_ENABLED" "PASSWORD_RECOVERY_ENABLED" "EMAIL_VERIFICATION_ENABLED" "ACCOUNT_CONSOLE_ENABLED" "MFA_ENABLED" "MFA_REQUIRED" "THEME_DIRECTORY_PATH" "THEME_FRAGMENTS_ENABLED" "USER_ADMIN_PASSWORD" "USER_ADMIN_PASSWORD_RESET" "CLIENT_SYSTEM_ENABLED" "CLIENT_SYSTEM_SECRET" "CLIENT_SYSTEM_SECRET_RESET" "REDIS" "SMTP" "SECRETS_ENCRYPTION_KEY" }} +{{- /* Pinned to the path the volumeMounts use, not inherited from the image, whose default + moved here in v1.0.0-beta.63: a mount that stops matching it fails silently (logs on + the container layer, file provisioning scanning a directory that is not there). */}} +WRITABLE_DIRECTORY_PATH: "/var/lib/authup" +{{- $reserved := list "DB_TYPE" "DB_HOST" "DB_PORT" "DB_USERNAME" "DB_DATABASE" "DB_PASSWORD" "PUBLIC_URL" "TRUSTED_ORIGINS" "TRUST_PROXY" "REGISTRATION_ENABLED" "PASSWORD_RECOVERY_ENABLED" "EMAIL_VERIFICATION_ENABLED" "ACCOUNT_CONSOLE_ENABLED" "MFA_ENABLED" "MFA_REQUIRED" "WRITABLE_DIRECTORY_PATH" "THEME_DIRECTORY_PATH" "THEME_FRAGMENTS_ENABLED" "USER_ADMIN_PASSWORD" "USER_ADMIN_PASSWORD_RESET" "CLIENT_SYSTEM_ENABLED" "CLIENT_SYSTEM_SECRET" "CLIENT_SYSTEM_SECRET_RESET" "REDIS" "SMTP" "SECRETS_ENCRYPTION_KEY" }} {{- range $key, $value := .Values.server.config }} {{- if has $key $reserved }} -{{- fail (printf "authup: server.config.%s collides with a first-class chart value — set it through the dedicated value instead." $key) }} +{{- $instead := ternary "server.extraEnvVars plus a matching server.extraVolumeMounts" "the dedicated value" (eq $key "WRITABLE_DIRECTORY_PATH") }} +{{- fail (printf "authup: server.config.%s collides with a first-class chart value — set it through %s instead." $key $instead) }} {{- end }} {{ $key }}: {{ include "authup.tplvalues.render" (dict "value" ($value | toString) "context" $) | quote }} {{- end }} @@ -102,12 +107,12 @@ provisioning files, config file). */}} {{- define "authup.server.volumeMounts" -}} - name: writable - mountPath: /usr/src/app/writable + mountPath: /var/lib/authup - name: tmp mountPath: /tmp {{- if and .Values.server.provisioning.enabled (or .Values.server.provisioning.files .Values.server.provisioning.existingConfigMap .Values.server.provisioning.existingSecret) }} - name: provisioning - mountPath: /usr/src/app/writable/provisioning + mountPath: /var/lib/authup/provisioning readOnly: true {{- end }} {{- if or .Values.server.configuration .Values.server.existingConfigmap }} diff --git a/charts/authup/templates/admin-console/httproute.yaml b/charts/authup/templates/admin-console/httproute.yaml index f3a4916..4eb249d 100644 --- a/charts/authup/templates/admin-console/httproute.yaml +++ b/charts/authup/templates/admin-console/httproute.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.adminConsole.enabled .Values.adminConsole.route.enabled }} +{{- if and .Values.adminConsole.enabled (include "authup.flag" (dict "value" .Values.adminConsole.route.enabled "context" $ "key" "adminConsole.route.enabled")) }} {{- include "authup.httproute" (dict "context" $ "component" "admin-console" diff --git a/charts/authup/templates/server/httproute.yaml b/charts/authup/templates/server/httproute.yaml index ab7c98f..c28478e 100644 --- a/charts/authup/templates/server/httproute.yaml +++ b/charts/authup/templates/server/httproute.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.server.enabled .Values.server.route.enabled }} +{{- if and .Values.server.enabled (include "authup.flag" (dict "value" .Values.server.route.enabled "context" $ "key" "server.route.enabled")) }} {{- include "authup.httproute" (dict "context" $ "component" "server" diff --git a/charts/authup/templates/validations.yaml b/charts/authup/templates/validations.yaml index ffac23c..7e8a499 100644 --- a/charts/authup/templates/validations.yaml +++ b/charts/authup/templates/validations.yaml @@ -50,6 +50,16 @@ store, token blocklist and MFA challenges fall back to a per-process memory cach {{- fail "authup: adminConsole.ingress.enabled requires adminConsole.ingress.hostname (or extraHosts / extraRules)." }} {{- end }} +{{/* +The route flags are validated here unconditionally. The render gates spell +`and .enabled (include "authup.flag" ...)`, and Go short-circuits +`and`, so a disabled component would never reach the helper: the schema used to +reject a bad value either way, and widening it to [boolean, string] moved that +job into the template. +*/}} +{{- $_ := include "authup.flag" (dict "value" .Values.server.route.enabled "context" $ "key" "server.route.enabled") }} +{{- $_ = include "authup.flag" (dict "value" .Values.adminConsole.route.enabled "context" $ "key" "adminConsole.route.enabled") }} + {{/* A Gateway API HTTPRoute rule without `matches` defaults to PathPrefix "/", i.e. it matches every request on every hostname the route attaches to. The route hostname is @@ -57,13 +67,13 @@ derived from the public URL's ORIGIN, so a sub-path public URL would silently at authup as the catch-all backend of a hostname it is meant to share. authup always serves at "/" and expects the proxy to strip the prefix, hence match AND rewrite. */}} -{{- if and .Values.server.enabled .Values.server.route.enabled (not .Values.server.route.matches) }} +{{- if and .Values.server.enabled (include "authup.flag" (dict "value" .Values.server.route.enabled "context" $ "key" "server.route.enabled")) (not .Values.server.route.matches) }} {{- $path := (urlParse (include "authup.server.publicUrl" .)).path }} {{- if and $path (ne $path "/") }} {{- fail (printf "authup: the server public URL carries the path %q but server.route.matches is empty, which the Gateway API defaults to PathPrefix \"/\" (a catch-all on every hostname this route attaches to). authup serves at \"/\" and expects the proxy to strip the prefix, so set both:\n server.route.matches: [{path: {type: PathPrefix, value: %s}}]\n server.route.filters: [{type: URLRewrite, urlRewrite: {path: {type: ReplacePrefixMatch, replacePrefixMatch: /}}}]" $path $path) }} {{- end }} {{- end }} -{{- if and .Values.adminConsole.enabled .Values.adminConsole.route.enabled (not .Values.adminConsole.route.matches) }} +{{- if and .Values.adminConsole.enabled (include "authup.flag" (dict "value" .Values.adminConsole.route.enabled "context" $ "key" "adminConsole.route.enabled")) (not .Values.adminConsole.route.matches) }} {{- $path := (urlParse (include "authup.adminConsole.publicUrl" .)).path }} {{- if and $path (ne $path "/") }} {{- fail (printf "authup: the admin console public URL carries the path %q but adminConsole.route.matches is empty, which the Gateway API defaults to PathPrefix \"/\" (a catch-all on every hostname this route attaches to). Set both:\n adminConsole.route.matches: [{path: {type: PathPrefix, value: %s}}]\n adminConsole.route.filters: [{type: URLRewrite, urlRewrite: {path: {type: ReplacePrefixMatch, replacePrefixMatch: /}}}]" $path $path) }} diff --git a/charts/authup/values.schema.json b/charts/authup/values.schema.json index 608aa7f..7324034 100644 --- a/charts/authup/values.schema.json +++ b/charts/authup/values.schema.json @@ -737,10 +737,13 @@ }, "enabled": { "default": false, - "description": "Create a Gateway API HTTPRoute for the UI", + "description": "Create a Gateway API HTTPRoute for the UI (tpl-rendered: a string rendering to\n\"true\" enables it, so an umbrella chart can drive this from one of its own switches;\n\"false\" and \"\" disable it, anything else fails the render)", "required": [], "title": "enabled", - "type": "boolean" + "type": [ + "boolean", + "string" + ] }, "filters": { "description": "Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix", @@ -2233,7 +2236,7 @@ }, "containerSecurityContext": { "additionalProperties": true, - "description": "Container security context. The upstream image runs as root and needs a\nwritable npm cache; the chart mounts emptyDirs at /usr/src/app/writable and\n/tmp to keep readOnlyRootFilesystem viable.", + "description": "Container security context. The upstream image runs as root and needs a\nwritable npm cache; the chart mounts emptyDirs at /var/lib/authup and\n/tmp to keep readOnlyRootFilesystem viable.", "properties": { "allowPrivilegeEscalation": { "default": false, @@ -3110,10 +3113,13 @@ }, "enabled": { "default": false, - "description": "Create a Gateway API HTTPRoute for server-core", + "description": "Create a Gateway API HTTPRoute for server-core (tpl-rendered: a string rendering to\n\"true\" enables it, so an umbrella chart can drive this from one of its own switches;\n\"false\" and \"\" disable it, anything else fails the render)", "required": [], "title": "enabled", - "type": "boolean" + "type": [ + "boolean", + "string" + ] }, "filters": { "description": "Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix", diff --git a/charts/authup/values.yaml b/charts/authup/values.yaml index 3fe6741..f205f23 100644 --- a/charts/authup/values.yaml +++ b/charts/authup/values.yaml @@ -595,7 +595,7 @@ server: # additionalProperties: true # @schema # -- Container security context. The upstream image runs as root and needs a - # writable npm cache; the chart mounts emptyDirs at /usr/src/app/writable and + # writable npm cache; the chart mounts emptyDirs at /var/lib/authup and # /tmp to keep readOnlyRootFilesystem viable. containerSecurityContext: enabled: true @@ -746,7 +746,13 @@ server: # -- Full custom rules (tpl-rendered; appended after the generated rules) extraRules: [] route: - # -- Create a Gateway API HTTPRoute for server-core + # @schema + # type: [boolean, string] + # required: true + # @schema + # -- Create a Gateway API HTTPRoute for server-core (tpl-rendered: a string rendering to + # "true" enables it, so an umbrella chart can drive this from one of its own switches; + # "false" and "" disable it, anything else fails the render) enabled: false # -- Route hostnames ([] = derived from server.publicUrl / ingress hostname; only the host is kept, a public # URL path is dropped and needs its own matches entry) @@ -1052,7 +1058,13 @@ adminConsole: # -- Full custom rules (tpl-rendered; appended after the generated rules) extraRules: [] route: - # -- Create a Gateway API HTTPRoute for the UI + # @schema + # type: [boolean, string] + # required: true + # @schema + # -- Create a Gateway API HTTPRoute for the UI (tpl-rendered: a string rendering to + # "true" enables it, so an umbrella chart can drive this from one of its own switches; + # "false" and "" disable it, anything else fails the render) enabled: false # -- Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname; only the host is kept, a public # URL path is dropped and needs its own matches entry)