From ab0cc146eaaded850addfaf600f9d9b57281b08a Mon Sep 17 00:00:00 2001 From: Alpinux Date: Sun, 27 Sep 2026 13:00:19 +0200 Subject: [PATCH] =?UTF-8?q?Reprendre=20la=20documentation=20de=20d=C3=A9pl?= =?UTF-8?q?oiement,=20qui=20vivait=20dans=20alpinux-owni?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ce dépôt d'infrastructure décrit la machine ; le déploiement de ce site-ci le regarde lui, et le tenir ailleurs garantissait qu'il divergerait du code qu'il décrit. Repris tel quel depuis alpinux-owni/docs/, où il est supprimé en retour. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01SFbwnJurBwTs7x7t93ecku --- INFRASTRUCTURE.md | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 INFRASTRUCTURE.md diff --git a/INFRASTRUCTURE.md b/INFRASTRUCTURE.md new file mode 100644 index 0000000..e3f9fad --- /dev/null +++ b/INFRASTRUCTURE.md @@ -0,0 +1,156 @@ +# static.alpinux.org — infrastructure + +Configuration de référence pour `static.alpinux.org` (CDN assets Alpinux + tableau de bord). + +## Fichiers + +| Fichier | Description | +|---------|-------------| +| `static.alpinux.org.vhost.conf` | Configuration Apache — **référence audit, non déployé manuellement** | +| `static-cdn.service` | Systemd unit pour l'app Flask (tableau de bord) | + +## Gestion via ISPConfig + +Le VirtualHost est **créé et géré par ISPConfig** (`https://owni.alpinux.org:8080`). + +Pour recréer ou reconfigurer le site : +1. *Sites → Ajouter un site web* — domaine `static.alpinux.org` +2. Onglet *SSL* → activer **Let's Encrypt** +3. PHP désactivé (onglet Options avancées) + +## Tableau de bord Flask (app/) + +L'app Flask (`static/app/`) sert la page d'accueil de `static.alpinux.org` après authentification AlpID. + +### Déploiement + +Depuis le poste de développement : + +```bash +cd org.alpinux.owni/static +scripts/deploy-app.sh +``` + +Le script : +- rsync `app/` → `/opt/static-cdn/` sur le serveur (hors web root) +- crée le venv Python et installe les dépendances +- installe et démarre `static-cdn.service` via systemd + +Créer le fichier `/opt/static-cdn/.env` sur le serveur après le premier déploiement : + +```bash +ssh alpinux.org +nano /opt/static-cdn/.env +sudo systemctl restart static-cdn +``` + +### Apache — directives proxy + +Ajouter dans ISPConfig → Sites → `static.alpinux.org` → onglet **Options** → champ +**Directives Apache personnalisées SSL** : + +```apache +# Fichiers CDN publics — Apache sert directement depuis DocumentRoot +ProxyPass /logo/ ! +ProxyPass /wiki/ ! +ProxyPass /error/ ! +ProxyPass /favicon.ico ! +ProxyPass /robots.txt ! + +# Tableau de bord → Flask sur 127.0.0.1:5003 +RequestHeader set X-Forwarded-Proto "https" +ProxyPreserveHost On +ProxyPass / http://127.0.0.1:5003/ +ProxyPassReverse / http://127.0.0.1:5003/ +``` + +Note : `/stats/` n'est **pas** exclu — il est servi par Flask avec authentification AlpID. + +`RequestHeader set X-Forwarded-Proto "https"` est indispensable : sans lui, Flask génère +le `redirect_uri` en HTTP et Keycloak refuse le callback. + +### Variables d'environnement (`/opt/static-cdn/.env`) + +| Variable | Obligatoire | Valeur / Description | +|----------|-------------|----------------------| +| `SECRET_KEY` | oui | Chaîne aléatoire longue (`python3 -c "import secrets; print(secrets.token_hex(32))"`) | +| `ALPID_CLIENT_ID` | oui | `static-cdn` (client créé dans Keycloak) | +| `ALPID_CLIENT_SECRET` | oui | Secret généré par Keycloak | +| `ALPID_DISCOVERY_URL` | oui | `https://alpid.alpinux.org/realms/master/.well-known/openid-configuration` | +| `ADMIN_GROUPS` | non | Groupes Keycloak autorisés (défaut : `admins`) — nécessite le claim `groups` activé côté Keycloak | +| `ADMIN_EMAILS` | non | Emails autorisés séparés par virgule — fallback si `groups` n'est pas dans le token | +| `ASSETS_ROOT` | oui | `/var/www/clients/client1/web17/web` | +| `STATS_FILE` | non | `/opt/static-cdn/goaccess.html` — rapport HTML GoAccess | +| `STATS_JSON` | non | `/opt/static-cdn/goaccess.json` — statistiques par fichier (badges "vues" dans browse) | + +### Keycloak — realm et client + +- **Realm** : `master` (seul realm actif sur cette instance AlpID) +- **Client ID** : `static-cdn` +- **Redirect URI** : `https://static.alpinux.org/auth/callback` +- **Scopes demandés** : `openid profile email` — le scope `groups` n'est **pas** activé + sur ce client ; l'autorisation se fait via `ADMIN_EMAILS` ou, si les deux sont absents, + tous les utilisateurs AlpID sont acceptés + +### Logique d'autorisation (priorité décroissante) + +1. Claim `groups` présent dans le token → vérification contre `ADMIN_GROUPS` +2. `ADMIN_EMAILS` défini dans `.env` → l'email du compte doit figurer dans la liste +3. Ni l'un ni l'autre → tout utilisateur authentifié via AlpID est accepté + +Pour restreindre à un seul administrateur : +``` +ADMIN_EMAILS=cedric.alpinux@acemail.fr +``` + +## Chemin web root + +ISPConfig attribue `/var/www/clients/client1/web17/web` au site `static.alpinux.org`. +Appartient à `web17:client1` (`drwx--x---`) — `sudo rsync` requis pour y écrire. + +### ACL pour l'app Flask + +Le service tourne en tant qu'`abonnelc` qui n'est ni `web17` ni dans `client1`. +Une ACL POSIX lui donne les droits de lecture nécessaires : + +```bash +sudo setfacl -R -m u:abonnelc:rx /var/www/clients/client1/web17/web +sudo setfacl -d -m u:abonnelc:rx /var/www/clients/client1/web17/web +``` + +ISPConfig peut réinitialiser les permissions du web root lors d'une modification du site — +relancer ces deux commandes si le tableau de bord retourne une `PermissionError`. + +## Statistiques GoAccess + +### Génération manuelle + +```bash +sudo goaccess /var/log/ispconfig/httpd/static.alpinux.org/access.log \ + --log-format=COMBINED --no-global-config \ + --output=/opt/static-cdn/goaccess.html +sudo goaccess /var/log/ispconfig/httpd/static.alpinux.org/access.log \ + --log-format=COMBINED --no-global-config \ + --output=/opt/static-cdn/goaccess.json +sudo chown abonnelc: /opt/static-cdn/goaccess.html /opt/static-cdn/goaccess.json +``` + +### Cron quotidien (root) + +```cron +0 4 * * * goaccess /var/log/ispconfig/httpd/static.alpinux.org/access.log \ + --log-format=COMBINED --no-global-config \ + --output=/opt/static-cdn/goaccess.html && \ + goaccess /var/log/ispconfig/httpd/static.alpinux.org/access.log \ + --log-format=COMBINED --no-global-config \ + --output=/opt/static-cdn/goaccess.json && \ + chown abonnelc: /opt/static-cdn/goaccess.html /opt/static-cdn/goaccess.json +``` + +Le rapport JSON alimente les **badges "Vues"** dans le navigateur de fichiers (browse). +Sans ce fichier, les badges sont masqués. + +## Accès rsync (assets CDN) + +Les assets sont synchronisés via `static/scripts/push-assets.sh` / `pull-assets.sh`. +Le dossier `app/` est exclu du rsync (déployé séparément, non servi depuis le web root).