Architektur aus Kisten

Gitrust-Strukturreferenz: Kisten, Module, Routen, Abhängigkeiten und CI-Komponenten.

Abhängigkeiten zwischen Kisten

graph LR
    RW["rustwarden-core"]

    Git["gitrust-git"]
    Core["gitrust-core"]
    Hooks["gitrust-hooks"]
    SSH["gitrust-ssh"]
    SshGuard["gitrust-ssh-guard"]
    Web["gitrust-web"]
    Bin["gitrust (binaire)"]

    Core --> RW
    Git -.->|"indépendant
(git2, tokio)"| RW Hooks --> Core Hooks --> RW SSH --> Core SSH --> Git SSH --> SshGuard SshGuard -.->|"SeaORM bans/ACL/events"| Core Web --> Core Web --> Git Web -.->|"CI pages"| Git Web -.->|"admin ACL/ban via Extension"| SshGuard Bin --> Core Bin --> Git Bin --> SSH Bin --> SshGuard Bin --> Web Bin --> Hooks Bin --> RW subgraph "Externes CI (Phase 5b)" Dagger["Dagger CLI"] CiEng["ci-engine (Python)"] Syft["Syft"] DTrack["Dependency-Track"] end Core -.->|"CiWorker
sous-processus"| Dagger Dagger -.-> CiEng Core -.-> Syft Syft -.-> DTrack

Kisten – Verantwortlichkeiten

gitrust-core

Kerngeschäft. Hängt nur von Rustwarden-Core und Standard-Rust-Kisten ab.

Module Rôle
config GitrustConfig::from_env() — ports SSH, chemin repos, limites
error GitrustError — erreurs domaine Git + IntoResponse HTTP
roles enum Role { Reader, Developer, Maintainer, Owner } avec Ord
types Newtypes validés : RepoSlug, TeamSlug, Fingerprint, TokenHash
models/ Entités SeaORM : repository, ssh_key, team, team_member, team_repository_access, ci_pipeline, ci_job, ci_log, ci_config, ci_variable
migrations/ Migrations SeaORM (tables gitrust) combinées avec les migrations core
services/ SshKeyService, RepositoryService, TeamService, AccessService, CiService, CiVariableService, CiDetectionService, CiWorker, NotificationService
dto/ Structures d'entrée/sortie pour les services

gitrust-git

Git-Operationen im Dateisystem. Verwenden Sie „git2“ (libgit2). Keine Abhängigkeit von der Datenbank.

Module Rôle
errors enum GitError (Git, RepoNotFound, RefNotFound, PathTraversal, ...)
bare_repo Init, open, delete, exists + validation chemin canonique sous base
branch / tag Listing branches (BranchInfo) et tags (TagInfo, annotated/lightweight)
reference resolve_ref (branch -> tag -> SHA -> revparse)
tree_browser list_tree (dirs first, sorted), TreeEntry, EntryKind
blob_reader read_blob -> Text/Binary, détection binaire (null bytes)
commit_log list_commits (paginé), find_commit, CommitInfo
readme find_readme (README.md > README > readme.md > ...)
pack_protocol advertise_refs, serve_pack (async, git-upload-pack/git-receive-pack)

gitrust-ssh

SSH-Server basierend auf „russh“. Authentifizierung mit öffentlichem Schlüssel.

Module (prévu) Rôle
server Démarrage, génération clé hôte Ed25519, wrapper du TcpListener par SecureListener (ssh-guard)
auth Authentification par fingerprint -> SshKeyService, appel à AuthTracker.record_auth_attempt après chaque tentative
session Handler SSH (exec, shell), porte ClientIdentity du listener jusqu'aux décisions auth
command_handler Parsing git-upload-pack/git-receive-pack

gitrust-ssh-guard

Couche de durcissement SSH intercalée entre le TcpListener et russh. Voir la page dédiée Crate gitrust-ssh-guard pour le détail des modules et l'API publique. Synthèse :

