← Alle Beiträge

Open Component Model: Software Bills of Delivery für Cloud-Native Anwendungen erstellen

Matthias Bruns · · 8 Min. Lesezeit
open-component-model software-supply-chain sbom cloud-native

Die Software-Supply-Chain ist zu einem kritischen Angriffsvektor geworden: 62% der Unternehmen erlebten 2023 einen Angriff auf ihre Software-Supply-Chain, so der State of Software Supply Chain Security Report. Herkömmliche Ansätze zur Verfolgung von Abhängigkeiten und Deployment-Artefakten greifen in Cloud-Native-Umgebungen zu kurz, wo sich Anwendungen über mehrere Repositories, Container-Images und Laufzeitkonfigurationen erstrecken. Das Open Component Model (OCM) schließt diese Lücke, indem es einen standardisierten Ansatz zur Erstellung von Software Bills of Delivery (SBOD) bietet, die das vollständige Bild dessen erfassen, was deployed wird.

Im Gegensatz zu Software Bills of Materials (SBOM), die sich auf Code-Abhängigkeiten konzentrieren, verfolgen Software Bills of Delivery die tatsächlichen Artefakte und Konfigurationen, die in Produktionsumgebungen gelangen. Diese Unterscheidung ist wichtig, wenn Sie nicht nur verstehen müssen, welcher Code geschrieben wurde, sondern was tatsächlich deployed wurde, wie es konfiguriert war und woher es stammt.

Das Open Component Model verstehen

Das Open Component Model ist ein offener Standard, der Software Bills of Delivery in einem technologieagnostischen, maschinenlesbaren Format beschreibt. OCM fokussiert sich speziell auf die Software-Artefakte, die für Software-Produkte ausgeliefert werden müssen, und bietet vollständige Sichtbarkeit und Kontrolle über die gesamte Supply-Chain bei gleichzeitiger Vereinfachung von Compliance-Prüfungen, Sicherheitsscans und Deployments.

Das Modell behandelt Software-Komponenten als erstklassige Entitäten, die versioniert, signiert und zwischen verschiedenen Umgebungen und Repositories transportiert werden können. Dieser Ansatz ermöglicht es Organisationen, nicht nur den Quellcode zu verfolgen, sondern die komplette Delivery-Pipeline einschließlich Build-Artefakten, Container-Images, Helm-Charts und Konfigurationsdateien.

Zentrale Konzepte

Components repräsentieren logische Software-Einheiten, die unabhängig versioniert und deployed werden können. Eine Komponente kann ein Microservice, eine Bibliothek oder ein kompletter Anwendungsstack sein.

Resources sind die tatsächlichen Artefakte, die einer Komponente zugeordnet sind - Container-Images, Binärdateien, Dokumentation oder Konfigurationsdateien. Jede Resource hat einen Typ, eine Version und eine Zugriffsmethode.

Sources verfolgen den Ursprung von Komponenten und verlinken zurück zu Source-Repositories, Build-Systemen oder anderen Komponenten. Dies schafft eine nachvollziehbare Spur von der Quelle bis zum Deployment.

References stellen Beziehungen zwischen Komponenten her und ermöglichen komplexe Abhängigkeitsgraphen bei gleichzeitiger Wahrung klarer Eigentumsgrenzen.

OCM für Multi-Repository-Tracking einrichten

Lassen Sie uns die Implementierung von OCM in einem realistischen Szenario durchgehen, in dem Sie Microservices über mehrere Repositories verteilt haben, jeder mit eigenen Build-Pipelines.

Installieren Sie zunächst das OCM CLI-Tool:

# OCM CLI herunterladen und installieren
curl -L https://github.com/open-component-model/ocm/releases/latest/download/ocm-linux-amd64.tar.gz | tar xz
sudo mv ocm /usr/local/bin/

Erstellen Sie einen Component-Descriptor für Ihren ersten Microservice. Diese YAML-Datei definiert die Komponente und ihre Resources:

# component-descriptor.yaml für user-service
apiVersion: ocm.software/v3alpha1
kind: ComponentVersion
metadata:
  name: acme.com/user-service
  version: v1.2.3
  provider:
    name: acme.com
spec:
  repositoryContexts:
  - type: OCIRegistry
    baseUrl: registry.acme.com/ocm
  sources:
  - name: user-service-source
    type: git
    version: v1.2.3
    access:
      type: github
      repoUrl: https://github.com/acme/user-service
      ref: v1.2.3
  resources:
  - name: user-service-image
    type: ociImage
    version: v1.2.3
    relation: local
    access:
      type: ociRegistry
      imageReference: registry.acme.com/user-service:v1.2.3
  - name: helm-chart
    type: helmChart
    version: v1.2.3
    relation: local
    access:
      type: ociRegistry
      imageReference: registry.acme.com/charts/user-service:v1.2.3
  - name: config
    type: yaml
    version: v1.2.3
    relation: local
    access:
      type: github
      repoUrl: https://github.com/acme/user-service-config
      ref: v1.2.3
      path: production.yaml

