Kai Ole Hartwig
10 min read
Low
By

TYPO3 on Kubernetes under IT-Grundschutz, part 14: The reference chart

For thirteen parts, this series looked at individual building blocks. Each part answered one question: what the image looks like, how the code is signed, how pods talk to each other. What remained open was how it all fits together.

This part gives the answer as code. The reference chart typo3-kubernetes-chart implements the series' building blocks in a single Helm chart. It is open source under the Apache License 2.0 and small enough to read in an afternoon.

Part 14 of 14 of the series "TYPO3 on Kubernetes under IT-Grundschutz". The overview of all parts is in part 1.

01 — What the chart contains

The chart installs TYPO3 with the building blocks that belong to the application. It deliberately leaves out the database. That runs externally, as a managed service or with an operator of its own.

Three profiles to compare

The ci/ directory holds three sets of example values. minimal is the smallest release that runs. full switches on every option. ha shows several TYPO3 replicas spread across zones. Valkey still runs as a single instance there, and the database lives outside the chart. Real high availability needs both to be resilient (part 12). The pipeline renders all three and validates them against the Kubernetes schemas. Anyone adapting the chart sees straight away what changes.

What a tenant brings

The chart does not ship a TYPO3 image. It expects a runtime image with PHP and FrankenPHP but without application code. The code arrives as the signed artefact. Configuration comes through environment variables, credentials come as files. The README describes this contract variable by variable, together with a short additional.php that implements it.

02 — How the code reaches the pod

Before PHP runs a single line, three init containers run one after the other. The first verifies the artefact's signature. The second pulls it into an empty volume. The third sets TYPO3 up against the database. If one of them fails, the pod does not start.

To check the SBOM attestation from part 3 in the cluster as well, switch on code.verify.attestation. That adds a fourth init container.

 

helm install tenant-a oci://ghcr.io/ohartwig/charts/typo3-kubernetes-chart \
  --version 0.2.0 --namespace tenant-a \
  --set image.repository=registry.example.org/tenant-a/typo3-runtime \
  --set code.artifact=registry.example.org/tenant-a/typo3-code@sha256:... \
  --set-file code.verify.publicKey=cosign.pub \
  --set ingress.host=typo3.example.org

 

Digest, not tag

The artefact is referenced by its digest, not by a tag. A tag can move between verification and download. cosign would then verify different bytes from the ones oras pulls. With a digest, both steps see the same thing. The chart warns at install time if a tag is used anyway.

One setup at a time

The setup runs in every pod. It has to, because extension:setup also publishes the public assets into that pod's own volume. A single job could not do that for every replica. But when several pods start together, for instance on a fresh install, they would work on the schema in parallel.

So the setup holds an advisory lock in the database. One pod sets up, the others wait. The lock belongs to the connection. If a pod dies mid-setup, the lock goes with it, and there is nothing to clean up.

The chart is signed too

Anyone recommending a signed supply chain should not ship the chart unsigned. Every version is signed at release through Sigstore, without a key of its own. The signature is bound to the repository's release workflow. Checking it takes cosign 3, because cosign 2 does not find the newer signature format:

 

cosign verify ghcr.io/ohartwig/charts/typo3-kubernetes-chart:0.2.0 \
  --certificate-identity-regexp '^https://github\.com/ohartwig/typo3-kubernetes-chart/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer token.actions.githubusercontent.com

03 — Privileges, network and keys

Every container in the chart runs with the same restrictions. That includes the init containers, the scheduler and the test pod. No root, a read-only file system, no capabilities, the RuntimeDefault seccomp profile. Service account tokens are not mounted, because TYPO3 never talks to the Kubernetes API. The chart therefore passes the restricted Pod Security Standard.

Default deny and individual allows

A NetworkPolicy first blocks the whole namespace. After that, each connection gets its own allow: ingress to TYPO3, TYPO3 to Valkey, TYPO3 to the database, the init containers to the registry. Valkey itself may not reach anything outside. By default, the allows for the database and the registry permit any address on the respective port. That is deliberate, because the chart does not know your addresses. Narrowing them is part of the installation.

