updated documentation in Dockerfile und Jenkinsfile and updated to --template "html"
All checks were successful
Tests / Declarative: Post Actions No test results found
csharp-secdevops-pipeline-pod/pipeline/head This commit looks good

This commit is contained in:
2026-05-13 10:10:04 +02:00
parent 06caba6243
commit ebd021eeb7
2 changed files with 50 additions and 40 deletions

View File

@@ -1,36 +1,37 @@
# --- Stage 1: Build (Die Bau-Umgebung) --- # Basis Image das für die Build Umgebung verwendet wird
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build-env FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build-env
# Wechseln ins Arbeitsverzeichnis
WORKDIR /app WORKDIR /app
# 1. Nur Projektdatei kopieren und Abhängigkeiten laden (Nutzt Docker-Caching) # Kopieren der Projektdatei in Arbeitsverzeichnis
COPY *.csproj ./ COPY *.csproj ./
#COPY *.sln ./ # Laden der Abhängigkeiten
#COPY MyHelloWorld/*.csproj ./MyHelloWorld/
#COPY MyHelloWorld.Tests/*.csproj ./MyHelloWorld.Tests/
RUN dotnet restore RUN dotnet restore
# 2. Den restlichen Quellcode kopieren und die App kompilieren # Kopieren des restlichen Quellcodes
COPY . ./ COPY . ./
#RUN dotnet publish MyHelloWorld/*.csproj -c Release -o out # Kompilieren eds Quellcodes (Projektdatei muss nicht zwingend angegeben werden)
RUN dotnet publish "SecDevOpsLab.csproj" -c Release -o out RUN dotnet publish "SecDevOpsLab.csproj" -c Release -o out
# --- Stage 2: Runtime (Das fertige, schlanke Image) --- # Noch schlangere Basis Image für die Runtime Umgebung
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
# Wechseln ins Arbeitsverzeichnis
WORKDIR /app WORKDIR /app
# 3. Port für die Web-App öffnen (Standard bei .NET 8 Web-Apps ist 8080) # Öffnen des Ports für die Web-App (Standard bei .NET 8 Web-Apps ist 8080)
EXPOSE 8080 EXPOSE 8080
# Konfigurieren des integrierten Kestrel-Webserver einer ASP.NET Core App
ENV ASPNETCORE_URLS=http://+:8080 ENV ASPNETCORE_URLS=http://+:8080
# 3. Den vorinstallierten User 'app' von Microsoft nutzen # Wechseln auf root (nur kurz)
# Erlaube dem User 'app' in das Verzeichnis zu schreiben (für SQLite)
USER root USER root
# Ändern des File Owner (wichtig dass die Sqlite DB geschrieben werden kann)
RUN mkdir -p /app/data && chown -R app:app /app/data && chmod -R 775 /app/data RUN mkdir -p /app/data && chown -R app:app /app/data && chmod -R 775 /app/data
# Wechsel auf non-root User (app ist ein vorinstallierter User von Microsoft)
USER app USER app
# 4. Nur die fertigen Binärdateien aus der Bau-Umgebung rüberschieben # Kopieren der fertigen Binärdateien aus der Build Umgebung
COPY --from=build-env /app/out . COPY --from=build-env /app/out .
# 5. Startbefehl festlegen # Festlegen des Start Befehls
# WICHTIG: Falls deine DLL anders heißt (z.B. MyHelloWorld.dll), passe den Namen hier an!
ENTRYPOINT ["dotnet", "SecDevOpsLab.dll"] ENTRYPOINT ["dotnet", "SecDevOpsLab.dll"]

61
Jenkinsfile vendored
View File

