19 Commits
Author SHA1 Message Date
jklein 31d37c9767 feat(cyberchef)!: NetworkPolicy, on by default
Major, as every chart in this rollout is: networkPolicy.enabled defaults to
true, so the object reaches running releases on the next upgrade.

The simplest policy of the sweep, and the one to read first. CyberChef is a
static web app - nginx serves files, the recipes run in the visitor's browser,
the pod holds no state and calls nothing - so what the policy buys is the two
CEILINGS rather than any source restriction: inbound is capped at 8080, the
single port the pod declares, and outbound at DNS, which is as tight as the
object goes without breaking name resolution.

The 8080 rule carries no source selector by default, and that is deliberate
rather than an oversight: 8080 is the port all three probes address, and the
kubelet probes from the IP of the worker node running the pod, which no chart
can know. Guessing it wrong leaves the pod permanently un-Ready, a symptom that
reads as a broken application. Narrowing it is networkPolicy.probeSources, and
the restricted branch then also carries the ingress controller - measured as
the rke2-traefik DaemonSet in kube-system, which serves this chart's HTTPRoute
as well, since the same Traefik backs the Gateway API here.

Part of #5
2026-10-14 13:59:51 +00:00
jklein 50f683776e feat(cyberchef): Gateway-API as an alternative to the Ingress (pilot)
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 13s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 6s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 18s
Validate Helm Charts / Build template-chart (pull_request) Successful in 7s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 17s
Install-test / Install-test cyberchef (pull_request) Successful in 1m0s
Validate Helm Charts / Install-test (pull_request) Successful in 1m0s
Validate Helm Charts / Selector match (pull_request) Successful in 12s
The Gateway-API pilot of Gitea #114: one chart, the convention, the CI that
holds it. The remaining 20 charts follow in their own PRs.

The form, additive and off by default:

    httpRoute:
      enabled: false     # counterpart to ingress.enabled
      parentRefs: []     # the Gateway - a platform object
      hostnames: []      # empty = [ingress.domain]

Only HTTPRoute with parentRefs is rendered: no Gateway, no gatewayClassName,
no Certificate, and no implementation-specific policy object. That subset is
identical on nginx-gateway-fabric, Cilium, Envoy Gateway and Istio, which is
what lets the implementation be chosen later without touching the charts
again. Class and certificate move to the platform.

Four guards, all against silent failures:

  - mutually exclusive with ingress.enabled, with a render abort naming BOTH
    value paths. A migration runs over a second hostname, not in parallel.
  - parentRefs mandatory in Gateway mode, render abort on the empty list: a
    route without a parent is created, is schema-valid, and routes nothing.
  - hostnames empty means [ingress.domain], so the hostname has one source.
  - the mode is a VALUE, not auto-detection: .Capabilities.APIVersions is
    unreliable at template time and a missing CRD fails at install time.

ingress.clusterIssuer was already only read inside the Ingress template, so
the "conditionally required" half of #114 needed no change in this chart -
checked rather than assumed, and it has to be re-checked per chart in the
sweep.

CI, both halves, because neither covers the other:

  - the install-test installs cyberchef in GATEWAY mode against the REAL
    Gateway-API CRDs (standard channel v1.6.3, pinned and Renovate-tracked,
    applied --server-side). A schema-less stub would accept a malformed route
    and prove only that something was created. The Ingress path of this one
    chart is no longer installed; it is a built-in type that never routed in
    k3d anyway, and helm lint renders this chart's defaults - Ingress mode -
    on every PR.
  - ci/run-httproute-match-test.sh, over every chart on every PR: every
    backendRefs entry must name a Service, on a port, that the SAME render
    creates, and parentRefs must not be empty. The CRDs accept both of those
    mistakes, so the schema cannot catch them, and the only report would be a
    route status no controller writes in CI.

What stays unprovable, and is written down instead of implied: that traffic
flows. No Gateway controller runs in k3d.

Minor: additive with unchanged defaults, proven by an empty helm template
diff against origin/main for the default values.

