Commit Graph
31 Commits
Author SHA1 Message Date
jklein e5f98435ed ci: redact declared secret values from install-test diagnostics
The failure path prints container logs, and an app may print a value it was
given - gluetun logs its Wireguard address on startup. diagnose() now runs
through a filter that replaces the values of the declared REQUIRED_ENV
variables with the variable name. GitHub masks them as well, but that is the
platform's promise rather than this harness's, and the script runs locally too,
where nothing masks anything.
2026-08-26 14:37:46 +02:00
jklein 69a280399c ci: run the freesailarr install-test for real instead of skipping it
freesailarr was excluded from the install-test because gluetun never becomes
healthy without a real Wireguard key and no app container behind its kill
switch can become Ready. The green "Install-test freesailarr pass" was a
25-second SKIP on the most complex chart in the repo - a signal that proved
nothing.

With a CI-only Mullvad key in 1Password the reason is gone:

- ci/run-install-test.sh learns a general rule: ci/<chart>/fixtures.env.yaml is
  rendered with envsubst before it is applied, restricted to the variable names
  a chart declares in REQUIRED_ENV. A missing or empty declared variable fails
  the run loudly and names the variable - it never skips the chart.
- The install-test job loads the key from 1Password Connect for freesailarr
  only, following the pattern publish-helm-chart.yml documents: values via
  steps.<id>.outputs.*, mapped into the step's own env, quoted, with ":?"
  guards that name the missing op:// field before a cluster is created.
- Fixtures for freesailarr: three media claims, the gluetun secret template
  with variable names only, firewallOutboundSubnets so the kill switch does not
  block cluster DNS, and INSTALL_TIMEOUT=900 for nine containers.

freesailarr/Chart.yaml goes 3.0.0+up1.0.0 -> 3.0.1+up1.0.0. That bump is
deliberately empty of content: the install-test matrix follows changed chart
directories, and without it this PR could not run the test it adds.
2026-08-26 14:30:42 +02:00
jklein 70c7106fe8 Give the Trivy scan render values for freesailarr
ci/run-trivy-config.sh renders every changed chart before scanning it and exits
non-zero when the render fails - which it does for freesailarr, because the
chart is excluded from the install-test and therefore has no
ci/freesailarr/test-values.yaml to fall back on. The scan job would go red
without ever scanning anything.

