Kai Ole Hartwig
7 min read
Low
By

TYPO3 on Kubernetes under IT-Grundschutz, part 3: Code as a signed artefact

A hardened image is of little use if the code inside it can be swapped unnoticed. The decisive question is therefore: how does the pod know it runs exactly what the pipeline built?

The answer is a chain of artefact, signature and verification. This part shows how it works and where it is anchored in IT-Grundschutz.

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

01 — Shipping runtime and code separately

The runtime from part 2 contains PHP and FrankenPHP, but no TYPO3 code. The code arrives as its own artefact. The pipeline packs the finished release with vendor/ and built assets into an archive. With oras it lands as an OCI artefact in the same registry as the images.

In the pod, the kubelet mounts the artefact as a read-only image volume, once per node and digest. The main container starts FrankenPHP on top. Until October 2026 an initContainer did this with ORAS, downloading and unpacking the archive on every pod start.

Why the separation pays off

This is a clean route for SYS.1.6.A4 planning the provision and distribution of images. How the artefact is structured is covered in detail in TYPO3 code as an OCI artefact. The architecture decision behind it is described in separating runtime and application.

02 — Signing and attaching an SBOM

An artefact in the registry is not yet a trustworthy artefact. The pipeline therefore signs it right after the push. The signing key lives in a KMS and never leaves it. The pipeline may use the key, but not read it.

 

# push the artefact and refer to it by digest
oras push registry.example.org/typo3/site:1.4.2 release.tar.gz

# signature with a key held in the KMS
cosign sign --key awskms:///alias/release-signing \
  registry.example.org/typo3/site@sha256:<digest>

# attach the SBOM as a signed attestation
cosign attest --key awskms:///alias/release-signing \
  --type cyclonedx --predicate sbom.json \
  registry.example.org/typo3/site@sha256:<digest>

 

The SBOM lists every dependency with its version. It is the basis for the scan gate in part 4. Because it is signed itself, it cannot be polished afterwards.

Without a transparency log, add --tlog-upload=false when signing and --insecure-ignore-tlog=true when verifying. Otherwise cosign uploads the signature to the public Rekor log and requires an entry there on verification.

The release is the signature

SYS.1.6.A13 asks for the release of images. In this chain the signature is the documented act of release. Only what passed every pipeline check gets signed. SYS.1.6.A12 distribution of secure images is covered too, because distribution happens by digest only.

03 — Verifying before the pod starts

A signature is only as good as its verification. The pod therefore runs a fixed sequence:

  1. Verify the artefact's signature against the public key.
  2. Verify the SBOM attestation.
  3. Only then does the container start, with the kubelet mounting the code as a read-only image volume pinned to the same digest.
initContainers:
  - name: verify-signature
    image: registry.example.org/tools/cosign@sha256:<digest>
    args: ["verify", "--key", "/keys/release.pub",
           "registry.example.org/typo3/site@sha256:<digest>"]
  - name: verify-sbom
    image: registry.example.org/tools/cosign@sha256:<digest>
    args: ["verify-attestation", "--key", "/keys/release.pub",
           "--type", "cyclonedx",
           "registry.example.org/typo3/site@sha256:<digest>"]
volumes:
  - name: code
    image:
      reference: registry.example.org/typo3/site@sha256:<digest>
      pullPolicy: IfNotPresent

 

Until October 2026 a third initContainer loaded the code into a volume with ORAS at this point. Image volumes have been stable since Kubernetes 1.36, and containerd mounts them too. How the switch went, and why an artefact first mounted empty, is covered in the field note on the image volume.

If a check fails, the pod does not start. The behaviour is fail-closed: better no new pod than an unverified one. During a rolling update (with maxUnavailable: 0) the old pod keeps running. This meets APP.4.4.A6 initialisation of pods.

initContainer or admission webhook

The alternative is an admission webhook, for example a policy controller. It verifies signatures cluster-wide for all images. That is broader, but adds a new dependency: if the webhook fails, no pods start any more, including the ones meant to fix things. The initContainer checks exactly the artefact that matters and only fails its own pod. In practice the initContainer is the more robust starting point. The webhook pays off once every image is signed and the controller itself runs highly available.

04 — The chain before it: commits, pipelines and digests

A signature proves that the pipeline built an artefact. It does not prove that the code in the pipeline was the right one. The chain therefore starts earlier.

Signed commits

Every commit on the main branch carries a signature. A list of allowed keys in the repository defines who may sign. A pipeline check rejects unsigned commits. How this works for humans, AI agents and bots alike is shown in who actually signed this commit?

Pipelines as code

The pipeline itself is versioned and assembled from reviewed templates. Changes to it go through merge requests like any other code. That covers APP.4.4.A2 planning automation with CI/CD and APP.4.4.A10 securing automation processes.

Digests instead of tags in the deployment

The GitOps repository records every version with its digest. A tag can be moved, a digest cannot. An update tool enters new versions via merge request, for example pinup. Every delivery is then a traceable change in the repository.

Frequently asked questions

Why not simply build the TYPO3 code into the image?+

Because runtime and application would then share one life cycle. Every PHP update would force a rebuild of all websites, and every release would touch the runtime. Kept apart, both can be signed, scanned and rolled out independently.

What happens if signature verification fails?+

The new pod does not start. During a rolling update with maxUnavailable: 0 the old pod stays in service and the website remains reachable. The failed deployment shows up in monitoring and is investigated before anything unverified runs.

Does the signature need a public transparency log?+

Not necessarily. A transparency log makes signatures publicly traceable. For private artefacts and keys held in your own KMS you can consciously do without it. The protected key then carries the whole chain of trust, and that should be documented.

Is an initContainer enough, or does it have to be an admission webhook?+

For the website's code the initContainer is enough, since it verifies exactly this artefact. An admission webhook additionally covers every other image in the cluster. It is a critical component in its own right, though, and should only come once it can run highly available.

Conclusion

The TYPO3 code enters the cluster as its own signed OCI artefact with an SBOM. An initContainer verifies signature and SBOM before every start and aborts on any mismatch. Before that, signed commits, versioned pipelines and digests secure the chain. This makes SYS.1.6.A4, A12 and A13 as well as APP.4.4.A2, A6 and A10 verifiable.

Continue with part 4: no CVEs above 7. Back to part 2: the hardened TYPO3 image.

I set up a supply chain in which only signed and verified TYPO3 code runs in production.

OCI artefacts, signatures with keys held in a KMS, SBOM attestations and a verification before every pod start.

Platform operations rather than advice on paper: if you want, I run the pipeline on an ongoing basis and keep the chain complete.

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.