Module Rôle
config GuardConfig::from_env() — lecture SSH_GUARD_*, sélection DeploymentProfile, validation
runtime GuardHandles::build(db) — assemblage partageable entre serveur SSH et routeur admin
listener SecureListener wrappe TcpListener : extraction IP réelle (PROXY v1/v2), ACL, flood, retour AcceptOutcome
proxy Parseur PROXY protocol v1/v2 avec timeout
identity ClientIdentity — IP + session_id + user/fingerprint enrichis pendant l'auth
tracker AuthTracker — persiste, émet, dispatche aux détecteurs
ban BanManager + EffectiveStatus — priorité deny > auto_ban > allow > default
events GuardEvent — schéma JSON stable (tag = "event") pour fail2ban / SIEM
sinks TracingSink, FileSink, MultiSink — destination des événements
detector/{brute_force,user_enumeration,key_scanning,connection_flood} 4 motifs d'attaque, push-based
store/{memory,postgres,hybrid} Backend bans/ACL/events — hybrid (RAM + write-through) par défaut

gitrust-web

SSR-Webschnittstelle (Server-Side Rendering) mit Askama (kompilierte Vorlagen) + HTMX (dynamische Interaktionen). Funktioniert im „headless()“-Modus: Die Framework-UI-Seiten („rustwarden-ui“) werden nicht gemountet, gitrust-web implementiert alle Seiten mit einem bestimmten Design (kontextbezogene Seitenleiste, Git-Navigation, DaisyUI) neu.

Straßenarchitektur:

