Veröffentlicht am: 9. Dezember 2025
9 Minuten Lesezeit
Implementierung einer sicheren CI/CD-Pipeline über sechs Stufen mit der GitLab-DevSecOps-Plattform.

Die Sicherheit der Software-Lieferkette ist ein zentrales Anliegen in der Softwareentwicklung. Unternehmen müssen die Authentizität und Integrität ihrer Software-Pakete nachweisen können. Diese Anleitung zeigt die Implementierung einer sicheren CI/CD-Pipeline für Python-Pakete mittels GitLab CI, einschließlich Paket-Signierung und Attestierung mit Sigstores Cosign.
In dieser Anleitung:
Vier Gründe für Signierung und Attestierung der Python-Pakete:
Die Gewährleistung der Code-Integrität und -Authentizität ist notwendig. Diese Pipeline erstellt nicht nur den Code, sondern erzeugt eine kryptographisch verifizierbare Dokumentation darüber, wie, wann und von wem das Paket erstellt wurde. Jede Stufe fungiert als Kontrollinstanz, die die Herkunft des Pakets prüft und dokumentiert.
Sechs Stufen einer GitLab-Pipeline, die die Sicherheit und Vertrauenswürdigkeit des Pakets gewährleisten:
Vor dem Paket-Build muss eine konsistente und sichere Build-Umgebung eingerichtet werden. Diese Konfiguration stellt sicher, dass jedes Paket mit denselben Werkzeugen, Einstellungen und Sicherheitskontrollen erstellt wird.
Die Pipeline benötigt spezifische Werkzeuge und Einstellungen.
Primäre Konfigurationen:
Hinweis zur Versionierung: Dieses Beispiel nutzt eine fest codierte Version ("1.0.0") statt einer Ableitung aus Git-Tags oder Commits. Dieser Ansatz gewährleistet vollständige Reproduzierbarkeit und macht das Pipeline-Verhalten vorhersagbarer. In Produktivumgebungen lässt sich semantische Versionierung basierend auf Git-Tags oder eine andere Versionierungsstrategie einsetzen, die zum Release-Prozess passt.
Werkzeug-Anforderungen:
curl, wget - Cosign für kryptographische Signierung - Python-Packaging-Tools: build, twine, setuptools, wheel PYTHON_VERSION: '3.10'
PACKAGE_NAME: ${CI_PROJECT_NAME}
PACKAGE_VERSION: "1.0.0"
FULCIO_URL: 'https://fulcio.sigstore.dev'
REKOR_URL: 'https://rekor.sigstore.dev'
CERTIFICATE_IDENTITY: 'https://gitlab.com/${CI_PROJECT_PATH}//.gitlab-ci.yml@refs/heads/${CI_DEFAULT_BRANCH}'
CERTIFICATE_OIDC_ISSUER: 'https://gitlab.com'
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
COSIGN_YES: "true"
GENERIC_PACKAGE_BASE_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}"
Caching beschleunigt nachfolgende Builds:
paths:
- ${PIP_CACHE_DIR}
Jede Software-Entwicklung beginnt mit der Erstellung. In dieser Pipeline transformiert die Build-Stage Quellcode in ein verteilbares Paket, bereit für verschiedene Python-Umgebungen.
Der Build-Prozess erstellt zwei standardisierte Formate:
Implementierung der Build-Stage:
extends: .python-job
stage: build
script:
- git init
- git config --global init.defaultBranch main
- git config --global user.email "[email protected]"
- git config --global user.name "CI"
- git add .
- git commit -m "Initial commit"
- export NORMALIZED_NAME=$(echo "${CI_PROJECT_NAME}" | tr '-' '_')
- sed -i "s/name = \".*\"/name = \"${NORMALIZED_NAME}\"/" pyproject.toml
- sed -i "s|\"Homepage\" = \".*\"|\"Homepage\" = \"https://gitlab.com/${CI_PROJECT_PATH}\"|" pyproject.toml
- python -m build
artifacts:
paths:
- dist/
- pyproject.toml
Die Build-Stage führt folgende Schritte aus:
git init) und konfiguriert es mit Basis-Einstellungen 2. Normalisiert den Paketnamen durch Umwandlung von Bindestrichen in Unterstriche, was für Python-Packaging erforderlich ist 3. Aktualisiert die Paket-Metadaten in pyproject.toml entsprechend den Projekt-Einstellungen 4. Erstellt sowohl Wheel- als auch Quell-Distributions-Pakete mittels python -m build 5. Bewahrt die erstellten Pakete und Konfiguration als Artefakte für nachfolgende StufenIst die Attestierung die Biografie des Pakets, so ist die Signierung dessen kryptographisches Siegel der Authentizität. Hier wird das Paket von einer bloßen Datei-Sammlung in ein verifiziertes, manipulationssicheres Artefakt transformiert.
Die Signierung-Stage nutzt Cosign zur Anwendung einer digitalen Signatur als unzerbrechliches Siegel. Dies ist kein einfacher Stempel, sondern ein komplexer kryptographischer Handshake, der die Integrität und Herkunft des Pakets beweist.
extends: .python+cosign-job
stage: sign
id_tokens:
SIGSTORE_ID_TOKEN:
aud: sigstore
script:
- |
for file in dist/*.whl dist/*.tar.gz; do
if [ -f "$file" ]; then
filename=$(basename "$file")
cosign sign-blob --yes \
--fulcio-url=${FULCIO_URL} \
--rekor-url=${REKOR_URL} \
--oidc-issuer $CI_SERVER_URL \
--identity-token $SIGSTORE_ID_TOKEN \
--output-signature "dist/${filename}.sig" \
--output-certificate "dist/${filename}.crt" \
"$file"
fi
done
artifacts:
paths:
- dist/
Diese Signierung-Stage führt mehrere zentrale Operationen aus:
.sig) für jedes Paket 4. Generiert ein Zertifikat (.crt), das die Authentizität der Signatur beweist 5. Speichert sowohl Signaturen als auch Zertifikate zusammen mit den Paketen als ArtefakteDie Verifizierung ist das finale Qualitätskontroll-Gate. Dies ist nicht nur eine Prüfung, sondern eine Sicherheitsuntersuchung, bei der jeder Aspekt des Pakets überprüft wird.
extends: .python+cosign-job
stage: verify
script:
- |
failed=0
for file in dist/*.whl dist/*.tar.gz; do
if [ -f "$file" ]; then
filename=$(basename "$file")
if ! cosign verify-blob \
--signature "dist/${filename}.sig" \
--certificate "dist/${filename}.crt" \
--certificate-identity "${CERTIFICATE_IDENTITY}" \
--certificate-oidc-issuer "${CERTIFICATE_OIDC_ISSUER}" \
"$file"; then
failed=1
fi
fi
done
if [ $failed -eq 1 ]; then
exit 1
fi
Die Verifizierung-Stage implementiert mehrere Sicherheitsprüfungen:
dist-Verzeichnis 2. Nutzt Cosign zur Überprüfung, dass die Signatur zum Paketinhalt passt 3. Bestätigt, dass die Zertifikats-Identität der erwarteten GitLab-Pipeline-Identität entspricht 4. Validiert, dass das Zertifikat vom vertrauenswürdigen OIDC-Provider ausgestellt wurde 5. Lässt die gesamte Pipeline fehlschlagen, wenn eine Prüfung fehlschlägt, sodass nur verifizierte Pakete fortfahrenBei der Veröffentlichung werden die verifizierten Pakete über die GitLab-Paket-Registry verfügbar gemacht. Dies ist eine sorgfältig orchestrierte Freigabe, die sicherstellt, dass nur verifizierte, authentifizierte Pakete ihr Ziel erreichen.
extends: .python-job
stage: publish
script:
- |
cat << EOF > ~/.pypirc
[distutils]
index-servers = gitlab
[gitlab]
repository = ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi
username = gitlab-ci-token
password = ${CI_JOB_TOKEN}
EOF
TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token \
twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi \
dist/*.whl dist/*.tar.gz
Die Veröffentlichung-Stage übernimmt mehrere wichtige Aufgaben:
.pypirc-Konfigurationsdatei mit GitLab-Paket-Registry-Zugangsdaten 2. Nutzt das GitLab-CI-Job-Token für sichere Authentifizierung 3. Lädt sowohl Wheel- als auch Quell-Distributions-Pakete in die GitLab-PyPI-Registry hoch 4. Stellt die Pakete zur Installation via pip bereitNach Veröffentlichung der Pakete müssen deren Signaturen und Zertifikate zur Verifizierung verfügbar gemacht werden. Diese werden in der generischen Paket-Registry von GitLab gespeichert und sind damit einfach zugänglich für Nutzer, die die Paket-Authentizität verifizieren möchten.
extends: .python+cosign-job
stage: publish_signatures
script:
- |
for file in dist/*.whl dist/*.tar.gz; do
if [ -f "$file" ]; then
filename=$(basename "$file")
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--fail \
--upload-file "dist/${filename}.sig" \
"${GENERIC_PACKAGE_BASE_URL}/${filename}.sig"
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--fail \
--upload-file "dist/${filename}.crt" \
"${GENERIC_PACKAGE_BASE_URL}/${filename}.crt"
fi
done
Die Signaturen-Veröffentlichung-Stage führt folgende Schlüsseloperationen aus:
.sig) in die generische Paket-Registry 3. Lädt die zugehörige Zertifikat-Datei (.crt) hoch 4. Stellt diese Verifizierungs-Artefakte für nachgelagerte Paket-Nutzer bereit 5. Verwendet dieselbe Version und denselben Paketnamen zur Aufrechterhaltung der Verbindung zwischen Paketen und SignaturenDie finale Stufe simuliert, wie Endnutzer die Authentizität des Pakets verifizieren werden. Diese Stufe dient sowohl als finale Prüfung als auch als praktisches Beispiel des Verifizierungsprozesses.
extends: .python+cosign-job
stage: consumer_verification
script:
- |
git init
git config --global init.defaultBranch main
mkdir -p pkg signatures
pip download --index-url "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/pypi/simple" \
"${NORMALIZED_NAME}==${PACKAGE_VERSION}" --no-deps -d ./pkg
pip download --no-binary :all: \
--index-url "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/pypi/simple" \
"${NORMALIZED_NAME}==${PACKAGE_VERSION}" --no-deps -d ./pkg
failed=0
for file in pkg/*.whl pkg/*.tar.gz; do
if [ -f "$file" ]; then
filename=$(basename "$file")
sig_url="${GENERIC_PACKAGE_BASE_URL}/${filename}.sig"
cert_url="${GENERIC_PACKAGE_BASE_URL}/${filename}.crt"
curl --fail --silent --show-error \
--header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--output "signatures/${filename}.sig" \
"$sig_url"
curl --fail --silent --show-error \
--header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--output "signatures/${filename}.crt" \
"$cert_url"
if ! cosign verify-blob \
--signature "signatures/${filename}.sig" \
--certificate "signatures/${filename}.crt" \
--certificate-identity "${CERTIFICATE_IDENTITY}" \
--certificate-oidc-issuer "${CERTIFICATE_OIDC_ISSUER}" \
"$file"; then
failed=1
fi
fi
done
if [ $failed -eq 1 ]; then
exit 1
fi
Diese Nutzer-Verifizierung-Stage simuliert die Endnutzer-Erfahrung durch:
Diese umfassende Pipeline bietet eine sichere und zuverlässige Methode zum Erstellen, Signieren und Veröffentlichen von Python-Paketen in die GitLab-Paket-Registry. Durch Befolgung dieser Praktiken und Implementierung der empfohlenen Sicherheitsmaßnahmen lässt sich sicherstellen, dass Pakete angemessen verifiziert und sicher an Nutzer verteilt werden.
Die Pipeline kombiniert moderne Sicherheitspraktiken mit effizienter Automatisierung zur Schaffung einer robusten Software-Lieferkette. Mittels Sigstores Cosign für Signierung und Attestierung sowie GitLabs integrierten Sicherheitsfunktionen lassen sich Nutzern vertrauenswürdige, kryptographisch verifizierte Pakete bereitstellen.
Starte noch heute deine Sicherheitsreise mit einer kostenlosen Testversion von GitLab Ultimate.
Teams, die kryptographische Paket-Signierung implementieren, adressieren möglicherweise auch Compliance-Anforderungen – beispielsweise in regulierten Branchen, wo kryptographische Signaturen und Herkunftsnachweise für bereitgestellte Software verlangt werden.
Für spezifische Compliance-Bewertungen und regulatorische Anforderungen empfiehlt sich Rücksprache mit entsprechender Fachberatung.
Dieser Blogbeitrag hat gefallen oder es gibt Fragen oder Feedback? Ein neues Diskussionsthema im GitLab-Community-Forum erstellen und Eindrücke austauschen.
Feedback teilen