mTLS without a service mesh

cert-manager builds a CA per namespace. Out of the box, the chart issues certificates valid for 30 days, a cautious starting value. With a high protection need, internalTls.duration lowers the lifetime to the 24 hours from part 5. Valkey requires client certificates and TLS 1.3. A sidecar reloads the server certificate after rotation, without a restart. The database can check the client certificate as well and map it to a user. This is the explicit model, without an extra control plane.

Secrets only as files

The chart does not render a single secret value. It only contains a reference to the secret store. The External Secrets Operator fetches the values and stores them as a Kubernetes Secret. In the pod they appear as files with mode 0440, never as environment variables. So a helm get values gives nothing away.

04 — Why not an operator?

The obvious question after thirteen parts: why a Helm chart and not a TYPO3 operator? An operator is a program of its own inside the cluster. It watches resources and steps in when their state drifts. That sounds like more automation. From a security point of view, it also adds attack surface.

What an operator costs

For a high protection need, APP.4.4.A16 even recommends operators for particularly critical applications. Anyone who deviates gives reasons, for example these:

A chart with GitOps covers most of a TYPO3 installation's lifecycle. Install, upgrade and rollback are declarative. Setup and migrations run in the init container, the scheduler as a CronJob. What logic is left fits into a job.

When an operator would pay off

The line lies where the lifecycle can no longer be described declaratively. For example with a very large number of tenants that come and go through an API. Or with processes that have to react to state inside TYPO3 itself. For a manageable number of installations that a team maintains through Git, that is rarely the case. There, an operator is more burden than help.

Deliberately not included

The chart leaves out whatever differs from one platform to the next: the database, storage for fileadmin, the WAF, monitoring and the CVE scan. The scan belongs in the pipeline before signing, as described in part 4. The cluster only checks the signature.

Mapping to IT-Grundschutz

Frequently asked questions

Is the chart meant for production use?+

It is a reference, not a product. The defaults are built for security, and the pipeline checks every change. Before using it, narrow the network allows, set your own resource values and rehearse the installation in a test environment. Teams that already run a platform often adopt individual templates rather than the whole chart.

Why is there no database included?+

A database needs storage, replication and backups with a lifecycle of their own. A managed service or a specialised operator does that better than an application chart. The chart connects to the database over TLS and can present a client certificate. An optional job takes a regular dump.

Does it work without cert-manager and the External Secrets Operator?+

Partly. A Secret you create yourself can replace the External Secrets Operator. cert-manager can be switched off. You then lose mTLS to Valkey and the client certificate for the database, though. With an increased protection need, that is not a good choice.

May I adapt and redistribute the chart?+

Yes. The chart is licensed under the Apache License 2.0. You may use, modify and redistribute it, commercial projects included. Improvements are welcome as pull requests. Please report security issues confidentially to the address in SECURITY.md.

Conclusion

The reference chart condenses the series into a little over a dozen templates. Signed code, hardened containers, mTLS, network rules and secrets without Git all mesh there. Every decision is commented in the code and explained in this series. An operator would improve little of it. Above all, it would add privileges and code of its own.

The chart is on GitHub. The overview of all parts is in part 1: Which modules actually apply. Back to part 13: Hardening TYPO3 itself.

I bring your TYPO3 safely onto Kubernetes. With the chart as the starting point.

Adaptation to your platform, a signing chain in the pipeline, network allows, secrets and mTLS. Plus the mapping to the IT-Grundschutz modules for your audit.

Platform operations rather than advice on paper: if you want, I run your TYPO3 with you on an ongoing basis and deliver the evidence.

Book a call →

About the author

Photo of Kai Ole Hartwig.

Kai Ole Hartwig

Freelance DevSecOps consultant · OnlyOle Consulting

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.