gitrust (port 4000)
├── /api/v1/auth/*          ← Routes API du framework (JSON)
│   ├── POST /login            AuthService::authenticate_user
│   ├── POST /register         UserService::create_user
│   ├── POST /refresh          RefreshTokenService (rotation JWT)
│   ├── POST /logout           JwtBlacklistService
│   ├── GET  /me               Claims extraction
│   ├── POST /forgot-password  PasswordResetService::request_reset
│   ├── POST /reset-password   PasswordResetService::reset_password
│   ├── GET  /verify-email/{t} EmailValidationService::verify_email
│   └── POST /resend-verif     EmailValidationService::resend
├── /api/v1/*                ← API REST Gitrust (JSON, auth JWT/PAT)
│   ├── /user, /user/repos     Profil utilisateur
│   ├── /repos/{o}/{r}         Détail dépôt, branches, tags, commits
│   ├── /repos/{o}/{r}/issues  CRUD issues + commentaires
│   ├── /repos/{o}/{r}/pulls   CRUD PRs + merge
│   └── /repos/{o}/{r}/ci/*   Pipelines CI (list, trigger, cancel, logs)
├── /*                       ← Pages SSR gitrust (HTML)
│   ├── /login, /register       Pages formulaires (design gitrust)
│   ├── /dashboard, /teams      Pages utilisateur
│   ├── /settings/*             Profile, SSH keys, sessions, notifications
│   ├── /notifications          Liste notifications + SSE stream
│   ├── /admin/*                Administration (+ admin CI)
│   ├── /{owner}/{repo}/*       Navigation dépôt Git
│   └── /{owner}/{repo}/ci/*    Pages CI (pipelines, logs, config, variables)
└── /static/*                ← Assets locaux (CSS, JS, HTMX)

Les routes API du framework sont montées via framework_api_routes() dans routes.rs. Elles sont utilisées par : - Links in E-Mails (Verifizierung, Passwort zurücksetzen) - Das Aktualisierungstoken (JWT-Rotation, Langzeitsitzung) - Programmgesteuerter Zugriff (GET /me, POST /logout)

SSR-Seiten und API-Routen existieren nebeneinander: HTML-Formulare werden an Gitrust-Handler (SSR) übermittelt, während Framework-Dienste Links zu API-Routen generieren.

Module Rôle
routes.rs Routeur principal : framework API + API REST Gitrust + pages SSR + fichiers statiques
handlers/ Handlers Axum SSR (un fichier par domaine) + handlers API JSON
templates.rs Structs Askama + SidebarContext + RepoNav
helpers.rs require_auth, require_admin, sidebar_*, resolve_repo_access
templates/ Templates HTML (Askama, extends base.html)
static_files.rs ServeDir pour /static

Gitrust-Hooks

„RustwardenHooks“ implementiert, um auf Framework-Ereignisse zu reagieren. Die Hooks werden über „Arc“ + „axum::Extension“ (in „main.rs“) in den Axum-Router eingefügt.

Hook Action
on_user_registered Crée le répertoire utilisateur sur le FS (anti path-traversal)
on_user_deleted Supprime le répertoire utilisateur + bare repos sur le FS
on_resource_shared Audit log si resource_type = "repository"
on_resource_unshared Audit log si resource_type = "repository"

Benachrichtigungen

Zweikanaliges Benachrichtigungssystem: In-App (Echtzeit-SSE + Seite) und E-Mail (Framework-SMTP-Worker).

Événement (pipeline échoue, PR commentée, etc.)
NotificationService::notify(user_id, event_type, title, body, link)
    ├── INSERT INTO notifications (in-app)
    │     → broadcast::Sender → SSE /notifications/stream
    │     → Badge header mis à jour en temps réel (HTMX)
    └── if user.email_on_{event} = true:
          EmailQueueService::enqueue() (framework SMTP worker)

Benachrichtigungseinstellungen gelten pro Benutzer (E-Mail ein/aus nach Ereignistyp).


CI/CD – Modi und Komponenten

Hybrides zweistufiges CI-System, aktiviert über „CI_ENABLED=true“.

Mode

Mode Détection Exécution
Easy .gitrust-ci.yml à la racine du repo Module Dagger Python générique (ci-engine) interprète le YAML
Power .dagger/ à la racine du repo dagger call -m .dagger/ exécute le module utilisateur directement
Aucun Ni l'un ni l'autre Pas de CI pour ce repo

CI-Architektur

git push (HTTP ou SSH)
receive-pack handler
    ├── CiDetectionService::detect(bare_repo, commit_sha)
    │     → Easy / Power / None
    ├── CiService::create_pipeline(repo_id, sha, mode)
    └── mpsc::Sender.send(job) ──► CiWorker (tokio::spawn)
                                       ├── 1. Validation statique
                                       │     Easy: schéma YAML, whitelist images/packages
                                       │     Power: analyse code .dagger/ (si activé)
                                       ├── 2. SBOM Gate (si CI_SBOM_ENABLED)
                                       │     syft scan → sbom.cdx.json
                                       │     → Dependency-Track (si CI_DTRACK_ENABLED)
                                       │     → PASS/FAIL selon politique
                                       ├── 3. Résolution variables (héritage team → repo)
                                       │     CiVariableService::resolve_for_pipeline()
                                       ├── 4. Exécution Dagger (isolé, timeout, cgroups)
                                       │     Easy: dagger call -m ci-engine test --profile=...
                                       │     Power: dagger call -m .dagger/ <function>
                                       └── 5. Rapport + audit
                                             Status, logs (broadcast SSE), ci_audit_log

Externe Komponenten (alle optional außer Dagger)

Composant Rôle Variable de contrôle
Dagger Engine Exécution des pipelines dans des containers isolés CI_ENABLED
ci-engine (Python) Moteur générique pour Easy Mode CI_DAGGER_MODULE_PATH
Syft Génération SBOM CycloneDX CI_SBOM_ENABLED
Dependency-Track Analyse de vulnérabilités sur les SBOM CI_DTRACK_ENABLED

CI-Variablen und Geheimnisse

Vererbung Team → Repo: Auf Teamebene definierte Variablen werden von allen Repos des Teams gemeinsam genutzt. Das Repo kann einen Teamschlüssel mit seinem eigenen Wert überladen. Die Geheimnisse sind basisverschlüsselt (AES-256-GCM) und in den Protokollen verborgen („***“).


Sicherheit (ANSSI PA-074)

Directive Scope
#![forbid(unsafe_code)] gitrust-core, gitrust-web, gitrust-hooks, gitrust-ssh-guard
#![deny(unsafe_code)] gitrust-git, gitrust-ssh (FFI libgit2/russh)
deny(clippy::unwrap_used, expect_used, panic) Tous les crates
deny(clippy::indexing_slicing) Tous les crates
deny(clippy::mem_forget) Tous les crates
Zeroize on drop TokenHash (ANSSI R23)
RSA minimum 4096 bits SshKeyService
Path traversal validation RepositoryService, gitrust-git
Slug validation + noms réservés RepoSlug, TeamSlug