SOGOMS v1.0.7 - 2FA obligatoire et Infrastructure Management
Phase 17g - Double Authentification: - TOTP avec Google Authenticator/Authy - QR code pour enrôlement - Codes de backup (10 codes usage unique) - Page /admin/security pour gestion 2FA - Page /admin/users avec Reset 2FA (super_admin) - 2FA obligatoire pour rôles configurés Phase 21 - Infrastructure Management: - SQLite pour données infra (/data/infra.db) - SSH Pool avec reconnexion auto - Gestion Incus (list, start, stop, restart, sync) - Gestion Nginx (test, reload, deploy, sync, certbot) - Interface admin /admin/infra - Formulaire ajout serveur - Page détail serveur avec containers et sites Fichiers créés: - internal/infra/ (db, models, migrations, repository, ssh, incus, nginx) - cmd/sogoms/admin/totp.go - cmd/sogoms/admin/handlers_2fa.go - cmd/sogoms/admin/handlers_infra.go - Templates: 2fa_*, security, users, infra, server_* 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
306
TODO.md
306
TODO.md
@@ -362,6 +362,54 @@ users:
|
||||
email: pierre@example.com
|
||||
```
|
||||
|
||||
### 17g. Double Authentification (2FA) - OBLIGATOIRE
|
||||
|
||||
**Prérequis de sécurité** : l'accès à l'admin SOGOMS doit être protégé par 2FA.
|
||||
|
||||
- [x] Package Go `github.com/pquerna/otp` pour TOTP
|
||||
- [x] Package Go `github.com/skip2/go-qrcode` pour QR codes
|
||||
- [x] Stockage 2FA dans admin_users.yaml (two_fa_enabled, two_fa_secret, backup_codes)
|
||||
- [x] Enrôlement TOTP :
|
||||
- [x] Page `/admin/2fa/setup` : configuration 2FA
|
||||
- [x] Génération secret TOTP (base32, 160 bits)
|
||||
- [x] Affichage QR code pour scan (Google Auth, Authy, etc.)
|
||||
- [x] Saisie code de vérification pour activer
|
||||
- [x] Génération 10 codes de backup format XXXX-XXXX (usage unique)
|
||||
- [x] Login avec 2FA :
|
||||
- [x] Après password valide → page saisie code TOTP (`/admin/2fa/verify`)
|
||||
- [x] Validation code TOTP (fenêtre ±30s)
|
||||
- [x] Option "code de backup" si téléphone perdu
|
||||
- [x] Session marquée `TwoFAVerified`
|
||||
- [ ] Fallback Email OTP :
|
||||
- [ ] Si TOTP non configuré → envoi code 6 chiffres par email
|
||||
- [ ] Code valide 10 minutes, usage unique
|
||||
- [ ] Utilise sogoms-smtp existant
|
||||
- [x] Politique :
|
||||
- [x] 2FA obligatoire pour rôles configurés (`required_roles`)
|
||||
- [x] 2FA optionnel pour autres rôles
|
||||
- [x] Forcer configuration 2FA à la première connexion si requis
|
||||
- [x] Recovery :
|
||||
- [x] Reset 2FA par super_admin (page `/admin/users`)
|
||||
- [x] Audit log des actions 2FA (2fa_reset loggé)
|
||||
- [x] Page `/admin/security` : gestion 2FA utilisateur
|
||||
- [x] Page `/admin/users` : liste utilisateurs + bouton Reset 2FA (super_admin)
|
||||
- [x] Config admin_users.yaml :
|
||||
```yaml
|
||||
two_fa:
|
||||
enabled: true
|
||||
issuer_name: "SOGOMS Admin"
|
||||
required_roles: [super_admin]
|
||||
|
||||
users:
|
||||
- username: pierre
|
||||
password_hash: "$2a$12$..."
|
||||
role: super_admin
|
||||
email: pierre@example.com
|
||||
two_fa_enabled: true
|
||||
two_fa_secret: "BASE32SECRET..."
|
||||
backup_codes: ["$2a$...", ...] # bcrypt hashed
|
||||
```
|
||||
|
||||
## Hors scope V1
|
||||
|
||||
- sogorch (orchestrateur scénarios)
|
||||
@@ -424,6 +472,264 @@ Note : le bouton "Scanner la base" (19b) fait office d'Update Schema.
|
||||
- [ ] JWT secret : auto-généré (openssl rand -base64 32)
|
||||
- [ ] Permissions fichiers : 600
|
||||
|
||||
## Phase 20 : Soft Delete
|
||||
|
||||
Objectif : supporter la suppression logique pour les tables ayant un champ `deleted_at`.
|
||||
|
||||
### 20a. Détection lors du scan DB
|
||||
|
||||
- [x] Introspection : détecter colonne `deleted_at` (TIMESTAMP ou DATETIME)
|
||||
- [x] Schema.yaml : ajouter propriété `soft_delete: true` sur la table
|
||||
- [x] Affichage admin : indicateur visuel tables avec soft delete (*)
|
||||
|
||||
### 20b. Comportement DELETE
|
||||
|
||||
- [x] Route DELETE : UPDATE `deleted_at = NOW()` au lieu de DELETE physique
|
||||
- [x] Queries YAML : support soft_delete via schema
|
||||
- [x] Réponse API : retourner `affected_rows` comme avant
|
||||
- [x] Support paramètre `raw` dans sogoms-db pour expressions SQL brutes
|
||||
|
||||
### 20c. Filtrage automatique SELECT
|
||||
|
||||
- [x] Routes list/show : ajouter `WHERE deleted_at IS NULL` automatiquement
|
||||
- [x] Schema-driven : BuildListQuery/BuildShowQuery avec filtre soft delete
|
||||
- [x] Queries YAML : fonction addSoftDeleteFilter() pour injection automatique
|
||||
- [ ] Option `include_deleted: true` pour voir les supprimés (admin)
|
||||
|
||||
### 20d. Restauration (optionnel)
|
||||
|
||||
- [ ] Route `POST /api/{resource}/{id}/restore` : remet `deleted_at = NULL`
|
||||
- [ ] Permission spécifique pour restauration
|
||||
- [ ] Logging de l'action restore
|
||||
|
||||
### 20e. Purge définitive (optionnel)
|
||||
|
||||
- [ ] Route `DELETE /api/{resource}/{id}/purge` : suppression physique
|
||||
- [ ] Permission admin requise
|
||||
- [ ] Confirmation double (paramètre `force=true`)
|
||||
|
||||
### 20f. Cascade Soft Delete
|
||||
|
||||
- [x] Détecter les tables enfants via FK (ex: tasks.project_id → projects)
|
||||
- [x] Lors du soft delete parent, soft delete automatique des enfants
|
||||
- [x] Récursion : petits-enfants supprimés avant enfants (depth-first)
|
||||
- [x] Option `cascade: true` dans schema.yaml (auto-détecté lors du scan)
|
||||
- [x] Auto-détection : cascade activé si parent a soft_delete ET enfants avec soft_delete
|
||||
- [ ] Logging des suppressions en cascade
|
||||
|
||||
---
|
||||
|
||||
## Phase 21 : Infrastructure Management
|
||||
|
||||
Objectif : piloter depuis l'admin SOGOMS les configurations Nginx, serveurs et containers Incus.
|
||||
|
||||
### 21a. Modèle de données
|
||||
|
||||
- [x] Table `servers` : id, name, host (IP/hostname), vpn_ip, ssh_port, ssh_user, ssh_key_file, has_incus, has_nginx, status
|
||||
- [x] Table `containers` : id, server_id, name, incus_name, ip, vpn_ip, image, status (running/stopped/unknown)
|
||||
- [x] Table `nginx_configs` : id, server_id, domain, type, template, upstream, ssl_enabled, config_content, status
|
||||
- [x] Table `app_bindings` : id, app_id, container_id, nginx_config_id, server_id, type
|
||||
- [x] Stockage : SQLite local `/data/infra.db` (flag `-infra-db`)
|
||||
- [x] Migration : auto-création tables au démarrage (`internal/infra/migrations.go`)
|
||||
|
||||
### 21b. SSH Pool (intégré dans sogoms-admin)
|
||||
|
||||
- [x] `internal/infra/ssh.go` : client SSH avec pool de connexions
|
||||
- [x] Pool de connexions SSH vers serveurs configurés
|
||||
- [x] Reconnexion automatique en cas de perte (isAlive check)
|
||||
- [x] Méthodes SSH :
|
||||
- [x] `Exec` / `ExecSimple` : exécute commande sur serveur
|
||||
- [x] `WriteFile` / `ReadFile` : lecture/écriture fichiers distants
|
||||
- [x] `CopyFile` / `CopyFrom` : copie fichiers local ↔ distant
|
||||
- [x] `StreamExec` : exécution avec streaming stdout/stderr
|
||||
- [x] Config : clé SSH stockée dans SQLite (chemin fichier)
|
||||
- [x] Sécurité : accès restreint super_admin uniquement
|
||||
|
||||
### 21c. Gestion Incus (containers)
|
||||
|
||||
- [x] `internal/infra/incus.go` : méthodes Incus via SSH
|
||||
- [x] Action `ListIncusContainers` : liste containers (`incus list --format json`)
|
||||
- [ ] Action `incus_create` : crée un container (image, nom, config)
|
||||
- [x] Action `StartIncusContainer` : démarre un container
|
||||
- [x] Action `StopIncusContainer` : arrête un container (graceful)
|
||||
- [x] Action `RestartIncusContainer` : redémarre un container
|
||||
- [ ] Action `incus_delete` : supprime un container (avec confirmation)
|
||||
- [x] Action `ExecInContainer` : exécute une commande dans un container
|
||||
- [ ] Action `incus_copy` : copie un container (backup/clone)
|
||||
- [ ] Action `incus_move` : migre un container vers un autre serveur
|
||||
- [ ] Action `incus_snapshot` : crée un snapshot
|
||||
- [x] Sync : synchronisation containers Incus → base SQLite
|
||||
- [ ] Templates : images préconfigurées (alpine-sogoms, alpine-node, alpine-nginx)
|
||||
- [ ] Réseau : attribution IP automatique ou manuelle
|
||||
|
||||
### 21d. Gestion Nginx
|
||||
|
||||
- [x] `internal/infra/nginx.go` : méthodes Nginx via SSH
|
||||
- [ ] Templates Nginx dans `config/nginx-templates/`
|
||||
- [ ] `frontend.conf.tmpl` : proxy vers container frontend
|
||||
- [ ] `api.conf.tmpl` : proxy vers sogoway (local ou distant via VPN)
|
||||
- [ ] `admin.conf.tmpl` : proxy vers sogoms-admin
|
||||
- [ ] `ssl.conf.tmpl` : config SSL commune (Let's Encrypt)
|
||||
- [x] Action `GenerateNginxProxyConfig` : génère config proxy standard
|
||||
- [x] Action `DeployNginxSite` : écrire + activer + recharger
|
||||
- [x] Action `TestNginxConfig` : `nginx -t` sur le serveur cible
|
||||
- [x] Action `ReloadNginx` : `systemctl reload nginx` sur le serveur cible
|
||||
- [x] Action `ListNginxSites` : liste sites-available/enabled
|
||||
- [x] Action `EnableNginxSite` / `DisableNginxSite` : gestion liens symboliques
|
||||
- [x] Action `DeleteNginxSite` : supprime une config
|
||||
- [x] Sync : synchronisation sites Nginx → base SQLite
|
||||
- [x] Gestion Let's Encrypt : `RequestSSLCertificate` via certbot
|
||||
- [ ] Rollback : sauvegarde config avant modification
|
||||
|
||||
### 21e. Interface Admin
|
||||
|
||||
- [x] Page `/admin/infra` : dashboard infrastructure (liste serveurs, stats)
|
||||
- [x] Lien "Infra" dans header (super_admin only)
|
||||
- [x] Section Serveurs :
|
||||
- [x] Liste serveurs avec statut (online/offline/unknown)
|
||||
- [x] Badges Incus/Nginx pour services disponibles
|
||||
- [x] Bouton test connexion SSH
|
||||
- [x] Formulaire ajout serveur (nom, host, vpn_ip, ssh_user, ssh_key_file, port)
|
||||
- [x] Page détail serveur : containers, configs nginx
|
||||
- [x] Bouton suppression serveur
|
||||
- [x] Section Containers :
|
||||
- [x] Liste containers par serveur
|
||||
- [x] Statut (running/stopped/unknown)
|
||||
- [x] Actions : start, stop, restart
|
||||
- [x] Bouton sync depuis Incus
|
||||
- [ ] Formulaire création container (serveur, image, nom, IP)
|
||||
- [ ] Logs container (dernières lignes)
|
||||
- [x] Section Nginx :
|
||||
- [x] Liste sites par serveur/domaine
|
||||
- [x] Statut : active/inactive
|
||||
- [x] Bouton sync depuis serveur
|
||||
- [x] Bouton reload Nginx
|
||||
- [ ] Éditeur config (lecture seule ou édition avancée)
|
||||
- [ ] Historique déploiements
|
||||
- [ ] Section Apps :
|
||||
- [ ] Vue unifiée : app → container frontend + config nginx + API sogoms
|
||||
- [ ] Wizard création app complète (voir 21f)
|
||||
|
||||
**⚠️ À TESTER** : Interface infra déployée, valider fonctionnement avec serveur réel.
|
||||
|
||||
### 21f. Orchestration (Workflows)
|
||||
|
||||
Workflows automatisés pour opérations complexes.
|
||||
|
||||
- [ ] Workflow `app_create_full` : création app complète
|
||||
1. Créer container frontend sur serveur cible
|
||||
2. Configurer container (packages, user, etc.)
|
||||
3. Générer config Nginx frontend
|
||||
4. Générer config Nginx API (proxy vers sogoway)
|
||||
5. Déployer configs Nginx
|
||||
6. Créer config app SOGOMS (`config/apps/{app}/`)
|
||||
7. Recharger sogoway
|
||||
- [ ] Workflow `app_migrate` : migration app vers autre serveur
|
||||
1. Snapshot container source
|
||||
2. Copier vers serveur destination
|
||||
3. Mettre à jour configs Nginx
|
||||
4. Basculer DNS (notification)
|
||||
5. Supprimer ancien container (optionnel)
|
||||
- [ ] Workflow `ssl_setup` : configuration SSL
|
||||
1. Vérifier DNS pointe vers serveur
|
||||
2. Exécuter certbot
|
||||
3. Mettre à jour config Nginx
|
||||
4. Recharger Nginx
|
||||
- [ ] Logging : toutes étapes loggées dans sogoms-logs
|
||||
- [ ] Rollback : annulation automatique si échec
|
||||
|
||||
### 21g. API Interne
|
||||
|
||||
Endpoints admin pour piloter l'infrastructure.
|
||||
|
||||
- [ ] `GET /admin/api/infra/servers` : liste serveurs
|
||||
- [ ] `POST /admin/api/infra/servers` : ajoute serveur
|
||||
- [ ] `DELETE /admin/api/infra/servers/{id}` : supprime serveur
|
||||
- [ ] `POST /admin/api/infra/servers/{id}/test` : teste connexion
|
||||
- [ ] `GET /admin/api/infra/containers` : liste containers (tous serveurs)
|
||||
- [ ] `GET /admin/api/infra/containers/{server_id}` : containers d'un serveur
|
||||
- [ ] `POST /admin/api/infra/containers` : crée container
|
||||
- [ ] `POST /admin/api/infra/containers/{id}/start` : démarre
|
||||
- [ ] `POST /admin/api/infra/containers/{id}/stop` : arrête
|
||||
- [ ] `DELETE /admin/api/infra/containers/{id}` : supprime
|
||||
- [ ] `GET /admin/api/infra/nginx` : liste configs nginx
|
||||
- [ ] `POST /admin/api/infra/nginx/generate` : génère config
|
||||
- [ ] `POST /admin/api/infra/nginx/deploy` : déploie config
|
||||
- [ ] `POST /admin/api/infra/nginx/reload/{server_id}` : reload nginx
|
||||
- [ ] `POST /admin/api/infra/workflows/{name}` : lance workflow
|
||||
|
||||
### 21h. Configuration exemple
|
||||
|
||||
```yaml
|
||||
# /secrets/infra_servers.yaml
|
||||
servers:
|
||||
- name: IN3
|
||||
host: 195.154.80.116
|
||||
vpn_ip: 11.1.2.1
|
||||
ssh_user: root
|
||||
ssh_key_file: /secrets/ssh_in3_key
|
||||
ssh_port: 22
|
||||
type: host
|
||||
incus: true
|
||||
|
||||
- name: IN4
|
||||
host: 195.154.xx.xx
|
||||
vpn_ip: 11.1.2.14
|
||||
ssh_user: root
|
||||
ssh_key_file: /secrets/ssh_in4_key
|
||||
ssh_port: 22
|
||||
type: host
|
||||
incus: true
|
||||
|
||||
# Templates Nginx
|
||||
nginx:
|
||||
templates_dir: /config/nginx-templates
|
||||
certbot_email: admin@sogoms.com
|
||||
```
|
||||
|
||||
```nginx
|
||||
# config/nginx-templates/api.conf.tmpl
|
||||
server {
|
||||
server_name {{.Domain}};
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://{{.ApiUpstream}};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
{{if .SSLEnabled}}
|
||||
listen 443 ssl;
|
||||
ssl_certificate /etc/letsencrypt/live/{{.Domain}}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{.Domain}}/privkey.pem;
|
||||
{{else}}
|
||||
listen 80;
|
||||
{{end}}
|
||||
}
|
||||
```
|
||||
|
||||
### 21i. Sécurité
|
||||
|
||||
- [ ] Clés SSH : fichiers séparés dans `/secrets/`, permissions 600
|
||||
- [ ] Accès : super_admin uniquement pour toutes opérations infra
|
||||
- [ ] Audit : toutes actions loggées (qui, quoi, quand, serveur)
|
||||
- [ ] Rate limiting : max 10 opérations/minute par user
|
||||
- [ ] Confirmation : double confirmation pour actions destructives (delete container)
|
||||
- [ ] Isolation : sogoms-infra tourne avec user dédié
|
||||
|
||||
### 21j. Dépendances
|
||||
|
||||
- Phase 17 (Admin UI) ✅
|
||||
- Phase 19 (Création App) ✅
|
||||
- Accès SSH aux serveurs (clés à configurer)
|
||||
- Incus installé sur les serveurs hôtes
|
||||
- [x] Package Go : `golang.org/x/crypto/ssh`
|
||||
- [x] Package Go : `github.com/mattn/go-sqlite3`
|
||||
|
||||
---
|
||||
|
||||
## Phase 18 : Application Geosector (Janvier-Février 2026)
|
||||
|
||||
Migration de l'API PHP 8.3 existante vers SOGOMS pour l'application Flutter (Web + mobiles).
|
||||
|
||||
Reference in New Issue
Block a user