Prompt final
This commit is contained in:
parent
5c396d5a94
commit
ca4669156a
1 changed files with 65 additions and 36 deletions
|
|
@ -1,35 +1,43 @@
|
||||||
# Prompt Claude Code : déploiement complet de Paillasse
|
# Prompt Claude Code : déploiement complet de Syllabus
|
||||||
|
|
||||||
> À coller dans Claude Code, lancé depuis la racine du dépôt `Paillasse` sur le Mac d'Adam.
|
> À coller dans Claude Code, lancé depuis la racine du dépôt `Paillasse` sur le Mac d'Adam.
|
||||||
> Remplacer les valeurs entre `<...>` avant de lancer.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0. Contexte
|
## 0. Contexte
|
||||||
|
|
||||||
Tu vas déployer **Paillasse** de bout en bout sur un serveur bare metal, jusqu'à une version utilisable par une vraie classe.
|
**Nom du produit : Syllabus** (ancien nom de travail : Paillasse).
|
||||||
|
|
||||||
|
- Tout ce que voient les utilisateurs (pages, titres, e-mails, mentions légales) dit **Syllabus**, avec les logos `design/images/logo-syllabus.png` et `design/images/logo-syllabus-icone.png`.
|
||||||
|
- Les noms techniques internes restent tels quels : le dépôt `Paillasse`, le dossier `paillasse-contenus/`, l'image `paillasse-debian`, le projet OpenStack `paillasse`. Ne les renomme pas.
|
||||||
|
- Si tu trouves « Paillasse » dans un texte visible, remplace-le par « Syllabus ».
|
||||||
|
|
||||||
|
Tu vas déployer **Syllabus** de bout en bout sur un serveur bare metal, jusqu'à une version utilisable par une vraie classe.
|
||||||
|
|
||||||
Lis d'abord `CLAUDE.md` et `paillasse-contenus/README.md`. Ils contiennent le contexte complet du projet, la stack et les règles.
|
Lis d'abord `CLAUDE.md` et `paillasse-contenus/README.md`. Ils contiennent le contexte complet du projet, la stack et les règles.
|
||||||
|
|
||||||
**Paillasse** : une plateforme de labs Linux et réseau pour les écoles. Chaque élève a une vraie VM Debian, avec une console dans le navigateur et une correction automatique par scripts. Pas d'IA dans le produit.
|
**Syllabus** : une plateforme de labs Linux et réseau pour les écoles. Chaque élève a une vraie VM Debian, avec une console dans le navigateur et une correction automatique par scripts. Pas d'IA dans le produit.
|
||||||
|
|
||||||
**Le serveur**
|
**Le serveur**
|
||||||
- Scaleway Dedibox **Start-2-L**, datacenter DC2 (France).
|
- Scaleway Dedibox **Start-2-L**, datacenter DC2 (France).
|
||||||
- Xeon D-1531 (6 cœurs / 12 threads), **32 Go de RAM**, 2 × 250 Go SSD.
|
- Xeon D-1531 (6 cœurs / 12 threads), **32 Go de RAM**, 2 × 250 Go SSD en RAID 1 (`/` fait 226 Go).
|
||||||
- **Une seule IP publique** : `<IP_SERVEUR>`.
|
- **Une seule IP publique** : `163.172.4.195`.
|
||||||
- Debian `<12 ou 13>`, installé par Adam depuis la console Dedibox.
|
- Debian `12`, installé par Adam depuis la console Dedibox.
|
||||||
- Accès SSH : `ssh <UTILISATEUR>@<IP_SERVEUR>`, avec la clé dédiée `~/.ssh/<CLE_CLAUDE>`. Crée l'alias `paillasse` dans `~/.ssh/config`.
|
- Accès SSH : `ssh adam@163.172.4.195`, avec la clé dédiée `~/.ssh/id_ed25519_claude`. Crée l'alias `paillasse` dans `~/.ssh/config`, avec `IdentitiesOnly yes` pour n'utiliser que cette clé.
|
||||||
|
- `sudo` fonctionne sans mot de passe pour `adam` (fichier `/etc/sudoers.d/90-adam`).
|
||||||
- Sauvegardes : **Dedibackup 100 Go** (FTP, joignable uniquement depuis le serveur). Adam a déposé les identifiants dans `/root/.dedibackup` (`chmod 600`). Ne les affiche jamais.
|
- Sauvegardes : **Dedibackup 100 Go** (FTP, joignable uniquement depuis le serveur). Adam a déposé les identifiants dans `/root/.dedibackup` (`chmod 600`). Ne les affiche jamais.
|
||||||
|
|
||||||
|
**Image et labs** : suis la section « Image commune » de `paillasse-contenus/README.md`, et crée ce que demandent les labs 04 et 07 (étape `preparation`, `/opt/labs/07/lycee.sql`, `/opt/paquets/`).
|
||||||
|
|
||||||
**La forge** : `ssh://git@forge.adam.belkacemi.me:2222/adam/Paillasse.git`. Tu n'y fais que `git push` : aucune connexion SSH à la machine qui l'héberge.
|
**La forge** : `ssh://git@forge.adam.belkacemi.me:2222/adam/Paillasse.git`. Tu n'y fais que `git push` : aucune connexion SSH à la machine qui l'héberge.
|
||||||
|
|
||||||
**Le domaine** : `<DOMAINE>`. Adam l'achète et configure le DNS lui-même.
|
**Le domaine** : pas encore acheté. Fais les étapes 1 à 11 sans domaine (accès par IP ou tunnel SSH). Avant l'étape 12, arrête-toi et demande-moi le domaine.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Règles non négociables
|
## 1. Règles non négociables
|
||||||
|
|
||||||
1. **Ne jamais toucher à `tripoli-cloud`** ni à aucune autre machine que le serveur Paillasse et le Mac d'Adam.
|
1. **Ne jamais toucher à `tripoli-cloud`** ni à aucune autre machine que le serveur Syllabus et le Mac d'Adam.
|
||||||
2. **Aucun secret** dans le code, dans Git, dans le journal ou dans la conversation.
|
2. **Aucun secret** dans le code, dans Git, dans le journal ou dans la conversation.
|
||||||
- Les secrets vivent dans des fichiers `chmod 600` sur le serveur, ou dans un `.env` ignoré par Git.
|
- Les secrets vivent dans des fichiers `chmod 600` sur le serveur, ou dans un `.env` ignoré par Git.
|
||||||
- Les secrets Ansible passent par **Ansible Vault**. Le mot de passe du vault reste hors du dépôt.
|
- Les secrets Ansible passent par **Ansible Vault**. Le mot de passe du vault reste hors du dépôt.
|
||||||
|
|
@ -42,7 +50,7 @@ Lis d'abord `CLAUDE.md` et `paillasse-contenus/README.md`. Ils contiennent le co
|
||||||
4. **Anti-coupure SSH** : avant toute modification du réseau, du pare-feu ou de SSH, programme un retour automatique à l'ancienne configuration dans 5 minutes (par exemple avec `systemd-run --on-active=5min`), puis annule-le une fois l'accès vérifié depuis une **nouvelle** session SSH.
|
4. **Anti-coupure SSH** : avant toute modification du réseau, du pare-feu ou de SSH, programme un retour automatique à l'ancienne configuration dans 5 minutes (par exemple avec `systemd-run --on-active=5min`), puis annule-le une fois l'accès vérifié depuis une **nouvelle** session SSH.
|
||||||
5. **Tu ne commandes et ne paies jamais rien.**
|
5. **Tu ne commandes et ne paies jamais rien.**
|
||||||
6. **Élèves mineurs** : minimisation des données.
|
6. **Élèves mineurs** : minimisation des données.
|
||||||
- Un élève = un pseudonyme et un code. Pas d'e-mail, pas de nom complet obligatoire.
|
- Un élève = un identifiant `prénom.initiale` et un code secret. Pas d'e-mail. Le nom complet est facultatif et visible uniquement du prof.
|
||||||
- Suppression automatique 90 jours après l'archivage d'une classe.
|
- Suppression automatique 90 jours après l'archivage d'une classe.
|
||||||
7. **Tout est du code** :
|
7. **Tout est du code** :
|
||||||
- chaque configuration passe par un rôle Ansible idempotent dans `ansible/` ;
|
- chaque configuration passe par un rôle Ansible idempotent dans `ansible/` ;
|
||||||
|
|
@ -84,6 +92,7 @@ openstack/ modèles Heat, flavors, scripts de construction d'images
|
||||||
portail/ application Laravel
|
portail/ application Laravel
|
||||||
proxy-console/ service Go
|
proxy-console/ service Go
|
||||||
labs/ copie de travail des labs YAML
|
labs/ copie de travail des labs YAML
|
||||||
|
design/ maquettes Claude Design (référence exacte, ne pas modifier)
|
||||||
docs/ journal.md, runbooks/, inventaire-serveur.md, post-mortems/
|
docs/ journal.md, runbooks/, inventaire-serveur.md, post-mortems/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -98,7 +107,7 @@ docs/ journal.md, runbooks/, inventaire-serveur.md, post-mortems/
|
||||||
- virtualisation matérielle (`vmx`) et `/dev/kvm` ;
|
- virtualisation matérielle (`vmx`) et `/dev/kvm` ;
|
||||||
- RAM, disques, RAID ;
|
- RAM, disques, RAID ;
|
||||||
- interfaces réseau, IP, passerelle, DNS.
|
- interfaces réseau, IP, passerelle, DNS.
|
||||||
- Vérifie quelle version stable de Kolla-Ansible supporte la Debian installée. Si elle ne supporte pas Debian 13, **arrête-toi** et recommande à Adam de réinstaller en Debian 12.
|
- Vérifie quelle version stable de Kolla-Ansible supporte Debian 12, et choisis-la.
|
||||||
- Écris `docs/inventaire-serveur.md`.
|
- Écris `docs/inventaire-serveur.md`.
|
||||||
|
|
||||||
**Critère :** inventaire écrit, KVM disponible, version de Kolla-Ansible choisie et justifiée.
|
**Critère :** inventaire écrit, KVM disponible, version de Kolla-Ansible choisie et justifiée.
|
||||||
|
|
@ -115,17 +124,19 @@ docs/ journal.md, runbooks/, inventaire-serveur.md, post-mortems/
|
||||||
**Critère :**
|
**Critère :**
|
||||||
- connexion par clé OK ;
|
- connexion par clé OK ;
|
||||||
- connexion par mot de passe refusée, root refusé ;
|
- connexion par mot de passe refusée, root refusé ;
|
||||||
- depuis le Mac, `nmap <IP_SERVEUR>` ne montre que 22, 80 et 443.
|
- depuis le Mac, `nmap 163.172.4.195` ne montre que 22, 80 et 443.
|
||||||
|
|
||||||
### Étape 3 : Sauvegardes vers Dedibackup
|
### Étape 3 : Sauvegardes vers Dedibackup
|
||||||
|
|
||||||
- Utilise **restic**, chiffré, avec **rclone** comme backend FTP vers Dedibackup.
|
- Dedibackup (`dedibackup-dc3.online.net`) est limité à **1000 fichiers** et passe par du FTP **non chiffré**.
|
||||||
- Le mot de passe restic est dans un fichier root en `chmod 600`.
|
- Fais des **archives quotidiennes** `tar.zst`, **chiffrées avec age** avant l'envoi.
|
||||||
|
- Rotation : 7 quotidiennes et 4 hebdomadaires.
|
||||||
- À sauvegarder : `/etc`, `/home`, `/root`, `/etc/kolla` (plus tard), les dumps MariaDB de l'app (plus tard), le dossier du portail.
|
- À sauvegarder : `/etc`, `/home`, `/root`, `/etc/kolla` (plus tard), les dumps MariaDB de l'app (plus tard), le dossier du portail.
|
||||||
- Programmation : timer systemd quotidien, rétention 7 jours / 4 semaines / 3 mois.
|
- Programmation : timer systemd quotidien.
|
||||||
|
- Identifiants dans `/root/.dedibackup` (variables `DEDIBACKUP_HOST`, `DEDIBACKUP_USER`, `DEDIBACKUP_PASS`). La clé privée age ne quitte jamais le serveur : rappelle-moi d'en garder une copie hors du serveur.
|
||||||
- Écris `docs/runbooks/sauvegardes.md`.
|
- Écris `docs/runbooks/sauvegardes.md`.
|
||||||
|
|
||||||
**Critère :** une sauvegarde est faite et **un fichier est restauré avec succès** dans `/tmp`.
|
**Critère :** une sauvegarde est envoyée et **un fichier est restauré avec succès** dans `/tmp`.
|
||||||
|
|
||||||
### Étape 4 : Réseau pour OpenStack (accord d'Adam obligatoire)
|
### Étape 4 : Réseau pour OpenStack (accord d'Adam obligatoire)
|
||||||
|
|
||||||
|
|
@ -157,11 +168,7 @@ L'objectif est de ne jamais mettre Neutron sur l'interface publique.
|
||||||
### Étape 6 : Première VM en console série
|
### Étape 6 : Première VM en console série
|
||||||
|
|
||||||
1. Crée le projet `paillasse` et un utilisateur de service avec des **application credentials**, stockés dans un fichier `chmod 600`.
|
1. Crée le projet `paillasse` et un utilisateur de service avec des **application credentials**, stockés dans un fichier `chmod 600`.
|
||||||
2. Construis l'image `paillasse-debian` avec `virt-customize`, à partir de l'image cloud Debian officielle :
|
2. Construis l'image `paillasse-debian` avec `virt-customize`, à partir de l'image cloud Debian 12 officielle, en suivant la section « Image commune » de `paillasse-contenus/README.md`.
|
||||||
- `qemu-guest-agent` ;
|
|
||||||
- un getty sur `ttyS0` ;
|
|
||||||
- l'utilisateur `eleve` ;
|
|
||||||
- les outils réseau des labs (iproute2, iputils, tcpdump, traceroute, dnsutils, et frr si le lab l'exige).
|
|
||||||
3. Propriété de l'image : `hw_qemu_guest_agent=yes`.
|
3. Propriété de l'image : `hw_qemu_guest_agent=yes`.
|
||||||
4. Crée la flavor `eleve.petit` : 1 vCPU, 768 Mo, 5 Go.
|
4. Crée la flavor `eleve.petit` : 1 vCPU, 768 Mo, 5 Go.
|
||||||
5. Démarre une VM et récupère l'adresse de sa console série avec l'API Nova `remote-consoles` (protocole `serial`).
|
5. Démarre une VM et récupère l'adresse de sa console série avec l'API Nova `remote-consoles` (protocole `serial`).
|
||||||
|
|
@ -173,31 +180,37 @@ L'objectif est de ne jamais mettre Neutron sur l'interface publique.
|
||||||
- Prometheus, node_exporter et Grafana : ceux de Kolla si possible.
|
- Prometheus, node_exporter et Grafana : ceux de Kolla si possible.
|
||||||
- Uptime Kuma en conteneur Docker, écoutant sur `127.0.0.1`.
|
- Uptime Kuma en conteneur Docker, écoutant sur `127.0.0.1`.
|
||||||
- Alertes : disque > 80 %, RAM > 90 %, service OpenStack arrêté, dernière sauvegarde de plus de 26 h, serveur injoignable.
|
- Alertes : disque > 80 %, RAM > 90 %, service OpenStack arrêté, dernière sauvegarde de plus de 26 h, serveur injoignable.
|
||||||
- Notifications par webhook : l'URL est un secret.
|
- Notifications par webhook : l'URL est un secret, demande-la-moi.
|
||||||
- Tant qu'il n'y a pas de domaine, accès par tunnel SSH uniquement.
|
- Tant qu'il n'y a pas de domaine, accès par tunnel SSH uniquement.
|
||||||
|
|
||||||
**Critère :** une alerte de test est **reçue** par Adam.
|
**Critère :** une alerte de test est **reçue** par Adam.
|
||||||
|
|
||||||
### Étape 8 : Portail Laravel minimal
|
### Étape 8 : Portail Laravel
|
||||||
|
|
||||||
- Nginx et PHP-FPM, avec une version de PHP compatible avec la dernière version de Laravel.
|
- Nginx et PHP-FPM, avec une version de PHP compatible avec la dernière version de Laravel.
|
||||||
- MariaDB et Redis **pour l'app** en Docker Compose, liés à `127.0.0.1`, sur des ports différents de ceux de Kolla (par exemple 3307 et 6380).
|
- MariaDB et Redis **pour l'app** en Docker Compose, liés à `127.0.0.1`, sur des ports différents de ceux de Kolla (par exemple 3307 et 6380).
|
||||||
- Laravel, avec Inertia et React (starter kit officiel), dans `portail/`.
|
- **Laravel avec Blade et Vite**, dans `portail/`.
|
||||||
|
- **Design** :
|
||||||
|
- le dossier `design/` est la référence exacte. Il contient déjà des vues Blade (`design/resources/views/`) et des routes (`design/routes/web.php`) : reprends-les **telles quelles** ;
|
||||||
|
- le JS existant (`design/js/`, dont `paillasse-terminal.js`) et le CSS (`design/css/`) sont conservés ;
|
||||||
|
- compare chaque page avec une capture Playwright de `design/`, et corrige jusqu'à ce qu'elles soient identiques ;
|
||||||
|
- si `design/` est absent, arrête-toi et demande-le-moi.
|
||||||
- Modèles :
|
- Modèles :
|
||||||
- `Etablissement`, `User` (prof), `Classe`, `Eleve` (pseudonyme et code uniquement) ;
|
- `Etablissement`, `User` (prof), `Classe`, `Eleve` (identifiant, code secret haché, nom complet facultatif) ;
|
||||||
- `Lab`, `Seance`, `LabSession` (élève, lab, id de la stack ou des VM, état, expiration).
|
- `Lab`, `Seance`, `LabSession` (élève, lab, id de la stack ou des VM, état, expiration).
|
||||||
- Connexion :
|
- Connexion, sur un seul écran `/connexion` :
|
||||||
- le prof avec un e-mail et un mot de passe ;
|
- le prof avec un e-mail et un mot de passe ;
|
||||||
- l'élève avec un code de classe et un code élève, sur un seul écran `/connexion`.
|
- l'élève avec son identifiant `prénom.initiale` (avec un numéro pour les homonymes) et un code secret de 6 caractères.
|
||||||
- Un prof crée une classe et génère les codes de ses élèves.
|
- Le prof crée une classe, saisit ou importe en CSV la liste des élèves, et génère leurs identifiants et codes (fiches imprimables avec QR code).
|
||||||
- Design : la palette et les polices de `CLAUDE.md`, sobre.
|
- **Une seule session active par compte** : la précédente est coupée et le prof est alerté.
|
||||||
- Tests **Pest** pour la connexion et la création de classe.
|
- **Salle d'attente de séance** : le prof admet les élèves.
|
||||||
|
- Tests **Pest** pour la connexion, la session unique et la création de classe.
|
||||||
|
|
||||||
**Critère :** les tests Pest passent, et un élève fictif se connecte et voit son tableau de bord.
|
**Critère :** les tests Pest passent, et un élève fictif se connecte et voit son tableau de bord, identique à la maquette.
|
||||||
|
|
||||||
### Étape 9 : Proxy console Go et xterm.js
|
### Étape 9 : Proxy console Go et xterm.js
|
||||||
|
|
||||||
- Service Go dans `proxy-console/`, écoutant sur `127.0.0.1:8081`, lancé par systemd. Nginx relaie `/console` en WebSocket sur le port 443.
|
- Service Go dans `proxy-console/`, écoutant sur `127.0.0.1:8081`, lancé par systemd. Nginx relaie `/console` en WebSocket.
|
||||||
- Déroulé :
|
- Déroulé :
|
||||||
1. Laravel demande à Nova l'adresse de la console série.
|
1. Laravel demande à Nova l'adresse de la console série.
|
||||||
2. Laravel la range dans Redis sous un **jeton aléatoire**, pour 5 minutes.
|
2. Laravel la range dans Redis sous un **jeton aléatoire**, pour 5 minutes.
|
||||||
|
|
@ -211,7 +224,7 @@ L'objectif est de ne jamais mettre Neutron sur l'interface publique.
|
||||||
### Étape 10 : Liaison Laravel ↔ OpenStack et lab 08
|
### Étape 10 : Liaison Laravel ↔ OpenStack et lab 08
|
||||||
|
|
||||||
- Client OpenStack dans Laravel, avec le client HTTP de Laravel ou `php-opencloud/openstack`, via les application credentials.
|
- Client OpenStack dans Laravel, avec le client HTTP de Laravel ou `php-opencloud/openstack`, via les application credentials.
|
||||||
- Modèle Heat du lab 08 (deux LAN et un routeur), d'après `paillasse-contenus/` :
|
- Modèle Heat du lab 08, d'après `paillasse-contenus/08-deux-lan-routeur/lab.yaml` :
|
||||||
- réseaux de lab **sans DHCP**, avec `port_security_enabled: false` ;
|
- réseaux de lab **sans DHCP**, avec `port_security_enabled: false` ;
|
||||||
- **aucun routeur vers l'extérieur** : les VM des labs n'ont pas accès à Internet.
|
- **aucun routeur vers l'extérieur** : les VM des labs n'ont pas accès à Internet.
|
||||||
- Jobs dans une file Redis :
|
- Jobs dans une file Redis :
|
||||||
|
|
@ -240,14 +253,16 @@ L'objectif est de ne jamais mettre Neutron sur l'interface publique.
|
||||||
- une configuration correcte donne « réussi » ;
|
- une configuration correcte donne « réussi » ;
|
||||||
- une configuration incorrecte donne « échoué » avec un message utile.
|
- une configuration incorrecte donne « échoué » avec un message utile.
|
||||||
|
|
||||||
### Étape 12 : Domaine, HTTPS et sécurité (Adam a configuré le DNS)
|
### Étape 12 : Domaine, HTTPS et sécurité
|
||||||
|
|
||||||
|
**Avant de commencer, arrête-toi et demande-moi le domaine** (je l'achète et je configure le DNS moi-même).
|
||||||
|
|
||||||
- Certificat Let's Encrypt (certbot, plugin Nginx) et renouvellement automatique.
|
- Certificat Let's Encrypt (certbot, plugin Nginx) et renouvellement automatique.
|
||||||
- En-têtes de sécurité : HSTS, CSP adaptée à xterm.js, etc.
|
- En-têtes de sécurité : HSTS, CSP adaptée à xterm.js, etc.
|
||||||
- Grafana et Uptime Kuma derrière authentification, et la page de statut publique.
|
- Grafana et Uptime Kuma derrière authentification, et la page de statut publique.
|
||||||
- Vérifie à nouveau que seuls 22, 80 et 443 sont ouverts, et qu'aucune VM élève n'a d'IP publique ni d'accès sortant.
|
- Vérifie à nouveau que seuls 22, 80 et 443 sont ouverts, et qu'aucune VM élève n'a d'IP publique ni d'accès sortant.
|
||||||
|
|
||||||
**Critère :** `https://<DOMAINE>` affiche un certificat valide, et le parcours prof → élève → lab → correction fonctionne en HTTPS.
|
**Critère :** le domaine affiche un certificat valide en HTTPS, et le parcours prof → élève → lab → correction fonctionne.
|
||||||
|
|
||||||
### Étape 13 : Tests de capacité et de résistance
|
### Étape 13 : Tests de capacité et de résistance
|
||||||
|
|
||||||
|
|
@ -279,6 +294,20 @@ Formulations autorisées : « Hébergé en France » (c'est vrai), et « Conform
|
||||||
|
|
||||||
**Critère :** pages en ligne, et un test Pest prouve que la purge fonctionne.
|
**Critère :** pages en ligne, et un test Pest prouve que la purge fonctionne.
|
||||||
|
|
||||||
|
### Étape 15 : Visio de classe (seulement après validation des étapes 1 à 11)
|
||||||
|
|
||||||
|
- Jitsi auto-hébergé, derrière Nginx.
|
||||||
|
- Accès par **jeton JWT** émis par Laravel : seuls les élèves de la classe entrent, avec un nom non modifiable.
|
||||||
|
- Le prof est seul modérateur. Tout le monde arrive micro et caméra coupés, et seul le prof donne la parole.
|
||||||
|
- Main levée synchronisée avec celle de l'app, salle d'attente, chat désactivé par défaut.
|
||||||
|
- Reprends exactement `design/apercu-visio.html` et `design/resources/views/visio.blade.php`.
|
||||||
|
|
||||||
|
**Critère :** un prof et deux élèves fictifs rejoignent la visio ; un élève lève la main, le prof lui donne la parole, et un intrus sans jeton ne peut pas entrer.
|
||||||
|
|
||||||
|
### Étape 16 : Nettoyage
|
||||||
|
|
||||||
|
Propose de retirer `/etc/sudoers.d/90-adam`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Fin de mission
|
## 4. Fin de mission
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue