feat(gitea): script d'installation LXC Alpine + README #19

Closed
opened 2026-07-30 15:43:54 +00:00 by Damien · 0 comments
Owner

Objectif

Nouveau script gitea/install.sh sur le modèle d'openbao/install.sh : point d'entrée unique, trois modes auto-détectés (hôte Proxmox → création LXC, hôte Proxmox + conteneur existant → update, dans le LXC → install ou update), plus les flags --install / --update pour le pipe via pct exec.

Remplace le déploiement actuel via community-scripts. La migration des données de l'instance existante est hors périmètre : cette issue produit une instance neuve.

Décision structurante : apk, pas de binaire téléchargé

Contrairement à openbao/install.sh et gitea-runner/install.sh qui téléchargent une release GitHub, Gitea passe par le gestionnaire de paquets Alpine.

Raison : les binaires officiels de dl.gitea.com embarquent SQLite via CGO et sont liés à la glibc. Sur musl, c'est un pari (gcompat + erreurs libc.so.6: version GLIBC_2.x not found). Alpine package Gitea nativement en community, build musl, avec un sous-paquet gitea-openrc.

apk add --no-cache gitea gitea-openrc

Conséquences sur le pattern habituel :

openbao / runner gitea
Install curl release GitHub apk add gitea gitea-openrc
Service OpenRC écrit à la main fourni, on override si besoin
Update swap binaire + backup apk upgrade gitea
Version /opt/*_version.txt apk info -v gitea

Le mode --update se réduit donc quasiment à refresh_os_packages + apk upgrade gitea. Pas de /opt/gitea_version.txt.

Vérifier au premier run que gitea est disponible en community sur la version d'Alpine sélectionnée par detect_latest_alpine_template() (3.22 au moment de la rédaction) et pas seulement sur edge. Si absent en stable : échouer bruyamment avec un message explicite plutôt que de tagger edge/community, qui n'a pas sa place sur une instance de production.

Configuration app.ini

Pilotée exclusivement par ini_set() (cf. #18), jamais par un heredoc écrasant. Le script est propriétaire des clés listées ci-dessous et ne touche à rien d'autre.

[server]
PROTOCOL   = http
HTTP_ADDR  = 127.0.0.1
HTTP_PORT  = 3000
DOMAIN     = gitea.arnodo.fr
ROOT_URL   = https://gitea.arnodo.fr/
DISABLE_SSH = true

[security]
INSTALL_LOCK                  = true
REVERSE_PROXY_LIMIT           = 2
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128,100.64.0.0/10

[service]
DISABLE_REGISTRATION     = true
REQUIRE_CAPTCHA_FOR_LOGIN = true
ENABLE_CAPTCHA           = true

[log]
MODE      = file
LEVEL     = info
ROOT_PATH = /var/log/gitea
COLORIZE  = false

[metrics]
ENABLED                     = true
TOKEN                       = <généré>
ENABLED_ISSUE_BY_REPOSITORY = true
ENABLED_ISSUE_BY_LABEL      = true

[actions]
ENABLED = true

[database]
DB_TYPE = sqlite3
PATH    = /var/lib/gitea/data/gitea.db

Points critiques :

  • ROOT_URL est l'URL publique, pas l'URL tailnet. Elle alimente les URL de clone affichées, les webhooks et les redirections. Ne pas y mettre gitea.taila5ad8.ts.net par réflexe « loopback first » : HTTP_ADDR (interne) et ROOT_URL (publique) sont deux choses distinctes.
  • DISABLE_SSH = true : usage exclusivement HTTPS confirmé, pas de serveur SSH interne à exposer.
  • COLORIZE = false : les codes ANSI cassent le <HOST> du filtre fail2ban de #21.
  • INSTALL_LOCK = true avant le premier démarrage du service. Sinon l'assistant d'installation web est exposé sur gitea.arnodo.fr et le premier arrivé devient admin.
  • REVERSE_PROXY_TRUSTED_PROXIES ne doit jamais valoir * (cf. CVE-2026-20896 sur les images Docker).
  • REVERSE_PROXY_LIMIT = 2 est une hypothèse (Traefik + tailscale serve) à valider empiriquement — voir la procédure ci-dessous.

Token metrics

Génération conditionnelle : si [metrics] TOKEN est déjà renseigné, ne pas y toucher — un rejeu qui régénère le token casse le scrape Prometheus. Sinon openssl rand -hex 32, écrit via ini_set, et affiché en fin d'install pour report manuel.

Surchargeable par GITEA_METRICS_TOKEN. La configuration Prometheus est hors périmètre.

Création de l'admin

Après INSTALL_LOCK = true et avant l'annonce de fin, en non-interactif :

su -s /bin/sh git -c "gitea admin user create --admin --username ... --email ... --password ... --must-change-password=true"

Idempotent : ne rien faire si un compte admin existe déjà. Mot de passe généré si GITEA_ADMIN_PASSWORD est absent, affiché une seule fois en sortie.

Exposition réseau

Loopback + tailscale serve --bg --https=443 http://127.0.0.1:3000, comme OpenBao. Gitea devient joignable sur https://gitea.taila5ad8.ts.netsans port, ce qui casse les consommateurs actuels pointant sur :3000 (traités dans #21 et #22).

Réutiliser la logique de configure_tailscale_proxy() d'openbao/install.sh : gestion du BackendState, TS_AUTHKEY optionnel avec repli sur instruction manuelle, tailscale serve idempotent.

Client rsyslog

Installer et configurer un forward de /var/log/gitea/gitea.log vers le proxy en TCP, via le module imfile de rsyslog, avec un tag identifiant le service. Le récepteur est traité en #20. Adresse du proxy surchargeable par SYSLOG_TARGET.

Choix de rsyslog plutôt que le sous-logger MODE = conn natif de Gitea : découplé, non bloquant (un logger réseau bloquant peut stall Gitea), et indépendant des changements de syntaxe des sous-loggers intervenus en 1.21.

Reste du script

  • LXC Alpine non privilégié, nesting=1, passthrough /dev/net/tun, --onboot 1, tags infra-script,gitea
  • Défauts : CORES=2, RAM=2048, DISK=16, LXC_TAG=gitea, GITEA_HOSTNAME=gitea
  • logrotate sur /var/log/gitea/gitea.log (daily, rotate 7, compress, copytruncate)
  • enable_tty1_autologin
  • MOTD dans /etc/profile.d/00-gitea.sh, heredoc quoté, état calculé au login : FQDN tailnet, version (apk info -v gitea), état du service, URL publique
  • Logs sur stderr, chargement de lib/common.sh avec le garde declare -F

gitea/README.md

Sur le modèle de openbao/README.md : tableau des modes, tableau des variables d'environnement, architecture. Plus deux sections spécifiques :

Ordre de déploiement — non négociable

L'ordre doit être documenté explicitement, avec l'avertissement suivant :

Activer le jail fail2ban (#21) avant d'avoir validé la chaîne X-Forwarded-For fait bannir le proxy Traefik lui-même au bout de 5 échecs, et gitea.arnodo.fr devient inaccessible dans son intégralité.

Séquence : #18#19validation XFF#20#21#22.

Procédure de validation XFF

Depuis une IP externe connue (partage de connexion mobile), provoquer un login raté, puis dans le LXC :

grep "Failed authentication" /var/log/gitea/gitea.log | tail -1
Résultat Interprétation Action
IP publique réelle Correct Passer à #20
100.x.x.x XFF pas assez déroulé Augmenter REVERSE_PROXY_LIMIT
127.0.0.1 tailscale serve masque tout Vérifier que 127.0.0.0/8 est dans les trusted proxies

Tant que cette ligne n'affiche pas l'IP réelle, #21 reste non déployée.

Fichiers touchés

  • gitea/install.sh (nouveau)
  • gitea/README.md (nouveau)
  • README.md racine — ligne dans le tableau « Available Scripts »

Critères d'acceptation

  • Install depuis un hôte Proxmox vierge : LXC créé, Gitea démarré, joignable sur le tailnet
  • Rejeu du même one-liner : aucune erreur, config préservée, aucune clé app.ini hors périmètre modifiée, token metrics inchangé
  • Rejeu après suppression manuelle d'une clé [security] : la clé est restaurée
  • L'assistant d'installation web n'est jamais accessible, même transitoirement
  • curl -H "Authorization: Bearer $TOKEN" https://gitea.<tailnet>.ts.net/metrics renvoie les métriques
  • Un compte admin existe, sans doublon après rejeu
  • Procédure de validation XFF exécutée et documentée dans le README

Dépendances

  • Bloquée par #18 (ini_set)
  • Bloque #21 et #22
## Objectif Nouveau script `gitea/install.sh` sur le modèle d'`openbao/install.sh` : point d'entrée unique, trois modes auto-détectés (hôte Proxmox → création LXC, hôte Proxmox + conteneur existant → update, dans le LXC → install ou update), plus les flags `--install` / `--update` pour le pipe via `pct exec`. Remplace le déploiement actuel via community-scripts. **La migration des données de l'instance existante est hors périmètre** : cette issue produit une instance neuve. ## Décision structurante : `apk`, pas de binaire téléchargé Contrairement à `openbao/install.sh` et `gitea-runner/install.sh` qui téléchargent une release GitHub, Gitea passe par le gestionnaire de paquets Alpine. Raison : les binaires officiels de `dl.gitea.com` embarquent SQLite via CGO et sont liés à la **glibc**. Sur musl, c'est un pari (`gcompat` + erreurs `libc.so.6: version GLIBC_2.x not found`). Alpine package Gitea nativement en community, build musl, avec un sous-paquet `gitea-openrc`. ```bash apk add --no-cache gitea gitea-openrc ``` Conséquences sur le pattern habituel : | | openbao / runner | gitea | |---|---|---| | Install | curl release GitHub | `apk add gitea gitea-openrc` | | Service OpenRC | écrit à la main | fourni, on override si besoin | | Update | swap binaire + backup | `apk upgrade gitea` | | Version | `/opt/*_version.txt` | `apk info -v gitea` | Le mode `--update` se réduit donc quasiment à `refresh_os_packages` + `apk upgrade gitea`. Pas de `/opt/gitea_version.txt`. **Vérifier au premier run** que `gitea` est disponible en `community` sur la version d'Alpine sélectionnée par `detect_latest_alpine_template()` (3.22 au moment de la rédaction) et pas seulement sur `edge`. Si absent en stable : échouer bruyamment avec un message explicite plutôt que de tagger `edge/community`, qui n'a pas sa place sur une instance de production. ## Configuration `app.ini` Pilotée **exclusivement** par `ini_set()` (cf. #18), jamais par un heredoc écrasant. Le script est propriétaire des clés listées ci-dessous et ne touche à rien d'autre. ```ini [server] PROTOCOL = http HTTP_ADDR = 127.0.0.1 HTTP_PORT = 3000 DOMAIN = gitea.arnodo.fr ROOT_URL = https://gitea.arnodo.fr/ DISABLE_SSH = true [security] INSTALL_LOCK = true REVERSE_PROXY_LIMIT = 2 REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128,100.64.0.0/10 [service] DISABLE_REGISTRATION = true REQUIRE_CAPTCHA_FOR_LOGIN = true ENABLE_CAPTCHA = true [log] MODE = file LEVEL = info ROOT_PATH = /var/log/gitea COLORIZE = false [metrics] ENABLED = true TOKEN = <généré> ENABLED_ISSUE_BY_REPOSITORY = true ENABLED_ISSUE_BY_LABEL = true [actions] ENABLED = true [database] DB_TYPE = sqlite3 PATH = /var/lib/gitea/data/gitea.db ``` Points critiques : - **`ROOT_URL` est l'URL publique**, pas l'URL tailnet. Elle alimente les URL de clone affichées, les webhooks et les redirections. Ne pas y mettre `gitea.taila5ad8.ts.net` par réflexe « loopback first » : `HTTP_ADDR` (interne) et `ROOT_URL` (publique) sont deux choses distinctes. - **`DISABLE_SSH = true`** : usage exclusivement HTTPS confirmé, pas de serveur SSH interne à exposer. - **`COLORIZE = false`** : les codes ANSI cassent le `<HOST>` du filtre fail2ban de #21. - **`INSTALL_LOCK = true` avant le premier démarrage du service.** Sinon l'assistant d'installation web est exposé sur `gitea.arnodo.fr` et le premier arrivé devient admin. - **`REVERSE_PROXY_TRUSTED_PROXIES` ne doit jamais valoir `*`** (cf. CVE-2026-20896 sur les images Docker). - `REVERSE_PROXY_LIMIT = 2` est une **hypothèse** (Traefik + `tailscale serve`) à valider empiriquement — voir la procédure ci-dessous. ## Token metrics Génération **conditionnelle** : si `[metrics] TOKEN` est déjà renseigné, ne pas y toucher — un rejeu qui régénère le token casse le scrape Prometheus. Sinon `openssl rand -hex 32`, écrit via `ini_set`, et affiché en fin d'install pour report manuel. Surchargeable par `GITEA_METRICS_TOKEN`. La configuration Prometheus est hors périmètre. ## Création de l'admin Après `INSTALL_LOCK = true` et avant l'annonce de fin, en non-interactif : ```bash su -s /bin/sh git -c "gitea admin user create --admin --username ... --email ... --password ... --must-change-password=true" ``` Idempotent : ne rien faire si un compte admin existe déjà. Mot de passe généré si `GITEA_ADMIN_PASSWORD` est absent, affiché une seule fois en sortie. ## Exposition réseau Loopback + `tailscale serve --bg --https=443 http://127.0.0.1:3000`, comme OpenBao. Gitea devient joignable sur `https://gitea.taila5ad8.ts.net` — **sans port**, ce qui casse les consommateurs actuels pointant sur `:3000` (traités dans #21 et #22). Réutiliser la logique de `configure_tailscale_proxy()` d'`openbao/install.sh` : gestion du `BackendState`, `TS_AUTHKEY` optionnel avec repli sur instruction manuelle, `tailscale serve` idempotent. ## Client rsyslog Installer et configurer un forward de `/var/log/gitea/gitea.log` vers le proxy en TCP, via le module `imfile` de rsyslog, avec un tag identifiant le service. Le récepteur est traité en #20. Adresse du proxy surchargeable par `SYSLOG_TARGET`. Choix de `rsyslog` plutôt que le sous-logger `MODE = conn` natif de Gitea : découplé, non bloquant (un logger réseau bloquant peut stall Gitea), et indépendant des changements de syntaxe des sous-loggers intervenus en 1.21. ## Reste du script - LXC Alpine non privilégié, `nesting=1`, passthrough `/dev/net/tun`, `--onboot 1`, tags `infra-script,gitea` - Défauts : `CORES=2`, `RAM=2048`, `DISK=16`, `LXC_TAG=gitea`, `GITEA_HOSTNAME=gitea` - logrotate sur `/var/log/gitea/gitea.log` (daily, rotate 7, compress, copytruncate) - `enable_tty1_autologin` - MOTD dans `/etc/profile.d/00-gitea.sh`, heredoc **quoté**, état calculé au login : FQDN tailnet, version (`apk info -v gitea`), état du service, URL publique - Logs sur stderr, chargement de `lib/common.sh` avec le garde `declare -F` ## `gitea/README.md` Sur le modèle de `openbao/README.md` : tableau des modes, tableau des variables d'environnement, architecture. Plus deux sections spécifiques : ### Ordre de déploiement — non négociable L'ordre doit être documenté explicitement, avec l'avertissement suivant : > Activer le jail fail2ban (#21) **avant** d'avoir validé la chaîne `X-Forwarded-For` fait bannir le proxy Traefik lui-même au bout de 5 échecs, et `gitea.arnodo.fr` devient inaccessible dans son intégralité. Séquence : #18 → #19 → **validation XFF** → #20 → #21 → #22. ### Procédure de validation XFF Depuis une IP externe connue (partage de connexion mobile), provoquer un login raté, puis dans le LXC : ```bash grep "Failed authentication" /var/log/gitea/gitea.log | tail -1 ``` | Résultat | Interprétation | Action | |---|---|---| | IP publique réelle | Correct | Passer à #20 | | `100.x.x.x` | XFF pas assez déroulé | Augmenter `REVERSE_PROXY_LIMIT` | | `127.0.0.1` | `tailscale serve` masque tout | Vérifier que `127.0.0.0/8` est dans les trusted proxies | Tant que cette ligne n'affiche pas l'IP réelle, #21 reste non déployée. ## Fichiers touchés - `gitea/install.sh` (nouveau) - `gitea/README.md` (nouveau) - `README.md` racine — ligne dans le tableau « Available Scripts » ## Critères d'acceptation - [ ] Install depuis un hôte Proxmox vierge : LXC créé, Gitea démarré, joignable sur le tailnet - [ ] **Rejeu du même one-liner** : aucune erreur, config préservée, aucune clé `app.ini` hors périmètre modifiée, token metrics inchangé - [ ] Rejeu après suppression manuelle d'une clé `[security]` : la clé est restaurée - [ ] L'assistant d'installation web n'est **jamais** accessible, même transitoirement - [ ] `curl -H "Authorization: Bearer $TOKEN" https://gitea.<tailnet>.ts.net/metrics` renvoie les métriques - [ ] Un compte admin existe, sans doublon après rejeu - [ ] Procédure de validation XFF exécutée et documentée dans le README ## Dépendances - **Bloquée par #18** (`ini_set`) - **Bloque #21 et #22**
Damien added this to the gitea-lxc-migration milestone 2026-07-30 15:43:54 +00:00
Damien added a new dependency 2026-07-30 19:08:31 +00:00
Damien added a new dependency 2026-07-30 19:10:45 +00:00
Damien added reference feat/gitea-lxc 2026-08-01 08:23:37 +00:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Reference: Damien/infra-scripts#19