OCM in CI/CD-Pipelines integrieren

Die wahre Stärke von OCM zeigt sich bei der Integration in Ihre Build- und Deployment-Pipelines. Hier erfahren Sie, wie Sie einen GitHub Actions-Workflow modifizieren, um Component-Descriptors zu generieren und zu veröffentlichen:

# .github/workflows/build-and-publish.yml
name: Build and Publish Component
on:
  push:
    tags: ['v*']

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    
    - name: Build and push image
      uses: docker/build-push-action@v5
      with:
        push: true
        tags: registry.acme.com/user-service:${{ github.ref_name }}
    
    - name: Install OCM CLI
      run: |
        curl -L https://github.com/open-component-model/ocm/releases/latest/download/ocm-linux-amd64.tar.gz | tar xz
        sudo mv ocm /usr/local/bin/
    
    - name: Generate component descriptor
      run: |
        envsubst < component-descriptor.template.yaml > component-descriptor.yaml
      env:
        VERSION: ${{ github.ref_name }}
        IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
        COMMIT_SHA: ${{ github.sha }}
    
    - name: Create and push component version
      run: |
        ocm create componentversion \
          --file component-descriptor.yaml \
          --provider acme.com
        
        ocm transfer componentversion \
          component-descriptor.yaml \
          registry.acme.com/ocm

Für Multi-Repository-Szenarien erstellen Sie einen separaten Workflow, der Komponenten zu einer übergeordneten Komponente aggregiert, die Ihre komplette Anwendung repräsentiert:

# Anwendungsebenen-Component-Descriptor
apiVersion: ocm.software/v3alpha1
kind: ComponentVersion
metadata:
  name: acme.com/ecommerce-platform
  version: v2.1.0
  provider:
    name: acme.com
spec:
  repositoryContexts:
  - type: OCIRegistry
    baseUrl: registry.acme.com/ocm
  componentReferences:
  - name: user-service
    componentName: acme.com/user-service
    version: v1.2.3
  - name: order-service
    componentName: acme.com/order-service
    version: v2.0.1
  - name: payment-service
    componentName: acme.com/payment-service
    version: v1.5.2
  resources:
  - name: deployment-manifest
    type: yaml
    version: v2.1.0
    relation: local
    access:
      type: github
      repoUrl: https://github.com/acme/platform-deployment
      ref: v2.1.0
      path: kubernetes/

Laufzeitkonfigurationen erfassen

Eine der Stärken von OCM ist die Verfolgung nicht nur von Build-Artefakten, sondern auch von Laufzeitkonfigurationen, die das Verhalten von Software in der Produktion beeinflussen. Dazu gehören Umgebungsvariablen, Feature-Flags und Infrastrukturkonfigurationen.

Erstellen Sie einen Resource-Typ für Laufzeitkonfigurationen:

resources:
- name: runtime-config
  type: json
  version: v1.2.3
  relation: local
  access:
    type: inline
    data: |
      {
        "environment": "production",
        "features": {
          "new_checkout_flow": true,
          "enhanced_security": true
        },
        "scaling": {
          "min_replicas": 3,
          "max_replicas": 10,
          "cpu_threshold": 70
        },
        "database": {
          "connection_pool_size": 20,
          "timeout_seconds": 30
        }
      }

Für Kubernetes-Deployments erfassen Sie die tatsächlich deployten Manifeste:

# Deployed-Konfiguration extrahieren und zur Komponente hinzufügen
kubectl get deployment user-service -o yaml > deployed-config.yaml

# Als Resource zum Component-Descriptor hinzufügen
ocm add resource component-descriptor.yaml \
  --name deployed-config \
  --type yaml \
  --version v1.2.3 \
  --inputFilePath deployed-config.yaml

Supply-Chain-Security implementieren

OCM bietet mehrere Mechanismen zur Gewährleistung der Supply-Chain-Sicherheit. Digitale Signaturen verifizieren die Authentizität von Komponenten, während Policy-Enforcement das Deployment nicht autorisierter oder verwundbarer Komponenten verhindert.

Komponenten signieren

Signieren Sie Component-Versionen mit OCMs eingebauten Signierungsfähigkeiten:

# Signierungsschlüssel generieren
ocm create rsakeypair acme-signing-key

# Component-Version signieren
ocm sign componentversion \
  --signature acme-signature \
  --private-key acme-signing-key.priv \
  registry.acme.com/ocm//acme.com/user-service:v1.2.3

Policy-Enforcement

Erstellen Sie Richtlinien, die Komponenten vor dem Deployment validieren:

# policy.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ocm-policy
data:
  policy.rego: |
    package ocm.policy
    
    # Alle Komponenten müssen signiert sein
    deny[msg] {
      not input.signatures
      msg := "Component must be signed"
    }
    
    # Komponenten müssen aus vertrauenswürdigen Quellen stammen
    deny[msg] {
      input.sources[_].access.repoUrl
      not startswith(input.sources[_].access.repoUrl, "https://github.com/acme/")
      msg := "Component must originate from trusted repository"
    }
    
    # Aktuelle Versionen erforderlich
    deny[msg] {
      version_age_days := days_since_version(input.metadata.version)
      version_age_days > 90
      msg := sprintf("Component version %s is too old (%d days)", [input.metadata.version, version_age_days])
    }