@@ -1,7 +1,7 @@
pipeline { pipeline {
agent { agent {
kubernetes { kubernetes {
// Definiert den Pod mit dem .NET 8 SDK Image // Definieren des Pod mit 3 Containern als Build Agent, Trivy und Kaniko
yaml ''' yaml '''
apiVersion: v1 apiVersion: v1
kind: Pod kind: Pod
@@ -26,15 +26,16 @@ pipeline {
} }
options { options {
// Log-Rotation: Bewahre nur die letzten 10 Builds auf // Aktivieren von Log Rotation:
// und lösche Berichte/Artefakte von Builds, die älter als 7 Tage sind. // *) Letzten 10 Builds werden aufbewahrt
// ** Builds, die älter als 7 Tage sind, werden gelöscht
buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '10', daysToKeepStr: '7')) buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '10', daysToKeepStr: '7'))
} }
stages { stages {
// Auschecken der Sourcen zum App Projekt
stage('Checkout Source') { stage('Checkout Source') {
steps { steps {
// Ersetze 'dein-user' und 'dein-repo' durch die Namen aus Gitea
git url: 'http://130.61.26.230:30080/dev-master/secdevops-csharp-app.git', git url: 'http://130.61.26.230:30080/dev-master/secdevops-csharp-app.git',
branch: 'master' branch: 'master'
} }
@@ -42,27 +43,29 @@ pipeline {
stage('Security: Trivy Scan') { stage('Security: Trivy Scan') {
steps { steps {
// Wiederherstellen der project.assets.json in obj/ für Trivy zum finden der transtiven Abhängigkeiten // Ausführen des 'dotnet restore' für den nachfolgenden Trivy Scan, der diese wiederhergestellte Dateien/NuGet Pakete und Abhängigkeiten scanned
// Generiert automatisch das obj/ mit der project.assets.json
// Alle Container eines Pods teilen sich das Jenkins Arbeitsverzeichnis
container('dotnet8') { container('dotnet8') {
sh 'dotnet restore' sh 'dotnet restore'
} }
// Ausführen des Trivy Scans
//
// Wichtig: trivy ersetzt -> dotnet list package --vulnerable --include-transitive
container('trivy') { container('trivy') {
// Wir erstellen ein Verzeichnis für den Report // Erzeugen des Directory zum Speichern des Reports
sh 'mkdir -p reports' sh 'mkdir -p reports'
// Wichtig: trivy ersetzt -> dotnet list package --vulnerable --include-transitive
// Der Befehl erzeugt die HTML-Datei // Ausführen des Scans hinsichtlich Vulnerabilities, Miskonfigurationen, Secrets und Licences im Jenkins Arbeitsverzeichnis
// --format template: Nutzt ein Layout // Abbruch bei bei kritischen Fehlern (--exit-code 1 --severity HIGH,CRITICAL)
// --template "@/contrib/html.tpl": Das Standard-Trivy-Layout sh 'trivy fs --scanners vuln,misconfig,secret,license --exit-code 1 --severity HIGH,CRITICAL --format template --template "html" -o reports/trivy-fs-report.html .'
// Scannt das Dateisystem auf Schwachstellen (NuGet) und Secrets
// --exit-code 1 lässt die Pipeline bei kritischen Fehlern abbrechen
sh 'trivy fs --scanners vuln,misconfig,secret,license --exit-code 1 --severity HIGH,CRITICAL --format template --template "@/contrib/html.tpl" -o reports/trivy-fs-report.html .'
} }
} }
} }
// Kompilieren der Anwendung (DLLs werden erzeugt)
// Schritt muss gar nicht durchgeführt werden, da kaniko das Image erzeugt
stage('Build with .NET 8') { stage('Build with .NET 8') {
steps { steps {
// Führt den Build-Befehl im spezialisierten Container aus // Führt den Build-Befehl im spezialisierten Container aus
@@ -87,7 +90,7 @@ pipeline {
stage('Set Build Name') { stage('Set Build Name') {
steps { steps {
script { script {
// Setzt den Namen des aktuellen Laufs auf die Version + Build-Nummer // Setzen des Namen des aktuellen Laufs auf die Version + Build-Nummer
currentBuild.displayName = "v1.0.0-build-${env.BUILD_NUMBER}" currentBuild.displayName = "v1.0.0-build-${env.BUILD_NUMBER}"
} }
} }
@@ -100,12 +103,18 @@ pipeline {
steps { steps {
container('kaniko') { container('kaniko') {
// Nutze die ID, die du in Jenkins für den Token vergeben hast // Stellt die Informationen aus dem Token in Form von Umgebungsvariablen der Jenkins Pipeline zur Verfügung
// Nachfolgend werden diese Credentials im JSON Format in config.json geschrieben
// Vorgehen ist zwar nicht extrem sicher, aber die Lebenszeit im Container ist kurz, dass diese base64 kodierten Daten zurückverwandelt werden könnten
// Erzeugen des Directory zum Speichern des Reports, falls das bei einem vorigen Schritt nicht durchgeführt wurde
sh 'mkdir -p reports'
withCredentials([usernamePassword(credentialsId: 'gitea-registry-token', withCredentials([usernamePassword(credentialsId: 'gitea-registry-token',
usernameVariable: 'GITEA_USER', usernameVariable: 'GITEA_USER',
passwordVariable: 'GITEA_TOKEN')]) { passwordVariable: 'GITEA_TOKEN')]) {
sh ''' sh '''
# WORKAROUND: Dem Container beibringen, wer git.example.com ist # WORKAROUND: Dem Container beibringen, wer git.example.com ist, ansonsten funktioniert das Übertragen des Images an Git nicht!!!
echo "130.61.26.230 git.example.com" >> /etc/hosts echo "130.61.26.230 git.example.com" >> /etc/hosts
# Erstellt die Docker-Konfiguration für Kaniko # Erstellt die Docker-Konfiguration für Kaniko
@@ -126,26 +135,27 @@ pipeline {
} }
} }
stage('Security: Trivy Image Scan') { stage('Security: Trivy Image Scan') {
when { when {
branch 'master' branch 'master'
} }
steps { steps {
// Trivy Scan wird auf das Image im Git Repository angewendet. Das Image wird heruntergeladen.
container('trivy') { container('trivy') {
// 1. Scan ausführen und als HTML-Report speichern (Achte auf den neuen Dateinamen) // 1. Scan ausführen und als HTML-Report speichern (Achte auf den neuen Dateinamen)
sh ''' sh '''
trivy image --insecure \ trivy image --insecure \
--severity HIGH,CRITICAL \ --severity HIGH,CRITICAL \
--format template \ --format template \
--template "@/contrib/html.tpl" \ --template "html" \
--exit-code 1 \
-o reports/trivy-image-report.html \ -o reports/trivy-image-report.html \
130.61.26.230:30080/dev-master/secdevops-csharp-app:latest 130.61.26.230:30080/dev-master/secdevops-csharp-app:latest
''' '''
// 2. Den Scan ein zweites Mal kurz ohne Report ausführen, damit die Pipeline bei Lücken blockiert // Den Scan ein zweites Mal kurz ohne Report ausführen, damit die Pipeline bei Lücken blockiert
sh 'trivy image --insecure --exit-code 1 --severity HIGH,CRITICAL 130.61.26.230:30080/dev-master/secdevops-csharp-app:latest' // sh 'trivy image --insecure --exit-code 1 --severity HIGH,CRITICAL 130.61.26.230:30080/dev-master/secdevops-csharp-app:latest'
} }
} }
} }
@@ -158,11 +168,10 @@ pipeline {
} }
} }
always { always {
// Sammelt die Testergebnisse ein (die wir im Test-Schritt erzeugen) // Suchen und Einelen von etwaigen Testreports von JUnit. Keine Vorhanden? -auch ok
// Das **/ bedeutet: Suche in allen Unterordnern nach .xml Dateien
junit testResults: '**/*.xml', allowEmptyResults: true junit testResults: '**/*.xml', allowEmptyResults: true
// Dieser Schritt macht den Report im Jenkins-Menü links sichtbar // Verwenden des HTML Publisher Modules zum Schreiben der gefundenen Testreports in das Build Menu und speichert den HTML Bericht dann historisch ab (keepAll)
publishHTML([ publishHTML([
allowMissing: false, allowMissing: false,
alwaysLinkToLastBuild: true, alwaysLinkToLastBuild: true,
@@ -172,7 +181,7 @@ pipeline {
reportName: 'Trivy Security Report' reportName: 'Trivy Security Report'
]) ])
// Meldet den Status zurück, wenn das Gitea-Plugin korrekt konfiguriert ist // Schreiben des Build Status in das Build Log
echo "Pipeline beendet: ${currentBuild.result}" echo "Pipeline beendet: ${currentBuild.result}"
} }
} }