alpinux-owni/README.md
Cédrix 89cc919906 Absorber alpinux-infra : un seul dépôt pour la machine
Il y avait deux dépôts d'infrastructure et aucune frontière entre eux. La
seule distinction défendable — « documentation » d'un côté, « fichiers de
configuration » de l'autre — ne tenait plus : infra avait fini par contenir
plus de documentation qu'owni, pendant qu'owni décrivait du concret (IP,
bases, comptes).

Tout vient donc ici : conf/ (vhosts de référence), dns/ (export de zone),
services/ (units systemd), scripts/ (sauvegarde) et docs/ (courrier sortant,
certificats, sauvegardes, et le déploiement par service).

Dans ce sens plutôt que l'inverse parce qu'« owni » nomme la machine, là où
« infra » ne dit pas de quoi il s'agit — et c'est le nom que Cédric emploie
spontanément, y compris pour le dossier de secrets.

Les renvois croisés entre les deux dépôts deviennent des liens internes : un
lien vers un dépôt qu'on s'apprête à archiver aurait pourri en silence.

Reste une incohérence, signalée plutôt que corrigée à la hâte :
docs/admin.md, static.md, wiki.md, dynamic.md et proxy-calendar.md décrivent
le déploiement de sites, pas la machine. Ils ont la même place ici que celle
que la messagerie n'avait pas — ils devraient rejoindre leurs dépôts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFbwnJurBwTs7x7t93ecku
2026-09-27 12:55:45 +02:00

250 lines
9.3 KiB
Markdown

