diff --git a/CHANGELOG.md b/CHANGELOG.md index bfecfa9..017e437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,14 @@ and the chart uses SemVer (`version`) plus the upstream Forail CalVer than replacing the loopback entries. - **Chart render checks in CI** pass a throwaway `secrets.forailAdminPassword`, so the now-required password does not fail `helm lint` / `helm template`. +- **In-cluster callers are no longer rejected by `forail.allowedHosts`.** Clients + that reach the API through the `forail-web` Service send the Service DNS name + as their `Host`, which the hardened list did not cover, so Django answered + `400` — the documented forail-operator install + (`--set forail.url=http://forail-web..svc.cluster.local:8013`) could not + resolve a single object. The chart now appends `forail-web`, + `forail-web.`, `forail-web..svc` and `forail-web..svc.cluster.local` + to whatever `forail.allowedHosts` is set to. Unknown hosts are still rejected. ### Security - **No working secret defaults**: `postgresPassword`, `forailSecretKey` and diff --git a/templates/NOTES.txt b/templates/NOTES.txt new file mode 100644 index 0000000..d27aaa6 --- /dev/null +++ b/templates/NOTES.txt @@ -0,0 +1,39 @@ +Forail {{ .Chart.AppVersion }} installed into namespace {{ .Release.Namespace }}. + +{{- if .Values.ingress.enabled }} + + URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.host }} + Map {{ .Values.ingress.host }} to any node running the ingress controller. +{{- end }} + Admin: admin + +Check the rollout with: + + kubectl -n {{ .Release.Namespace }} get pods + +{{- if not .Values.task.privileged }} + +──────────────────────────────────────────────────────────────────────── + WARNING — job execution is NOT enabled in this install. +──────────────────────────────────────────────────────────────────────── + +forail-task runs project updates and automation jobs through podman inside +the task pod, and podman cannot mount its overlay storage in a +non-privileged container. With task.privileged=false every job fails a +moment after launch with + + [graphdriver] prior storage driver overlay failed: + mount /var/lib/containers/storage/overlay: permission denied + +and the only thing visible in the UI is a project or job stuck in "Pending". + +This is the safe default on purpose: a privileged pod with a host cgroup +mount is a trivial container escape to node-root. Enable it only when you +have somewhere safe to run it -- ideally dedicated, tainted execution +nodes: + + helm upgrade {{ .Release.Name }} ... \ + --set task.privileged=true \ + --set task.hostCgroup=true + +{{- end }} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 3a7a798..51c24c8 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -76,8 +76,18 @@ Wraps DB, Redis, secrets, OTel, admin into one block to avoid drift. secretKeyRef: name: forail-secrets key: forailAdminPassword +{{- $ns := include "forail.namespace" . }} - name: FORAIL_ALLOWED_HOSTS - value: {{ .Values.forail.allowedHosts | quote }} + {{- /* + In-cluster consumers reach the API through the forail-web Service, so their + Host header is the Service DNS name, never the ingress host. Django answers + 400 ("The request could not be understood by the server.") for any Host not + listed, which broke the documented forail-operator install + (--set forail.url=http://forail-web..svc.cluster.local:8013). These four + names are cluster-internal and derived from the release, so appending them + keeps needtofix M9 tight while making in-cluster access work by default. + */}} + value: {{ printf "%s,forail-web,forail-web.%s,forail-web.%s.svc,forail-web.%s.svc.cluster.local" .Values.forail.allowedHosts $ns $ns $ns | quote }} - name: FORAIL_CSRF_TRUSTED_ORIGINS value: {{ .Values.forail.csrfTrustedOrigins | quote }} - name: FORAIL_COOKIE_SECURE diff --git a/values.yaml b/values.yaml index 1e881e5..b6cb167 100644 --- a/values.yaml +++ b/values.yaml @@ -21,7 +21,12 @@ images: pullPolicy: IfNotPresent assistant: repository: ghcr.io/forail-platform/forail-assistant - tag: "2026.07.0" + # forail-assistant is NOT part of the 2026.07.0 release -- it ships + # disabled (assistant.enabled=false below) and was not built or tested for + # it, so this stays on the last published build. Pointing it at a tag that + # was never pushed would give ImagePullBackOff the moment anyone enabled + # it. Bump this in lockstep the next time the assistant is released. + tag: "2026.06.0" pullPolicy: IfNotPresent postgres: repository: postgres @@ -73,6 +78,12 @@ forail: # here; 127.0.0.1 and localhost MUST stay in the list because the in-cluster # liveness/readiness probes reach the app over the loopback address (Django # rejects any Host not listed with HTTP 400, which would crash-loop the pod). + # The chart also appends the forail-web Service DNS names (forail-web, + # forail-web., forail-web..svc, forail-web..svc.cluster.local) to + # whatever you set here, so in-cluster clients that call the Service directly + # — forail-operator with + # --set forail.url=http://forail-web..svc.cluster.local:8013 — are not + # rejected. Do not list them here yourself. allowedHosts: "forail.lan,127.0.0.1,localhost" # NOTE: avoid the .local TLD — Avahi/mDNS (`mdns_minimal [NOTFOUND=return]` # in nsswitch.conf on most desktop Linux distros) intercepts every @@ -242,3 +253,6 @@ ingress: # 1) kubectl -n forail exec deploy/forail-web -- forail-manage create_oauth2_token --user admin # 2) helm install forail-operator -n forail \ # --set forail.token= --set forail.url=http://forail-web.forail.svc.cluster.local:8013 +# That Service DNS name is covered by forail.allowedHosts automatically (see +# above). If you point the operator at the ingress host instead, or front it +# with another name, pass --set forail.hostHeader=.