Part of #114
2026-10-12 15:16:26 +00:00
jklein e0b552d93e Replace the obsolete no-dependencies rationale in every chart helper
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 8s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Successful in 31s
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Successful in 1m34s
Validate Helm Charts / Install-test ${{ fromJSON(needs.detect-changed-charts.outputs.install-charts) }} (pull_request) Successful in 15m49s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Apache Tika Helm Chart / Publish Apache Tika Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish BentoPDF Helm Chart / Publish BentoPDF Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Calibre Web Automated Helm Chart / Publish Calibre Web Automated Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Cyberchef Helm Chart / Publish Cyberchef Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Freesailarr Helm Chart / Publish Freesailarr Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Gotenberg Helm Chart / Publish Gotenberg Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Homeassistant Helm Chart / Publish Homeassistant Helm Chart (push) Successful in 8s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Matrix Synapse Helm Chart / Publish Matrix Synapse Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Minecraft-Router Helm Chart / Publish Minecraft-Router Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Minecraft-Server Helm Chart / Publish Minecraft-Server Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Outline Helm Chart / Publish Outline Helm Chart (push) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Patchmon Helm Chart / Publish Patchmon Helm Chart (push) Successful in 6s
Publish Paperless-NGX Helm Chart / Publish Paperless-NGX Helm Chart (push) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Rallly Helm Chart / Publish Rallly Helm Chart (push) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Samba Helm Chart / Publish Samba Helm Chart (push) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Satisfactory Helm Chart / Publish Satisfactory Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish TeamSpeak Server Helm Chart / Publish TeamSpeak Server Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Urlaubsverwaltung Helm Chart / Publish Urlaubsverwaltung Helm Chart (push) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish VoucherVault Helm Chart / Publish VoucherVault Helm Chart (push) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish Zipline Helm Chart / Publish Zipline Helm Chart (push) Successful in 6s
The doc comments on the resources and defaultedDict helpers justified the
copy-per-chart with "charts in this repo deliberately have no dependencies".
That rule fell with #214: a dependency is admissible now, just expensive, and
it sits in the README as guideline 14, not 13. The justification still holds,
but its reason is a different one - sharing the helper would mean a library
chart, a second artefact to publish, version and pull for something that
changes about once a year. searxng got that wording in PR #38; this carries it
to 21 further charts, 40 occurrences in all.

rss-bridge is deliberately left out. Its install-test fails on the Gitea
runner for a reason that predates this branch, and touching the chart would
keep it in the install-test matrix and the check red. Its copy of the sentence
is carried by #51, which has to open the chart anyway.

Comments only: every touched chart renders byte-identical against its
ci/<chart>/test-values.yaml. Each published chart takes a patch bump,
appVersion and +up suffix untouched; template-chart is never published and
takes none, but is corrected because every new chart inherits from it.

Two repo files carried the same claim at no version cost:
validate-charts.yml explained the all-charts quantity render test with it, and
change-app-version.yml still counted nine images for freesailarr - MediathekArr
made it ten.
2026-09-27 10:27:57 +00:00
jklein c371179d76 cyberchef: format quantities without exponential notation (9.0.1+up11.4.0)
multiplyQuantity turned its input into a string with printf "%v". The null-safe fallback helpers from #99 route values through fromYaml, which parses every number as float64, and Go switches float64 to exponential notation from about 1e6 upwards. A bare byte count like 1048576 became "1.048576e+06" and the suffix check aborted the render.

The non-integer product used the same printf "%v" on output. That path never reaches the suffix check, so it rendered an invalid quantity silently, which only the API server rejects on apply. cyberchef types limitFactor as "number", so a fractional factor is allowed by the schema and that path is reachable with ordinary values.

Both call sites now go through a new formatNumber helper that forces decimal notation and trims the padding zeros; strings such as "512Mi" pass through untouched. Round-trip and resources helper are unchanged. The wording matches the canonical fix already merged for template-chart and the other charts, so the shared copy stays free of drift.
2026-08-29 01:03:19 +02:00
jklein 4a61f763e8 cyberchef: seccompProfile RuntimeDefault in the pod context
Sets seccompProfile.type: RuntimeDefault in the pod security context, so
the container runtime's default syscall filter applies to every container
in the pod. Verified in k3d: /proc/1/status reports Seccomp: 2 (filter
mode) with one filter loaded, and nginx keeps serving the app.

The value lives in the pod context rather than the container context
because it covers init containers and any container added later without
being repeated; a container that needs a different filter can still
override it under securityContext.container, which wins over the pod
value.

Major bump: runtime behavior changes, even though it stays invisible for
this workload.

Refs #84
2026-08-23 23:00:02 +02:00
jklein 4e97097a64 cyberchef: honour ingress.enabled, add null-safe hardening
Three changes, all about values that do not do what they promise:

