Serveur Gitea
Serveur : Hetzner VPS —
89.167.14.242/2a01:4f9:c014:fa7b::1Domaine :gitea.mindlet.app(web) /ssh-gitea.mindlet.app(git SSH) Date de mise en place : 1er février 2026
Table des matières
Section titled “Table des matières”- Architecture générale
- Configuration DNS et Cloudflare
- Sécurisation du serveur
- Configuration Gitea
- Firewall (UFW)
- Fail2ban
- Guide d’administration
- Arborescence des fichiers
1. Architecture générale
Section titled “1. Architecture générale”flowchart TB
subgraph Internet
Client[Client]
end
subgraph Cloudflare["Cloudflare (Proxy)"]
WAF[WAF / DDoS Protection]
end
subgraph VPS["Hetzner VPS — 89.167.14.242"]
subgraph Security["Sécurité"]
UFW[UFW Firewall]
F2B[Fail2ban IPS]
end
subgraph DockerEnv["Docker"]
GiteaContainer["Gitea Container<br/>• Web UI :3000<br/>• SSH :22→222<br/>• SQLite DB"]
end
end
Client -->|"HTTPS :443"| WAF
Client -->|"SSH :222<br/>(direct)"| VPS
WAF -->|"HTTP :3000"| GiteaContainer
Flux réseau
Section titled “Flux réseau”| Protocole | Chemin | Ports |
|---|---|---|
| HTTPS (web) | Client → Cloudflare (TLS termination) → VPS:3000 → Container:3000 | 443 → 3000 |
| SSH (git) | Client → VPS:222 → Container:22 (direct, pas de proxy Cloudflare) | 222 → 22 |
| SSH (admin) | Client → VPS:22 → sshd host | 22 |
2. Configuration DNS et Cloudflare
Section titled “2. Configuration DNS et Cloudflare”Problème résolu : Timeout SSH
Section titled “Problème résolu : Timeout SSH”Les commandes git push / git clone via SSH vers gitea.mindlet.app:222 aboutissaient à un timeout. Cause : le domaine était en mode Proxied (nuage orange) dans Cloudflare, qui ne transmet que le trafic HTTP/HTTPS.
Solution : Sous-domaine SSH dédié
Section titled “Solution : Sous-domaine SSH dédié”| Enregistrement | Type | Valeur | Proxy |
|---|---|---|---|
gitea.mindlet.app | A / AAAA | Cloudflare proxy | ON (orange) |
ssh-gitea.mindlet.app | A | 89.167.14.242 | OFF (gris) |
ssh-gitea.mindlet.app | AAAA | 2a01:4f9:c014:fa7b::1 | OFF (gris) |
Configuration Gitea pour afficher les bonnes URLs de clone :
GITEA__server__SSH_DOMAIN: ssh-gitea.mindlet.appPourquoi deux domaines ? Le sous-domaine SSH expose l’IP réelle mais est protégé par fail2ban et authentification par clé. L’interface web reste protégée par Cloudflare.
3. Sécurisation du serveur
Section titled “3. Sécurisation du serveur”3.1. Durcissement SSH (sshd)
Section titled “3.1. Durcissement SSH (sshd)”Fichier : /etc/ssh/sshd_config.d/hardening.conf
PermitRootLogin prohibit-passwordPasswordAuthentication noPermitEmptyPasswords noMaxAuthTries 3MaxSessions 5X11Forwarding noAllowAgentForwarding noAllowTcpForwarding no| Directive | Justification |
|---|---|
PermitRootLogin prohibit-password | Root uniquement par clé SSH |
PasswordAuthentication no | Clés SSH obligatoires, pas de brute force possible |
MaxAuthTries 3 | 3 essais max par connexion |
X11Forwarding no | Serveur headless, pas de GUI |
AllowAgentForwarding no | Empêche le pivot via agent SSH compromis |
AllowTcpForwarding no | Pas de tunnel/proxy SSH |
3.2. Clés SSH des utilisateurs
Section titled “3.2. Clés SSH des utilisateurs”| Utilisateur | Fichier |
|---|---|
root | /root/.ssh/authorized_keys |
antocreadev | /home/antocreadev/.ssh/authorized_keys |
# Connexion adminssh root@89.167.14.242ssh antocreadev@89.167.14.2424. Configuration Gitea
Section titled “4. Configuration Gitea”4.1. Docker Compose
Section titled “4.1. Docker Compose”Fichier : /root/gitea/docker-compose.yml
Les variables GITEA__section__CLÉ sont converties en directives app.ini.
4.2. Directives de sécurité
Section titled “4.2. Directives de sécurité”| Variable | Valeur | Justification |
|---|---|---|
DISABLE_REGISTRATION | true | Pas d’inscription publique |
REQUIRE_SIGNIN_VIEW | true | Authentification obligatoire |
ENABLE_CAPTCHA | true | Protection formulaires |
ENABLE_OPENID_SIGNIN | false | Ferme le vecteur OpenID |
ENABLE_OPENID_SIGNUP | false | Ferme le vecteur OpenID |
REVERSE_PROXY_TRUSTED_PROXIES | IPs Cloudflare | Seul Cloudflare peut définir X-Forwarded-For |
4.3. Logging
Section titled “4.3. Logging”GITEA__log__MODE: "console,file"GITEA__log.file__FILE_NAME: "/data/gitea/log/gitea.log"Fichier log sur l’hôte : /root/gitea/gitea/gitea/log/gitea.log (surveillé par fail2ban)
5. Firewall (UFW)
Section titled “5. Firewall (UFW)”Règles en place
Section titled “Règles en place”| Port | Accès | Justification |
|---|---|---|
| 22/tcp | Monde | SSH admin (clé + fail2ban) |
| 80/tcp | Monde | Redirection HTTP → HTTPS |
| 443/tcp | Monde | HTTPS via Cloudflare |
| 222/tcp | Monde | SSH Git (clé uniquement) |
| 3000/tcp | IPs Cloudflare | Port interne Gitea |
Pourquoi restreindre le port 3000 ?
Section titled “Pourquoi restreindre le port 3000 ?”Empêche l’accès direct en contournant Cloudflare (protection DDoS, WAF, headers).
Maintenance : Cloudflare peut ajouter de nouvelles plages IP. Voir cloudflare.com/ips.
6. Fail2ban
Section titled “6. Fail2ban”Configuration
Section titled “Configuration”Fichier : /etc/fail2ban/jail.local
| Jail | Port | Log | Seuil | Ban |
|---|---|---|---|---|
sshd | 22 | /var/log/auth.log | 3 échecs / 10 min | 3h |
gitea | 80, 443, 3000 | /root/gitea/gitea/gitea/log/gitea.log | 5 échecs / 10 min | 1h |
Filtre Gitea
Section titled “Filtre Gitea”Fichier : /etc/fail2ban/filter.d/gitea.conf
[Definition]failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>ignoreregex =Commandes utiles
Section titled “Commandes utiles”# Statut globalfail2ban-client status
# IPs banniesfail2ban-client status sshdfail2ban-client status gitea
# Débannirfail2ban-client set sshd unbanip 1.2.3.4
# Tester un filtrefail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf7. Guide d’administration
Section titled “7. Guide d’administration”7.1. Ajouter un utilisateur développeur
Section titled “7.1. Ajouter un utilisateur développeur”Créer le compte Gitea
Section titled “Créer le compte Gitea”docker exec -u git gitea gitea admin user create \ --username "nouveau_dev" \ --password "MotDePasseTemporaire123!" \ --email "dev@example.com" \ --must-change-passwordOu via l’interface : Site Administration → User Accounts → Create User Account
Le développeur ajoute sa clé SSH
Section titled “Le développeur ajoute sa clé SSH”- Se connecter sur
https://gitea.mindlet.app - Settings → SSH / GPG Keys → Add Key
- Coller le contenu de
~/.ssh/id_ed25519.pub
Clone et travail
Section titled “Clone et travail”git clone ssh://git@ssh-gitea.mindlet.app:222/organisation/repo.gitcd repogit push origin main7.2. Supprimer un utilisateur
Section titled “7.2. Supprimer un utilisateur”# Supprimer de Giteadocker exec -u git gitea gitea admin user delete --username "ancien_dev"
# Supprimer l'accès SSH VPS (si applicable)deluser ancien_devrm -rf /home/ancien_dev7.3. Maintenance périodique
Section titled “7.3. Maintenance périodique”Mettre à jour les IPs Cloudflare
Section titled “Mettre à jour les IPs Cloudflare”curl -s https://www.cloudflare.com/ips-v4curl -s https://www.cloudflare.com/ips-v6ufw status | grep 3000Mettre à jour Gitea
Section titled “Mettre à jour Gitea”cd /root/gitea# Modifier la version dans docker-compose.ymldocker compose pulldocker compose down && docker compose up -dSauvegardes
Section titled “Sauvegardes”# Arrêter pour cohérencedocker compose -f /root/gitea/docker-compose.yml down
# Sauvegardertar czf /root/backup-gitea-$(date +%Y%m%d).tar.gz -C /root/gitea gitea/
# Redémarrerdocker compose -f /root/gitea/docker-compose.yml up -dOu via Gitea (sauvegarde à chaud) :
docker exec -u git gitea gitea dump -c /data/gitea/conf/app.ini8. Arborescence des fichiers
Section titled “8. Arborescence des fichiers”/root/gitea/├── docker-compose.yml # Configuration Docker Compose└── gitea/ # Volume Docker (/data) └── gitea/ ├── conf/app.ini # Configuration Gitea ├── gitea.db # Base SQLite └── log/gitea.log # Logs (surveillé par fail2ban)
/etc/ssh/sshd_config.d/└── hardening.conf # Durcissement SSH
/etc/fail2ban/├── jail.local # Jails (sshd + gitea)└── filter.d/gitea.conf # Filtre regex Gitea