Comprendre l'architecture globale de gitrust¶
Ce que vous allez comprendre¶
- Identifier les processus qui s'exécutent au sein d'une instance gitrust et leur rôle respectif
- Analyser le chemin d'une requête HTTP et d'un push SSH du client jusqu'à la réponse
- Évaluer les implications opérationnelles de cette architecture pour les décisions d'administration (sauvegarde, mise à l'échelle, durcissement)
Le problème concret¶
Vous administrez gitrust et vous devez décider : « Sur quel processus dois-je redémarrer quand je modifie la configuration SMTP ? Pourquoi la clé SSH hôte ne doit-elle jamais être régénérée ? Qu'est-ce qui se passe si PostgreSQL est indisponible pendant 2 minutes ? »
Ces questions n'ont de réponse claire que si vous comprenez l'architecture interne.
L'analogie¶
Pensez à gitrust comme à un immeuble de bureaux avec un seul accueil (le binaire gitrust) qui abrite plusieurs services distincts :
- Le guichet HTTP reçoit les navigateurs et les appels API
- Le guichet SSH reçoit les clients
git - La salle des archives (PostgreSQL) conserve toutes les données
- Les ateliers (workers CI, import) font du travail en arrière-plan
- Le vestiaire (système de fichiers) garde les manteaux (les dépôts bare)
Un seul processus, mais plusieurs « couloirs » internes. Si l'immeuble ferme (redémarrage), tous les guichets ferment simultanément.
Le modèle¶
Composants au runtime¶
graph TB
subgraph "Processus gitrust (binaire unique)"
AX[Serveur HTTP axum
:4000]
GUARD[ssh-guard
SecureListener + détecteurs]
SSH[Serveur SSH Russh
:2222]
CIW[Worker CI async
Dagger]
IMW[Worker Import async
git clone]
EMW[Worker Email async
SMTP queue]
end
subgraph "Dépendances externes"
PG[(PostgreSQL
:5432)]
FS[Système de fichiers
GIT_REPOS_BASE_PATH]
SMTP[Serveur SMTP
sortant]
DAGGER[Dagger Engine
Docker]
end
subgraph "Clients"
BR[Navigateur / API]
GIT[Client git SSH]
CI[Push git → CI trigger]
end
BR -->|HTTP/HTTPS via nginx| AX
GIT -->|SSH :2222| GUARD
GUARD -->|"AcceptOutcome::Accepted"| SSH
CI -->|git push → hook| CIW
AX -->|SeaORM| PG
AX -.->|"admin ACL/ban"| GUARD
GUARD -->|Bans, ACL, events| PG
SSH -->|Lit authorized_keys| PG
SSH -->|git-receive-pack| FS
AX -->|Lit/écrit dépôts| FS
CIW -->|dagger call| DAGGER
IMW -->|git clone| FS
EMW -->|SMTP| SMTP
Flux d'une requête HTTP (ex. : afficher un dépôt)¶
sequenceDiagram
participant B as Navigateur
participant N as Nginx (TLS)
participant AX as axum handler
participant DB as PostgreSQL
participant FS as Système de fichiers
B->>N: GET https://gitrust.domain/alice/mon-depot
N->>AX: proxy_pass http://127.0.0.1:4000/alice/mon-depot
AX->>DB: SELECT * FROM repositories WHERE owner=alice AND slug=mon-depot
DB-->>AX: Repository { id, disk_path, ... }
AX->>FS: git log --oneline HEAD (libgit2)
FS-->>AX: Liste des commits
AX-->>N: HTML (template Askama rendu)
N-->>B: HTTP 200 + HTML
Flux d'un push SSH (ex. : git push origin main)¶
sequenceDiagram
participant G as git client
participant SSH as Russh :2222
participant DB as PostgreSQL
participant FS as Dépôt bare (.git)
participant H as gitrust-hooks
G->>SSH: Connexion SSH, propose clé publique
SSH->>DB: SELECT id FROM ssh_keys WHERE fingerprint=SHA256:...
DB-->>SSH: user_id = alice
SSH-->>G: Authentifié
G->>SSH: git-receive-pack /alice/mon-depot.git
SSH->>DB: Vérifie permission write sur le dépôt
DB-->>SSH: OK (Developer ou Owner)
SSH->>FS: Reçoit les objets Git, met à jour les refs
FS-->>SSH: Succès
SSH->>H: Déclenche post-receive hook
H->>DB: Enregistre l'événement push
H-->>SSH: Fin
SSH-->>G: remote: OK
Implications opérationnelles¶
Un seul processus, une seule unité d'arrêt¶
gitrust est un binaire unique. Redémarrer le service (systemctl restart gitrust) interrompt simultanément :
- Le serveur HTTP (quelques secondes d'indisponibilité web)
- Le serveur SSH (les push en cours sont coupés)
- Les workers CI/Import (les tâches en cours peuvent être interrompues et reprises au prochain démarrage)
Conséquence : planifiez les redémarrages (mise à jour, changement de .env) en dehors des heures de pointe.
PostgreSQL est le point central¶
Toutes les décisions d'authentification, d'autorisation et de persistance passent par PostgreSQL. Si PostgreSQL est indisponible :
- Les connexions HTTP retournent 503
- Les connexions SSH sont refusées (impossible de valider la clé publique)
- Les workers mettent leurs tâches en file d'attente locale
Conséquence : la sauvegarde et la haute disponibilité de PostgreSQL sont prioritaires sur tout le reste.
Le système de fichiers et la base de données doivent être cohérents¶
Un dépôt existe à deux endroits : en base (repositories table) et sur disque (disk_path). Une restauration partielle (base seule, ou disque seul) laisse l'instance dans un état incohérent.
Conséquence : toujours sauvegarder et restaurer la base ET les dépôts ensembles (voir Sauvegarder et restaurer).
ssh-guard intercale un sas devant le serveur SSH¶
Toutes les connexions sur :2222 passent d'abord par SecureListener (ssh-guard). Cette couche extrait l'IP réelle (PROXY protocol si nginx stream est devant), consulte ACL et bans actifs, applique un cap TCP par IP, puis ne remet le TcpStream à russh que si tout est en règle. Un événement JSON stable est émis pour chaque décision (connection_accepted, connection_dropped, auth_failed, ip_banned, …) — pratique pour fail2ban et SIEM.
Conséquence : modifier SSH_GUARD_* dans .env change immédiatement le comportement défensif après redémarrage. Les ACL admin (allow/deny par CIDR) sont en revanche modifiables à chaud via l'UI : la table ssh_guard_acl est partagée par référence entre le routeur HTTP et le listener SSH. Voir Configurer ssh-guard et ssh-guard : détection d'attaques SSH.
La clé SSH hôte identifie l'instance¶
La clé Ed25519 dans SSH_HOST_KEY_PATH est l'identité SSH de votre serveur. Tous vos utilisateurs ont ce fingerprint dans leur ~/.ssh/known_hosts. La régénérer provoque une alerte REMOTE HOST IDENTIFICATION HAS CHANGED sur toutes les machines de vos utilisateurs.
Conséquence : sauvegardez la clé SSH hôte et ne la régénérez jamais sauf en cas de compromission avérée.
Alternatives et compromis¶
Pourquoi un seul binaire plutôt que des microservices ?¶
gitrust a choisi l'architecture monolithe modulaire (crates Rust séparées, un binaire) pour : - Simplicité opérationnelle : un seul processus à surveiller, une seule unité de déploiement - Performances : pas de sérialisation/désérialisation inter-services sur le chemin critique - Cohérence transactionnelle : les transactions PostgreSQL couvrent plusieurs domaines (auth + repository + audit) sans coordinateur distribué
Le compromis : mise à l'échelle horizontale plus contrainte (voir Modèle de déploiement).
Pourquoi Russh plutôt que sshd ?¶
L'intégration du serveur SSH dans le processus gitrust permet :
- Authentification via la base de données (pas de authorized_keys fichier)
- Autorisation au niveau dépôt (pas juste au niveau système)
- Logs d'audit unifiés
Le compromis : gitrust gère sa propre implémentation SSH — les mises à jour de sécurité SSH dépendent des releases gitrust.
Vérifier votre compréhension¶
-
Un utilisateur ne peut plus faire
git pushmais peut se connecter à l'interface web. Quel composant est en cause ? Quelles informations cherchez-vous en premier dans les logs ? -
Vous devez augmenter la mémoire allouée aux workers CI sans affecter le serveur HTTP. Est-ce possible avec l'architecture actuelle ? Que faudrait-il changer ?