Integrieren Sie Policy-Validierung in Ihre Deployment-Pipeline:

# Komponente vor Deployment gegen Policy validieren
ocm verify componentversion \
  --policy policy.yaml \
  registry.acme.com/ocm//acme.com/user-service:v1.2.3

Abfragen und Reporting

OCM ermöglicht ausgeklügelte Abfragen Ihrer Software-Supply-Chain. Extrahieren Sie Abhängigkeitsgraphen, identifizieren Sie verwundbare Komponenten und generieren Sie Compliance-Reports.

Abfrage aller Komponenten, die ein bestimmtes Base-Image verwenden:

# Alle Komponenten finden, die ubuntu:20.04 verwenden
ocm query components \
  --repo registry.acme.com/ocm \
  --filter 'resources[?type==`ociImage`].access.imageReference | [?contains(@, `ubuntu:20.04`)]'

Vollständige Bill of Delivery für eine Anwendung generieren:

# Kompletten Abhängigkeitsbaum extrahieren
ocm tree componentversion \
  --repo registry.acme.com/ocm \
  acme.com/ecommerce-platform:v2.1.0 \
  --output json > supply-chain-report.json

Benutzerdefinierte Reports mit der OCM API erstellen:

// Go-Beispiel für die Generierung benutzerdefinierter Reports
package main

import (
    "context"
    "fmt"
    "github.com/open-component-model/ocm/pkg/contexts/ocm"
    "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ocireg"
)

func generateSupplyChainReport(componentName, version string) error {
    ctx := ocm.New()
    defer ctx.Finalize()
    
    repo, err := ocireg.NewRepository(ctx, "registry.acme.com/ocm")
    if err != nil {
        return err
    }
    
    cv, err := repo.LookupComponentVersion(componentName, version)
    if err != nil {
        return err
    }
    
    // Component-Referenzen rekursiv durchlaufen
    refs := cv.GetComponentReferences()
    for _, ref := range refs {
        fmt.Printf("Dependency: %s@%s\n", ref.GetComponentName(), ref.GetVersion())
        
        // Referenzierte Komponente abrufen und auf Schwachstellen prüfen
        refCV, err := repo.LookupComponentVersion(ref.GetComponentName(), ref.GetVersion())
        if err != nil {
            continue
        }
        
        // Sicherheitsmetadaten extrahieren
        resources := refCV.GetResources()
        for _, res := range resources {
            if res.Meta().GetType() == "ociImage" {
                fmt.Printf("  Image: %s\n", res.Meta().GetName())
                // Hier Integration mit Vulnerability-Scanner
            }
        }
    }
    
    return nil
}

Best Practices für Produktions-Deployment

Automatisierte Component-Generierung: Erstellen Sie niemals manuell Component-Descriptors. Integrieren Sie die Generierung in Ihre CI/CD-Pipelines, um Konsistenz und Genauigkeit zu gewährleisten.

Alles versionieren: Berücksichtigen Sie nicht nur Anwendungsversionen, sondern auch Versionen von Build-Tools, Base-Images und Deployment-Konfigurationen. Dies ermöglicht die präzise Reproduktion jedes Deployments.

Progressive Validierung implementieren: Beginnen Sie mit grundlegendem Component-Tracking und fügen Sie schrittweise Signierung, Policy-Enforcement und Vulnerability-Scanning hinzu, während Ihre Prozesse reifen.

Klare Verantwortlichkeiten etablieren: Definieren Sie, welche Teams für die Wartung von Component-Descriptors für geteilte Abhängigkeiten und Infrastruktur-Komponenten verantwortlich sind.

Supply-Chain-Drift überwachen: Auditieren Sie regelmäßig deployete Komponenten gegen ihre Descriptors, um nicht autorisierte Änderungen oder Konfigurationsdrift zu identifizieren.

Die Open Component Model-Spezifikation bietet die Grundlage für umfassende Sichtbarkeit der Software-Supply-Chain. Durch die Implementierung von OCM in Ihrer Multi-Repository-Umgebung gewinnen Sie die Transparenz, die für die Sicherung und Auditierung Ihrer Cloud-Native-Anwendungen erforderlich ist, bei gleichzeitiger Beibehaltung der Flexibilität zur Weiterentwicklung Ihrer Architektur.

OCM verwandelt Supply-Chain-Sicherheit von einer Compliance-Checkbox in eine strategische Fähigkeit, die zuversichtliche, schnelle Software-Auslieferung ermöglicht. Die Investition in die Implementierung umfassender Component-Verfolgung zahlt sich durch reduzierte Sicherheitsvorfälle, schnellere Incident-Response und vereinfachtes Compliance-Reporting aus.

Lesebarkeit

Schriftgröße