1. Ingress gate (#93): the Ingress template is wrapped in
   {{- if .Values.ingress.enabled }}. The key already existed in
   values.yaml with default true but was never read, so switching it off
   did nothing. Both required() calls for domain and clusterIssuer live
   inside the Ingress file, so gating also removes the contradiction that
   one had to supply a domain and issuer in order to turn off an Ingress
   one does not want.

2. Null-safety for hardening, probes and resources (#99): an override
   written without children is YAML null and erased the whole default
   sub-tree, rendering "securityContext: null" - an unhardened container
   that starts and reports healthy.

3. values.schema.json types (#68 + #99): the blocks covered by the
   null-safety accept null. This is load-bearing only for keys that have
   no chart default (e.g. resources.limits): there Helm has nothing to
   delete, so the null survives into schema validation and a strict
   "object" type rejected it outright.

Rendering with normal values is byte-identical to 7.0.0.
2026-08-23 20:27:00 +02:00
jklein e6e3ad961e Add NOTES.txt to the cyberchef chart
Prints what an operator would otherwise have to look up after installing:
the rendered Ingress URL, the cert-manager Certificate to watch while the
browser still warns, and the fact that the chart needs no secrets and no
volumes.

Adds three warnings that the values alone do not tell: CyberChef ships no
authentication, so everything that reaches the URL can use it; the app is
static and holds no server-side state, so upgrades and rollbacks need no
migration; and cyberchef.env reaches nginx but never the served bundle.

Scaled-down installations are called out conditionally, naming whichever of
scaleDown / cyberchef.replicas caused it, via the chart's own replicas
helper so the notice cannot drift from the rendered Deployment.

The URL is only ever rendered from .Values.ingress.domain, so no domain is
stored in the chart. Refs #43.
2026-08-23 20:19:41 +02:00
jklein 2cfc2bfbba Add strict values.schema.json to cyberchef
additionalProperties: false on every chart-owned object level, so a key
the chart does not read is rejected at render time instead of being
silently ignored. Objects passed verbatim into the pod spec (probes,
securityContext, global) stay open - Kubernetes owns their schema there.

Major bump: a schema can break existing deployments that carry dead keys.

Refs #68
2026-08-23 19:19:44 +02:00
jklein 22fdb58396 Strict clusterIssuer also for cyberchef and urlaubsverwaltung
Per the maintainer decision (option a: ingress.clusterIssuer strictly
required in ALL charts) the two remaining defaults are removed as well:

- cyberchef: drop the letsencrypt default; bump raised from minor to
  major (6.0.0+up11.4.0) as this is a breaking values change.
- urlaubsverwaltung: drop the letsencrypt-issuer default; major bump
  to 6.0.0+up6.7.0.
- CI test-values for both charts now set clusterIssuer explicitly.

A repo-wide grep confirms no chart carries a clusterIssuer default
anymore.
2026-08-22 23:13:23 +02:00
jklein c88ce80750 Conventions batch 4: env blocks, homeassistant className default, strict clusterIssuer
- apache-tika, gotenberg, cyberchef, homeassistant, patchmon: additive env
  value blocks rendered through tpl (value, secretKeyRef and configMapKeyRef
  forms), following the template-chart/outline pattern; patchmon gets separate
  blocks for the server container and the guacd sidecar.
- homeassistant: ingress.className now defaults to nginx; an empty className
  omits ingressClassName from the rendered Ingress, so the flexible upstream
  ingress style stays intact.
- outline, paperless-ngx: remove the ingress.clusterIssuer defaults
  (letsencrypt / letsencrypt-issuer) - the value is now strictly required,
  consistent with the repo-wide ingress standard; CI test-values set it
  explicitly.

Chart bumps: apache-tika 7.2.0, gotenberg 4.2.0, cyberchef 5.2.0,
patchmon 5.2.0, homeassistant 4.2.0 (minor); outline 5.0.0,
paperless-ngx 10.0.0 (major).
2026-08-22 20:10:06 +02:00
jklein f091c33205 Conventions batch 2: flexible ingress standard, configurable probes
Establish the flexible ingress standard from #32 in template-chart and
document it in README/CLAUDE.md: ingress.domain and ingress.clusterIssuer
stay required, new ingress.className value (default nginx) replaces the
hardcoded ingressClassName, new optional ingress.annotations are merged
with the cert-manager cluster-issuer annotation, TLS secret name
unchanged (tls-<release>-<chart>-ingress).

Apply the standard to cyberchef and patchmon (the two charts without an
own batch-3 major PR) and make the patchmon probes values-configurable
(server + guacd, rendered through tpl so the default Host header keeps
following ingress.domain). Defaults render semantically identical to the
previous hardcoded output.

cyberchef 5.0.1 -> 5.1.0, patchmon 5.0.1 -> 5.1.0 (minor, additive).
2026-08-22 15:35:09 +02:00
jklein a69542383e Conventions batch 1: icons, template file names, cosmetics
- Add icon: to all 17 Chart.yaml (official project icons); removes the
  last helm lint --strict INFO finding across the repo
- Rename template files to the <app>.<kind>.yaml scheme (apache-tika,
  gotenberg, teamspeak-server PVC, paperless-ngx configmap) - pure
  git mv, no content changes
- template-chart: fix missing space in {{ .Chart.Name}} and rename the
  ConfigMap resource -config-map -> -configmap (chart is never published)
- rallly: fix typos in required messages (rallySecret -> ralllySecret,
  databseSecret -> databaseSecret)
- README: document the deliberate PVC-name exception from the naming
  scheme (renames would provision new empty volumes)
- Patch-bump all 16 published charts

Rendered resources are byte-identical for all published charts (only
helm's '# Source:' comments change for the renamed files); the only
render change is the template-chart ConfigMap name.

Part of #32
2026-08-22 15:21:52 +02:00
jklein 45a764cb46 Update cyberchef application version to 11.4.0
Major jump 10.22.1 -> 11.4.0: the upstream image switched to
nginxinc/nginx-unprivileged (serving port 80 -> 8080, pid/temp dirs
under /tmp). Adjust container port, writable-volume mounts and the
pod security context (sysctl net.ipv4.ip_unprivileged_port_start no
longer needed). Chart major bump 4.1.0 -> 5.0.0 accordingly.
2026-08-22 09:09:40 +02:00
jklein 6779e49355 Add per-workload replicas values and a chart-global scaleDown flag
Every Deployment/StatefulSet now takes its replica count from a replicas
value (default 1, explicit 0 allowed) via a new per-chart <chart>.replicas
helper; the helper uses a kindIs "invalid" nil check so an explicit 0 is
not swallowed by Helm's default function. A chart-global scaleDown flag
(default false) takes precedence: it renders every workload with
replicas: 0 and suspends the paperless-ngx consume CronJob, stopping the
app without uninstalling it (PVCs, Secrets, Services and Ingress remain).

Defaults leave the rendered manifests byte-identical to main, so all
published charts get a minor version bump; the template-chart carries the
reference helper implementation and stays unversioned.

Fixes #25
2026-08-22 08:12:40 +02:00
jklein 91a2fbcb05 Compute memory/ephemeral-storage limits from requests, drop default CPU limits
Every chart now renders its container resources through a duplicated
_helpers.tpl helper (reference implementation in template-chart): requests
ship as chart defaults, missing memory and ephemeral-storage limits are
computed as resources.limitFactor x the request (default 3, satisfactory
keeps 2 to preserve its previously published 8Gi limit), an explicitly set
limit always wins, and a CPU limit is only rendered when set explicitly.
Fixed limit defaults are removed from all values.yaml files; teamspeak-server
gains a values-driven resources block instead of hardcoded template values.
Major version bump for all published charts.
2026-08-21 23:34:09 +02:00
jklein b5eba4cbf1 Harden pod and container security contexts across all workloads
Add securityContext hardening (runAsNonRoot, readOnlyRootFilesystem,
capabilities drop ALL, allowPrivilegeEscalation false) to the 10 workloads
that lacked it: apache-tika, cyberchef, gotenberg, homeassistant,
matrix-delegation-nginx, paperless-server, paperless-consume cronjob
(plus automountServiceAccountToken false), patchmon-frontend,
urlaubsverwaltung and zipline. All contexts are configurable via
securityContext.pod / securityContext.container in values.yaml; writable
paths are provided as emptyDirs. Image-forced deviations (Home Assistant
root start for s6-overlay + NET_RAW for discovery, paperless root init
container for /run ownership) are documented in the charts' values.yaml.
Major version bump for every changed published chart.
2026-08-21 21:49:05 +02:00
jklein 78d78973c5 Ensure liveness, readiness and startup probes on all workloads
Every container in every Deployment/StatefulSet now defines all three
probes. The paperless-ngx consume CronJob intentionally has none (batch
workload).

- Add probes: apache-tika, calibre-web-automated, cyberchef, gotenberg,
  homeassistant, patchmon-backend, samba (tcp/445), teamspeak-server
  (tcp/30033), zipline (/api/healthcheck)
- Fix voucher-vault null probe values with real defaults
- Fix startup-probe semantics in template-chart and rallly (no
  initialDelaySeconds, generous failureThreshold)
- Switch outline probes to its /_health endpoint
- Add readiness/startup probes to satisfactory
- Add startup probes to matrix-synapse, matrix-delegation-nginx,
  paperless-server, patchmon-frontend, urlaubsverwaltung
- Major version bump for all published charts
2026-08-21 17:36:53 +02:00
jklein 4f0136f188 feat(cyberchef): cleanup 2026-03-19 12:11:32 +01:00
jklein 07e036de72 feat(cyberchef): added initial chart 2026-03-17 17:33:47 +01:00