---
id: SEC10
pillar: security
title: SEC 10. How do you secure the software supply chain?
description: Most of what runs in production was written by strangers. How to know what your artefacts contain, where they came from, and that nothing changed on the way.
status: draft
services: [container-registry, git]
sidebar:
  order: 19
  label: Supply chain
source_url: "https://framework.stackit.cloud/architecture/pillars/security/sec-10-supply-chain/"
source_file: "docs/architecture/pillars/security/sec-10-supply-chain.mdx"
---

The majority of the code running in a typical production workload was not written by the team that
operates it. It arrived as dependencies, base images and build tooling, each with its own
dependencies, and each is a path into your environment that bypasses your own code review
entirely.

The supply chain question has three parts: knowing what you have, knowing it is not vulnerable,
and knowing that what runs is what you built.

## Best practices

- [`SEC 10.1`](/architecture/pillars/security/sec-10-supply-chain/#sec-101-know-what-your-artefacts-are-built-from) Know what your artefacts are built from
- [`SEC 10.2`](/architecture/pillars/security/sec-10-supply-chain/#sec-102-scan-continuously-rather-than-only-at-build-time) Scan continuously rather than only at build time
- [`SEC 10.3`](/architecture/pillars/security/sec-10-supply-chain/#sec-103-control-where-base-images-and-dependencies-come-from) Control where base images and dependencies come from
- [`SEC 10.4`](/architecture/pillars/security/sec-10-supply-chain/#sec-104-make-the-path-from-source-to-production-tamper-evident) Make the path from source to production tamper-evident

---

## SEC 10.1 Know what your artefacts are built from

**Risk if not established:** High

The question that matters during a disclosure is simple and usually unanswerable: which of our
running systems contain this component? Without an inventory, answering it means inspecting every
artefact under time pressure, and that is the day you find out how many artefacts you have.

Produce the inventory at build time, when the information is available, rather than reconstructing
it afterwards. It needs to cover transitive dependencies, since those outnumber direct ones by an
order of magnitude and are where the disclosures usually land.

Cover both layers of a container: the application dependencies and the base image contents. An
inventory of one is half an answer.

Keep it per built artefact and keep it as long as that artefact might be running. An inventory of
the current build does not help when the disclosure affects a version deployed four months ago and
still in production somewhere.

**On STACKIT.** Generating the inventory happens in the pipeline, and <LinkChip href="https://docs.stackit.cloud/products/developer-platform/git/basics/stackit-pipelines/">STACKIT
Pipelines</LinkChip>
being GitHub Actions compatible means the existing tooling for this can be reused rather than
rebuilt.

<LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/">Container Registry</LinkChip>
stores the artefacts and scans them, which gives you a per-image view of known vulnerabilities.

What it does not hold is the inventory itself. A scan answers "what in this image is known to be
vulnerable today"; it does not answer "does this image contain that component", which is the
question a disclosure three hours old actually poses, before any scanner database has caught up.
Generate the bill of materials in the pipeline, store it with the image, and treat it as the thing
you search when the next disclosure lands.

**Tradeoffs.** **Operational Excellence.** Generating and retaining inventories is a pipeline step
and a storage cost, both small. The value appears entirely during disclosures, which is the same
shape as most of this pillar.

**Verify.** A vulnerability is disclosed in a widely used library this afternoon. How long would
it take you to list every running workload that contains it?

---

## SEC 10.2 Scan continuously rather than only at build time

**Risk if not established:** High

A build-time scan tells you what was known when you built. Vulnerabilities are disclosed after
that, which means an artefact that passed its scan on Monday may be vulnerable on Thursday without
anything having changed.

Continuous scanning of what is stored and what is running is what closes that gap. It is the
difference between finding out at the next deployment, which for a stable service might be months,
and finding out this week.

Scan the layers separately, because they have different owners and different fix paths.
Application dependencies are updated by changing a manifest; base images are updated by rebuilding
from a newer base; infrastructure components you operate are updated by their own mechanism.

The hard part is not detection but triage. A scanner will report more than you can fix, most of it
in components that are present but not reachable from any code path you execute. Prioritize by
whether the vulnerable path is actually used and by exposure, and record the rest as accepted
under [`REL 3.4`](/architecture/pillars/reliability/rel-03-failure-mode-analysis/#rel-34-record-accepted-risks-explicitly-with-who-accepted-them) rather than leaving an unbounded backlog.

**On STACKIT.** <LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/">Container
Registry</LinkChip> provides
<LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/how-tos/automated-vulnerability-scanning-with-trivy/">automated vulnerability
scanning</LinkChip>
for stored images, which covers the artefacts you push and gives the continuous view for images
that are not being rebuilt. Check which scanner backs it, since that determines which
vulnerability databases feed your findings and whether you can reproduce one locally.

Source-level dependency scanning runs in <LinkChip href="https://docs.stackit.cloud/products/developer-platform/git/basics/stackit-pipelines/">STACKIT
Pipelines</LinkChip>
with whichever scanner you use, and scheduling a scan on a timer rather than only on commit is
what makes it continuous for a repository that is not changing.

Acting on the findings connects to [`SEC 8.2`](/architecture/pillars/security/sec-08-hardening-and-patching/#sec-82-define-a-patch-cadence-with-a-maximum-exposure-window-per-severity): the maximum exposure window per severity applies to
dependency vulnerabilities in the same way it applies to operating system ones.

**Tradeoffs.** **Operational Excellence.** Continuous scanning produces a continuous stream of
findings, and a stream nobody triages is noise. Setting a severity threshold for what blocks and
what reports is the difference between a control and an alert channel people mute.

**Verify.** When was your oldest running production image last scanned? What happens when a new
vulnerability is disclosed in an image that has not been rebuilt for six months?

---

## SEC 10.3 Control where base images and dependencies come from

**Risk if not established:** Medium

Pulling directly from public registries at build time means your build depends on a third party
being available, honest and unaltered. Availability alone is a reliability argument; the security
argument is that a compromised or replaced upstream package reaches your production environment
through a path nobody reviews.

Two controls. **Pull through a registry you control**, so that the artefact you build from is one
you have and can scan, and so that a build is not at the mercy of an upstream deletion. And **pin
versions** rather than following a moving tag, so that rebuilding produces the same result and an
upstream change is a deliberate update rather than a surprise.

Curate the base images rather than letting every team choose. A small set of approved, scanned,
regularly rebuilt base images gives you a place to fix a vulnerability once, and it makes `SEC
8.1` achievable rather than repeated per team.

Apply the same thinking to build tooling. A pipeline action or plugin from a public source runs
with the pipeline's permissions, which under [`OPS 4.1`](/architecture/pillars/operational-excellence/ops-04-deployment-automation/#ops-41-build-one-automated-path-from-source-to-production-used-by-everyone) are considerable.

**On STACKIT.** <LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/">Container
Registry</LinkChip> is where
controlled images live, and <LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/how-tos/automate-workflows-with-robot-accounts/">robot
accounts</LinkChip>
are the per-consumer identities for automated pull and push, which is [`SEC 4.2`](/architecture/pillars/security/sec-04-identity/#sec-42-give-workloads-their-own-identities-rather-than-sharing-human-credentials) applied to the
artefact path.

For pipeline actions, the GitHub Actions compatibility of <LinkChip href="https://docs.stackit.cloud/products/developer-platform/git/basics/stackit-pipelines/">STACKIT
Pipelines</LinkChip>
is a convenience and a supply chain consideration at once: reusing a community action means
running someone else's code in a privileged context. Pinning actions to a specific revision rather
than a moving tag is the same discipline as pinning a dependency.

The registry does not proxy or cache an upstream public registry, so controlling where your images
come from is a mirroring process you build and operate rather than a setting you turn on. That is
a real cost and it belongs in the estimate: something has to pull the approved images, push them,
and keep doing it. The compensation is that a deliberate mirror is also a review point, which a
transparent cache is not.

**Tradeoffs.** **Operational Excellence.** A curated image set is a thing to maintain, and teams
will want images that are not on it. **Reliability**, in the positive direction: a controlled
registry removes an external dependency from your build path.

**Verify.** Where do your base images come from, and are their versions pinned? If the upstream
registry were unavailable, could you still build and deploy?

---

## SEC 10.4 Make the path from source to production tamper-evident

**Risk if not established:** Medium

The final question is whether what runs in production is what your pipeline produced from reviewed
source. If an artefact can be modified or substituted between build and deployment, every control
before that point is bypassable.

Three properties, in increasing order of effort:

**A single path.** [`OPS 4.1`](/architecture/pillars/operational-excellence/ops-04-deployment-automation/#ops-41-build-one-automated-path-from-source-to-production-used-by-everyone) already argues for this on operational grounds. Its security value is
that there is one route to audit rather than several.

**Provenance.** A record of which commit produced which artefact, and which artefact was deployed
where. This is mostly free if the pipeline is the only path and it records what it did.

**Verification at deployment.** The strongest form, where the runtime checks that an artefact
carries a valid signature from your pipeline before running it. That requires signing and a
verification step, and it is the part most estates have not built.

Protect the pipeline itself proportionally. It can change production, its identity is frequently
more privileged than any human, and compromising it compromises everything downstream. [`SEC 5`](/architecture/pillars/security/sec-05-least-privilege/)
applies to it in full.

**On STACKIT.** <LinkChip href="https://docs.stackit.cloud/products/developer-platform/git/">STACKIT Git</LinkChip> with
<LinkChip href="https://docs.stackit.cloud/products/developer-platform/git/basics/stackit-pipelines/">Pipelines</LinkChip>
and
<LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/">Container Registry</LinkChip>
give you source, build and artefact storage in one place, which makes the provenance chain
straightforward to record.

Actions taken by the pipeline identity appear in the
<LinkChip href="https://docs.stackit.cloud/platform/audit-log/">audit log</LinkChip> like any other, which is the
platform-side record of what was deployed and when.

Verification is available rather than something to assemble. <LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/how-tos/verifying-authenticity-with-image-signing/">Image
signing</LinkChip>
supports established open-source signing tooling for OCI artefacts, so this does not require
building anything. More consequentially, a project admin can enforce **content trust**, which
blocks any attempt to pull an unsigned image. That moves enforcement from a step somebody
remembers into a property of the registry, which is the difference between signing being available
and signing being guaranteed.

<LinkChip href="https://docs.stackit.cloud/products/developer-platform/container-registry/how-tos/ensuring-image-integrity-with-immutability/">Image
immutability</LinkChip>
is the complementary control: a tag that cannot be overwritten cannot be silently substituted
after it was scanned and approved.

> From the STACKIT docs: [Ensuring image integrity with immutability › How immutability rules work](https://docs.stackit.cloud/products/developer-platform/container-registry/how-tos/ensuring-image-integrity-with-immutability/#how-immutability-rules-work) (Source updated 13.07.2026, copied 05.10.2026)

Tag immutability allows administrators to define policies at the project level that prevent specific tags from being overwritten. When an immutability rule is in effect, any attempt to push an image with a tag that already exists and matches the rule will be rejected by the registry. This guarantees that a tag, once applied, will always point to that same image digest.

Immutability rules are configured with the following logic:

- **Project-Scoped**: Rules are defined within a specific project’s settings.
- **OR Logic**: If multiple rules are created, a tag is considered immutable if it matches _any_ of the defined rules.
- **Matching and Excluding**: Rules can be configured to either match specific repository and tag patterns or to match everything _except_ for specific patterns.

An immutable tag protects not only itself but the entire underlying artifact digest to which it is attached. This “locks” the image layers and must be considered when planning tag retention and garbage collection strategies.

Admission control on the cluster side is not part of Kubernetes Engine. Content trust covers what
comes from your own registry, which is most of the substitution risk; refusing an unsigned image
that arrived from anywhere else needs an admission controller you install and operate yourself,
Kyverno being one of the common choices. That is a deliberate piece of work rather than a setting,
so decide whether the threat model asks for it before taking it on.

**Tradeoffs.** **Operational Excellence.** Signing and verification add a step, a key to manage
under [`SEC 7.4`](/architecture/pillars/security/sec-07-encryption/#sec-74-manage-the-key-lifecycle-as-the-most-critical-state-you-hold), and a failure mode where a correctly built artefact is rejected. It is worth the
effort where the threat model includes artefact substitution and is over-engineering where it does
not.

**Verify.** For a workload running in production right now, can you identify the commit it was
built from and confirm that the artefact has not been altered since?

---

## Related

- [`SEC 8.3`](/architecture/pillars/security/sec-08-hardening-and-patching/#sec-83-patch-the-whole-stack-not-only-the-operating-system) Patching the whole stack, which shares the scanning
- [`SEC 9.4`](/architecture/pillars/security/sec-09-secrets/#sec-94-detect-secrets-that-have-leaked-into-places-they-should-not-be) Secret detection, which runs in the same pipeline
- [`OPS 4.1`](/architecture/pillars/operational-excellence/ops-04-deployment-automation/#ops-41-build-one-automated-path-from-source-to-production-used-by-everyone) Deployment automation, whose single path this depends on
- [`OPS 2.2`](/architecture/pillars/operational-excellence/ops-02-development-standards/#ops-22-enforce-in-the-pipeline-rather-than-in-review-comments) Automated enforcement, where these checks belong
- [`SOV 6`](/architecture/pillars/sovereignty/sov-06-jurisdictional-chain/) Jurisdictional chain, which asks a different question about the same dependencies
