updated documentation in Dockerfile und Jenkinsfile and updated to --template "html"
This commit is contained in:
29
Dockerfile
29
Dockerfile
@@ -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
61
Jenkinsfile
vendored
@@ -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}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user