Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.<ns>.svc.cluster.local:8013`) could not
resolve a single object. The chart now appends `forail-web`,
`forail-web.<ns>`, `forail-web.<ns>.svc` and `forail-web.<ns>.svc.cluster.local`
to whatever `forail.allowedHosts` is set to. Unknown hosts are still rejected.

### Security
- **No working secret defaults**: `postgresPassword`, `forailSecretKey` and
Expand Down
39 changes: 39 additions & 0 deletions templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -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 }}
12 changes: 11 additions & 1 deletion templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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.<ns>.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
Expand Down
16 changes: 15 additions & 1 deletion values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.<ns>, forail-web.<ns>.svc, forail-web.<ns>.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.<ns>.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
Expand Down Expand Up @@ -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 <chart-path> -n forail \
# --set forail.token=<PAT> --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=<host in allowedHosts>.
Loading