The file supplies only the values the chart declares required (secret ref,
cluster issuer, the three media claims and one ingress host per app, since the
chart has no global ingress.domain) and nothing that would alter the security
posture the scan is meant to look at.
2026-08-24 13:51:29 +02:00
jklein 914c2d0205 Add freesailarr chart: gluetun-tunneled media automation stack
Single-pod StatefulSet running gluetun (appVersion 3.41.1) as Wireguard
VPN gateway with qbittorrent, prowlarr, flaresolverr, radarr, recyclarr,
sonarr, seerr and bazarr sharing its network namespace. Config volumes
via volumeClaimTemplates, media libraries as existing claims, per-app
enable/ingress toggles, computed FIREWALL_INPUT_PORTS, hardened
security contexts with documented per-image deviations. Wires up
publish workflow, change-app-version choice, install-test exclusion
and README overview.
2026-08-24 13:41:46 +02:00
jklein 6b25d082a7 renovate: chart-aware config with an automatic version bump
Renovate only ever rewrites appVersion or an image tag, so every PR it
opened against a chart directory failed the build-chart version check
(#138, #139). ci/renovate-bump-chart.sh recomputes
version: <semver>+up<appVersion> as a postUpgradeTasks hook: chart major
when the app major moves, chart patch otherwise.

Each chart renders its main image as <repo>:{{ .Chart.AppVersion }}, so
repository and version live in different files and Renovate cannot join
them. The mapping is one customManager per chart in renovate.json5,
deliberately not a comment inside each Chart.yaml - that would touch all
20 chart directories and force 20 publishes for a comment change.

Patch and digest updates automerge after a 3-day minimumReleaseAge once
the pipeline is green; majors arrive one line at a time to match the
stepwise app-major procedure. ci/** stays excluded: the install-test
fixtures pin throwaway infrastructure to major lines on purpose.
2026-08-24 11:42:43 +02:00
jklein 7e6b3beed5 rss-bridge: pin the unprivileged-port sysctl, quote env values
Follow-up to the template-chart hardening fixes in #133, which landed
while this branch was open.

The env block now quotes names, values and keyRef fields. Without it a
value like "5" renders as a YAML number and the API server rejects the
whole manifest. RSS-Bridge takes numeric settings that way
(RSSBRIDGE_http_timeout), so this was reachable with the first env entry
anyone adds.

The port-80 question is settled by measurement rather than by the
"it works in k3d" reasoning #133 warns about. Same pod spec in k3d
(k3s v1.35.5), only ip_unprivileged_port_start changed:

  1024, capabilities.drop [ALL]      nginx: bind() to 0.0.0.0:80 failed
                                     (13: Permission denied)
  1024, plus NET_BIND_SERVICE        identical failure - for a non-root
                                     process the capability lands in the
                                     permitted but not the effective
                                     set, so it buys nothing
  0                                  binds, HTTP 200

So the chart cannot buy safety with a capability the way samba does, and
port 80 is not negotiable either: nginx is configured for it inside the
image and the entrypoint's only lever is a sed against a read-only
/etc/nginx. What it can do is stop depending on the node default - the
pod securityContext now pins the sysctl to 0, so the bind no longer
relies on cluster configuration. Verified: the chart-installed pod
reports 0 and answers 200 through the Service.

Worth recording how that failure looks without probes: the entrypoint
backgrounds nginx and then execs php-fpm, so a failed bind leaves a
container reporting Running with no web server at all. Both test pods
above were Running. The probes on /?action=health are what turn it into
a pod that never becomes Ready.

trivy config still reports 0 to act on; the KSV-0117 statement is
updated to the measured reasoning.
2026-08-23 23:39:49 +02:00
jklein e016650f91 rss-bridge: qualify the image registry, except the port-80 finding
Two findings from the Trivy config scan that landed on main in #129.

AVD-KSV-0125 is fixed rather than excepted: writing the image as
docker.io/rssbridge/rss-bridge puts a real registry in the registry
position instead of "rssbridge", which is what ci/trivy/data/ksv0125.yaml
asks for. Verified in k3d that the qualified reference resolves to the
same digest (sha256:0eca99b2...) and still runs.

AVD-KSV-0117 (containerPort below 1024) becomes a scoped, documented
exception. nginx listens on 80 inside the image, and the entrypoint's
only way to change that is a sed against /etc/nginx/sites-enabled/default
which a read-only root filesystem rejects - so the alternative to port 80
is giving up readOnlyRootFilesystem. The premise of the check does not
hold here anyway: the container binds 80 as uid 33 with all capabilities
dropped and no NET_BIND_SERVICE.

trivy config now reports 0 to act on for this chart.
2026-08-23 23:33:23 +02:00
jklein 3fa67d55ef rss-bridge: new chart
Takes over the four loose manifests in cluster-config/rss-bridge/ as a
proper chart, on the current template-chart standard: strict values
schema, null-safe helpers, computed limits, replicas/scaleDown,
checksum/config, NOTES.txt.

Two things deviate from the repo standard on purpose:

- The Ingress models host AND TLS secret name per entry instead of the
  single conventional tls-<release>-<chart>-ingress. The installation
  this replaces has grown three separate certificates, one per host.
  Renaming them would make cert-manager issue three new ones and leave
  each host unreachable until it did - a self-inflicted outage on a
  deployment that has been up for over a year. Entries sharing a secret
  name are grouped into one tls entry, and omitting the name falls back
  to the convention, so consolidating later is a values change.

- Requests and limits are decoupled. The old manifest requested 2Gi of
  ephemeral storage, identical to its limit, reserving two gigabytes for
  a workload measured at 2.2 MB after fetching all five whitelisted
  bridges. The request drops to 64Mi; the limit stays at 2Gi.

Hardening was verified rather than assumed. The image ships no USER and
runs as root by default, but runs fully hardened once its writable paths
are provided: uid/gid 33, readOnlyRootFilesystem, all capabilities
dropped, seccompProfile RuntimeDefault. The writable paths were found by
running the image with a writable rootfs, exercising every bridge and
reading its diff layer - /app/cache, /run, /run/php, /var/lib/nginx.
/tmp did not show up that way, because PHP's opcache creates its lock
file there and unlinks it immediately; it only surfaced when the
hardened container died at startup. Stopping at the first writable path
would have shipped a chart that cannot start.

Probes use the app's own /?action=health, which renders fixed JSON
without touching the cache or any remote site, so neither a scraped site
going down nor an unwritable cache can restart the pod.

Fixes #128
2026-08-23 23:33:23 +02:00
jklein 00ea301e33 template-chart: seccomp profile, unprivileged port, and two placeholder fixes
Five changes to the scaffold every future chart is copied from:

1. seccompProfile.type: RuntimeDefault in the pod security context, in
   line with the rest of the seccomp series (#84). Pod context, because
   from there it also covers init containers and any container added
   later without being repeated.

2. containerPort 80 -> 8080, clearing AVD-KSV-0117. Note that the usual
   justification for this does not hold: a non-root container CAN bind
   port 80 on current Kubernetes, because the kubelet sets
   net.ipv4.ip_unprivileged_port_start=0 in the pod network namespace.
   Measured, not assumed - see the values.yaml comment. The reason to
   stay above 1024 is that this is a runtime default rather than a
   guarantee, and that every real chart here already does.

3. The ConfigMap placeholder key is renamed from the literal "key" to
   "example-setting", clearing AVD-KSV-01010 (secret-scanner patterns
   match any key named "key").

4. The image placeholder is fully qualified as ghcr.io/ORG/IMAGE, so the
   template stops teaching the unqualified form that produced the
   repo's unqualified Docker Hub images.

5. env values are rendered through "quote". Without it a value like
   "8080" renders as a YAML number and the API server rejects the whole
   manifest ("cannot unmarshal number into Go struct field
   EnvVar.value of type string"). Every real chart in the repo already
   quotes here; only the template did not. Found by instantiating the
   template and installing it, not by rendering it.

Verified by instantiating the scaffold into a working chart per the
README procedure and installing it in k3d: the workload becomes Ready,
the Service routes to the app on 8080 and an ephemeral container added
to the running pod inherits Seccomp: 2 from the pod context.

Refs #84
2026-08-23 23:29:51 +02:00
jklein 705ee066a4 CI: record the paperless-ngx root init container as a documented exception
The pod runs rootless as uid/gid 1000; only init-run-ownership is root, to
chown the /run emptyDir that s6-overlay requires to be owned by the runtime
uid. Justified in paperless-ngx/values.yaml, so AVD-KSV-0105/-0022/-0106 are
excepted for that chart with the reason, instead of being reported as if the
workload itself ran as root.
2026-08-23 23:14:12 +02:00
jklein 1ae5e1a7c3 CI: report Trivy misconfiguration findings for changed charts
Adds a trivy-config job to the PR pipeline that renders each changed chart
with Helm and runs `trivy config` over the rendered manifests. Reporting
only - TRIVY_CONFIG_MODE=report always exits 0, so the job never blocks a PR.

Rendered manifests rather than the chart directory: `trivy config <chart-dir>`
renders with default values, which fails on every chart here because of their
required() values - and Trivy does not fail with it, it logs "Skipping chart"
and prints an empty, green report.

Values come from the install-test fixtures (ci/<chart>/test-values.yaml), the
only committed per-chart values in the repo; none of them touches
securityContext, images or capabilities, so the scan sees the chart's own
hardening. Charts without fixtures get ci/trivy/values/<chart>.yaml.

Exceptions live in ci/trivy/trivyignore.yaml in Trivy's native ignore-file
schema, each with a statement - the script refuses to run without one and
reports suppressed findings with their reason instead of hiding them.
AVD-KSV-0125 is configured instead of excepted: ci/trivy/data/ksv0125.yaml
lists the trusted registries, so the check stays able to flag a genuinely
foreign source.

Refs #84.
2026-08-23 23:14:12 +02:00
jklein 4f489b0854 paperless-ngx: an empty barcodes block falls back to the chart defaults
A "barcodes:" key left without children (e.g. after deleting the last
remaining sub-key, PAPERLESS_CONSUMER_BARCODE_SCANNER, which paperless-ngx
3.0 removed) is YAML null and replaced the whole default sub-tree on merge.
Rendering then failed on the nil pointer, so the release simply stopped
updating.

An empty or null block now means "the chart defaults apply". The merge runs
through a new paperless-ngx.defaultedDict helper: nulls never delete, nested
maps merge recursively, and explicitly set values win - including false and 0,
which "default" and "mergeOverwrite" would swallow. A scalar where the
defaults define a map is rejected with the values path in the message.

Major bump: the meaning of a values override changes.

Fixes #61
2026-08-23 19:31:20 +02:00
jklein 227bcebeb6 Bring minecraft-server up to the chart style guidelines (1.0.0+upjava8)
Stage 2 of the Fleet migration for minecraft-server. Major bump: runtime
behaviour changes (probes, hardening) and the values interface breaks
(resource renames, strict schema).

Two real bugs first:
- The container declared port 35565 while the Service targeted 25565. It
  only worked because targetPort was numeric. Both are now server.port
  (25565) and the Service targets the named port.
- The download-serverpack init container was guarded by the map
  .Values.curseforgeServerpack instead of .enabled, so it rendered for
  every release and silently no-opped on an empty wget URL.

Conventions:
- Resources renamed to <release>-<chart>-<kind>, templates to
  <app>.<kind>.yaml. The PVC deliberately keeps its historical name -
  renaming it would provision a new, empty volume and orphan the worlds.
- Probes (liveness/readiness/startup) via the image's own mc-health, so
  Ready means the server answers a server-list ping. Startup budget 20 min.
- Hardened, values-configurable security contexts: non-root uid 1000,
  read-only root filesystem with an emptyDir at /tmp, all caps dropped.
- Resources from values with the limitFactor helper; an unset memory
  request is derived from the JVM heap so the two cannot drift apart.
- Additive env block rendered through tpl, pinned init image, storage
  class as a value, RCON wiring only when actually enabled.
- curseforge/ftb/curseforgeServerpack are now mutually exclusive.
- minecraft.version is pinned to the Java line instead of LATEST.
- The mc-router auto-discovery annotation is gone; the service type it
  implicitly switched is now the explicit value server.service.type.

Schema pilot (#68): values.schema.json with additionalProperties false on
every chart-owned level, rejecting all three known dead keys.
2026-08-23 16:40:44 +02:00
jklein 198058b8bb Update minecraft-server to the java21 line (0.4.0+upjava21)
Last of the three Java-line publishes; main comes to rest on java21, the line
of the only running server, and the registry holds all three tags.

Only the two version lines in Chart.yaml change. Because the source chart in
cluster-config is itself on java21, this final state renders byte-identical to
it for mc-ftb-skies-2 with its real values file - the published end state is
exactly what cluster-config runs today.

The CI install values move along with the appVersion, as in the java17 PR:
the JVM is fixed per published chart version and no single Minecraft version
boots on all three lines. Since the chart has no probes, a version the JVM
cannot load would crash after the rollout returned and still leave a green
install-test behind. minecraft.version therefore moves 1.20.1 -> 1.21.1, which
is also what ftb-skies-2 sets in its real values file, so CI exercises the
production version. ci/ is outside the chart directory and is neither packaged
nor published.
2026-08-23 16:12:15 +02:00
jklein eed1a48fc0 Update minecraft-server to the java17 line (0.4.0+upjava17)
Second of the three Java-line publishes. Only the two version lines in
Chart.yaml change; the rendered output differs from the published java8 chart
in exactly one line, the image tag.

The CI install values move along with the appVersion: the image tag is the
Java line, so the JVM is fixed per published chart version and the Minecraft
version has to match it (Java 8 runs up to 1.16.5, 1.18-1.20.4 need Java 17).
No single Minecraft version boots on all three lines, and because the chart
has no probes a version the JVM cannot load would still leave a green
install-test behind. minecraft.version therefore moves 1.12.2 -> 1.20.1.
ci/ is outside the chart directory and is neither packaged nor published.
2026-08-23 16:05:53 +02:00
jklein a68192e5fb Import minecraft-server chart for Fleet migration stage 1 (java8 line)
Imports the chart from cluster-config functionally unchanged, wires it into
the publish/validate/install-test pipelines and publishes it as the first of
three Java lines (0.4.0+upjava8).

The only deviation from the source chart is the chart-global scaleDown flag
plus a server.replicas value, following the satisfactory pattern: two of the
three servers stand at 0 replicas in the cluster, which the chart could not
express, and Fleet would otherwise start them on reconcile. The default
(scaleDown: false) still renders replicas: 1, so the rendering of the running
server is unchanged - verified byte-identical against the three real values
files for all three Java lines.

Known deficiencies (hardening, probes, naming scheme, hardcoded values,
missing icon, the misleading mc-router auto-discovery annotations) are left
untouched on purpose and are scheduled for stage 2.
2026-08-23 15:57:17 +02:00
jklein c20ad389f2 Minecraft-router chart: naming scheme, hardening, probes, values interface
Stage 2 of the Fleet migration for minecraft-router: bring the chart up to the
repo style guidelines. Chart 0.1.1+up1.25.1 -> 1.0.0+up1.25.1 (major: resource
renames, mappings becomes required, probes and security hardening change the
runtime behavior; appVersion unchanged).

- Resources renamed to {{ .Release.Name }}-{{ .Chart.Name }}-<kind>, templates
  to <chart>.<kind>.yaml; the deployment label now matches its own selector.
- mappings is required and must not be empty (the old default rendered
  "<nil>": "<nil>:25565"); every entry needs domain and internalUrl.
- Ports come from values: server.port (router listen port, also passed as
  --port), server.backendPort (default backend port, overridable per mapping),
  server.apiPort (--api-binding, used by the probes). Defaults stay 25565 /
  25565 / 8080, server.outwardPort keeps its 25565 default and comment.
- _helpers.tpl added (resources/limitFactor and replicas/scaleDown helpers),
  resources come from values, no CPU limit.
- replicas value plus the chart-global scaleDown flag.
- Additive env block rendered through tpl (value / secretKeyRef /
  configMapKeyRef).
- Values-configurable securityContext.pod / .container with hardened defaults:
  non-root uid/gid 10000, readOnlyRootFilesystem, no privilege escalation, all
  capabilities dropped.
- Liveness, readiness and startup probes on the mc-router API: GET /routes with
  an "Accept: application/json" header (the API router matches on that header
  and answers 404 without it).
- icon added to Chart.yaml.
- CI fixture supplies mappings; README chart table entry updated.
2026-08-23 15:14:49 +02:00
jklein 3be5111630 Import minecraft-router chart unchanged (Fleet migration stage 1)
Takes the existing mc-router chart over from cluster-config as-is so the
deployment can move to Fleet without any change to the rolled-out state.
Convention alignment follows in stage 2, the app update in stage 3.

The only deviation from "unchanged": Chart.yaml carried name:
minecraft-server (copy-paste from the sister chart). The publish job pushes
by the glob <directory>-*.tgz while helm package names the archive after the
Chart.yaml name, so without this correction the push finds no file.

Version 0.1.1+up1.25.1 - chart semver stays at 0.1.1, the +up suffix is repo
policy and does not affect rendering.

Also wires up CI: publish workflow, change-app-version choice list,
install-test values (CI-only node-port override, since 25565 is outside the
k3d default range) and the README chart table.
2026-08-23 12:58:21 +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 274294d2ef Merge pull request #56 from jk-cluster/feat/paperless-ngx-conventions
Paperless-ngx chart: naming scheme, secret refs, pinned init images (conventions batch 3)
2026-08-22 17:43:13 +00:00
jklein 29ac6d09c5 Paperless-ngx chart: naming scheme, secret refs, pinned init images (conventions batch 3)
Batch-3 major PR for paperless-ngx (issue #32):

- secrets.redisRef/databaseRef/paperlessRef -> redisSecretRef/
  databaseSecretRef/paperlessSecretRef (consistent with the other
  SecretRef values in the same block); CI fixtures updated
- TLS secret renamed to the standard tls-<release>-<chart>-ingress
  (domain no longer part of the name)
- busybox init/cronjob image pinned via paperless.busybox.image
  values, default bumped 1.28 -> 1.38.0
- probes values-configurable (defaults unchanged, incl. the 600s
  startup budget); consume CronJob deliberately stays probe-less
- ingress standard: ingress.className (default nginx) and
  ingress.annotations merge; domain/clusterIssuer stay required
- additive paperless.env block rendered through tpl (value,
  secretKeyRef, configMapKeyRef forms)
- disabled scripts ConfigMap renamed to ...-configmap; PVC names kept
  as documented naming-scheme exception (comments in the templates)
- chart 8.0.1+up3.0.5 -> 9.0.0+up3.0.5 (breaking values changes)
2026-08-22 19:39:54 +02:00
jklein 3499873ff2 Merge pull request #55 from jk-cluster/feat/urlaubsverwaltung-conventions
Urlaubsverwaltung chart: naming scheme, configurable probes (conventions batch 3)
2026-08-22 17:30:20 +00:00
jklein 9d22b3b921 urlaubsverwaltung chart: naming scheme, configurable probes, flexible ingress
- Rename all resources from <chart>-<release>-uv-<kind> to the repo
  standard <release>-<chart>-<kind> (deployment, service, ingress, both
  OnePasswordItem secrets); drop the uv infix. No PVCs in this chart, so
  no storage migration is needed.
- TLS secret renamed to the standard tls-<release>-<chart>-ingress
  (was tls-<chart>-<release>-ingress).
- Probes lifted into values (urlaubsverwaltung.livenessProbe/
  readinessProbe/startupProbe); defaults are exactly the previously
  hardcoded, issue-#9-calibrated settings.
- Repo-standard flexible ingress: ingress.className (default nginx),
  ingress.annotations merged with the cert-manager annotation,
  ingress.enabled flag; domain/clusterIssuer stay required.
- Additive urlaubsverwaltung.env block rendered through tpl with
  value/secretKeyRef/configMapKeyRef forms.
- CI fixtures updated to the new secret names.

Major bump 4.1.2+up6.7.0 -> 5.0.0+up6.7.0 (resource renames).
2026-08-22 19:27:14 +02:00
jklein 0021becba2 Matrix-Synapse chart: security hardening, naming scheme, configurable probes
- Values-driven securityContext for the Synapse pod/container with hardened
  defaults: runAsNonRoot as uid/gid 1000 (matching the image's previous gosu
  drop target), readOnlyRootFilesystem, allowPrivilegeEscalation false, all
  capabilities dropped; /tmp mounted as emptyDir. The now-ineffective UID/GID
  env vars are removed (the securityContext controls the user).
- Naming scheme: delegation Deployment renamed to ...-delegation-nginx-deployment,
  ConfigMap to ...-delegation-nginx-configmap (references and service selector
  updated).
- matrix.configSecretRef moved to secrets.configSecretRef (repo standard).
- Probes for Synapse and the delegation nginx are values-configurable;
  defaults are the previous hardcoded settings.
- Flexible ingress standard: ingress.className (default nginx) and
  ingress.annotations merged with the cert-manager annotation.
- Additive matrix.env block rendered through tpl (value/secretKeyRef/
  configMapKeyRef forms).
- CI fixtures: secret ref moved, seed job image synced to appVersion.
- Chart 4.2.1+upv1.159.0 -> 5.0.0+upv1.159.0 (major; app version unchanged).
2026-08-22 19:16:21 +02:00
jklein 48cb5c01de Zipline chart: ingress standard, secret ref structure (conventions batch 3)
Repo-standard flexible ingress (required clusterIssuer instead of the
hardcoded letsencrypt-issuer, domain moved from config.domain to
ingress.domain, className value, annotations merge, standard TLS secret
name), secrets.<name>SecretRef structure, core secret renamed to the
standard -secret name, additive tpl-rendered env block, documented PVC
naming exception. Major bump 4.1.2+up4.7.0 -> 5.0.0+up4.7.0.
2026-08-22 19:01:04 +02:00
jklein f6de7ffa80 Calibre-Web-Automated chart: naming scheme, security hardening (conventions batch 3)
All audit findings for the calibre-web-automated chart (#32) in one major
bump, 3.1.1+upv4.0.6 -> 4.0.0+upv4.0.6:

- Rename StatefulSet, Service, OnePasswordItem secret and Ingress from the
  inverted <chart>-<release>-cwa-<kind> scheme to
  <release>-<chart>-<kind>; spec.serviceName and all references follow.
  Both PVCs deliberately keep their historical names (documented naming
  exception; renaming would orphan the data).
- TLS secret renamed to the standard tls-<release>-<chart>-ingress.
- Ingress on the repo standard: ingress.className (default nginx),
  ingress.annotations merged with the chart-managed annotations,
  ingress.clusterIssuer now required (was silently defaulted to
  "letsencrypt-issuer").
- Security hardening, values-configurable via securityContext.pod /
  securityContext.container: allowPrivilegeEscalation false, all
  capabilities dropped except the minimal set the s6-overlay init needs
  (CHOWN, DAC_OVERRIDE, FOWNER, KILL, SETGID, SETUID). Documented
  deviations: the image must start as root (s6 drops to PUID 1000) and
  readOnlyRootFilesystem stays false (read-only mode disables PUID/PGID
  remapping upstream and breaks the ImageMagick policy setup).
- Additive env value rendered through tpl (value / secretKeyRef /
  configMapKeyRef forms).
- CI fixtures: secret renamed to the new scheme, clusterIssuer added to
  test-values.

Part of #32.
2026-08-22 18:46:07 +02:00
jklein 69111ae470 Samba chart: security hardening, configurable node ports, values cleanup
- Add securityContext.pod/container values with hardened defaults; the
  dockur/samba image requires root and a writable root filesystem, so the
  remaining deviations from the baseline are documented in values.yaml
  (caps reduced to CHOWN, DAC_OVERRIDE, FOWNER, NET_BIND_SERVICE, SETGID,
  SETUID; allowPrivilegeEscalation: false)
- Make the NodePorts configurable (samba.smbNodePort/netbiosNodePort,
  defaults 30445/30139 as published before)
- Rename secrets.sambaUsersRef to secrets.sambaUsersSecretRef (breaking)
- Rename the ConfigMap from -config-cfm to -configmap
- Add the tpl-rendered env value convention (value, secretKeyRef,
  configMapKeyRef forms)
- Fix the CI users.conf fixture to the username:uid:groupname:gid:password
  format the pinned image actually parses
- Bump chart to 4.0.0+up4.23.10

Part of #32
2026-08-22 15:50:30 +02:00
jklein fbe0dbac81 Update patchmon to 2.1.3
Chart rewrite for the 2.x single-server architecture: one stateless
server deployment (frontend embedded, /api served by the same process),
no PVC, single service and ingress path, optional guacd sidecar
(disabled by default), 2.x env vocabulary (TRUST_PROXY, readonly role),
probes send the public hostname as Host header.

Fixes #29: all three reported spots (PVC storageClass guard, RWX assets
PVC, frontend nginx 'backend' hostname) are removed by this rewrite.
2026-08-22 13:18:59 +02:00
jklein ea68a69a3e Update app versions (batch): tika, gotenberg, synapse, rallly, samba, urlaubsverwaltung, voucher-vault, zipline, teamspeak
Nine non-critical app-version bumps, each with a chart patch bump:

- apache-tika 3.2.3.0-full -> 3.3.1.0-full
- gotenberg 8.26.0 -> 8.36.0
- matrix-synapse v1.149.1 -> v1.159.0
- rallly 4.8.1 -> 4.12.3
- samba 4.23.5 -> 4.23.10
- urlaubsverwaltung 6.5.0 -> 6.7.0
- voucher-vault 1.27.4 -> 1.30.1
- zipline 4.6.4 -> 4.7.0
- teamspeak-server v6.0.0-beta8 -> 6.0.0-beta12.1 (new tag format has no v prefix)

Release notes of every chart were checked for environment-variable
changes; none of the charts needs an env adjustment.

The teamspeak-server install-test is reactivated: beta12.1 ships a
valid default license, so the exclusion is removed, CI fixtures are
added under ci/teamspeak-server/, and the install-test was verified
in a local k3d cluster. README list of excluded charts updated.
2026-08-22 08:44:00 +02:00
jklein cc6084ca58 Add install-test stage to the PR pipeline (k3d)
New install-test job in validate-charts.yml: for exactly the charts a PR
changes, install them into an ephemeral k3d cluster (pinned k3d v5.9.0,
k3s v1.35.5-k3s1) and require every Deployment/StatefulSet to become
Ready. Runs non-blocking (continue-on-error) during the stabilization
phase.

Fixtures live at the repo root under ci/ (not inside chart directories,
so they neither trigger publishes on merge nor get packaged): a stub
OnePasswordItem CRD, per-chart test values and fixtures (dummy secrets
with in-cluster DSNs, throwaway postgres/redis/dex, synapse-generate
seed job, static hostPath PVs where charts pin storage classes or need
RWX), plus a shared ci/run-install-test.sh harness that also runs
locally.

Excluded with documented reasons (ci/excluded-charts.txt): satisfactory
(multi-GB image), teamspeak-server (TS6 beta license expired, image
crashes on start), template-chart (placeholder image).

All 14 non-excluded charts verified locally in k3d using only the
committed fixtures.
2026-08-21 23:12:38 +02:00