ci(quick-261009-p0m): Sicherheitspruefung als reiner Bericht in der Pipeline
- Skript .gitea/scripts/security-scan.sh: gitleaks, pnpm audit, osv-scanner, Semgrep, Trivy (Quellstand und Abbilder), angepinnt und per SHA256 geprueft, Exit immer 0 - Job security nach publish (nur main und Tags v*, continue-on-error, Zeitlimit 30 Minuten, kein Secret) - Ausnahmelisten .gitleaks.toml und .semgrepignore fuer geprueft harmlose Treffer - docs/ci-cd-setup.md beschreibt den Job Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+71
-3
@@ -119,7 +119,7 @@ hardcoden.
|
||||
|
||||
Die CI/CD-Pipeline (`.gitea/workflows/ci.yml`) laeuft bei jedem Push auf die
|
||||
Zweige `main` und `live` sowie bei jedem Tag `v*` (z. B. `v1.0.0`) und besteht
|
||||
aus vier aufeinander aufbauenden Jobs:
|
||||
aus fuenf Jobs (vier bauen aufeinander auf, der fuenfte ist ein reiner Bericht):
|
||||
|
||||
1. **quality** -- Lint und TypeScript Type-Check (Lint ist derzeit ein Leerlauf,
|
||||
siehe WINDOWS #35; der Type-Check ist echt)
|
||||
@@ -128,9 +128,12 @@ aus vier aufeinander aufbauenden Jobs:
|
||||
und bei Tags `v*`, siehe unten)
|
||||
4. **publish** -- Docker Images mit Versionsstempel bauen und in die Gitea-Registry
|
||||
veroeffentlichen
|
||||
5. **security** -- Sicherheitspruefung (nur Bericht): nach `publish`, nur auf `main`
|
||||
und bei Tags `v*`; bricht nie ab, blockiert nie etwas, bekommt kein Secret
|
||||
|
||||
Ablauf: `quality` -> `test` -> `desktop` -> `publish` (jeder Job nur bei Erfolg
|
||||
des vorherigen; `desktop` selbst laeuft nur, wenn die `if`-Bedingung zutrifft
|
||||
Ablauf: `quality` -> `test` -> `desktop` -> `publish` -> `security` (jeder Job nur
|
||||
bei Erfolg des vorherigen; `security` ist nur ein Bericht und steht in keinem
|
||||
`needs` eines anderen Jobs; `desktop` selbst laeuft nur, wenn die `if`-Bedingung zutrifft
|
||||
-- auf einem Push nach `live` ohne Tag entfaellt der Job, `publish` startet in
|
||||
diesem Fall trotzdem, weil `needs: desktop` bei einem uebersprungenen Job nicht
|
||||
blockiert). Der Job `publish` besteht aus sechs Schritten:
|
||||
@@ -264,6 +267,58 @@ Hinweis: Die fruehere Entscheidung D-13 (Images lokal bauen, keine Registry) ist
|
||||
ueberholt -- seit der Einfuehrung der Gitea-Registry werden Images gepusht und von
|
||||
den Servern per `docker compose pull` geholt.
|
||||
|
||||
### Job `security`: Sicherheitspruefung (nur Bericht)
|
||||
|
||||
Der Job `security` (quick-261009-p0m) laeuft nach `publish`, damit er die Veroeffentlichung
|
||||
nie verzoegert oder verhindert. Er laeuft nur auf `main` und bei Tags `v*`; die
|
||||
Bedingung steht ausdruecklich am Job, weil ein uebersprungenes `needs` in Gitea nicht
|
||||
blockiert (sonst liefe er auch nach einem Push auf `live`). Er hat `continue-on-error`,
|
||||
ein Zeitlimit von 30 Minuten (ein haengender Scan haelt den einzigen Runner nicht fest)
|
||||
und kein Secret. Alles steckt in einem Skript, das auch lokal laeuft:
|
||||
|
||||
```bash
|
||||
GITHUB_REF=refs/heads/main sh .gitea/scripts/security-scan.sh --print-plan # ohne Netz
|
||||
GITHUB_REF=refs/heads/main sh .gitea/scripts/security-scan.sh all
|
||||
```
|
||||
|
||||
**Was geprueft wird:** gitleaks ueber die gesamte Git-Historie (Zugangsdaten), `pnpm audit
|
||||
--prod` und osv-scanner ueber `pnpm-lock.yaml` und die Desktop-`Cargo.lock` (bekannte
|
||||
Schwachstellen), Semgrep (statische Code-Analyse) und Trivy ueber den Quellstand sowie
|
||||
ueber die frisch gebauten Abbilder `api` und `web` (`main` -> `:beta`, Tag `v*` -> `:live`).
|
||||
Die Abbilder liegen nach `publish` im Docker-Daemon des Hosts (Socket-Mount, Abschnitt 5)
|
||||
und werden direkt von dort gelesen.
|
||||
|
||||
**Quellstand:** Die Quellpruefungen lesen einen `git archive HEAD`-Export in einem
|
||||
temporaeren Ordner, gitleaks nur die Git-Historie. Es wird also genau der eingecheckte
|
||||
Stand geprueft; nicht eingecheckte Dateien eines Arbeitsordners erreichen nie ein
|
||||
Werkzeug, einen Bericht oder ein Artefakt.
|
||||
|
||||
**Angepinnte Werkzeuge:** gitleaks 8.30.1, Trivy 0.75.0, osv-scanner 2.6.0, Semgrep 1.180.0
|
||||
(`pipx`), pnpm 9.15.0. Die drei Programme werden von der offiziellen
|
||||
GitHub-Veroeffentlichung geladen und vor jeder Benutzung gegen eine SHA256-Summe im Skript
|
||||
geprueft (auch das zwischengespeicherte Archiv); stimmt die Summe nicht oder fehlt das
|
||||
Netz, wird nur dieses Werkzeug uebersprungen. Marktplatz-Aktionen fuer Scanner werden
|
||||
bewusst nicht benutzt (veraenderliche Etiketten). **Version anheben:** im Kopf von
|
||||
`security-scan.sh` die Version, die URL und die SHA256-Summe aus der offiziellen
|
||||
Pruefsummendatei der Veroeffentlichung gemeinsam austauschen und einmal lokal
|
||||
`sh .gitea/scripts/security-scan.sh all` laufen lassen. Werkzeuge und die Trivy-Datenbank
|
||||
liegen im persistenten Zwischenspeicher des Runners (`/opt/hostedtoolcache/tessera-security`);
|
||||
der Layer-Zwischenspeicher von Trivy wird nach jedem Lauf geloescht, damit die Platte nicht
|
||||
vollaeuft.
|
||||
|
||||
**Ausnahmelisten:** `.gitleaks.toml` (geprueft harmlose Treffer: Testschluessel des
|
||||
Zertifikatsmanagers, Schluessel-Ausschnitte in Tests und Notizen) und `.semgrepignore`
|
||||
(Tests, Testdaten, Planungsnotizen). Ein neuer Treffer wird nie vorsorglich freigegeben,
|
||||
sondern erst geprueft.
|
||||
|
||||
**Wo man das Ergebnis sieht:** Im Protokoll des Jobs stehen je Werkzeug eine Zeile
|
||||
`SECURITY-SUMMARY ...` (nur Zahlen) und am Ende `SECURITY-SUMMARY fertig (nur Bericht,
|
||||
Exit 0)`; dieselben Zeilen stehen in `summary.txt`. Die Rohberichte (JSON) haengen,
|
||||
soweit der Runner es zulaesst, als Artefakt `sicherheitsberichte` (30 Tage) am Lauf. Das
|
||||
Artefakt ist nur "best effort" (Gitea lehnt `upload-artifact@v4` ab, es laeuft `@v3`);
|
||||
verlassen Sie sich auf die Zeilen im Protokoll. Der Job braucht ungefaehr fuenf bis acht
|
||||
Minuten nach `publish`.
|
||||
|
||||
## 5. Sicherheitshinweise
|
||||
|
||||
### Docker Socket
|
||||
@@ -420,3 +475,16 @@ ausdruecklich auf die oeffentliche Adresse gesetzt wird. Fehlende Anhaenge
|
||||
lassen sich jederzeit vom Host nachtragen:
|
||||
`GITEA_TOKEN=... DESKTOP_DIST=<Ordner mit manifest.json> sh .gitea/scripts/publish-release.sh --tag vX.Y.Z`
|
||||
(die Pakete liegen im API-Abbild unter `/app/desktop-dist`).
|
||||
|
||||
### Job `security` ist rot oder gelb
|
||||
|
||||
Der Job ist ein reiner Bericht und beeinflusst weder Abbilder noch Freigaben (er laeuft
|
||||
erst nach `publish` und steht in keinem `needs`). Rot oder gelb heisst deshalb nie, dass
|
||||
etwas nicht ausgeliefert wurde. Zur Ursache im Job-Protokoll die Zeilen
|
||||
`SECURITY-SUMMARY <werkzeug> skipped reason=...` lesen: `download` oder `checksum` (Werkzeug
|
||||
nicht ladbar bzw. Summe stimmt nicht -- Netz pruefen, nie die Summe ohne Pruefung
|
||||
austauschen), `kein-pipx` / `install` (Semgrep), `kein-pnpm`, `abbild-fehlt` (das Abbild
|
||||
liegt nicht im Docker-Daemon des Hosts), `fehler` (Werkzeug ist abgestuerzt oder hat
|
||||
nichts geschrieben, z. B. Zeitlimit), `kein-bericht`. Ein haengender Lauf endet nach 30
|
||||
Minuten von selbst. Fehlt nur das Artefakt, ist das erwartbar (best effort); die Zahlen
|
||||
stehen trotzdem im Protokoll.
|
||||
|
||||
Reference in New Issue
Block a user