CRA & Long-Term-Support: Warum Git und Code allein nicht reichen
Stell dir ein realistisches Long-Term-Support-Szenario vor: Ein Kunde meldet 2036 eine kritische Schwachstelle. Du hast einen Release-Branch, du hast den Fix, du hast sogar die Pipeline-Definition als YAML im Repo. Und trotzdem scheitert der Build. Das SDK ist End of Life (EOL), der Build-Server wurde mehrfach modernisiert, eine 3rd-Party Komponente ist nicht mehr abrufbar, ein Zertifikat ist abgelaufen und eine kommerzielle Abhängigkeit lässt sich ohne gültige Lizenz nicht mehr herunterladen oder nutzen. Wenn du langfristig Support versprichst, dann verkaufst du nicht nur Features, sondern auch die Fähigkeit, Jahre später noch sicher Patches liefern zu können.
Genau hier setzt der Cyber Resilience Act (CRA) an. Er macht Security-Updates über den angegebenen Support-Zeitraum zu einem Liefergegenstand, nicht zu einer Absichtserklärung. Wenn du dich tiefer mit dem CRA-Kontext beschäftigen willst, liefern die EU-Seiten zum Cyber Resilience Act und zu den Herstellerpflichten einen guten Überblick. Für eine kompakte Einordnung schau in unsere Beiträge CRA kompakt: Was kommt auf Maschinenbauer zu? und die gesamte Artikel-Reihe zum CRA.
Im vorherigen Artikel ging es um Branching-Modelle und eine gute Branching-Strategie bleibt die Voraussetzung. Der Fokus diesmal liegt auf Buildbarkeit und Lieferfähigkeit über lange Zeiträume: Toolchain, Dependencies, Lizenzen, Build-Umgebung und die dazugehörigen Nachweise.
Was dir Branching wirklich garantiert
Branching löst ein wichtiges Problem: Versionierung, Parallelität und Backporting. Du kannst neue Features im Hauptbranch entwickeln und gleichzeitig einen Release-Branch pflegen, in dem nur Security-Fixes landen. Du kannst Hotfixes gezielt ausrollen, ohne die aktuelle Entwicklung zu blockieren.
Was Branching dir nicht garantiert, ist die Umgebung, in der dieser Code tatsächlich in ein auslieferbares Artefakt verwandelt wird. Ein Branch speichert Text. Deine Lieferfähigkeit hängt aber an einer langen Kette von Annahmen: Compiler und SDKs, Paketquellen, Build-Agents, Signatur- und Deployment-Secrets, Zertifikate, Lizenzberechtigungen, Netzfreigaben und manchmal sogar an einzelnen, historisch gewachsenen Tools.
Das Risiko ist deshalb tückisch: Du hast den richtigen Code, aber keinen reproduzierbaren Weg zu einem nachvollziehbaren Build. Im CRA-Kontext ist das mehr als unbequem. Wenn du für den Support-Zeitraum Schwachstellen behandeln musst, wird Update-Fähigkeit zur Pflicht. Und Update-Fähigkeit endet nicht beim Merge, sondern erst beim ausgelieferten Patch.
Buildbarkeit ist ein eigenes Requirement
Buildbarkeit klingt trivial, ist aber ein eigenständiges Requirement: Der Weg vom Source Code zum Artefakt muss wiederholbar und nachvollziehbar sein. Nicht unbedingt bit-identisch in jedem Kontext, aber zumindest so stabil, dass du erklären kannst, was gebaut wurde, wie es gebaut wurde und warum dieses Ergebnis vertrauenswürdig ist.
Warum wird das nach 10 Jahren schwer? Weil die Welt um deinen Code herum weiterläuft. Build Tools laufen aus dem Support, Abhängigkeiten sind nicht mehr verfügbar, Policies werden restriktiver und Sicherheitsannahmen von gestern sind heute nicht mehr akzeptabel.
Das Zielbild ist daher: Gleiche Inputs führen zu einem nachvollziehbaren Ergebnis. Dazu gehören nicht nur Quellcode und Konfiguration, sondern auch eine dokumentierte Build-Umgebung und ein definiertes Dependency-Set. Frameworks wie das NIST Secure Software Development Framework (SSDF) und die SLSA-Spezifikation geben dafür Begriffe und Reifegrade an die Hand. Du musst sie nicht voll implementieren, aber sie helfen bei der Frage, welche Nachweise später wirklich zählen.
Praktisch ist Reproduzierbarkeit vor allem dort wertvoll, wo du sehr viele Installationen hast, sicherheitskritische Artefakte baust oder Audit-Druck spürst: Libraries, Container Images, Firmware, sicherheitsrelevante Services. In anderen Fällen reicht ein good enough, solange es bewusst definiert ist. Das Entscheidende ist nicht Perfektion, sondern ein klarer Anspruch, der zu deinem Produkt und zum Support-Versprechen passt.
Abhängigkeiten, Paketquellen und Lizenzen als unsichtbare Fehlerquelle
Wenn du dir Build-Fails aus der Praxis anschaust, sind Dependencies fast immer beteiligt. Und zwar nicht nur technisch, sondern auch organisatorisch und rechtlich. Du kannst den besten Fix der Welt haben. Wenn du ihn nicht bauen oder nicht ausliefern darfst, ist er wertlos.
Drei typische Drift-Risiken sind besonders relevant:
- Version drift: Ohne Lockfiles und konsequentes Pinning bekommst du beim Build im Zweifel irgendwas. Das kann heute zufällig funktionieren und in zwei Jahren still brechen.
- Source-of-truth drift: Registry, Feed oder URL verschwindet, wird umstrukturiert oder liefert plötzlich andere Inhalte aus. Auch vollkommen legitime Änderungen wie zurückgezogene oder gelöschte Paketversionen können dich Jahre später überraschen.
- Legal drift: Lizenzen ändern sich, Abos laufen aus, ein Vendor wird gekauft oder stellt ein Produkt ein. Dann ist der Build nicht kaputt, sondern rechtlich blockiert.
Gegenmaßnahmen sind selten glamourös, aber hochwirksam: Lockfiles erzwingen, Versionen pinnen, auch für transitive Dependencies. Interne Mirrors oder Proxies für npm, NuGet und pip sind eine Investition in Verfügbarkeit. Ein Artifact Repository wie Nexus oder Artifactory reduziert externe Abhängigkeiten. Und für die rechtliche Seite brauchst du ein gepflegtes Lizenz-Inventar und einen Beschaffungs- beziehungsweise Renewal-Prozess.
Und dann gibt es noch eine vierte Kategorie, die in Embedded-, OT- und Maschinenbau-Kontexten gerne vergessen wird: physischer Drift. Das Device, für das du 2036 ein Update liefern sollst, ist möglicherweise nicht mehr im Haus, wurde verschrottet oder steht beim Kunden in einer Konfiguration, die du nicht einfach nachstellen kannst. Im Zweifel kannst du die Hardware auch nicht einfach nachproduzieren, weil Komponenten abgekündigt sind oder die Fertigungslinie längst umgestellt wurde. Deshalb lohnt sich eine bewusste Strategie: Halte repräsentative Geräteversionen (oder zumindest Baugruppen/Controller-Revisionen) als Referenz vor und ergänze das durch Simulatoren, Emulation oder Hardware-in-the-Loop, damit du Security-Fixes auch dann noch testen kannst, wenn das Originalgerät nicht verfügbar ist.
YAML im Repo ist noch keine Build-Umgebung
Viele Teams fühlen sich sicher, sobald die Pipeline as code im Repository liegt. Das ist ein guter Schritt, aber die YAML ist nur die Anleitung. Damit daraus ein Build wird, brauchst du eine Engine, Runner, Credentials, Images, Zugänge zu Registries und oft auch eine definierte Umgebung.
Über lange Zeit brechen genau diese Dinge an unscheinbaren Stellen:
- Hosted Runner Images ändern sich.
latestist bequem, aber es ist auch eine Einladung zu unkontrolliertem Drift. - Actions, Tasks und Extensions werden deprecated oder ändern Default-Verhalten.
- Self-hosted Agents werden ersetzt. Dabei gehen Tools, Zertifikate oder Caches verloren, die niemand dokumentiert hatte.
Als Gegenmaßnahme hilft eine einfache Denkweise: Behandle Runner und Pipeline-Bausteine wie Abhängigkeiten. Versioniere sie, update sie kontrolliert und dokumentiere, welche Anforderungen die Pipeline an ihre Umgebung stellt.
Build-Umgebung einfrieren
Wenn du langfristige Buildbarkeit ernst meinst, kommst du an einem Einfrieren der Build-Umgebung kaum vorbei. Gemeint ist nicht, dass du nie wieder Update in deine Build-Umgebung einspielen sollst. Gemeint ist, dass du definierst, was die Build-Umgebung ist und wie du sie reproduzierst.
Ein pragmatischer Weg sind containerisierte Builds. Der Container wird zur Build-Environment Spec: Betriebssystem, Pakete, Toolchain-Versionen, zusätzliche Tools. Wichtig ist dabei, Base-Images bewusst zu pinnen und Updates kontrolliert zu fahren. Docker beschreibt im Kontext von Build Best Practices sehr klar, warum Tags drift verursachen können und warum Pinning, kleine Base-Images und regelmäßige Rebuilds sinnvoll sind.
Dev Containers sind die zweite, sehr teamnahe Option. Sie lösen nicht nur CI-Probleme, sondern sorgen auch lokal für Reproduzierbarkeit und schnelleres Onboarding. Die Dev Container Spezifikation macht klar, dass es sich um einen Standard handelt, nicht um ein einzelnes Tool. Das ist hilfreich, wenn du in fünf Jahren nicht an eine bestimmte IDE-Integration gebunden sein willst.
Wenn du maximale Reproduzierbarkeit willst, kannst du noch einen Schritt weiter gehen und die Build-Umgebung komplett deklarativ beschreiben und einfrieren. Ein guter Einstieg dazu ist Zero to Nix. Nix-ähnliche Ansätze legen Toolchain und System-Abhängigkeiten so fest, dass der Build möglichst unabhängig von der Maschine bleibt, auf der er läuft. Das ist ein größerer Schritt und lohnt sich besonders bei hochkritischen Artefakten oder bei sehr langen Support-Zeiträumen. Kurz gesagt: Nix schafft starke Reproduzierbarkeit auf Tool-Ebene. Für sehr lange Support-Zeiträume brauchst du trotzdem eine Archivierungs- und Mirror-Strategie, sonst hängt auch Nix an der Verfügbarkeit externer Quellen.
Der zentrale Trade-off bleibt: Security-Patches versus Reproduzierbarkeit. Das löst du nicht technisch, sondern durch bewusstes Update-Management. Du brauchst ein Verfahren und eine Umgebung, die sowohl kontrollierte Updates als auch saubere Nachweise ermöglichen. Dabei darfst du auch die Fähigkeiten deines Teams nicht vergessen. Es wird schwierig, Fixes zu liefern, wenn zwar alle Tools vorhanden sind aber keiner mehr da ist, der sie versteht.
So machst du dich CRA- und LTS-fähig
Wenn du das alles liest, wirkt es schnell wie ein Mammutprojekt. In der Praxis ist es hilfreicher, in Etappen zu denken. Nicht, weil du klein anfangen willst, sondern weil du schnell die größten Risiken aus dem System nehmen kannst.
Quick-Wins:
- Erzwinge Lockfiles und etabliere einen klaren Update-Rhythmus für Dependencies.
- Lege Versionen für Pipeline-Tasks, Runner und Images fest und vermeide
latest. - Schreibe einen Build Contract: benötigte Tools und Versionen, benötigte Feeds, Secrets, Zertifikate, Netz-Zugriffe.
Mittelfristig:
- Führe Build-Container oder Dev Containers ein, damit Build und Debugging reproduzierbar werden.
- Etabliere ein Artefakt-Repository für externe Abhängigkeiten und Mirrors von offiziellen Feeds
- Baue ein Lizenz-Inventar auf und definiere Beschaffung und Renewal, inklusive der Frage was passiert, wenn ein Vendor weg ist.
Langfristig:
- Erhöhe schrittweise deinen SLSA-Reifegrad, dort wo es sinnvoll ist, und härtet die Build-Kette.
- Mache Release-Artefakte auditierbar: SBOM plus Provenance plus Archivierung der Build-Umgebung.
Wenn du dafür einen pragmatischen Maßnahmenkatalog suchst, ist das NIST SSDF eine gute Grundlage.
Lieferfähigkeit ist das eigentliche Versprechen
Langfristiger Support ist am Ende ein Vertrauensthema. Du sagst nicht nur zu, dass du patchen willst. Du sagst zu, dass du patchen kannst, auch dann, wenn die unmittelbare Projekt-Realität längst weitergezogen ist.
Wenn du Git, Branches und Pipeline-YAML hast, bist du auf einem guten Weg. Aber erst ein stabiler, dokumentierter Build-Pfad, ein kontrolliertes Dependency- und Lizenz-Setup und eine auditierbare Release-Dokumentation machen dich wirklich LTS-fähig.
Wie stellst du heute sicher, dass du in 10 Jahren noch patches liefern kannst? Schau gerne bei uns auf LinkedIn vorbei und diskutiere mit.