# org.alpinux.owni
Accueil des projets de l'association **Alpinux** (le LUG de Savoie).
Chaque sous-dossier est un dépôt git indépendant avec son propre remote sur Gitea.
Ce dossier racine est l'espace de travail : son dépôt
[alpinux-owni](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-owni) ne versionne que
ce `README.md` et le `.gitignore` — le contenu des sous-dossiers appartient à leur dépôt.
---
## Projets
| Dossier | Domaine | Technologie | Dépôt Gitea |
|---------|---------|-------------|-------------|
| `feedback/` | feedback.alpinux.org | à construire | [alpinux-feedback](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-feedback) |
| `home/` | alpinux.org | HTML statique | [alpinux-home](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-home) |
| `portail/` | portail.alpinux.org | en construction | [alpinux-portail](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-portail) |
### Sortis de cet espace
Ces projets ont leur propre dossier, au même niveau que celui-ci — un dépôt, un dossier :
| Dossier | Domaine | Dépôt Gitea |
|---------|---------|-------------|
| `~/Projects/alpinux.wiki` | wiki.alpinux.org | [alpinux-wiki](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-wiki) |
| `~/Projects/alpinux.admin` | admin.alpinux.org | [alpinux-admin](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-admin) |
| `~/Projects/alpinux.dynamic` | dynamic.alpinux.org | [alpinux-dynamic](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-dynamic) |
| `~/Projects/alpinux.static` | static.alpinux.org | [alpinux-static](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-static) |
| `~/Projects/alpinux.gitea` | gitea.alpinux.org | [alpinux-gitea](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-gitea) |
| `~/Projects/alpinux.messagerie` | messagerie.alpinux.org | [alpinux.messagerie](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.messagerie) |
Leurs procédures de déploiement sont décrites dans leur propre README.
Gitea : **https://gitea.alpinux.org/alpinux.cedrica5l**
ISPConfig : **https://owni.alpinux.org:8080**
AlpID (SSO) : **https://alpid.alpinux.org** — realm `master`
---
## Déployer un site
**Chaque projet décrit son déploiement dans son propre dépôt.** Ce dépôt-ci
parle du serveur, pas des sites qu'il héberge : dupliquer les procédures ici
garantirait qu'elles divergent.
| Projet | Où est la procédure |
|---|---|
| `static` | `alpinux-static/README.md` — `scripts/deploy-app.sh`, `scripts/push-assets.sh` |
| `wiki` | webhook Gitea à chaque push ; <https://wiki.alpinux.org/technique/deploiement-wiki/> |
| `home` | `git pull` sur le serveur |
| `messagerie` | `alpinux.messagerie/docs/installation.md` — **copie de fichiers, base d'abord** |
| autres | le README du dépôt concerné |
Dans tous les cas : versionner avec `git push` **avant** de déployer.
---
## Authentification AlpID
Tous les projets Flask utilisent **AlpID** (SSO Keycloak).
- Chaque projet a son propre client Keycloak (`admin`, `dynamic`, `static-cdn`, …)
- Scopes : `openid profile email`
- Autorisation : claim `groups` → sinon `ADMIN_EMAILS` → sinon tout utilisateur AlpID
- Discovery URL : `https://alpid.alpinux.org/realms/master/.well-known/openid-configuration`
---
## Credentials locaux
Le fichier **`.credentials`** (ignoré par git) centralise les accès à renseigner localement :
```
.credentials ← à compléter manuellement, jamais commité
```
Il contient : token Gitea API, accès ISPConfig, accès AlpID admin, secrets clients Keycloak.
Utiliser `source .credentials` dans un script pour charger les variables.
---
## Règle Claude Code
Lancer Claude depuis le sous-dossier du projet pour limiter le contexte :
```bash
cd ~/Projects/alpinux.static && claude
cd ~/Projects/alpinux.wiki && claude
```
---
## Comptes personnels vs comptes de service
Alias SSH : `alpinux.org` → compte `abonnelc`.
### Règle absolue
Un compte personnel (`abonnelc` ou tout autre) ne doit jouer **aucun rôle dans le fonctionnement à long terme** des services :
- pas `User=` dans un unit systemd
- pas propriétaire des fichiers de l'app ou des logs
- pas dans la liste des groupes dont dépend un service en production
- pas référencé dans un `chown`, `setfacl`, ou cron de production
Si un service dépend d'un compte personnel, sa disparition (départ, suppression du compte, changement de login) fait tomber le service en production.
### Rôle d'abonnelc
`abonnelc` est un **compte d'administration ponctuelle**, limité à :
- créer ou modifier les fichiers `.env` sur le serveur
- redémarrer un service après un déploiement
- effectuer des opérations admin exceptionnelles
### Comptes de service
Chaque service tourne sous son propre utilisateur système dédié (ex. `static-cdn` pour `static-cdn.service`).
C'est ce compte qui possède les fichiers, les logs, et les droits nécessaires — pas `abonnelc`.
---
## Fichiers de référence
Ce dépôt ne contient pas seulement de la documentation : il garde les
configurations telles qu'elles devraient être, pour auditer et reconstruire.
> **ISPConfig gère les vhosts Apache réels — ne jamais déployer `conf/`
> directement.** Ces fichiers sont des références, pas des sources : ISPConfig
> régénère les siens et écraserait toute modification à la main.
## Structure
```
├── conf/ → VirtualHost Apache (référence, un fichier par service)
├── dns/ → Zone DNS (export daté, l'autorité est chez OVH)
├── services/ → Units systemd (à déployer dans /etc/systemd/system/)
└── docs/ → Documentation déploiement par service
```
---
## Services
| Domaine | Port interne | Service systemd | Répertoire serveur |
|---------|-------------|-----------------|-------------------|
| alpinux.org | — (statique) | — | ISPConfig web root |
| wiki.alpinux.org | — (statique rsync) | — | `/var/www/clients/client1/web2/web/wiki-static` |
| admin.alpinux.org | `127.0.0.1:5002` | `alpinux-admin` | `/home/alpinux/site/admin` |
| dynamic.alpinux.org | `127.0.0.1:5001` | `dynamic-alpinux` | `/home/alpinux/dynamic` |
| static.alpinux.org | `127.0.0.1:5003` | `static-cdn` | `/opt/static-cdn` |
---
## DNS
`dns/alpinux.org.zone` est un **export daté** de la zone `alpinux.org` — dernier
relevé le **2026-05-05** (serial `2026050501`). L'autorité est chez OVH : ce fichier
ne pilote rien, il sert à reconstituer la zone si elle est perdue ou écrasée.
Il contient les MX, SPF, DMARC, MTA-STS, DKIM (clé publique), les SRV CalDAV/CardDAV
et les CNAME de tous les sous-domaines. Le réexporter après toute modification chez
OVH, sinon il décrit un état qui n'existe plus.
---
---
## Documentation d'exploitation
| Document | Sujet |
|---|---|
| [`docs/courrier-sortant.md`](docs/courrier-sortant.md) | relais, SPF/DKIM/DMARC, plafonds, boîtes du service |
| [`docs/certificats.md`](docs/certificats.md) | les deux mécanismes TLS, leurs pièges, les réparations |
| [`docs/sauvegardes.md`](docs/sauvegardes.md) | sauvegarder les bases, et surtout les restaurer |
| `docs/admin.md`, `docs/static.md`, `docs/wiki.md`, `docs/dynamic.md`, `docs/proxy-calendar.md` | déploiement par service — à rapatrier dans leurs dépôts |
---
## Serveur
| | |
|---|---|
| **Hostname** | `owni.alpinux.org` |
| **OS** | Debian 12 (bookworm) |
| **IPv4** | `51.91.79.148` |
| **IPv6** | `2001:41d0:404:200::3f85/128` |
| **Passerelle IPv6** | `2001:41d0:404:200::1` |
| **SSH** | `ssh alpinux.org` (alias → `abonnelc@owni.alpinux.org`) |
Config IPv6 persistante : `/etc/network/interfaces.d/60-ipv6.cfg`
Cloud-init réseau désactivé : `/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg`
---
## Courrier sortant
Tout le courrier de la machine passe par un relais depuis les 25-26/09/2026
(`mail.acemail.fr`), et l'IP qui parle aux destinataires n'est plus celle
d'owni. SPF, DKIM, DMARC, les plafonds de Postfix, les boîtes du service et
les pièges qui vont avec :
→ **[`docs/courrier-sortant.md`](docs/courrier-sortant.md)**
---
## Certificats TLS
Deux mécanismes coexistent — un certificat certbot multi-domaines pour huit
sites, un certificat par site émis par ISPConfig via acme.sh — avec un piège
qui a déjà cassé deux fois le HTTPS d'un site pendant plusieurs semaines.
→ **[`docs/certificats.md`](docs/certificats.md)**
---
## Bases de données
Six bases applicatives, déclarées dans ISPConfig, plus celles du système.
| Base | Service | Sauvegarde |
|---|---|---|
| `c1_messagerie_db` | messagerie.alpinux.org | quotidienne, **1 copie** |
| `c1dolibarr` | dolibarr.alpinux.org — fichier des adhérents | **aucune** |
| `c1gitea` | gitea.alpinux.org — tous les dépôts | **aucune** |
| `c1alpid` | alpid.alpinux.org — le SSO | **aucune** |
| `c1_installparty` | installparty.alpinux.org | **aucune** |
| `c1_evenements` | événements | **aucune** |
S'y ajoutent `dbispconfig`, `roundcube` et `phpmyadmin`.
`sudo mysql` passe par le socket unix, sans mot de passe.
---
## Sauvegardes
Les sept bases sont tirées chaque nuit vers le poste par
`scripts/sauvegarder-owni.sh`. La procédure de **restauration** — et ses
pièges, dont le worker de la messagerie qui réexpédierait une campagne — est
écrite à côté :
→ **[`docs/sauvegardes.md`](docs/sauvegardes.md)**
Ce qui n'est **pas** sauvegardé : les fichiers des sites, les boîtes mail,
`/etc`. Ce sont des choix, écrits comme tels dans le document.
---