Structurer l'exploitation Ansible Chezlepro
This commit is contained in:
parent
8fac83bcdb
commit
61e2c7ca67
92 changed files with 2829 additions and 853 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,7 +1,9 @@
|
|||
*.retry
|
||||
.vault-pass
|
||||
facts_cache/
|
||||
.ansible/
|
||||
*.vault
|
||||
*.vault.yml
|
||||
*.secret
|
||||
*.pem
|
||||
*.key
|
||||
|
|
|
|||
241
AGENTS.md
241
AGENTS.md
|
|
@ -114,7 +114,7 @@ Avant de proposer un changement comme terminé, vérifier au minimum la syntaxe
|
|||
Exemple pour le template Debian 13 Proxmox :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_prepare.yml --syntax-check
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_preparer.yml --syntax-check
|
||||
```
|
||||
|
||||
Pour un autre playbook, remplacer le chemin par le playbook concerné.
|
||||
|
|
@ -210,13 +210,12 @@ Les playbooks peuvent être classés par domaine :
|
|||
|
||||
```text
|
||||
playbooks/
|
||||
├── baseline/
|
||||
├── hardening/
|
||||
├── groupes/
|
||||
├── maintenance/
|
||||
├── monitoring/
|
||||
├── networking/
|
||||
├── proxmox/
|
||||
├── vm_templates/
|
||||
├── modeles_vm/
|
||||
├── web/
|
||||
├── database/
|
||||
├── identity/
|
||||
|
|
@ -228,6 +227,207 @@ Les rôles peuvent être ajoutés progressivement selon les besoins.
|
|||
|
||||
Ne pas créer de structure inutile uniquement pour donner une impression de complétude.
|
||||
|
||||
La conformité normale des VM déployées doit passer par `playbooks/groupes/`.
|
||||
|
||||
Ne pas maintenir en parallèle des playbooks de couches génériques comme `playbooks/socle/` ou `playbooks/durcissement/` lorsqu'un groupe opérationnel exprime déjà cet état voulu.
|
||||
|
||||
---
|
||||
|
||||
## Interface opérateur Makefile
|
||||
|
||||
Le `Makefile` est l'interface opérateur privilégiée pour les gestes courants.
|
||||
|
||||
Les commandes `make` doivent simplifier l'exploitation Ansible sans masquer les playbooks réellement exécutés.
|
||||
|
||||
Préférer quelques cibles claires et utiles :
|
||||
|
||||
- validation du dépôt ;
|
||||
- inspection des inventaires ;
|
||||
- ajout ou mise à jour d'un hôte dans un inventaire ;
|
||||
- association d'un hôte à ses groupes ;
|
||||
- déploiement ou remise en conformité d'un hôte ;
|
||||
- déploiement ou remise en conformité d'un groupe ;
|
||||
- préparation, vérification et nettoyage protégé d'un modèle de VM.
|
||||
|
||||
Ne pas multiplier les cibles `make` secondaires si elles ne correspondent pas à un geste réel d'exploitation.
|
||||
|
||||
Les cibles d'exploitation des VM doivent privilégier les groupes :
|
||||
|
||||
```text
|
||||
make deployer HOTE=web-01
|
||||
make deployer-groupe GROUPE=serveurs_debian
|
||||
make appliquer GROUPE=serveurs_debian
|
||||
```
|
||||
|
||||
Éviter les cibles parallèles qui réappliquent les mêmes rôles par couche, par exemple `make socle`, `make durcissement`, `make converger` ou `make deployer-vm`.
|
||||
|
||||
Toute cible `make` qui lance une action destructive ou risquée doit exiger une confirmation explicite.
|
||||
|
||||
---
|
||||
|
||||
## Convention groupes et playbooks
|
||||
|
||||
Chaque groupe opérationnel Ansible doit avoir un playbook homonyme dans `playbooks/groupes/`.
|
||||
|
||||
La convention attendue est :
|
||||
|
||||
```text
|
||||
groupe Ansible : serveurs_debian
|
||||
playbook : playbooks/groupes/serveurs_debian.yml
|
||||
```
|
||||
|
||||
L'appartenance aux groupes détermine les services, intégrations et politiques appliqués à une VM.
|
||||
|
||||
`playbooks/groupes/` est la source officielle de conformité pour les VM déployées.
|
||||
|
||||
Un playbook de groupe doit cibler son groupe homonyme, pas `all`, sauf justification explicite.
|
||||
|
||||
Les concepts comme socle Debian ou durcissement commun doivent être représentés par des groupes explicites :
|
||||
|
||||
```text
|
||||
serveurs_debian -> socle commun Debian
|
||||
serveurs_durcis -> durcissement commun
|
||||
clients_dns -> intégration cliente DNS
|
||||
clients_pki -> intégration cliente PKI / ACME
|
||||
```
|
||||
|
||||
Ne pas dupliquer ces mêmes rôles dans des playbooks de couches séparés.
|
||||
|
||||
Quand un nouveau groupe opérationnel est ajouté :
|
||||
|
||||
- créer le playbook homonyme dans `playbooks/groupes/` ;
|
||||
- documenter son intention opérationnelle ;
|
||||
- prévoir les rôles nécessaires ;
|
||||
- valider au minimum sa syntaxe ;
|
||||
- l'ajouter aux facilités d'exploitation si l'opérateur doit l'utiliser directement.
|
||||
|
||||
---
|
||||
|
||||
## Cycle de vie et conformité des VM
|
||||
|
||||
Set-OPS doit gérer le cycle de vie complet des VM Chezlepro :
|
||||
|
||||
```text
|
||||
VM Debian minimale
|
||||
→ goldenisation du template
|
||||
→ clonage
|
||||
→ identité initiale cloud-init
|
||||
→ conformité par groupes Ansible
|
||||
→ intégrations transversales
|
||||
→ conformité continue
|
||||
```
|
||||
|
||||
Le template est seulement la fondation. Les VM existantes et futures doivent converger vers l'état voulu par les playbooks de groupes.
|
||||
|
||||
Les rôles et playbooks doivent donc être conçus pour être relancés régulièrement, sans effet secondaire inutile.
|
||||
|
||||
Le groupe d'inventaire attendu pour les VM Debian gérées est :
|
||||
|
||||
```text
|
||||
serveurs_debian
|
||||
```
|
||||
|
||||
Les groupes spécialisés doivent s'ajouter selon les besoins réels, par exemple :
|
||||
|
||||
```text
|
||||
clients_dns
|
||||
clients_pki
|
||||
clients_ldap
|
||||
clients_supervision
|
||||
clients_metriques
|
||||
serveurs_web
|
||||
serveurs_bases_donnees
|
||||
```
|
||||
|
||||
Ne pas cibler `all` par défaut pour un playbook qui ne s'applique pas réellement à tous les hôtes.
|
||||
|
||||
---
|
||||
|
||||
## Services centraux et intégrations clientes
|
||||
|
||||
Pour chaque service d'infrastructure, distinguer deux responsabilités :
|
||||
|
||||
```text
|
||||
service serveur : installe et configure le service central
|
||||
intégration cliente : raccorde les VM au service central
|
||||
```
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
PowerDNS serveur → clients DNS / resolver / enregistrements
|
||||
step-ca serveur → confiance CA / ACME client
|
||||
LDAP serveur → SSSD / NSS / PAM client
|
||||
Keycloak serveur → intégrations OIDC applicatives
|
||||
Prometheus → node_exporter sur les VM
|
||||
Icinga2 → agent ou checks distants
|
||||
Grafana → datasources et dashboards côté plateforme
|
||||
```
|
||||
|
||||
Quand un nouveau service central est ajouté, prévoir aussi le ou les rôles clients nécessaires pour intégrer les VM existantes et futures.
|
||||
|
||||
Les intégrations clientes doivent être :
|
||||
|
||||
- idempotentes ;
|
||||
- activables par inventaire ou variables ;
|
||||
- désactivées par défaut si leur dépendance centrale n'existe pas ;
|
||||
- documentées avec leurs prérequis ;
|
||||
- testables avec `--syntax-check` et, lorsque possible, `--check`.
|
||||
|
||||
Ne pas mettre les intégrations applicatives ou les agents spécialisés dans le golden template, sauf justification opérationnelle explicite.
|
||||
|
||||
---
|
||||
|
||||
## Variables et inventaires
|
||||
|
||||
Les variables de rôle doivent utiliser un préfixe correspondant au rôle.
|
||||
|
||||
Exemples :
|
||||
|
||||
```yaml
|
||||
ssh_durcissement_port: 22
|
||||
nftables_socle_enabled: false
|
||||
fail2ban_ssh_enabled: true
|
||||
```
|
||||
|
||||
Séparer clairement :
|
||||
|
||||
```text
|
||||
variables de template : construction du golden template
|
||||
variables de conformité : état voulu des VM déployées
|
||||
variables applicatives : services et rôles spécialisés
|
||||
```
|
||||
|
||||
Les variables de conformité ne doivent pas couper l'accès SSH, DNS ou réseau sans validation explicite.
|
||||
|
||||
Toute variable susceptible de provoquer une action destructive, bloquante ou irréversible doit exiger une confirmation explicite.
|
||||
|
||||
---
|
||||
|
||||
## Langue et nommage
|
||||
|
||||
La surface destinée à l'opérateur doit être en français.
|
||||
|
||||
À franciser :
|
||||
|
||||
- noms de groupes d'inventaire ;
|
||||
- cibles `make` ;
|
||||
- variables de commande maison ;
|
||||
- libellés et messages des scripts maison ;
|
||||
- noms des playbooks maison quand ils sont exposés à l'opérateur ;
|
||||
- documentation d'exploitation.
|
||||
|
||||
À ne pas franciser automatiquement :
|
||||
|
||||
- mots-clés Ansible (`hosts`, `roles`, `tasks`, `handlers`, `vars`, `become`) ;
|
||||
- noms de modules Ansible (`ansible.builtin.apt`, `ansible.builtin.template`, etc.) ;
|
||||
- conventions techniques imposées par un outil ;
|
||||
- noms de rôles existants, sauf demande explicite ou migration ciblée justifiée.
|
||||
|
||||
Les variables de rôle peuvent rester alignées sur le nom technique du rôle, mais elles doivent conserver un préfixe clair et stable.
|
||||
|
||||
Ne pas faire de renommage massif uniquement pour franciser si cela augmente le risque sans bénéfice opérationnel immédiat.
|
||||
|
||||
---
|
||||
|
||||
## Template Debian 13 Proxmox
|
||||
|
|
@ -246,7 +446,7 @@ Il peut contenir :
|
|||
- `cloud-guest-utils` ;
|
||||
- chrony ;
|
||||
- outils de diagnostic de base ;
|
||||
- hardening raisonnable ;
|
||||
- durcissement raisonnable ;
|
||||
- AppArmor ;
|
||||
- auditd ;
|
||||
- fail2ban SSH ;
|
||||
|
|
@ -277,23 +477,20 @@ Les services spécialisés doivent être installés ensuite par des playbooks d
|
|||
|
||||
## SSH
|
||||
|
||||
Pendant la construction du template, l’état transitoire accepté est :
|
||||
Le template Debian 13 Proxmox doit être construit avec un accès SSH par clé dès le départ.
|
||||
|
||||
```text
|
||||
PasswordAuthentication yes
|
||||
PermitRootLogin no
|
||||
PubkeyAuthentication yes
|
||||
```
|
||||
Cloud-init doit injecter le `ciuser` et sa clé publique avant l'exécution d'Ansible.
|
||||
|
||||
État cible recommandé après validation des clés SSH :
|
||||
État attendu :
|
||||
|
||||
```text
|
||||
PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
PubkeyAuthentication yes
|
||||
AuthenticationMethods publickey
|
||||
```
|
||||
|
||||
Ne jamais désactiver l’authentification par mot de passe avant d’avoir confirmé que l’accès par clé fonctionne.
|
||||
Ne pas lancer le playbook de préparation tant que l'accès SSH par clé au compte technique n'est pas confirmé.
|
||||
|
||||
Toute modification SSH doit valider la configuration avant rechargement :
|
||||
|
||||
|
|
@ -343,7 +540,7 @@ Le nettoyage final avant conversion en template doit être protégé par une con
|
|||
Exemple :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_cleanup.yml -e confirm_template_cleanup=true
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
|
||||
```
|
||||
|
||||
Le nettoyage peut inclure :
|
||||
|
|
@ -382,6 +579,22 @@ Les corrections de rôles, handlers, playbooks et templates doivent être consig
|
|||
|
||||
---
|
||||
|
||||
## Stabilisation des changements
|
||||
|
||||
Après une série cohérente de changements validés, recommander un commit propre avant de poursuivre vers un nouveau chantier.
|
||||
|
||||
Avant de recommander un commit :
|
||||
|
||||
- vérifier l'état Git ;
|
||||
- résumer les fichiers créés, modifiés, déplacés ou supprimés ;
|
||||
- indiquer les validations exécutées ;
|
||||
- signaler les validations non exécutées ;
|
||||
- mentionner les risques ou limites connus.
|
||||
|
||||
Ne pas créer de commit sans demande explicite.
|
||||
|
||||
---
|
||||
|
||||
## Comportement attendu de Codex
|
||||
|
||||
Avant de modifier :
|
||||
|
|
|
|||
130
CHANGELOG.md
130
CHANGELOG.md
|
|
@ -1,15 +1,67 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-06-21
|
||||
|
||||
### Ajouté
|
||||
- Ajout de facilités `make` pour exploiter Ansible sur les VM Debian déployées :
|
||||
- diagnostic `ping`, `sudo` et `faits` ;
|
||||
- variables `LIMITE`, `VERIFICATION`, `DIFF`, `ETIQUETTES`, `SAUTER_ETIQUETTES` et `VARIABLES`.
|
||||
- Ajout de facilités `make` pour inspecter et valider les inventaires Ansible :
|
||||
- `hote-ajouter` ;
|
||||
- `hote-groupes` ;
|
||||
- `hote-afficher` ;
|
||||
- `inventaire-verifier` ;
|
||||
- `inventaire-lister` ;
|
||||
- `inventaire-graphe` ;
|
||||
- `inventaire-hote` ;
|
||||
- `inventaire-lab` ;
|
||||
- `inventaire-production`.
|
||||
- Ajout de `deployer` et `deployer-groupe` comme commandes opérateur simplifiées pour appliquer les playbooks de groupes.
|
||||
- Ajout de `scripts/inventory_host.py` pour gérer les entrées d'inventaire et leur appartenance aux groupes sans édition YAML manuelle.
|
||||
- Ajout de la convention `1 groupe opérationnel = 1 playbook homonyme` avec les premiers playbooks :
|
||||
- `playbooks/groupes/serveurs_debian.yml` ;
|
||||
- `playbooks/groupes/serveurs_durcis.yml`.
|
||||
- Ajout du playbook `playbooks/maintenance/verifier_hote_debian.yml` pour valider une VM Debian 13 gérée par Set-OPS après socle et durcissement.
|
||||
- Ajout de directives `AGENTS.md` pour formaliser le `Makefile` comme interface opérateur, la convention groupe/playbook, la francisation de la surface opérateur et les points de stabilisation avant commit.
|
||||
- Ajout du playbook `playbooks/proxmox/cloner_vm_debian.yml` et des cibles `make cloner-vm` / `make creer-vm` pour créer des clones Debian via l'API Proxmox sans stocker de secret dans le dépôt.
|
||||
- Ajout de `inventories/lab/group_vars/proxmox.yml` pour les paramètres Proxmox non sensibles et de `proxmox.vault.yml.example` pour préparer les secrets API avec Ansible Vault.
|
||||
- Ajout de `make config` pour renseigner les paramètres Proxmox et créer ou éditer le Vault API depuis le terminal.
|
||||
|
||||
### Modifié
|
||||
- Francisation de la surface opérateur :
|
||||
- variables `make` ;
|
||||
- cibles `make` ;
|
||||
- groupes d'inventaire ;
|
||||
- chemins des playbooks maison ;
|
||||
- libellés du script de gestion d'inventaire.
|
||||
- Documentation du flux de déploiement d'une VM clonée depuis le modèle doré avec les commandes `make`.
|
||||
- Optimisation de la procédure de bootstrap du modèle Debian 13 pour utiliser Cloud-Init comme source unique de l'identité initiale au lieu de maintenir une création manuelle parallèle du compte et de la clé SSH.
|
||||
- Simplification de `make cloner-vm` et `make creer-vm` pour que les paramètres Proxmox communs viennent des variables Ansible plutôt que de la ligne de commande ou de l'environnement.
|
||||
- Ajustement de `make config` pour ne plus redemander les valeurs Cloud-Init déjà portées par le modèle Proxmox.
|
||||
- Déplacement du VLAN hors de `make config` afin de l'exiger explicitement à chaque création de VM avec `VLAN=...`.
|
||||
- Retrait des options `DEMANDER_MDP` et `DEMANDER_SUDO` du `Makefile`; le flux suppose SSH par clé et sudo NOPASSWD déjà fonctionnels.
|
||||
- Retrait des playbooks de couches redondants `playbooks/socle/debian_commun.yml` et `playbooks/durcissement/debian_serveur_durcissement.yml`; la conformité des VM déployées passe maintenant par `playbooks/groupes/`.
|
||||
- Bonification de `AGENTS.md` pour désigner `playbooks/groupes/` comme source officielle de conformité et éviter les cibles `make` de couches parallèles.
|
||||
- Simplification de l'aide du `Makefile` pour exposer d'abord les gestes opérateur : inventaire, groupes, déploiement.
|
||||
- Clarification de `make aide` pour expliquer les différences entre `creer-vm`, `cloner-vm`, `deployer`, `deployer-groupe` et `appliquer`.
|
||||
- Réorganisation de `make aide` par objets d'exploitation : VM, hôtes, groupes, inventaires, modèle et validation.
|
||||
- Retrait des commandes opérateur explicites de ping et sudo; ces vérifications sont maintenant implicites dans `deployer` et `preparer-modele`.
|
||||
- Ajout d'une demande automatique de mot de passe Ansible Vault pour `make cloner-vm` et `make creer-vm` lorsqu'un `proxmox.vault.yml` chiffré est présent.
|
||||
- Amélioration du message d'erreur lorsque `proxmox.vault.yml` ne peut pas être chargé.
|
||||
- Ajustement du playbook Proxmox pour laisser apparaître les erreurs API utiles sans masquer entièrement les échecs de clonage.
|
||||
- Modification de `make deployer HOTE=...` pour appliquer les playbooks correspondant aux groupes de l'hôte.
|
||||
- Ajout d'une vérification explicite de la bibliothèque Python `proxmoxer` avant les appels API Proxmox.
|
||||
- Retrait de la taille disque clone des paramètres Proxmox persistés afin de ne pas tenter de réduire un disque hérité du modèle.
|
||||
|
||||
## 2026-06-19
|
||||
|
||||
### Ajouté
|
||||
- Structure initiale globale du dépôt Set-OPS.
|
||||
- Playbooks de création du template Debian 13 Proxmox.
|
||||
- Rôles de base Debian.
|
||||
- Rôles de hardening template-safe.
|
||||
- Rôles de durcissement template-safe.
|
||||
- Playbook de vérification.
|
||||
- Playbook de nettoyage final avant conversion en template.
|
||||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-06-20
|
||||
|
||||
|
|
@ -26,10 +78,10 @@
|
|||
- préférence pour les correctifs ciblés au lieu des régénérations massives ;
|
||||
- rappel que `Set-OPS` ne doit pas être restructuré autour d’un seul besoin ponctuel.
|
||||
- Ajout d’une logique de template Debian 13 Proxmox :
|
||||
- `playbooks/vm_templates/debian13_proxmox_prepare.yml` ;
|
||||
- `playbooks/vm_templates/debian13_proxmox_verify.yml` ;
|
||||
- `playbooks/vm_templates/debian13_proxmox_cleanup.yml`.
|
||||
- Ajout d’une couche de hardening template-safe :
|
||||
- `playbooks/modeles_vm/debian13_proxmox_preparer.yml` ;
|
||||
- `playbooks/modeles_vm/debian13_proxmox_verifier.yml` ;
|
||||
- `playbooks/modeles_vm/debian13_proxmox_nettoyer.yml`.
|
||||
- Ajout d’une couche de durcissement template-safe :
|
||||
- AppArmor ;
|
||||
- auditd ;
|
||||
- fail2ban SSH ;
|
||||
|
|
@ -37,6 +89,40 @@
|
|||
- journald ;
|
||||
- sysctl de sécurité ;
|
||||
- nftables installé et préparé, mais désactivé par défaut dans le template.
|
||||
- Ajout d’un document d’architecture pour les intégrations futures des VM :
|
||||
- supervision ;
|
||||
- métriques ;
|
||||
- identité ;
|
||||
- PKI interne ;
|
||||
- DNS interne.
|
||||
- Documentation détaillée des ajouts appliqués au modèle doré Debian 13 Proxmox depuis une installation minimale.
|
||||
- Ajout d'un runbook explicite pour le playbook de préparation du modèle doré Debian 13 Proxmox.
|
||||
- Ajout d'un runbook manuel précis pour le bootstrap initial de la VM vanille Debian 13 :
|
||||
- correction des sources APT après installation depuis DVD ;
|
||||
- installation minimale de `sudo`, SSH, `cloud-init`, `cloud-guest-utils` et `qemu-guest-agent` ;
|
||||
- utilisation de Cloud-Init pour injecter le compte `ansible`, sa clé SSH, le réseau et le point de bascule vers le `Makefile`.
|
||||
- Ajout d'un document de cycle de vie des VM Chezlepro :
|
||||
- VM vanille ;
|
||||
- goldenisation ;
|
||||
- clonage ;
|
||||
- socle ;
|
||||
- durcissement ;
|
||||
- conformité continue.
|
||||
- Ajout de directives `AGENTS.md` pour la convergence des VM existantes et futures :
|
||||
- cycle de vie des VM ;
|
||||
- distinction service central et intégration cliente ;
|
||||
- groupes d'inventaire par intégration ;
|
||||
- séparation des variables de template, conformité et services.
|
||||
- Ajout d'un `Makefile` comme interface d'exploitation courante :
|
||||
- validation ;
|
||||
- syntax-checks ;
|
||||
- préparation, vérification et nettoyage protégé du template ;
|
||||
- socle et durcissement des VM Debian.
|
||||
- Durcissement SSH du template dès la construction :
|
||||
- accès par clé publique obligatoire ;
|
||||
- authentification par mot de passe désactivée ;
|
||||
- forwarding et tunnels SSH désactivés par défaut ;
|
||||
- limites de tentatives, sessions et démarrages SSH resserrées.
|
||||
|
||||
### Modifié
|
||||
- Renforcement attendu de `CLAUDE.md` pour rappeler que `AGENTS.md` est la source d’autorité principale.
|
||||
|
|
@ -47,15 +133,31 @@
|
|||
- valider les playbooks touchés ;
|
||||
- signaler les tests non exécutés ;
|
||||
- mettre à jour `CHANGELOG.md` si pertinent.
|
||||
- Migration des variables de rôles vers des préfixes propres à chaque rôle pour respecter `ansible-lint`.
|
||||
- Mise à jour de la variable de confirmation du nettoyage final :
|
||||
- `template_cleanup_confirm`.
|
||||
- Correction de la structure du `CHANGELOG.md` pour retirer le titre dupliqué.
|
||||
- Simplification ciblée de l'arborescence `roles/` :
|
||||
- retrait des doublons non utilisés sous `roles/base/` ;
|
||||
- retrait des doublons non utilisés sous `roles/security/` ;
|
||||
- retrait des doublons non utilisés sous `roles/vm_template/`.
|
||||
- Ajout d'un renvoi depuis la procédure manuelle vers la justification du golden template.
|
||||
- Séparation documentaire et opérationnelle entre variables de template et variables de conformité.
|
||||
- Extension du playbook de groupe `playbooks/groupes/serveurs_debian.yml` pour maintenir le socle commun des VM Debian déployées.
|
||||
- Mise à jour de la documentation pour exiger un `ciuser` cloud-init avec clé publique avant l'exécution d'Ansible.
|
||||
- Déplacement des listes de paquets communes dans les defaults des rôles `common_packages` et `hardening_packages`.
|
||||
- Déplacement des variables de construction du template dans `inventories/lab/group_vars/modeles_vm.yml`.
|
||||
- Déplacement des variables de conformité Debian dans `inventories/production/group_vars/serveurs_debian.yml`.
|
||||
- Ciblage du playbook de durcissement sur `serveurs_debian` au lieu de `all`.
|
||||
- Renforcement du playbook de vérification du modèle doré.
|
||||
|
||||
### Corrigé à appliquer
|
||||
- Vérifier tous les rôles contenant `notify`.
|
||||
- Ajouter les handlers manquants dans les rôles concernés, notamment :
|
||||
### Corrigé
|
||||
- Vérification des rôles contenant `notify`.
|
||||
- Confirmation des handlers SSH requis dans les rôles concernés :
|
||||
- `roles/ssh_baseline/handlers/main.yml` ;
|
||||
- `roles/ssh_hardening/handlers/main.yml`.
|
||||
|
||||
### À valider
|
||||
- Vérifier que chaque `notify` pointe vers un handler existant.
|
||||
- Relancer le playbook de préparation du template.
|
||||
- Lancer le playbook de vérification.
|
||||
- Ne pas lancer le nettoyage final avant validation complète de la VM.
|
||||
### Validé
|
||||
- Vérification que chaque `notify` du template Debian 13 Proxmox pointe vers un handler existant.
|
||||
- Relance du playbook de préparation du template Debian 13 Proxmox.
|
||||
- Nettoyage final non lancé.
|
||||
|
|
|
|||
358
Makefile
Normal file
358
Makefile
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
SHELL := /usr/bin/env bash
|
||||
|
||||
export ANSIBLE_HOME ?= $(CURDIR)/.ansible
|
||||
export ANSIBLE_LOCAL_TEMP ?= $(CURDIR)/.ansible/tmp
|
||||
export ANSIBLE_SSH_CONTROL_PATH_DIR ?= $(CURDIR)/.ansible/cp
|
||||
export ANSIBLE_SSH_ARGS ?= -F /dev/null -o ControlMaster=no
|
||||
|
||||
INVENTAIRE_LAB ?= inventories/lab/hosts.yml
|
||||
INVENTAIRE_PRODUCTION ?= inventories/production/hosts.yml
|
||||
FICHIER_INVENTAIRE ?= $(INVENTAIRE_PRODUCTION)
|
||||
GROUPE_MODELE ?= modeles_vm
|
||||
GROUPE_DEBIAN ?= serveurs_debian
|
||||
LIMITE ?= $(GROUPE_DEBIAN)
|
||||
HOTE ?=
|
||||
ADRESSE_IP ?=
|
||||
GROUPES ?= $(GROUPE_DEBIAN)
|
||||
GROUPE ?= $(GROUPE_DEBIAN)
|
||||
UTILISATEUR_ANSIBLE ?= ansible
|
||||
VMID_MODELE ?=
|
||||
VMID ?=
|
||||
NOEUD_PROXMOX ?=
|
||||
STOCKAGE_PROXMOX ?=
|
||||
FORMAT_DISQUE ?=
|
||||
TAILLE_DISQUE ?=
|
||||
DISQUE_PROXMOX ?=
|
||||
CIDR ?= 24
|
||||
PASSERELLE ?=
|
||||
DNS ?=
|
||||
DHCP ?= false
|
||||
CIUSER ?=
|
||||
CLE_SSH_PUBLIQUE ?=
|
||||
PONT_PROXMOX ?=
|
||||
VLAN ?=
|
||||
DEMARRER ?=
|
||||
CLONE_COMPLET ?=
|
||||
|
||||
CONFIRMER ?= false
|
||||
VERIFICATION ?= false
|
||||
DIFF ?= false
|
||||
ETIQUETTES ?=
|
||||
SAUTER_ETIQUETTES ?=
|
||||
VARIABLES ?=
|
||||
|
||||
OPTIONS_PLAYBOOK :=
|
||||
ifneq ($(LIMITE),)
|
||||
OPTIONS_PLAYBOOK += --limit $(LIMITE)
|
||||
endif
|
||||
ifeq ($(VERIFICATION),true)
|
||||
OPTIONS_PLAYBOOK += --check
|
||||
endif
|
||||
ifeq ($(DIFF),true)
|
||||
OPTIONS_PLAYBOOK += --diff
|
||||
endif
|
||||
ifneq ($(ETIQUETTES),)
|
||||
OPTIONS_PLAYBOOK += --tags $(ETIQUETTES)
|
||||
endif
|
||||
ifneq ($(SAUTER_ETIQUETTES),)
|
||||
OPTIONS_PLAYBOOK += --skip-tags $(SAUTER_ETIQUETTES)
|
||||
endif
|
||||
ifneq ($(VARIABLES),)
|
||||
OPTIONS_PLAYBOOK += -e $(VARIABLES)
|
||||
endif
|
||||
|
||||
PLAYBOOK_PREPARER_MODELE := playbooks/modeles_vm/debian13_proxmox_preparer.yml
|
||||
PLAYBOOK_VERIFIER_MODELE := playbooks/modeles_vm/debian13_proxmox_verifier.yml
|
||||
PLAYBOOK_NETTOYER_MODELE := playbooks/modeles_vm/debian13_proxmox_nettoyer.yml
|
||||
PLAYBOOK_VERIFIER_HOTE := playbooks/maintenance/verifier_hote_debian.yml
|
||||
PLAYBOOK_PROXMOX_CLONER_VM := playbooks/proxmox/cloner_vm_debian.yml
|
||||
DOSSIER_PLAYBOOKS_GROUPES := playbooks/groupes
|
||||
|
||||
.DEFAULT_GOAL := aide
|
||||
|
||||
.PHONY: ansible-runtime
|
||||
ansible-runtime:
|
||||
@mkdir -p "$(ANSIBLE_LOCAL_TEMP)"
|
||||
@mkdir -p "$(ANSIBLE_SSH_CONTROL_PATH_DIR)"
|
||||
|
||||
.PHONY: aide
|
||||
aide:
|
||||
@printf '%s\n' 'Set-OPS'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'VM'
|
||||
@printf '%s\n' ' Creer une VM complete:'
|
||||
@printf '%s\n' ' make creer-vm \'
|
||||
@printf '%s\n' ' HOTE=web-01 \'
|
||||
@printf '%s\n' ' VMID=101 \'
|
||||
@printf '%s\n' ' VLAN=12 \'
|
||||
@printf '%s\n' ' ADRESSE_IP=192.168.12.101 \'
|
||||
@printf '%s\n' ' PASSERELLE=192.168.12.1 \'
|
||||
@printf '%s\n' ' GROUPES="serveurs_debian serveurs_durcis"'
|
||||
@printf '%s\n' ' Cloner seulement, sans inventaire:'
|
||||
@printf '%s\n' ' make cloner-vm HOTE=web-01 VMID=101 VLAN=12 ADRESSE_IP=192.168.12.101 PASSERELLE=192.168.12.1'
|
||||
@printf '%s\n' ' Configurer Proxmox et le Vault API:'
|
||||
@printf '%s\n' ' make config'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'Hotes'
|
||||
@printf '%s\n' ' Remettre un hote en conformite selon ses groupes:'
|
||||
@printf '%s\n' ' make deployer HOTE=web-01'
|
||||
@printf '%s\n' ' Afficher un hote:'
|
||||
@printf '%s\n' ' make hote-afficher HOTE=web-01'
|
||||
@printf '%s\n' ' Ajouter un hote existant:'
|
||||
@printf '%s\n' ' make hote-ajouter HOTE=web-01 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"'
|
||||
@printf '%s\n' ' Modifier ses groupes:'
|
||||
@printf '%s\n' ' make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"'
|
||||
@printf '%s\n' ' Diagnostiquer:'
|
||||
@printf '%s\n' ' make verifier-hote LIMITE=web-01'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'Groupes'
|
||||
@printf '%s\n' ' Appliquer un groupe complet:'
|
||||
@printf '%s\n' ' make deployer-groupe GROUPE=serveurs_debian'
|
||||
@printf '%s\n' ' Rejouer directement le playbook d un groupe:'
|
||||
@printf '%s\n' ' make appliquer GROUPE=serveurs_debian'
|
||||
@printf '%s\n' ' Convention:'
|
||||
@printf '%s\n' ' groupe serveurs_debian -> playbooks/groupes/serveurs_debian.yml'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'Inventaires'
|
||||
@printf '%s\n' ' Graphe de production:'
|
||||
@printf '%s\n' ' make inventaire'
|
||||
@printf '%s\n' ' Graphe explicite:'
|
||||
@printf '%s\n' ' make inventaire-graphe FICHIER_INVENTAIRE=inventories/production/hosts.yml'
|
||||
@printf '%s\n' ' Verifier les inventaires:'
|
||||
@printf '%s\n' ' make inventaire-verifier'
|
||||
@printf '%s\n' ' Lister les donnees brutes:'
|
||||
@printf '%s\n' ' make inventaire-lister'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'Modele Debian 13 Proxmox'
|
||||
@printf '%s\n' ' Construire et verifier:'
|
||||
@printf '%s\n' ' make preparer-modele'
|
||||
@printf '%s\n' ' make verifier-modele'
|
||||
@printf '%s\n' ' Nettoyage final protege:'
|
||||
@printf '%s\n' ' make nettoyer-modele CONFIRMER=true'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'Validation'
|
||||
@printf '%s\n' ' make syntaxe'
|
||||
@printf '%s\n' ' make lint'
|
||||
@printf '%s\n' ' make verifier'
|
||||
@printf '%s\n' ''
|
||||
@printf '%s\n' 'Variables frequentes'
|
||||
@printf '%s\n' ' HOTE=web-01 GROUPE=serveurs_debian GROUPES="serveurs_debian serveurs_durcis"'
|
||||
@printf '%s\n' ' VMID=101 VLAN=12 ADRESSE_IP=192.168.12.101 PASSERELLE=192.168.12.1'
|
||||
@printf '%s\n' ' FICHIER_INVENTAIRE=inventories/production/hosts.yml CONFIRMER=true'
|
||||
|
||||
.PHONY: lint
|
||||
lint: ansible-runtime
|
||||
ansible-lint
|
||||
|
||||
.PHONY: syntaxe syntaxe-modele syntaxe-nettoyage syntaxe-verification-modele syntaxe-verification-hote syntaxe-groupes syntaxe-proxmox
|
||||
syntaxe: syntaxe-modele syntaxe-verification-modele syntaxe-nettoyage syntaxe-verification-hote syntaxe-groupes syntaxe-proxmox
|
||||
|
||||
syntaxe-modele: ansible-runtime
|
||||
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_PREPARER_MODELE) --syntax-check
|
||||
|
||||
syntaxe-verification-modele: ansible-runtime
|
||||
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_VERIFIER_MODELE) --syntax-check
|
||||
|
||||
syntaxe-nettoyage: ansible-runtime
|
||||
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_NETTOYER_MODELE) --syntax-check
|
||||
|
||||
syntaxe-verification-hote: ansible-runtime
|
||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) $(PLAYBOOK_VERIFIER_HOTE) --syntax-check
|
||||
|
||||
syntaxe-groupes: ansible-runtime
|
||||
@for playbook in $(DOSSIER_PLAYBOOKS_GROUPES)/*.yml; do \
|
||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --syntax-check; \
|
||||
done
|
||||
|
||||
syntaxe-proxmox: ansible-runtime
|
||||
ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) --syntax-check
|
||||
|
||||
.PHONY: verifier
|
||||
verifier: lint syntaxe
|
||||
|
||||
.PHONY: inventaire hote-ajouter hote-groupes hote-afficher appliquer deployer deployer-groupe cloner-vm creer-vm config inventaire-verifier inventaire-lister inventaire-graphe inventaire-hote inventaire-lab inventaire-production
|
||||
inventaire: inventaire-production
|
||||
|
||||
config:
|
||||
python3 scripts/config_proxmox.py
|
||||
|
||||
hote-ajouter: ansible-runtime
|
||||
@if [[ -z "$(HOTE)" || -z "$(ADRESSE_IP)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote ADRESSE_IP=adresse_ip.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) ajouter --hote $(HOTE) --adresse-ip $(ADRESSE_IP) --utilisateur-ansible $(UTILISATEUR_ANSIBLE) --groupes "$(GROUPES)"
|
||||
$(MAKE) inventaire-verifier
|
||||
$(MAKE) hote-afficher HOTE="$(HOTE)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
||||
|
||||
hote-groupes: ansible-runtime
|
||||
@if [[ -z "$(HOTE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote GROUPES="groupe1 groupe2".'; \
|
||||
exit 2; \
|
||||
fi
|
||||
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) definir-groupes --hote $(HOTE) --groupes "$(GROUPES)"
|
||||
$(MAKE) inventaire-verifier
|
||||
$(MAKE) hote-afficher HOTE="$(HOTE)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
||||
|
||||
hote-afficher: ansible-runtime
|
||||
@if [[ -z "$(HOTE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) afficher --hote $(HOTE)
|
||||
|
||||
appliquer: ansible-runtime
|
||||
@if [[ -z "$(GROUPE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec GROUPE=nom_groupe.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
@if [[ ! -f "$(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml" ]]; then \
|
||||
printf '%s\n' 'Refus: aucun playbook pour ce groupe: $(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml'; \
|
||||
exit 2; \
|
||||
fi
|
||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml" --limit $(GROUPE)
|
||||
|
||||
deployer:
|
||||
@if [[ -z "$(HOTE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
$(MAKE) _verifier-acces-hote LIMITE="$(HOTE)"
|
||||
$(MAKE) _verifier-privileges-hote LIMITE="$(HOTE)"
|
||||
@playbooks="$$(python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) playbooks --hote $(HOTE) --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES))" || exit $$?; \
|
||||
if [[ -z "$$playbooks" ]]; then \
|
||||
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
|
||||
exit 2; \
|
||||
fi; \
|
||||
for playbook in $$playbooks; do \
|
||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)"; \
|
||||
done
|
||||
$(MAKE) verifier-hote LIMITE="$(HOTE)"
|
||||
|
||||
deployer-groupe:
|
||||
@if [[ -z "$(GROUPE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec GROUPE=nom_groupe.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
$(MAKE) appliquer GROUPE="$(GROUPE)"
|
||||
|
||||
cloner-vm: ansible-runtime
|
||||
@if [[ -z "$(HOTE)" || -z "$(VMID)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec HOTE=nom VMID=id_clone.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
@if [[ -z "$(VLAN)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec VLAN=id_vlan.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
@if ! [[ "$(VLAN)" =~ ^[0-9]+$$ ]] || (( 10#$(VLAN) < 1 || 10#$(VLAN) > 4094 )); then \
|
||||
printf '%s\n' 'Refus: VLAN doit etre un nombre entre 1 et 4094.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
@if [[ -n "$(CLE_SSH_PUBLIQUE)" && ! -f "$(CLE_SSH_PUBLIQUE)" ]]; then \
|
||||
printf '%s\n' 'Refus: cle publique SSH introuvable: $(CLE_SSH_PUBLIQUE)'; \
|
||||
exit 2; \
|
||||
fi
|
||||
@if [[ "$(DHCP)" != "true" && ( -z "$(ADRESSE_IP)" || -z "$(CIDR)" || -z "$(PASSERELLE)" ) ]]; then \
|
||||
printf '%s\n' 'Refus: fournir ADRESSE_IP, CIDR et PASSERELLE, ou utiliser DHCP=true.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
@ipconfig='ip=dhcp'; \
|
||||
if [[ "$(DHCP)" != "true" ]]; then \
|
||||
ipconfig='ip=$(ADRESSE_IP)/$(CIDR),gw=$(PASSERELLE)'; \
|
||||
fi; \
|
||||
extra_vars=( \
|
||||
-e proxmox_clone_nom="$(HOTE)" \
|
||||
-e proxmox_clone_vmid="$(VMID)" \
|
||||
-e proxmox_clone_ipconfig0="$$ipconfig" \
|
||||
); \
|
||||
[[ -n "$(VMID_MODELE)" ]] && extra_vars+=( -e proxmox_clone_vmid_modele="$(VMID_MODELE)" ); \
|
||||
[[ -n "$(NOEUD_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_noeud="$(NOEUD_PROXMOX)" ); \
|
||||
[[ -n "$(STOCKAGE_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_stockage="$(STOCKAGE_PROXMOX)" ); \
|
||||
[[ -n "$(FORMAT_DISQUE)" ]] && extra_vars+=( -e proxmox_clone_format="$(FORMAT_DISQUE)" ); \
|
||||
[[ -n "$(CLONE_COMPLET)" ]] && extra_vars+=( -e proxmox_clone_complet="$(CLONE_COMPLET)" ); \
|
||||
[[ -n "$(TAILLE_DISQUE)" ]] && extra_vars+=( -e proxmox_clone_taille_disque="$(TAILLE_DISQUE)" ); \
|
||||
[[ -n "$(DISQUE_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_disque="$(DISQUE_PROXMOX)" ); \
|
||||
[[ -n "$(DNS)" ]] && extra_vars+=( -e proxmox_clone_dns="$(DNS)" ); \
|
||||
[[ -n "$(CIUSER)" ]] && extra_vars+=( -e proxmox_clone_ciuser="$(CIUSER)" ); \
|
||||
[[ -n "$(CLE_SSH_PUBLIQUE)" ]] && extra_vars+=( -e proxmox_clone_cle_publique_fichier="$(CLE_SSH_PUBLIQUE)" ); \
|
||||
[[ -n "$(PONT_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_pont="$(PONT_PROXMOX)" ); \
|
||||
extra_vars+=( -e proxmox_clone_vlan="$(VLAN)" ); \
|
||||
[[ -n "$(DEMARRER)" ]] && extra_vars+=( -e proxmox_clone_demarrer="$(DEMARRER)" ); \
|
||||
vault_args=(); \
|
||||
vault_file="inventories/lab/group_vars/proxmox.vault.yml"; \
|
||||
if [[ -f "$$vault_file" ]]; then \
|
||||
read -r premiere_ligne < "$$vault_file" || true; \
|
||||
case "$$premiere_ligne" in \
|
||||
'$$ANSIBLE_VAULT'*) \
|
||||
if [[ -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
|
||||
vault_args+=( --ask-vault-pass ); \
|
||||
fi; \
|
||||
;; \
|
||||
esac; \
|
||||
fi; \
|
||||
ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) "$${vault_args[@]}" "$${extra_vars[@]}"
|
||||
|
||||
creer-vm:
|
||||
@if [[ -z "$(ADRESSE_IP)" ]]; then \
|
||||
printf '%s\n' 'Refus: creer-vm doit recevoir ADRESSE_IP pour inscrire l hote dans l inventaire.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
$(MAKE) cloner-vm HOTE="$(HOTE)" VMID_MODELE="$(VMID_MODELE)" VMID="$(VMID)" NOEUD_PROXMOX="$(NOEUD_PROXMOX)" STOCKAGE_PROXMOX="$(STOCKAGE_PROXMOX)" FORMAT_DISQUE="$(FORMAT_DISQUE)" TAILLE_DISQUE="$(TAILLE_DISQUE)" DISQUE_PROXMOX="$(DISQUE_PROXMOX)" ADRESSE_IP="$(ADRESSE_IP)" CIDR="$(CIDR)" PASSERELLE="$(PASSERELLE)" DNS="$(DNS)" DHCP="$(DHCP)" CIUSER="$(CIUSER)" CLE_SSH_PUBLIQUE="$(CLE_SSH_PUBLIQUE)" PONT_PROXMOX="$(PONT_PROXMOX)" VLAN="$(VLAN)" DEMARRER="$(DEMARRER)" CLONE_COMPLET="$(CLONE_COMPLET)"
|
||||
$(MAKE) hote-ajouter HOTE="$(HOTE)" ADRESSE_IP="$(ADRESSE_IP)" GROUPES="$(GROUPES)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
||||
|
||||
inventaire-verifier: ansible-runtime
|
||||
ansible-inventory -i $(INVENTAIRE_LAB) --list > /dev/null
|
||||
ansible-inventory -i $(INVENTAIRE_PRODUCTION) --list > /dev/null
|
||||
|
||||
inventaire-lister: ansible-runtime
|
||||
ansible-inventory -i $(FICHIER_INVENTAIRE) --list
|
||||
|
||||
inventaire-graphe: ansible-runtime
|
||||
ansible-inventory -i $(FICHIER_INVENTAIRE) --graph
|
||||
|
||||
inventaire-hote: ansible-runtime
|
||||
@if [[ -z "$(HOTE)" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
ansible-inventory -i $(FICHIER_INVENTAIRE) --host $(HOTE)
|
||||
|
||||
inventaire-lab:
|
||||
$(MAKE) inventaire-graphe FICHIER_INVENTAIRE="$(INVENTAIRE_LAB)"
|
||||
|
||||
inventaire-production:
|
||||
$(MAKE) inventaire-graphe FICHIER_INVENTAIRE="$(INVENTAIRE_PRODUCTION)"
|
||||
|
||||
.PHONY: _verifier-acces-modele _verifier-privileges-modele preparer-modele verifier-modele nettoyer-modele
|
||||
_verifier-acces-modele: ansible-runtime
|
||||
ansible -i $(INVENTAIRE_LAB) $(GROUPE_MODELE) -m ping -e ansible_become=false
|
||||
|
||||
_verifier-privileges-modele: ansible-runtime
|
||||
ansible -i $(INVENTAIRE_LAB) $(GROUPE_MODELE) -b -m command -a "whoami"
|
||||
|
||||
preparer-modele: ansible-runtime _verifier-acces-modele _verifier-privileges-modele
|
||||
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_PREPARER_MODELE)
|
||||
|
||||
verifier-modele: ansible-runtime
|
||||
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_VERIFIER_MODELE)
|
||||
|
||||
nettoyer-modele: ansible-runtime
|
||||
@if [[ "$(CONFIRMER)" != "true" ]]; then \
|
||||
printf '%s\n' 'Refus: relancer avec CONFIRMER=true pour le nettoyage final du modele.'; \
|
||||
exit 2; \
|
||||
fi
|
||||
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_NETTOYER_MODELE) -e template_cleanup_confirm=true
|
||||
|
||||
.PHONY: _verifier-acces-hote _verifier-privileges-hote faits verifier-hote
|
||||
_verifier-acces-hote: ansible-runtime
|
||||
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -m ping -e ansible_become=false
|
||||
|
||||
_verifier-privileges-hote: ansible-runtime
|
||||
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -b -m command -a "whoami"
|
||||
|
||||
faits: ansible-runtime
|
||||
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -m setup -a "filter=ansible_distribution*"
|
||||
|
||||
verifier-hote: ansible-runtime
|
||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) $(PLAYBOOK_VERIFIER_HOTE) $(OPTIONS_PLAYBOOK)
|
||||
78
README.md
78
README.md
|
|
@ -5,8 +5,7 @@ Dépôt Ansible central pour construire, configurer, maintenir et documenter les
|
|||
## Domaines prévus
|
||||
|
||||
- templates de VM Proxmox ;
|
||||
- socle Debian ;
|
||||
- hardening ;
|
||||
- groupes de conformité ;
|
||||
- maintenance ;
|
||||
- web ;
|
||||
- bases de données ;
|
||||
|
|
@ -20,9 +19,9 @@ Dépôt Ansible central pour construire, configurer, maintenir et documenter les
|
|||
Template Debian 13 Proxmox :
|
||||
|
||||
```text
|
||||
playbooks/vm_templates/debian13_proxmox_prepare.yml
|
||||
playbooks/vm_templates/debian13_proxmox_verify.yml
|
||||
playbooks/vm_templates/debian13_proxmox_cleanup.yml
|
||||
playbooks/modeles_vm/debian13_proxmox_preparer.yml
|
||||
playbooks/modeles_vm/debian13_proxmox_verifier.yml
|
||||
playbooks/modeles_vm/debian13_proxmox_nettoyer.yml
|
||||
```
|
||||
|
||||
## Principe
|
||||
|
|
@ -37,3 +36,72 @@ Les services spécialisés seront installés ensuite sur les clones :
|
|||
- Docker/Podman ;
|
||||
- monitoring complet ;
|
||||
- applications métier.
|
||||
|
||||
## Exploitation courante
|
||||
|
||||
Un `Makefile` fournit les commandes d'exploitation principales.
|
||||
|
||||
Afficher l'aide :
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
Valider le dépôt :
|
||||
|
||||
```bash
|
||||
make verifier
|
||||
```
|
||||
|
||||
Inspecter les inventaires :
|
||||
|
||||
```bash
|
||||
make inventaire
|
||||
make hote-afficher HOTE=web-01
|
||||
```
|
||||
|
||||
Ajouter une VM à l'inventaire production et l'associer aux groupes qui détermineront sa configuration :
|
||||
|
||||
```bash
|
||||
make hote-ajouter HOTE=web-01 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"
|
||||
make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"
|
||||
```
|
||||
|
||||
Chaque groupe opérationnel doit avoir son playbook homonyme dans `playbooks/groupes/`.
|
||||
|
||||
La conformité normale des VM passe par ces playbooks de groupes. Les rôles de socle et de durcissement sont appliqués par `serveurs_debian` et `serveurs_durcis`, pas par des playbooks de couches séparés.
|
||||
|
||||
Créer un clone depuis le modèle Debian 13 via l'API Proxmox :
|
||||
|
||||
```bash
|
||||
make config
|
||||
make creer-vm HOTE=web-01 VMID=101 VLAN=12 ADRESSE_IP=192.168.12.101 PASSERELLE=192.168.12.1 GROUPES="serveurs_debian serveurs_durcis"
|
||||
make deployer HOTE=web-01
|
||||
```
|
||||
|
||||
Les valeurs Cloud-Init communes déjà présentes dans le modèle Proxmox sont héritées par les clones.
|
||||
|
||||
Préparer le golden template Debian 13 Proxmox :
|
||||
|
||||
```bash
|
||||
make preparer-modele
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
Le nettoyage final du template est protégé :
|
||||
|
||||
```bash
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Déployer ou remettre en conformité une VM Debian clonée depuis le template :
|
||||
|
||||
```bash
|
||||
make deployer HOTE=web-01
|
||||
```
|
||||
|
||||
Déployer ou remettre en conformité un groupe :
|
||||
|
||||
```bash
|
||||
make deployer-groupe GROUPE=serveurs_debian
|
||||
```
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ Il ne doit pas contenir :
|
|||
Le playbook :
|
||||
|
||||
```text
|
||||
playbooks/vm_templates/debian13_proxmox_prepare.yml
|
||||
playbooks/modeles_vm/debian13_proxmox_preparer.yml
|
||||
```
|
||||
|
||||
a échoué sur un handler manquant :
|
||||
|
|
@ -91,7 +91,7 @@ Rôles à vérifier en priorité :
|
|||
|
||||
```text
|
||||
roles/ssh_baseline/
|
||||
roles/ssh_hardening/
|
||||
roles/ssh_durcissement/
|
||||
```
|
||||
|
||||
Chaque rôle utilisant `notify` doit avoir son propre fichier :
|
||||
|
|
@ -110,23 +110,23 @@ Créer ou corriger :
|
|||
|
||||
```text
|
||||
roles/ssh_baseline/handlers/main.yml
|
||||
roles/ssh_hardening/handlers/main.yml
|
||||
roles/ssh_durcissement/handlers/main.yml
|
||||
```
|
||||
|
||||
Contenu attendu :
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Validate and reload ssh
|
||||
block:
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
listen: Validate and reload ssh
|
||||
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
listen: Validate and reload ssh
|
||||
```
|
||||
|
||||
Puis vérifier tous les `notify` :
|
||||
|
|
@ -143,19 +143,19 @@ find roles -path '*/handlers/main.yml' -print
|
|||
Après correction :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_prepare.yml --syntax-check
|
||||
make syntax-template
|
||||
```
|
||||
|
||||
Puis relancer :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_prepare.yml
|
||||
make preparer-modele
|
||||
```
|
||||
|
||||
Puis vérifier :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_verify.yml
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -165,7 +165,7 @@ ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_pr
|
|||
Ne lancer le nettoyage final que lorsque la VM est validée :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_cleanup.yml -e confirm_template_cleanup=true
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Ensuite seulement :
|
||||
|
|
|
|||
|
|
@ -5,14 +5,59 @@ Set-OPS est un dépôt global d'exploitation.
|
|||
## Domaines
|
||||
|
||||
```text
|
||||
baseline : socle commun Debian
|
||||
hardening : sécurité
|
||||
socle : socle commun Debian
|
||||
durcissement : sécurité
|
||||
maintenance : mises à jour
|
||||
monitoring : supervision
|
||||
proxmox : hyperviseurs
|
||||
vm_templates : modèles de VM
|
||||
modeles_vm : modèles de VM
|
||||
web : services web
|
||||
database : bases de données
|
||||
backup : sauvegardes
|
||||
applications : services applicatifs
|
||||
```
|
||||
|
||||
## Rôles Ansible
|
||||
|
||||
Les rôles actifs sont conservés directement sous `roles/`.
|
||||
|
||||
Les sous-arborescences catégorielles de rôles ne doivent pas dupliquer un rôle actif. Un nouveau rôle doit être ajouté seulement lorsqu'un besoin opérationnel réel existe.
|
||||
|
||||
Les valeurs communes aux rôles doivent rester dans les defaults des rôles quand elles sont valables pour tous les environnements. Les inventaires ne doivent contenir que les politiques propres à leur contexte.
|
||||
|
||||
## Groupes et playbooks
|
||||
|
||||
Les groupes opérationnels de l'inventaire doivent correspondre à un playbook homonyme :
|
||||
|
||||
```text
|
||||
inventories/production/hosts.yml
|
||||
serveurs_debian
|
||||
|
||||
playbooks/groupes/serveurs_debian.yml
|
||||
```
|
||||
|
||||
Cette relation rend l'exploitation lisible :
|
||||
|
||||
```text
|
||||
appartenance à un groupe
|
||||
→ playbook correspondant
|
||||
→ état voulu appliqué par Ansible
|
||||
```
|
||||
|
||||
Les groupes structurels ou vides peuvent exister, mais un groupe assigné à une VM de production doit avoir un playbook s'il exprime une intention de configuration.
|
||||
|
||||
## Cycle de vie VM
|
||||
|
||||
Le cycle de vie des VM Chezlepro est documenté dans :
|
||||
|
||||
```text
|
||||
docs/vm-lifecycle.md
|
||||
```
|
||||
|
||||
## Intégrations futures
|
||||
|
||||
Les intégrations transversales des VM sont documentées dans :
|
||||
|
||||
```text
|
||||
docs/integrations-vm.md
|
||||
```
|
||||
|
|
|
|||
43
docs/integrations-vm.md
Normal file
43
docs/integrations-vm.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Intégrations futures des VM
|
||||
|
||||
Les serveurs centraux ne sont pas encore en place. Les rôles d'intégration doivent donc rester optionnels et désactivés par défaut tant que leurs dépendances n'existent pas.
|
||||
|
||||
## Séparation
|
||||
|
||||
```text
|
||||
Template Debian 13 Proxmox : socle commun minimal
|
||||
Cloud-init : identité initiale du clone
|
||||
Set-OPS + Ansible : configuration réelle du serveur
|
||||
```
|
||||
|
||||
Le template ne doit pas être modifié pour chaque nouveau serveur. Les intégrations sont appliquées après clonage selon les groupes d'inventaire et les playbooks dédiés.
|
||||
|
||||
## Domaines prévus
|
||||
|
||||
```text
|
||||
DNS interne : PowerDNS, résolveurs, enregistrements applicatifs
|
||||
PKI interne : step-ca, ACME, confiance CA locale
|
||||
Identité : LDAP, SSSD, Keycloak/OIDC selon le besoin
|
||||
Supervision : Icinga2 agent ou checks distants
|
||||
Métriques : Prometheus node_exporter
|
||||
Visualisation : Grafana côté plateforme centrale
|
||||
```
|
||||
|
||||
## Principes Ansible
|
||||
|
||||
- Chaque rôle d'intégration doit être désactivé par défaut.
|
||||
- Chaque variable de rôle doit utiliser le préfixe du rôle.
|
||||
- Les secrets ne doivent jamais être stockés en clair dans le dépôt.
|
||||
- Un rôle ne doit pas échouer si son intégration est désactivée.
|
||||
- Les playbooks applicatifs doivent être appliqués sur les clones, pas sur le template.
|
||||
- Les opérations pouvant couper l'accès réseau ou SSH doivent exiger une confirmation explicite.
|
||||
|
||||
## Ordre logique de construction
|
||||
|
||||
1. DNS interne.
|
||||
2. PKI interne et confiance CA.
|
||||
3. Identité système et applicative.
|
||||
4. Supervision et métriques.
|
||||
5. Visualisation et alerting.
|
||||
|
||||
Cet ordre reste indicatif. Les rôles doivent rester découplés pour permettre une adoption progressive.
|
||||
312
docs/modeles_vm/debian13-proxmox.md
Normal file
312
docs/modeles_vm/debian13-proxmox.md
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
# Golden template Debian 13 Proxmox
|
||||
|
||||
## Objet
|
||||
|
||||
Ce document justifie les ajouts appliqués par Set-OPS à une installation Debian 13 minimale pour obtenir le golden template Proxmox de Chezlepro.
|
||||
|
||||
Le template doit rester un socle commun, clonable et sécuritaire. Il ne doit pas devenir un serveur applicatif ni intégrer des dépendances propres à un rôle final.
|
||||
|
||||
## État de départ
|
||||
|
||||
L'état de départ attendu est décrit dans :
|
||||
|
||||
```text
|
||||
docs/procedure-template-debian13-proxmox.md
|
||||
```
|
||||
|
||||
Résumé :
|
||||
|
||||
```text
|
||||
Debian 13 minimal
|
||||
aucun environnement graphique
|
||||
SSH installé
|
||||
partition EFI
|
||||
partition racine ext4
|
||||
pas de LVM
|
||||
pas de swap
|
||||
disque agrandissable
|
||||
CloudInit Drive Proxmox présent
|
||||
ciuser et clé SSH injectés par Cloud-Init
|
||||
aucun secret
|
||||
aucune donnée propre à un clone final
|
||||
```
|
||||
|
||||
## Runbook du playbook prepare
|
||||
|
||||
### Objectif
|
||||
|
||||
Le playbook `debian13_proxmox_preparer.yml` transforme une VM Debian 13 minimale en socle golden template Chezlepro.
|
||||
|
||||
Il installe les composants communs, configure l'accès Ansible, applique le durcissement template-safe et prépare la VM pour cloud-init.
|
||||
|
||||
Il ne fait pas le nettoyage final avant conversion en template.
|
||||
|
||||
### Prérequis côté VM
|
||||
|
||||
Avant de lancer le playbook, la VM doit respecter ces conditions :
|
||||
|
||||
```text
|
||||
Debian installé et démarré
|
||||
réseau fonctionnel
|
||||
SSH joignable depuis le poste Ansible
|
||||
CloudInit Drive présent côté Proxmox
|
||||
utilisateur Ansible initial injecté par Cloud-Init
|
||||
clé publique SSH injectée par Cloud-Init
|
||||
sudo ou accès root possible
|
||||
APT fonctionnel
|
||||
aucun rôle applicatif déjà installé
|
||||
VM destinée à devenir un template, pas un serveur de production
|
||||
```
|
||||
|
||||
Vérifications utiles depuis le poste Ansible :
|
||||
|
||||
```bash
|
||||
ssh ansible@192.168.12.99
|
||||
ansible -i inventories/lab/hosts.yml modeles_vm -m ping
|
||||
ansible -i inventories/lab/hosts.yml modeles_vm -m setup
|
||||
```
|
||||
|
||||
Adapter l'utilisateur et l'adresse IP selon `inventories/lab/hosts.yml`.
|
||||
|
||||
### Prérequis côté dépôt
|
||||
|
||||
Avant l'exécution :
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
make syntax-template
|
||||
```
|
||||
|
||||
L'inventaire doit contenir la VM dans le groupe :
|
||||
|
||||
```text
|
||||
modeles_vm
|
||||
```
|
||||
|
||||
Les variables du template sont dans :
|
||||
|
||||
```text
|
||||
inventories/lab/group_vars/modeles_vm.yml
|
||||
```
|
||||
|
||||
### Commande de préparation
|
||||
|
||||
Lancement lorsque l'accès par clé et sudo sont fonctionnels :
|
||||
|
||||
```bash
|
||||
make preparer-modele
|
||||
```
|
||||
|
||||
Le flux `make` suppose que SSH par clé et sudo NOPASSWD sont déjà fonctionnels pour `ansible`.
|
||||
|
||||
### Déroulement attendu
|
||||
|
||||
Le playbook :
|
||||
|
||||
1. vérifie que la cible est Debian ;
|
||||
2. avertit si la version majeure n'est pas Debian 13 ;
|
||||
3. installe les paquets communs ;
|
||||
4. active `qemu-guest-agent` ;
|
||||
5. installe et configure `cloud-init` ;
|
||||
6. configure le compte technique Ansible ;
|
||||
7. active la synchronisation du temps ;
|
||||
8. configure SSH ;
|
||||
9. applique le durcissement template-safe ;
|
||||
10. prépare nftables sans l'activer ;
|
||||
11. affiche le statut cloud-init.
|
||||
|
||||
### Résultat attendu
|
||||
|
||||
Une première exécution peut changer la VM.
|
||||
|
||||
Une relance sur une VM déjà préparée doit idéalement finir avec :
|
||||
|
||||
```text
|
||||
failed=0
|
||||
changed=0
|
||||
```
|
||||
|
||||
Un résultat `changed=0` à la relance est le signal que le playbook est idempotent dans l'état actuel.
|
||||
|
||||
### Erreurs fréquentes
|
||||
|
||||
| Symptôme | Cause probable | Action |
|
||||
| --- | --- | --- |
|
||||
| `UNREACHABLE` | IP, SSH, utilisateur ou clé SSH incorrecte. | Vérifier `inventories/lab/hosts.yml`, cloud-init et tester `ssh`. |
|
||||
| échec `become` | Sudo NOPASSWD absent ou utilisateur non autorisé. | Corriger l'accès sudo initial, puis relancer `make preparer-modele`. |
|
||||
| échec APT | DNS, passerelle, miroir Debian ou verrou APT. | Vérifier réseau, DNS et processus APT en cours. |
|
||||
| erreur handler SSH | Handler manquant ou nom `notify` incohérent. | Vérifier les handlers du rôle SSH avant de relancer. |
|
||||
| avertissement Debian non 13 | La cible n'est pas Debian 13. | Ne pas convertir en template Debian 13 sans justification. |
|
||||
|
||||
### Point d'arrêt
|
||||
|
||||
Après `prepare`, ne pas convertir immédiatement la VM en template.
|
||||
|
||||
Exécuter d'abord :
|
||||
|
||||
```bash
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
Le playbook de vérification contrôle notamment Debian 13, les paquets requis, l'absence de swap actif, l'absence d'environnement graphique, les services de base, `nftables` désactivé, sudo NOPASSWD et le durcissement SSH effectif.
|
||||
|
||||
Le nettoyage final se lance seulement après validation complète :
|
||||
|
||||
```bash
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Ne pas lancer le cleanup final sur une VM encore en diagnostic ou sur un serveur final.
|
||||
|
||||
## Préparation Ansible
|
||||
|
||||
Le playbook applique les rôles suivants :
|
||||
|
||||
```text
|
||||
common_packages
|
||||
qemu_guest_agent
|
||||
cloud_init
|
||||
sudo_ansible
|
||||
chrony
|
||||
ssh_baseline
|
||||
systemd_ssh_auto
|
||||
motd
|
||||
hardening_packages
|
||||
sysctl_durcissement
|
||||
core_dumps
|
||||
unattended_upgrades
|
||||
apparmor
|
||||
auditd
|
||||
fail2ban_ssh
|
||||
journald
|
||||
ssh_durcissement
|
||||
nftables_socle
|
||||
```
|
||||
|
||||
## Justification des ajouts
|
||||
|
||||
| Rôle | Ajout ou changement | Justification | Limite volontaire |
|
||||
| --- | --- | --- | --- |
|
||||
| `common_packages` | Installe les outils système et diagnostic de base. | Permet l'administration locale, le dépannage réseau, l'exécution fiable d'Ansible et les opérations de maintenance courantes. | Aucun service applicatif spécialisé n'est installé. |
|
||||
| `qemu_guest_agent` | Installe et active `qemu-guest-agent`. | Donne à Proxmox une visibilité propre sur l'état de la VM et facilite les opérations d'arrêt, IP reporting et gestion depuis l'hyperviseur. | Ne remplace pas la supervision applicative. |
|
||||
| `cloud_init` | Installe `cloud-init` et `cloud-guest-utils`, puis active les services cloud-init. | Donne au clone son identité initiale : hostname, utilisateur, clé SSH, IP, DNS et agrandissement de disque. | Cloud-init ne sert pas à configurer les applications. |
|
||||
| `sudo_ansible` | Crée ou maintient le compte technique et son sudo NOPASSWD. | Permet l'administration automatisée par Ansible sans stocker de mot de passe dans le dépôt. | Ce compte doit rester technique et contrôlé. |
|
||||
| `chrony` | Installe et active la synchronisation du temps. | Un temps correct est nécessaire pour TLS, journaux, audit, Kerberos/LDAP futur, monitoring et corrélation d'incidents. | La source NTP définitive peut être ajustée plus tard par inventaire. |
|
||||
| `ssh_baseline` | Pose la base SSH : root interdit, clé publique obligatoire, mot de passe désactivé. | Force un accès administrateur par clé dès la construction du template. | Cloud-init doit avoir injecté le `ciuser` et sa clé avant Ansible. |
|
||||
| `systemd_ssh_auto` | Désactive le comportement `systemd.ssh_auto` si demandé. | Évite une exposition SSH automatique via mécanismes transitoires non désirés dans un template serveur. | Ne remplace pas la politique SSH principale. |
|
||||
| `motd` | Déploie un message d'accueil sobre. | Identifie le contexte Chezlepro et rappelle que la machine est gérée par Set-OPS. | Ne doit pas contenir d'information sensible. |
|
||||
| `hardening_packages` | Installe les paquets de sécurité communs. | Fournit les briques nécessaires au durcissement sans configurer de service applicatif. | Les politiques strictes par rôle restent appliquées sur les clones. |
|
||||
| `sysctl_durcissement` | Applique des paramètres noyau de sécurité. | Réduit des comportements réseau et noyau risqués avec des réglages standards pour serveur Linux. | Les réglages spécifiques à une charge de travail peuvent être ajustés par rôle. |
|
||||
| `core_dumps` | Désactive les core dumps. | Limite le risque de fuite de secrets ou données sensibles dans des dumps mémoire. | Un serveur de debug peut réactiver un comportement adapté hors template. |
|
||||
| `unattended_upgrades` | Configure les mises à jour automatiques de sécurité. | Réduit l'exposition aux vulnérabilités connues sur les clones qui restent proches du socle. | Les redémarrages automatiques restent désactivés par défaut. |
|
||||
| `apparmor` | Installe et active AppArmor. | Ajoute une couche de confinement standard Debian avec faible coût opérationnel. | Les profils applicatifs spécifiques seront gérés par les rôles applicatifs. |
|
||||
| `auditd` | Installe auditd et des règles de base. | Fournit une trace système utile pour exploitation, investigation et conformité minimale. | Les règles lourdes ou propres à une application ne vont pas dans le template. |
|
||||
| `fail2ban_ssh` | Active une protection SSH simple contre les essais répétés. | Réduit le bruit et les attaques opportunistes sur SSH sans dépendre d'une plateforme centrale. | Ne remplace pas un pare-feu ni une politique d'accès réseau. |
|
||||
| `journald` | Limite l'usage disque et la rétention des journaux. | Évite qu'une VM issue du template remplisse son disque à cause des logs système. | La centralisation des logs viendra plus tard si nécessaire. |
|
||||
| `ssh_durcissement` | Ajoute les paramètres SSH plus stricts : port, délais, tentatives, keepalive, forwarding et tunnels désactivés. | Réduit la surface d'exposition SSH sans dépendre d'un rôle applicatif. | Les exceptions, comme un bastion ou du forwarding contrôlé, doivent être portées par un rôle dédié. |
|
||||
| `nftables_socle` | Installe nftables et prépare une configuration, mais garde le service désactivé par défaut. | Prépare le standard pare-feu sans risquer de couper l'accès au template ou aux clones. | L'activation se fait par rôle serveur avec règles adaptées. |
|
||||
|
||||
## Paquets communs
|
||||
|
||||
Les paquets communs sont choisis pour l'exploitation réelle :
|
||||
|
||||
```text
|
||||
administration : sudo, acl, bash-completion, tmux, vim, nano
|
||||
transport : curl, wget, ca-certificates, gnupg
|
||||
ansible : python3, python3-apt, python3-pip
|
||||
diagnostic : htop, iotop, iftop, sysstat, lsof, ncdu, tree, file, less
|
||||
réseau : dnsutils, iproute2, iputils-ping, net-tools, traceroute, mtr-tiny, tcpdump, netcat-openbsd, socat
|
||||
maintenance : rsync, unzip, zip, tar, jq, logrotate, unattended-upgrades, apt-listchanges, needrestart
|
||||
virtualisation : qemu-guest-agent, cloud-init, cloud-guest-utils
|
||||
temps : chrony
|
||||
```
|
||||
|
||||
Ces paquets sont acceptés dans le template parce qu'ils sont utiles sur presque tous les serveurs et ne transforment pas la VM en serveur applicatif.
|
||||
|
||||
## Choix de sécurité importants
|
||||
|
||||
### SSH
|
||||
|
||||
État attendu dès la construction :
|
||||
|
||||
```text
|
||||
PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
PubkeyAuthentication yes
|
||||
AuthenticationMethods publickey
|
||||
```
|
||||
|
||||
Le playbook `prepare` ne doit être lancé qu'après validation de l'accès SSH par clé au compte technique.
|
||||
|
||||
### Pare-feu
|
||||
|
||||
`nftables` est installé et préparé, mais désactivé par défaut.
|
||||
|
||||
Ce choix est volontaire : un pare-feu générique dans un template peut couper l'accès SSH ou bloquer un futur rôle serveur. L'activation doit être faite sur un clone avec des règles correspondant à son usage.
|
||||
|
||||
### Nettoyage final
|
||||
|
||||
Le nettoyage final est séparé et protégé par confirmation explicite. Il ne doit être lancé qu'au moment où la VM est validée et prête à être convertie en template.
|
||||
|
||||
## Vérification
|
||||
|
||||
```bash
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
## Validation minimale
|
||||
|
||||
Avant de déclarer le template prêt :
|
||||
|
||||
```bash
|
||||
make verifier
|
||||
make preparer-modele
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
La relance du playbook de préparation doit idéalement finir avec :
|
||||
|
||||
```text
|
||||
failed=0
|
||||
changed=0
|
||||
```
|
||||
|
||||
## Nettoyage
|
||||
|
||||
```bash
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Ne pas lancer le nettoyage final sur un serveur de production ou sur une VM qui n'est pas destinée à devenir un template.
|
||||
|
||||
## Hors périmètre du template
|
||||
|
||||
Les éléments suivants ne doivent pas être installés dans le golden template :
|
||||
|
||||
```text
|
||||
NGINX
|
||||
PostgreSQL
|
||||
MariaDB
|
||||
Docker
|
||||
Podman
|
||||
Redis
|
||||
GitLab
|
||||
Nextcloud
|
||||
monitoring complet
|
||||
agents applicatifs spécialisés
|
||||
données propres à un clone
|
||||
secrets
|
||||
clés privées
|
||||
```
|
||||
|
||||
Les intégrations futures sont documentées dans :
|
||||
|
||||
```text
|
||||
docs/integrations-vm.md
|
||||
```
|
||||
|
||||
Le cycle complet VM vanille, golden template, clone et conformité continue est documenté dans :
|
||||
|
||||
```text
|
||||
docs/vm-lifecycle.md
|
||||
```
|
||||
|
|
@ -1,12 +1,18 @@
|
|||
# Procédure manuelle — VM Debian 13 prête à convertir en template Proxmox
|
||||
# Procédure manuelle — VM Debian 13 vanille pour Proxmox
|
||||
|
||||
## Objet du document
|
||||
|
||||
Ce document décrit le travail manuel à effectuer pour créer une VM Debian 13 propre dans Proxmox, jusqu’au moment où elle est prête à être convertie en modèle/template.
|
||||
Ce document décrit le travail manuel à effectuer pour créer une VM Debian 13 vanille dans Proxmox, jusqu’au moment où elle peut être prise en charge par Set-OPS.
|
||||
|
||||
Le résultat attendu est une VM minimale, sobre, clonable, compatible avec cloud-init, prête à être prise en charge par Ansible et par le dépôt `Set-OPS`.
|
||||
Le résultat attendu est une VM minimale, sobre, joignable en SSH, capable d'installer des paquets depuis les dépôts Debian, compatible avec cloud-init et prête à recevoir le playbook de préparation du golden template.
|
||||
|
||||
Ce document s’arrête juste avant la conversion finale en template Proxmox.
|
||||
Ce document s’arrête au point de bascule vers le `Makefile`.
|
||||
|
||||
Les ajouts appliqués ensuite par Ansible, leur validation et leur justification sont documentés dans :
|
||||
|
||||
```text
|
||||
docs/modeles_vm/debian13-proxmox.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -19,10 +25,12 @@ Objectifs :
|
|||
- système Debian 13 minimal ;
|
||||
- aucun environnement graphique ;
|
||||
- SSH actif ;
|
||||
- compte technique `ansible` disponible ;
|
||||
- `ansible` autorisé à utiliser sudo sans mot de passe ;
|
||||
- `qemu-guest-agent` installé ;
|
||||
- `cloud-init` installé ;
|
||||
- disque CloudInit Proxmox attaché ;
|
||||
- compte technique `ansible` injecté par cloud-init ;
|
||||
- clé publique SSH injectée par cloud-init ;
|
||||
- `ansible` autorisé à utiliser sudo ;
|
||||
- partitionnement simple et agrandissable ;
|
||||
- aucune partition swap bloquant la croissance du disque ;
|
||||
- aucune donnée propre à une VM finale ;
|
||||
|
|
@ -304,7 +312,7 @@ Le serveur web, les rôles applicatifs et les services métier seront installés
|
|||
|
||||
Après l’installation, démarrer la VM et se connecter en console ou par SSH.
|
||||
|
||||
Passer root :
|
||||
Passer root si le compte root est disponible :
|
||||
|
||||
```bash
|
||||
su -
|
||||
|
|
@ -316,9 +324,55 @@ ou, si sudo est déjà disponible :
|
|||
sudo -i
|
||||
```
|
||||
|
||||
Toutes les commandes de bootstrap suivantes sont exécutées dans la VM.
|
||||
|
||||
---
|
||||
|
||||
## 13. Mise à jour du système
|
||||
## 13. Réparer APT après installation DVD
|
||||
|
||||
Si Debian a été installé depuis l'ISO/DVD complet, APT peut garder une source `cdrom:`. Dans ce cas, `apt update` ou `apt install` échoue parce que le système cherche les paquets sur le média d'installation plutôt que sur les miroirs Debian.
|
||||
|
||||
Vérifier les sources :
|
||||
|
||||
```bash
|
||||
grep -R "^[[:space:]]*deb cdrom:" /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true
|
||||
```
|
||||
|
||||
Si une ligne `deb cdrom:` est présente, la commenter ou la retirer.
|
||||
|
||||
Configuration simple attendue pour Debian 13 :
|
||||
|
||||
```bash
|
||||
cat > /etc/apt/sources.list <<'EOF'
|
||||
deb http://deb.debian.org/debian trixie main contrib non-free-firmware
|
||||
deb http://security.debian.org/debian-security trixie-security main contrib non-free-firmware
|
||||
deb http://deb.debian.org/debian trixie-updates main contrib non-free-firmware
|
||||
EOF
|
||||
```
|
||||
|
||||
Si l'installation a créé des fichiers `.sources` sous `/etc/apt/sources.list.d/`, vérifier qu'ils ne contredisent pas cette configuration et qu'ils ne pointent pas vers `cdrom:`.
|
||||
|
||||
Vérifier le réseau avant de continuer :
|
||||
|
||||
```bash
|
||||
ip route
|
||||
cat /etc/resolv.conf
|
||||
ping -c 3 1.1.1.1
|
||||
ping -c 3 deb.debian.org
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
une route par défaut existe
|
||||
un serveur DNS est configuré
|
||||
le ping IP fonctionne
|
||||
la résolution DNS fonctionne
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Mise à jour du système
|
||||
|
||||
Exécuter :
|
||||
|
||||
|
|
@ -329,46 +383,38 @@ apt full-upgrade -y
|
|||
|
||||
---
|
||||
|
||||
## 14. Paquets de base
|
||||
## 15. Paquets de bootstrap
|
||||
|
||||
Installer les paquets nécessaires au modèle :
|
||||
Installer seulement les paquets nécessaires pour que Set-OPS puisse prendre le relais :
|
||||
|
||||
```bash
|
||||
apt install -y \
|
||||
sudo \
|
||||
openssh-server \
|
||||
qemu-guest-agent \
|
||||
cloud-init \
|
||||
cloud-guest-utils \
|
||||
curl \
|
||||
wget \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
curl \
|
||||
vim \
|
||||
nano \
|
||||
bash-completion \
|
||||
htop \
|
||||
lsof \
|
||||
ncdu \
|
||||
tree \
|
||||
net-tools \
|
||||
iproute2 \
|
||||
dnsutils \
|
||||
chrony
|
||||
nano
|
||||
```
|
||||
|
||||
Rôle des paquets principaux :
|
||||
|
||||
```text
|
||||
sudo : délégation administrative
|
||||
openssh-server : accès distant initial
|
||||
qemu-guest-agent : communication Proxmox ↔ VM
|
||||
cloud-init : personnalisation au premier boot du clone
|
||||
cloud-guest-utils : fournit notamment growpart
|
||||
chrony : synchronisation du temps
|
||||
```
|
||||
|
||||
Les autres paquets communs, outils de diagnostic, chrony et composants de durcissement sont installés ensuite par `make preparer-modele`.
|
||||
|
||||
---
|
||||
|
||||
## 15. Activation du QEMU Guest Agent
|
||||
## 16. Activation du QEMU Guest Agent
|
||||
|
||||
Activer le service :
|
||||
|
||||
|
|
@ -390,126 +436,21 @@ VM → Options → QEMU Guest Agent → Enabled
|
|||
|
||||
---
|
||||
|
||||
## 16. Configuration du compte ansible
|
||||
## 17. Identité initiale par Cloud-Init
|
||||
|
||||
### Ajouter le compte au groupe sudo
|
||||
À partir d'ici, ne pas créer manuellement le compte `ansible` ni son fichier `authorized_keys`, sauf dépannage.
|
||||
|
||||
```bash
|
||||
usermod -aG sudo ansible
|
||||
```
|
||||
|
||||
### Autoriser sudo sans mot de passe
|
||||
|
||||
Créer le fichier dédié :
|
||||
|
||||
```bash
|
||||
cat > /etc/sudoers.d/90-ansible <<'EOF'
|
||||
ansible ALL=(ALL) NOPASSWD:ALL
|
||||
EOF
|
||||
```
|
||||
|
||||
Appliquer les permissions :
|
||||
|
||||
```bash
|
||||
chmod 440 /etc/sudoers.d/90-ansible
|
||||
```
|
||||
|
||||
Valider la syntaxe :
|
||||
|
||||
```bash
|
||||
visudo -cf /etc/sudoers.d/90-ansible
|
||||
```
|
||||
|
||||
Le résultat attendu est :
|
||||
L'identité initiale doit venir de Proxmox + Cloud-Init :
|
||||
|
||||
```text
|
||||
/etc/sudoers.d/90-ansible: parsed OK
|
||||
Proxmox : ciuser, clé SSH, réseau, DNS
|
||||
Debian : cloud-init applique ces paramètres au démarrage
|
||||
Ansible : configure ensuite le vrai socle du serveur
|
||||
```
|
||||
|
||||
Tester depuis le compte `ansible` :
|
||||
Cette séparation évite de maintenir deux méthodes concurrentes pour créer le même accès.
|
||||
|
||||
```bash
|
||||
sudo -n true && echo OK
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17. SSH
|
||||
|
||||
### État transitoire
|
||||
|
||||
Pendant la construction du modèle, l’authentification par mot de passe peut rester active :
|
||||
|
||||
```text
|
||||
PasswordAuthentication yes
|
||||
```
|
||||
|
||||
C’est pratique tant que la clé SSH n’est pas encore injectée.
|
||||
|
||||
### État cible
|
||||
|
||||
Quand l’accès par clé SSH est confirmé, l’état cible sera :
|
||||
|
||||
```text
|
||||
PermitRootLogin no
|
||||
PubkeyAuthentication yes
|
||||
PasswordAuthentication no
|
||||
```
|
||||
|
||||
Ne pas désactiver le login par mot de passe avant d’avoir confirmé que l’accès par clé fonctionne.
|
||||
|
||||
### Tester une connexion SSH par mot de passe
|
||||
|
||||
Depuis un poste d’administration :
|
||||
|
||||
```bash
|
||||
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no ansible@IP_DE_LA_VM
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18. Cloud-init dans Debian
|
||||
|
||||
Vérifier que cloud-init est installé :
|
||||
|
||||
```bash
|
||||
cloud-init --version
|
||||
```
|
||||
|
||||
Vérifier le service :
|
||||
|
||||
```bash
|
||||
systemctl status cloud-init --no-pager
|
||||
```
|
||||
|
||||
Cloud-init servira à appliquer, au premier démarrage d’un clone :
|
||||
|
||||
- hostname ;
|
||||
- utilisateur initial ;
|
||||
- clé SSH publique ;
|
||||
- IP ;
|
||||
- passerelle ;
|
||||
- DNS ;
|
||||
- agrandissement de la partition racine si le disque a été étendu.
|
||||
|
||||
Cloud-init ne doit pas remplacer Ansible pour les rôles serveur complexes.
|
||||
|
||||
Séparation recommandée :
|
||||
|
||||
```text
|
||||
cloud-init : identité initiale de la VM
|
||||
Ansible : configuration réelle du serveur
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 19. Ajout du CloudInit Drive dans Proxmox
|
||||
### Ajouter le CloudInit Drive
|
||||
|
||||
Éteindre la VM :
|
||||
|
||||
|
|
@ -535,19 +476,11 @@ Exemple :
|
|||
qm set 9000 --ide2 local-lvm:cloudinit
|
||||
```
|
||||
|
||||
ou :
|
||||
|
||||
```bash
|
||||
qm set 9000 --ide2 truenas-lvm:cloudinit
|
||||
```
|
||||
|
||||
Le nom exact du stockage dépend de la configuration Proxmox.
|
||||
|
||||
---
|
||||
### Définir les paramètres Cloud-Init du modèle
|
||||
|
||||
## 20. Paramètres cloud-init dans Proxmox
|
||||
|
||||
Après ajout du CloudInit Drive, Proxmox permet de définir les paramètres cloud-init.
|
||||
Pour la VM qui deviendra le modèle, utiliser une identité technique minimale, pas une identité de serveur final.
|
||||
|
||||
Exemple DHCP :
|
||||
|
||||
|
|
@ -557,7 +490,7 @@ qm set 9000 --sshkeys ~/.ssh/id_ed25519.pub
|
|||
qm set 9000 --ipconfig0 ip=dhcp
|
||||
```
|
||||
|
||||
Exemple IP statique :
|
||||
Exemple IP statique temporaire pour joindre la VM de modèle :
|
||||
|
||||
```bash
|
||||
qm set 9000 --ciuser ansible
|
||||
|
|
@ -566,13 +499,86 @@ qm set 9000 --ipconfig0 ip=192.168.11.150/24,gw=192.168.11.1
|
|||
qm set 9000 --nameserver 192.168.11.1
|
||||
```
|
||||
|
||||
Pour le modèle, éviter de lui donner une identité finale trop spécifique.
|
||||
Pour les clones, ces valeurs seront remplacées par l'identité réelle du serveur.
|
||||
|
||||
L’identité définitive doit plutôt être appliquée aux clones.
|
||||
### Réinitialiser l'état Cloud-Init avant le test
|
||||
|
||||
Comme `cloud-init` vient d'être installé après le premier démarrage Debian, nettoyer son état avant de tester l'injection Proxmox :
|
||||
|
||||
```bash
|
||||
cloud-init clean --logs
|
||||
reboot
|
||||
```
|
||||
|
||||
Au redémarrage, cloud-init doit appliquer le `ciuser`, la clé SSH et les paramètres réseau.
|
||||
|
||||
---
|
||||
|
||||
## 21. Vérifications avant nettoyage
|
||||
## 18. SSH et sudo après Cloud-Init
|
||||
|
||||
### Tester la connexion par clé
|
||||
|
||||
Depuis le poste d'administration :
|
||||
|
||||
```bash
|
||||
ssh -o PreferredAuthentications=publickey ansible@IP_DE_LA_VM
|
||||
```
|
||||
|
||||
Résultat attendu : la connexion fonctionne sans saisir le mot de passe du compte `ansible`.
|
||||
|
||||
### Vérifier sudo
|
||||
|
||||
Tester :
|
||||
|
||||
```bash
|
||||
sudo -v
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo -n true && echo OK
|
||||
```
|
||||
|
||||
Le résultat attendu est `OK`. Le flux `make` ne demande pas de mot de passe interactif.
|
||||
|
||||
### Correction minimale si sudo manque
|
||||
|
||||
Si cloud-init a créé `ansible` sans privilège sudo, corriger seulement ce point depuis la console Proxmox ou un compte administrateur existant :
|
||||
|
||||
```bash
|
||||
usermod -aG sudo ansible
|
||||
```
|
||||
|
||||
Puis retester `sudo -v` avec le compte `ansible`.
|
||||
|
||||
Ne pas créer manuellement `/home/ansible/.ssh/authorized_keys` dans le flux normal. Si la clé ne fonctionne pas, corriger les paramètres Cloud-Init dans Proxmox, puis relancer :
|
||||
|
||||
```bash
|
||||
cloud-init clean --logs
|
||||
reboot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 19. SSH durci attendu
|
||||
|
||||
Le modèle Chezlepro suppose que Cloud-Init injecte le `ciuser` et sa clé publique avant la prise en charge par Ansible.
|
||||
|
||||
L'exigence immédiate est que l'accès par clé fonctionne pour `ansible`.
|
||||
|
||||
L'état final sera appliqué et validé par Set-OPS :
|
||||
|
||||
```text
|
||||
PermitRootLogin no
|
||||
PubkeyAuthentication yes
|
||||
PasswordAuthentication no
|
||||
AuthenticationMethods publickey
|
||||
```
|
||||
|
||||
Ne pas lancer le playbook de préparation tant que l'accès SSH par clé ne fonctionne pas.
|
||||
|
||||
---
|
||||
|
||||
## 20. Vérifications avant passage à Set-OPS
|
||||
|
||||
Redémarrer la VM une dernière fois si nécessaire, puis vérifier :
|
||||
|
||||
|
|
@ -598,52 +604,71 @@ qemu-guest-agent actif
|
|||
ssh actif
|
||||
cloud-init installé
|
||||
compte ansible fonctionnel
|
||||
sudo NOPASSWD fonctionnel pour ansible
|
||||
connexion SSH par clé fonctionnelle
|
||||
sudo fonctionnel pour ansible
|
||||
CloudInit Drive présent côté Proxmox
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 22. Nettoyage avant conversion en template
|
||||
## 21. Point de bascule vers Set-OPS
|
||||
|
||||
Avant de convertir la VM en modèle, nettoyer l’état propre à cette installation.
|
||||
À partir de ce point, ne pas continuer manuellement la configuration du socle. Utiliser le `Makefile` depuis le dépôt Set-OPS.
|
||||
|
||||
Exécuter dans la VM :
|
||||
Depuis le poste Ansible :
|
||||
|
||||
```bash
|
||||
sudo cloud-init clean --logs
|
||||
sudo apt clean
|
||||
make preparer-modele
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
Nettoyer l’identifiant machine :
|
||||
Le cleanup final est séparé et protégé :
|
||||
|
||||
```bash
|
||||
sudo truncate -s 0 /etc/machine-id
|
||||
sudo rm -f /var/lib/dbus/machine-id
|
||||
sudo ln -s /etc/machine-id /var/lib/dbus/machine-id
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Optionnel : vider les journaux persistants si présents :
|
||||
Ne pas lancer le cleanup final tant que `make verifier-modele` n'a pas réussi.
|
||||
|
||||
---
|
||||
|
||||
## 22. Dépannage du bootstrap initial
|
||||
|
||||
| Symptôme | Cause probable | Correction |
|
||||
| --- | --- | --- |
|
||||
| `apt install` demande le DVD | Source APT `cdrom:` encore active. | Refaire la section 13 et relancer `apt update`. |
|
||||
| `Temporary failure resolving deb.debian.org` | DNS absent ou incorrect. | Vérifier `/etc/resolv.conf`, Proxmox cloud-init DNS, passerelle et réseau. |
|
||||
| `Network is unreachable` | Pas de route par défaut. | Vérifier l'IP, la passerelle et `ip route`. |
|
||||
| SSH refuse la clé | Clé Cloud-Init absente, mauvais `ciuser`, ou cloud-init déjà initialisé avant l'ajout des paramètres. | Corriger les paramètres Proxmox, puis `cloud-init clean --logs` et redémarrer. |
|
||||
| `sudo: a password is required` | Sudo NOPASSWD pas encore posé. | Corriger sudo NOPASSWD pour `ansible`, puis relancer `make preparer-modele`. |
|
||||
| `ansible is not in the sudoers file` | Le compte a été créé par Cloud-Init sans privilège sudo. | Ajouter seulement `ansible` au groupe `sudo`, puis laisser Set-OPS gérer NOPASSWD. |
|
||||
| Cloud-init sans effet | `cloud-init` absent au premier boot concerné, disque CloudInit absent, ou VM déjà initialisée. | Installer `cloud-init`, ajouter le CloudInit Drive, corriger les paramètres Proxmox, puis `cloud-init clean --logs` et redémarrer. |
|
||||
|
||||
---
|
||||
|
||||
## 23. Nettoyage avant conversion en template
|
||||
|
||||
Le nettoyage est fait par Set-OPS, pas à la main, sauf diagnostic exceptionnel.
|
||||
|
||||
Commande attendue :
|
||||
|
||||
```bash
|
||||
sudo journalctl --rotate
|
||||
sudo journalctl --vacuum-time=1s
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Optionnel : supprimer l’historique shell du compte courant :
|
||||
Ce nettoyage peut inclure :
|
||||
|
||||
```bash
|
||||
history -c
|
||||
```
|
||||
|
||||
Puis éteindre :
|
||||
|
||||
```bash
|
||||
sudo shutdown -h now
|
||||
```text
|
||||
cloud-init clean --logs
|
||||
nettoyage du cache APT
|
||||
rotation ou purge contrôlée des journaux
|
||||
remise à zéro de /etc/machine-id
|
||||
suppression des historiques shell
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 23. Point d’arrêt : VM prête à convertir
|
||||
## 24. Point d’arrêt : VM prête à convertir
|
||||
|
||||
À ce stade, la VM doit être arrêtée.
|
||||
|
||||
|
|
@ -673,8 +698,9 @@ qemu-guest-agent installé
|
|||
cloud-init installé
|
||||
cloud-guest-utils installé
|
||||
sudo installé
|
||||
compte ansible présent
|
||||
sudo NOPASSWD pour ansible
|
||||
compte ansible injecté par cloud-init
|
||||
clé publique SSH injectée par cloud-init
|
||||
sudo NOPASSWD pour ansible après préparation Set-OPS
|
||||
partition EFI 512 Mo
|
||||
partition / ext4
|
||||
aucune partition swap
|
||||
|
|
@ -687,7 +713,7 @@ VM éteinte
|
|||
|
||||
---
|
||||
|
||||
## 24. Conversion en template Proxmox
|
||||
## 25. Conversion en template Proxmox
|
||||
|
||||
Cette commande n’est exécutée qu’après validation finale :
|
||||
|
||||
|
|
@ -705,49 +731,63 @@ qm template 9000
|
|||
|
||||
---
|
||||
|
||||
## 25. Flux normal après conversion
|
||||
## 26. Flux normal après conversion
|
||||
|
||||
Après conversion du modèle :
|
||||
Après conversion du modèle, le flux normal passe par `make` et l'API Proxmox.
|
||||
|
||||
Les paramètres communs Proxmox sont dans :
|
||||
|
||||
```text
|
||||
1. Cloner le template.
|
||||
2. Agrandir le disque si nécessaire.
|
||||
3. Définir hostname, IP, DNS et clé SSH via cloud-init.
|
||||
inventories/lab/group_vars/proxmox.yml
|
||||
```
|
||||
|
||||
Les secrets d'API doivent être dans un fichier Vault non versionné :
|
||||
|
||||
```text
|
||||
inventories/lab/group_vars/proxmox.vault.yml
|
||||
```
|
||||
|
||||
Créer le clone et l'ajouter à l'inventaire :
|
||||
|
||||
```text
|
||||
1. Cloner le template via l'API Proxmox.
|
||||
2. Agrandir le disque si demandé.
|
||||
3. Définir l'identité Cloud-Init.
|
||||
4. Démarrer la VM.
|
||||
5. Laisser cloud-init appliquer l’identité initiale.
|
||||
6. Se connecter en SSH.
|
||||
7. Appliquer les rôles Ansible depuis Set-OPS.
|
||||
5. Ajouter l'hôte à l'inventaire Set-OPS.
|
||||
```
|
||||
|
||||
Exemple :
|
||||
|
||||
```bash
|
||||
qm clone 9000 101 --name web-01 --full true
|
||||
qm resize 101 scsi0 32G
|
||||
qm set 101 --ciuser ansible
|
||||
qm set 101 --sshkeys ~/.ssh/id_ed25519.pub
|
||||
qm set 101 --ipconfig0 ip=192.168.11.101/24,gw=192.168.11.1
|
||||
qm set 101 --nameserver 192.168.11.1
|
||||
qm start 101
|
||||
make creer-vm \
|
||||
HOTE=web-01 \
|
||||
VMID=101 \
|
||||
VLAN=11 \
|
||||
ADRESSE_IP=192.168.11.101 \
|
||||
PASSERELLE=192.168.11.1 \
|
||||
GROUPES="serveurs_debian serveurs_durcis"
|
||||
```
|
||||
|
||||
Puis :
|
||||
Le VLAN est volontairement saisi au moment de créer chaque VM. Il ne fait pas partie des valeurs persistantes de `make config`.
|
||||
|
||||
Après le premier démarrage, tester l'accès :
|
||||
|
||||
```bash
|
||||
ssh ansible@192.168.11.101
|
||||
```
|
||||
|
||||
Ensuite seulement :
|
||||
Ensuite appliquer la conformité Ansible :
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/production/hosts.yml playbooks/baseline.yml
|
||||
make deployer HOTE=web-01
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 26. Résumé court
|
||||
## 27. Résumé court
|
||||
|
||||
Le modèle idéal est simple :
|
||||
La VM vanille idéale est simple :
|
||||
|
||||
```text
|
||||
Debian 13 minimal
|
||||
|
|
@ -766,11 +806,11 @@ sudo
|
|||
qemu-guest-agent
|
||||
cloud-init
|
||||
cloud-guest-utils
|
||||
compte ansible
|
||||
sudo NOPASSWD pour ansible
|
||||
cloud-init clean
|
||||
machine-id vidé
|
||||
VM éteinte
|
||||
CloudInit Drive Proxmox
|
||||
ciuser ansible défini dans Proxmox
|
||||
clé publique SSH définie dans Proxmox
|
||||
sudo fonctionnel pour ansible
|
||||
APT réseau fonctionnel
|
||||
```
|
||||
|
||||
La philosophie :
|
||||
|
|
|
|||
194
docs/vm-lifecycle.md
Normal file
194
docs/vm-lifecycle.md
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
# Cycle de vie des VM Chezlepro
|
||||
|
||||
Ce document décrit le cycle normal d'une VM Debian Chezlepro, depuis l'installation minimale jusqu'à la conformité continue par Ansible.
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
```text
|
||||
Debian 13 minimale
|
||||
→ goldenisation du template
|
||||
→ cleanup final
|
||||
→ conversion Proxmox en template
|
||||
→ clonage
|
||||
→ identité initiale par cloud-init
|
||||
→ conformité par groupes Ansible
|
||||
→ intégrations par rôle
|
||||
→ conformité continue
|
||||
```
|
||||
|
||||
## 1. VM vanille
|
||||
|
||||
La VM vanille est une Debian 13 minimale installée manuellement ou par procédure Proxmox.
|
||||
|
||||
Elle contient seulement le nécessaire pour être joignable et prise en charge :
|
||||
|
||||
```text
|
||||
Debian 13
|
||||
SSH
|
||||
réseau fonctionnel
|
||||
cloud-init installé
|
||||
CloudInit Drive Proxmox présent
|
||||
utilisateur initial injecté par Cloud-Init
|
||||
clé publique SSH injectée par Cloud-Init
|
||||
sudo ou accès root possible
|
||||
aucun rôle applicatif
|
||||
aucun secret
|
||||
```
|
||||
|
||||
Cette VM n'est pas encore un golden template Chezlepro.
|
||||
|
||||
## 2. Goldenisation
|
||||
|
||||
La goldenisation est faite par :
|
||||
|
||||
```bash
|
||||
make preparer-modele
|
||||
```
|
||||
|
||||
Ce playbook ajoute le socle Chezlepro commun au template :
|
||||
|
||||
```text
|
||||
paquets communs
|
||||
qemu-guest-agent
|
||||
cloud-init
|
||||
compte technique Ansible
|
||||
chrony
|
||||
SSH socle
|
||||
durcissement template-safe
|
||||
nftables préparé mais désactivé
|
||||
```
|
||||
|
||||
Les détails et justifications sont dans :
|
||||
|
||||
```text
|
||||
docs/modeles_vm/debian13-proxmox.md
|
||||
```
|
||||
|
||||
## 3. Validation et cleanup
|
||||
|
||||
Après la préparation :
|
||||
|
||||
```bash
|
||||
make verifier-modele
|
||||
```
|
||||
|
||||
Le cleanup final est séparé et protégé :
|
||||
|
||||
```bash
|
||||
make nettoyer-modele CONFIRMER=true
|
||||
```
|
||||
|
||||
Le cleanup ne doit jamais être lancé sur une VM de production.
|
||||
|
||||
## 4. Clonage
|
||||
|
||||
Le clone reçoit son identité initiale par Proxmox et cloud-init :
|
||||
|
||||
```text
|
||||
hostname
|
||||
utilisateur initial
|
||||
clé SSH
|
||||
IP ou DHCP
|
||||
passerelle
|
||||
DNS
|
||||
agrandissement disque
|
||||
```
|
||||
|
||||
Cloud-init ne remplace pas Ansible pour la configuration réelle du serveur.
|
||||
|
||||
## 5. Groupes opérationnels
|
||||
|
||||
Une VM déployée doit être placée dans les groupes d'inventaire qui décrivent l'état voulu.
|
||||
|
||||
Chaque groupe opérationnel doit avoir un playbook homonyme :
|
||||
|
||||
```text
|
||||
serveurs_debian -> playbooks/groupes/serveurs_debian.yml
|
||||
serveurs_durcis -> playbooks/groupes/serveurs_durcis.yml
|
||||
```
|
||||
|
||||
L'appartenance aux groupes devient la déclaration d'intention :
|
||||
|
||||
```text
|
||||
web-01 dans serveurs_debian -> applique le socle Debian
|
||||
web-01 dans serveurs_durcis -> applique le durcissement commun
|
||||
```
|
||||
|
||||
Un groupe sans playbook ne doit pas être assigné à une VM de production tant que son rôle n'est pas implémenté.
|
||||
|
||||
## 6. Socle Debian
|
||||
|
||||
Le groupe `serveurs_debian` maintient les composants de base :
|
||||
|
||||
```bash
|
||||
make appliquer GROUPE=serveurs_debian
|
||||
```
|
||||
|
||||
## 7. Hardening commun
|
||||
|
||||
Le groupe `serveurs_durcis` maintient les couches de sécurité communes :
|
||||
|
||||
```bash
|
||||
make appliquer GROUPE=serveurs_durcis
|
||||
```
|
||||
|
||||
L'accès SSH par mot de passe est désactivé dès le socle. L'activation de nftables doit rester dépendante des règles réseau propres au rôle du serveur.
|
||||
|
||||
## 8. Déploiement par Makefile
|
||||
|
||||
Pour une VM déjà clonée et démarrée :
|
||||
|
||||
```bash
|
||||
make hote-ajouter HOTE=web-01 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"
|
||||
make deployer HOTE=web-01
|
||||
```
|
||||
|
||||
La cible `deployer` lit les groupes de l'hôte, cherche les playbooks correspondants dans `playbooks/groupes/`, puis les applique dans l'ordre de l'inventaire.
|
||||
|
||||
Ensuite, elle lance la vérification post-déploiement :
|
||||
|
||||
```text
|
||||
playbooks/groupes/serveurs_debian.yml
|
||||
playbooks/groupes/serveurs_durcis.yml
|
||||
verifier-hote
|
||||
```
|
||||
|
||||
Pour converger un groupe complet :
|
||||
|
||||
```bash
|
||||
make deployer-groupe GROUPE=serveurs_debian
|
||||
```
|
||||
|
||||
## 9. Variables template et conformité
|
||||
|
||||
Les variables du template servent à construire le golden template dans `inventories/lab/group_vars/modeles_vm.yml`.
|
||||
|
||||
Les variables de conformité servent aux VM déployées dans `inventories/production/group_vars/serveurs_debian.yml`.
|
||||
|
||||
Différence attendue :
|
||||
|
||||
```text
|
||||
template : SSH par clé seulement, pare-feu non activé
|
||||
conformité VM : même base SSH, règles réseau adaptées aux serveurs finaux
|
||||
```
|
||||
|
||||
Ne pas durcir une variable de conformité si son application peut couper l'accès sans validation préalable.
|
||||
|
||||
## 10. Intégrations futures
|
||||
|
||||
Les intégrations transversales sont ajoutées après socle et durcissement :
|
||||
|
||||
```text
|
||||
DNS interne
|
||||
PKI interne
|
||||
identité
|
||||
supervision
|
||||
métriques
|
||||
visualisation
|
||||
```
|
||||
|
||||
Elles sont documentées dans :
|
||||
|
||||
```text
|
||||
docs/integrations-vm.md
|
||||
```
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# Template Debian 13 Proxmox
|
||||
|
||||
## Préparation
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_prepare.yml --ask-pass --ask-become-pass
|
||||
```
|
||||
|
||||
## Vérification
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_verify.yml
|
||||
```
|
||||
|
||||
## Nettoyage
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/lab/hosts.yml playbooks/vm_templates/debian13_proxmox_cleanup.yml -e confirm_template_cleanup=true
|
||||
```
|
||||
|
|
@ -1,119 +1,2 @@
|
|||
---
|
||||
chezlepro_timezone: "America/Toronto"
|
||||
chezlepro_admin_user: "ansible"
|
||||
|
||||
# SSH pendant la construction du template.
|
||||
# Passer à "no" seulement après validation des clés SSH.
|
||||
chezlepro_ssh_port: 22
|
||||
chezlepro_ssh_password_authentication: "yes"
|
||||
chezlepro_ssh_permit_root_login: "no"
|
||||
chezlepro_ssh_pubkey_authentication: "yes"
|
||||
chezlepro_ssh_client_alive_interval: 300
|
||||
chezlepro_ssh_client_alive_count_max: 2
|
||||
chezlepro_ssh_max_auth_tries: 4
|
||||
chezlepro_ssh_login_grace_time: "30s"
|
||||
|
||||
# systemd-ssh-generator / VSOCK
|
||||
chezlepro_disable_systemd_ssh_auto: true
|
||||
|
||||
# Cloud-init
|
||||
cloud_init_ssh_pwauth: true
|
||||
cloud_init_manage_etc_hosts: true
|
||||
cloud_init_disable_root: true
|
||||
cloud_init_ssh_deletekeys: true
|
||||
|
||||
# Pare-feu : préparé mais non activé dans le template.
|
||||
chezlepro_nftables_enabled: false
|
||||
chezlepro_firewall_allow_ssh: true
|
||||
chezlepro_firewall_allow_icmp: true
|
||||
chezlepro_firewall_input_policy: "drop"
|
||||
chezlepro_firewall_forward_policy: "drop"
|
||||
chezlepro_firewall_output_policy: "accept"
|
||||
chezlepro_firewall_extra_tcp_ports: []
|
||||
chezlepro_firewall_extra_udp_ports: []
|
||||
|
||||
# Fail2ban
|
||||
chezlepro_fail2ban_enabled: true
|
||||
chezlepro_fail2ban_bantime: "1h"
|
||||
chezlepro_fail2ban_findtime: "10m"
|
||||
chezlepro_fail2ban_maxretry: 5
|
||||
|
||||
# Sécurité
|
||||
chezlepro_auditd_enabled: true
|
||||
chezlepro_apparmor_enabled: true
|
||||
chezlepro_sysctl_apply: true
|
||||
chezlepro_disable_core_dumps: true
|
||||
|
||||
# Unattended upgrades
|
||||
chezlepro_unattended_upgrades_enabled: true
|
||||
chezlepro_unattended_remove_unused_dependencies: true
|
||||
chezlepro_unattended_automatic_reboot: false
|
||||
|
||||
# Journald
|
||||
chezlepro_journald_system_max_use: "200M"
|
||||
chezlepro_journald_runtime_max_use: "100M"
|
||||
chezlepro_journald_max_retention_sec: "1month"
|
||||
|
||||
# Nettoyage final
|
||||
confirm_template_cleanup: false
|
||||
|
||||
chezlepro_common_packages:
|
||||
- sudo
|
||||
- openssh-server
|
||||
- qemu-guest-agent
|
||||
- cloud-init
|
||||
- cloud-guest-utils
|
||||
- python3
|
||||
- python3-apt
|
||||
- python3-pip
|
||||
- acl
|
||||
- curl
|
||||
- wget
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- git
|
||||
- vim
|
||||
- nano
|
||||
- bash-completion
|
||||
- tmux
|
||||
- rsync
|
||||
- unzip
|
||||
- zip
|
||||
- tar
|
||||
- jq
|
||||
- htop
|
||||
- iotop
|
||||
- iftop
|
||||
- sysstat
|
||||
- lsof
|
||||
- ncdu
|
||||
- tree
|
||||
- file
|
||||
- less
|
||||
- dnsutils
|
||||
- iproute2
|
||||
- iputils-ping
|
||||
- net-tools
|
||||
- traceroute
|
||||
- mtr-tiny
|
||||
- tcpdump
|
||||
- netcat-openbsd
|
||||
- socat
|
||||
- chrony
|
||||
- logrotate
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
- needrestart
|
||||
|
||||
chezlepro_hardening_packages:
|
||||
- apparmor
|
||||
- apparmor-utils
|
||||
- auditd
|
||||
- audispd-plugins
|
||||
- fail2ban
|
||||
- nftables
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
- debsums
|
||||
- needrestart
|
||||
- libpam-pwquality
|
||||
|
|
|
|||
58
inventories/lab/group_vars/modeles_vm.yml
Normal file
58
inventories/lab/group_vars/modeles_vm.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
# Variables de construction du golden template Debian 13 Proxmox.
|
||||
|
||||
sudo_ansible_admin_user: "ansible"
|
||||
|
||||
# SSH : cloud-init doit avoir injecté ciuser et clé publique avant Ansible.
|
||||
ssh_baseline_password_authentication: "no"
|
||||
ssh_baseline_permit_root_login: "no"
|
||||
ssh_baseline_pubkey_authentication: "yes"
|
||||
|
||||
ssh_hardening_port: 22
|
||||
ssh_hardening_password_authentication: "no"
|
||||
ssh_hardening_permit_root_login: "no"
|
||||
ssh_hardening_pubkey_authentication: "yes"
|
||||
ssh_hardening_client_alive_interval: 300
|
||||
ssh_hardening_client_alive_count_max: 2
|
||||
ssh_hardening_max_auth_tries: 3
|
||||
ssh_hardening_login_grace_time: "20s"
|
||||
|
||||
systemd_ssh_auto_disable: true
|
||||
|
||||
cloud_init_ssh_pwauth: false
|
||||
cloud_init_manage_etc_hosts: true
|
||||
cloud_init_disable_root: true
|
||||
cloud_init_ssh_deletekeys: true
|
||||
|
||||
# Pare-feu : préparé mais non activé dans le template.
|
||||
nftables_baseline_enabled: false
|
||||
nftables_baseline_allow_ssh: true
|
||||
nftables_baseline_allow_icmp: true
|
||||
nftables_baseline_ssh_port: 22
|
||||
nftables_baseline_input_policy: "drop"
|
||||
nftables_baseline_forward_policy: "drop"
|
||||
nftables_baseline_output_policy: "accept"
|
||||
nftables_baseline_extra_tcp_ports: []
|
||||
nftables_baseline_extra_udp_ports: []
|
||||
|
||||
fail2ban_ssh_enabled: true
|
||||
fail2ban_ssh_port: 22
|
||||
fail2ban_ssh_bantime: "1h"
|
||||
fail2ban_ssh_findtime: "10m"
|
||||
fail2ban_ssh_maxretry: 5
|
||||
|
||||
auditd_enabled: true
|
||||
apparmor_enabled: true
|
||||
sysctl_hardening_apply: true
|
||||
core_dumps_disable: true
|
||||
|
||||
unattended_upgrades_enabled: true
|
||||
unattended_upgrades_remove_unused_dependencies: true
|
||||
unattended_upgrades_automatic_reboot: false
|
||||
|
||||
journald_system_max_use: "200M"
|
||||
journald_runtime_max_use: "100M"
|
||||
journald_max_retention_sec: "1month"
|
||||
|
||||
template_cleanup_confirm: false
|
||||
template_cleanup_admin_user: "ansible"
|
||||
8
inventories/lab/group_vars/proxmox.vault.yml.example
Normal file
8
inventories/lab/group_vars/proxmox.vault.yml.example
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
# Copier ce fichier vers proxmox.vault.yml, puis le chiffrer avec :
|
||||
# ansible-vault encrypt inventories/lab/group_vars/proxmox.vault.yml
|
||||
#
|
||||
# Ne jamais versionner proxmox.vault.yml.
|
||||
|
||||
proxmox_api_token_id: ""
|
||||
proxmox_api_token_secret: ""
|
||||
20
inventories/lab/group_vars/proxmox.yml
Normal file
20
inventories/lab/group_vars/proxmox.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
# Parametres non sensibles pour les operations Proxmox.
|
||||
# Les secrets API doivent aller dans proxmox.vault.yml.
|
||||
|
||||
proxmox_api_host: asgard
|
||||
proxmox_api_user: ansible@pve
|
||||
proxmox_api_port: '8006'
|
||||
proxmox_validate_certs: false
|
||||
proxmox_clone_noeud: asgard
|
||||
proxmox_clone_vmid_modele: 99999
|
||||
proxmox_clone_source_nom: modeleChezlepro
|
||||
proxmox_clone_stockage: TrueNAS
|
||||
proxmox_clone_format: ''
|
||||
proxmox_clone_complet: true
|
||||
proxmox_clone_timeout: 600
|
||||
proxmox_clone_disque: scsi0
|
||||
proxmox_clone_interface: net0
|
||||
proxmox_clone_pont: vmbr1
|
||||
proxmox_clone_parefeu_interface: false
|
||||
proxmox_clone_demarrer: true
|
||||
|
|
@ -1,20 +1,26 @@
|
|||
all:
|
||||
children:
|
||||
vm_templates:
|
||||
modeles_vm:
|
||||
hosts:
|
||||
basiqueChezlepro:
|
||||
ansible_host: 192.168.12.99
|
||||
ansible_user: ansible
|
||||
ansible_become: true
|
||||
|
||||
web_servers:
|
||||
serveurs_debian:
|
||||
hosts: {}
|
||||
|
||||
database_servers:
|
||||
serveurs_durcis:
|
||||
hosts: {}
|
||||
|
||||
monitoring_servers:
|
||||
serveurs_web:
|
||||
hosts: {}
|
||||
|
||||
proxmox_hosts:
|
||||
serveurs_bases_donnees:
|
||||
hosts: {}
|
||||
|
||||
serveurs_supervision:
|
||||
hosts: {}
|
||||
|
||||
hotes_proxmox:
|
||||
hosts: {}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
---
|
||||
# Variables globales de production à remplir progressivement.
|
||||
# Variables globales de production.
|
||||
# Les politiques Debian communes sont dans group_vars/serveurs_debian.yml.
|
||||
|
|
|
|||
54
inventories/production/group_vars/serveurs_debian.yml
Normal file
54
inventories/production/group_vars/serveurs_debian.yml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
# Variables de conformité communes aux VM Debian déployées.
|
||||
|
||||
sudo_ansible_admin_user: "ansible"
|
||||
|
||||
ssh_baseline_password_authentication: "no"
|
||||
ssh_baseline_permit_root_login: "no"
|
||||
ssh_baseline_pubkey_authentication: "yes"
|
||||
|
||||
ssh_hardening_port: 22
|
||||
ssh_hardening_password_authentication: "no"
|
||||
ssh_hardening_permit_root_login: "no"
|
||||
ssh_hardening_pubkey_authentication: "yes"
|
||||
ssh_hardening_client_alive_interval: 300
|
||||
ssh_hardening_client_alive_count_max: 2
|
||||
ssh_hardening_max_auth_tries: 3
|
||||
ssh_hardening_login_grace_time: "20s"
|
||||
|
||||
systemd_ssh_auto_disable: true
|
||||
|
||||
cloud_init_ssh_pwauth: false
|
||||
cloud_init_manage_etc_hosts: true
|
||||
cloud_init_disable_root: true
|
||||
cloud_init_ssh_deletekeys: true
|
||||
|
||||
# Pare-feu : ne pas activer globalement sans validation des règles par rôle.
|
||||
nftables_baseline_enabled: false
|
||||
nftables_baseline_allow_ssh: true
|
||||
nftables_baseline_allow_icmp: true
|
||||
nftables_baseline_ssh_port: 22
|
||||
nftables_baseline_input_policy: "drop"
|
||||
nftables_baseline_forward_policy: "drop"
|
||||
nftables_baseline_output_policy: "accept"
|
||||
nftables_baseline_extra_tcp_ports: []
|
||||
nftables_baseline_extra_udp_ports: []
|
||||
|
||||
fail2ban_ssh_enabled: true
|
||||
fail2ban_ssh_port: 22
|
||||
fail2ban_ssh_bantime: "1h"
|
||||
fail2ban_ssh_findtime: "10m"
|
||||
fail2ban_ssh_maxretry: 5
|
||||
|
||||
auditd_enabled: true
|
||||
apparmor_enabled: true
|
||||
sysctl_hardening_apply: true
|
||||
core_dumps_disable: true
|
||||
|
||||
unattended_upgrades_enabled: true
|
||||
unattended_upgrades_remove_unused_dependencies: true
|
||||
unattended_upgrades_automatic_reboot: false
|
||||
|
||||
journald_system_max_use: "200M"
|
||||
journald_runtime_max_use: "100M"
|
||||
journald_max_retention_sec: "1month"
|
||||
|
|
@ -1,12 +1,28 @@
|
|||
all:
|
||||
children:
|
||||
vm_templates:
|
||||
modeles_vm:
|
||||
hosts: {}
|
||||
web_servers:
|
||||
serveurs_debian:
|
||||
hosts:
|
||||
web-01:
|
||||
ansible_host: 192.168.12.101
|
||||
ansible_user: ansible
|
||||
web-02:
|
||||
ansible_host: 192.168.12.102
|
||||
ansible_user: ansible
|
||||
serveurs_durcis:
|
||||
hosts:
|
||||
web-01:
|
||||
ansible_host: 192.168.12.101
|
||||
ansible_user: ansible
|
||||
web-02:
|
||||
ansible_host: 192.168.12.102
|
||||
ansible_user: ansible
|
||||
serveurs_web:
|
||||
hosts: {}
|
||||
database_servers:
|
||||
serveurs_bases_donnees:
|
||||
hosts: {}
|
||||
monitoring_servers:
|
||||
serveurs_supervision:
|
||||
hosts: {}
|
||||
proxmox_hosts:
|
||||
hotes_proxmox:
|
||||
hosts: {}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
- name: Appliquer le socle Debian commun
|
||||
hosts: all
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- common_packages
|
||||
- chrony
|
||||
- ssh_baseline
|
||||
22
playbooks/groupes/serveurs_debian.yml
Normal file
22
playbooks/groupes/serveurs_debian.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
- name: Appliquer le groupe serveurs_debian
|
||||
hosts: serveurs_debian
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- common_packages
|
||||
- qemu_guest_agent
|
||||
- cloud_init
|
||||
- sudo_ansible
|
||||
- chrony
|
||||
- ssh_baseline
|
||||
- systemd_ssh_auto
|
||||
- motd
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Appliquer le hardening Debian serveur Chezlepro
|
||||
hosts: all
|
||||
- name: Appliquer le groupe serveurs_durcis
|
||||
hosts: serveurs_durcis
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
38
playbooks/maintenance/verifier_hote_debian.yml
Normal file
38
playbooks/maintenance/verifier_hote_debian.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
- name: Vérifier une VM Debian gérée par Set-OPS
|
||||
hosts: serveurs_debian
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
tasks:
|
||||
- name: Vérifier que la cible est Debian 13
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
- ansible_facts.distribution_major_version == "13"
|
||||
fail_msg: "Cette vérification attend une VM Debian 13."
|
||||
|
||||
- name: Collecter les services
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Vérifier les services du socle
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- '"ssh.service" in ansible_facts.services'
|
||||
- '"qemu-guest-agent.service" in ansible_facts.services'
|
||||
- '"chrony.service" in ansible_facts.services'
|
||||
- ansible_facts.services["ssh.service"].state == "running"
|
||||
- ansible_facts.services["qemu-guest-agent.service"].state == "running"
|
||||
- ansible_facts.services["chrony.service"].state == "running"
|
||||
fail_msg: "Un service du socle Debian n'est pas actif."
|
||||
|
||||
- name: Vérifier sudo non interactif via become
|
||||
ansible.builtin.command: whoami
|
||||
changed_when: false
|
||||
register: verifier_hote_debian_whoami
|
||||
|
||||
- name: Valider l'élévation root
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- verifier_hote_debian_whoami.stdout == "root"
|
||||
fail_msg: "L'élévation sudo ne retourne pas root."
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Nettoyage final avant conversion en template Proxmox
|
||||
hosts: vm_templates
|
||||
hosts: modeles_vm
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: Préparer le template Debian 13 Proxmox Chezlepro
|
||||
hosts: vm_templates
|
||||
hosts: modeles_vm
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
130
playbooks/modeles_vm/debian13_proxmox_verifier.yml
Normal file
130
playbooks/modeles_vm/debian13_proxmox_verifier.yml
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
- name: Vérifier le template Debian 13 Proxmox Chezlepro
|
||||
hosts: modeles_vm
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
tasks:
|
||||
- name: Vérifier que la cible est Debian 13
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
- ansible_facts.distribution_major_version == "13"
|
||||
fail_msg: "Ce template doit être une Debian 13."
|
||||
|
||||
- name: Afficher les partitions
|
||||
ansible.builtin.command: lsblk -f
|
||||
register: lsblk_output
|
||||
changed_when: false
|
||||
|
||||
- name: Résultat lsblk
|
||||
ansible.builtin.debug:
|
||||
var: lsblk_output.stdout_lines
|
||||
|
||||
- name: Vérifier l'absence de swap actif
|
||||
ansible.builtin.command: swapon --show
|
||||
register: swapon_output
|
||||
changed_when: false
|
||||
failed_when: swapon_output.stdout | length > 0
|
||||
|
||||
- name: Collecter les paquets installés
|
||||
ansible.builtin.package_facts:
|
||||
manager: auto
|
||||
|
||||
- name: Valider les paquets requis du template
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'sudo' in ansible_facts.packages"
|
||||
- "'openssh-server' in ansible_facts.packages"
|
||||
- "'qemu-guest-agent' in ansible_facts.packages"
|
||||
- "'cloud-init' in ansible_facts.packages"
|
||||
- "'cloud-guest-utils' in ansible_facts.packages"
|
||||
- "'chrony' in ansible_facts.packages"
|
||||
- "'apparmor' in ansible_facts.packages"
|
||||
- "'auditd' in ansible_facts.packages"
|
||||
- "'fail2ban' in ansible_facts.packages"
|
||||
- "'nftables' in ansible_facts.packages"
|
||||
- "'unattended-upgrades' in ansible_facts.packages"
|
||||
fail_msg: "Un paquet requis du golden template est absent."
|
||||
|
||||
- name: Vérifier l'absence d'environnement graphique
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'task-desktop' not in ansible_facts.packages"
|
||||
- "'xserver-xorg' not in ansible_facts.packages"
|
||||
- "'gdm3' not in ansible_facts.packages"
|
||||
- "'sddm' not in ansible_facts.packages"
|
||||
- "'lightdm' not in ansible_facts.packages"
|
||||
fail_msg: "Un environnement graphique semble installé."
|
||||
|
||||
- name: Collecter les services
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Valider les services de base
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.services['ssh.service'].state == 'running'
|
||||
- ansible_facts.services['qemu-guest-agent.service'].state == 'running'
|
||||
- ansible_facts.services['chrony.service'].state == 'running'
|
||||
- ansible_facts.services['auditd.service'].state == 'running'
|
||||
- ansible_facts.services['fail2ban.service'].state == 'running'
|
||||
fail_msg: "Un service de base n'est pas actif."
|
||||
|
||||
- name: Vérifier que AppArmor est actif
|
||||
ansible.builtin.command: aa-enabled
|
||||
register: apparmor_state
|
||||
changed_when: false
|
||||
|
||||
- name: Valider l'état nftables du template
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.services['nftables.service'].state != 'running'
|
||||
fail_msg: "nftables ne doit pas être actif par défaut dans le template."
|
||||
|
||||
- name: Vérifier cloud-init
|
||||
ansible.builtin.command: cloud-init --version
|
||||
register: cloud_init_version
|
||||
changed_when: false
|
||||
|
||||
- name: Afficher cloud-init
|
||||
ansible.builtin.debug:
|
||||
var: cloud_init_version.stdout
|
||||
|
||||
- name: Vérifier sudo NOPASSWD du compte Ansible
|
||||
ansible.builtin.command: sudo -n true
|
||||
become: false
|
||||
changed_when: false
|
||||
|
||||
- name: Collecter la configuration SSH effective
|
||||
ansible.builtin.command: sshd -T
|
||||
register: sshd_effective_config
|
||||
changed_when: false
|
||||
|
||||
- name: Valider le hardening SSH effectif
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'passwordauthentication no' in sshd_effective_config.stdout"
|
||||
- "'kbdinteractiveauthentication no' in sshd_effective_config.stdout"
|
||||
- "'permitrootlogin no' in sshd_effective_config.stdout"
|
||||
- "'pubkeyauthentication yes' in sshd_effective_config.stdout"
|
||||
- "'authenticationmethods publickey' in sshd_effective_config.stdout"
|
||||
- "'x11forwarding no' in sshd_effective_config.stdout"
|
||||
- "'allowtcpforwarding no' in sshd_effective_config.stdout"
|
||||
- "'allowagentforwarding no' in sshd_effective_config.stdout"
|
||||
- "'allowstreamlocalforwarding no' in sshd_effective_config.stdout"
|
||||
- "'permittunnel no' in sshd_effective_config.stdout"
|
||||
- "'permituserenvironment no' in sshd_effective_config.stdout"
|
||||
- "'maxauthtries 3' in sshd_effective_config.stdout"
|
||||
- "'logingracetime 20' in sshd_effective_config.stdout"
|
||||
- "'maxsessions 2' in sshd_effective_config.stdout"
|
||||
fail_msg: "La configuration SSH effective ne respecte pas le hardening attendu."
|
||||
|
||||
- name: Vérifier les erreurs critiques du boot courant
|
||||
ansible.builtin.command: journalctl -b -p err..alert --no-pager
|
||||
register: boot_errors
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Afficher les erreurs du boot courant
|
||||
ansible.builtin.debug:
|
||||
var: boot_errors.stdout_lines
|
||||
|
|
@ -1,3 +1,79 @@
|
|||
# Playbooks Proxmox
|
||||
|
||||
Espace réservé aux futurs playbooks.
|
||||
Ces playbooks pilotent Proxmox par API depuis le poste Ansible.
|
||||
|
||||
Les valeurs Cloud-Init communes déjà définies sur le modèle Proxmox sont héritées par les clones. Set-OPS ne les redemande pas.
|
||||
|
||||
## Prérequis du poste Ansible
|
||||
|
||||
Le clonage Proxmox utilise les modules `community.general.proxmox_*`.
|
||||
|
||||
Le poste Ansible doit donc avoir :
|
||||
|
||||
- la collection Ansible `community.general` ;
|
||||
- la bibliothèque Python `proxmoxer` disponible dans le Python utilisé par Ansible.
|
||||
|
||||
Sur Debian, installer généralement :
|
||||
|
||||
```bash
|
||||
sudo apt install python3-proxmoxer
|
||||
```
|
||||
|
||||
Vérification locale :
|
||||
|
||||
```bash
|
||||
python3 -c 'import proxmoxer'
|
||||
```
|
||||
|
||||
Les paramètres non sensibles sont dans :
|
||||
|
||||
```text
|
||||
inventories/lab/group_vars/proxmox.yml
|
||||
```
|
||||
|
||||
Assistant terminal :
|
||||
|
||||
```bash
|
||||
make config
|
||||
```
|
||||
|
||||
Les secrets d'API doivent être dans un fichier Vault non versionné :
|
||||
|
||||
```text
|
||||
inventories/lab/group_vars/proxmox.vault.yml
|
||||
```
|
||||
|
||||
Créer le fichier depuis l'exemple, puis le chiffrer :
|
||||
|
||||
```bash
|
||||
cp inventories/lab/group_vars/proxmox.vault.yml.example inventories/lab/group_vars/proxmox.vault.yml
|
||||
ansible-vault encrypt inventories/lab/group_vars/proxmox.vault.yml
|
||||
```
|
||||
|
||||
Créer un clone Debian depuis le modèle :
|
||||
|
||||
```bash
|
||||
make creer-vm \
|
||||
HOTE=web-01 \
|
||||
VMID=101 \
|
||||
VLAN=12 \
|
||||
ADRESSE_IP=192.168.12.101 \
|
||||
PASSERELLE=192.168.12.1 \
|
||||
GROUPES="serveurs_debian serveurs_durcis"
|
||||
```
|
||||
|
||||
`VLAN` doit être saisi à chaque création de VM pour éviter de réutiliser par erreur un réseau d'un autre clone.
|
||||
|
||||
Le disque du clone hérite de la taille du modèle. Pour l'agrandir à la création, fournir explicitement une taille supérieure à celle du modèle :
|
||||
|
||||
```bash
|
||||
make creer-vm HOTE=web-01 VMID=101 VLAN=12 ADRESSE_IP=192.168.12.101 PASSERELLE=192.168.12.1 TAILLE_DISQUE=64G GROUPES="serveurs_debian serveurs_durcis"
|
||||
```
|
||||
|
||||
Proxmox ne permet pas de réduire un disque existant.
|
||||
|
||||
`creer-vm` clone la VM, applique les paramètres Cloud-Init et ajoute l'hôte à l'inventaire. La conformité Ansible reste séparée :
|
||||
|
||||
```bash
|
||||
make deployer HOTE=web-01
|
||||
```
|
||||
|
|
|
|||
202
playbooks/proxmox/cloner_vm_debian.yml
Normal file
202
playbooks/proxmox/cloner_vm_debian.yml
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
---
|
||||
- name: Cloner une VM Debian depuis le modele Proxmox
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Verifier les fichiers de variables Proxmox optionnels
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
loop:
|
||||
- "{{ playbook_dir }}/../../inventories/lab/group_vars/proxmox.yml"
|
||||
- "{{ playbook_dir }}/../../inventories/lab/group_vars/proxmox.vault.yml"
|
||||
- "{{ playbook_dir }}/../../inventories/lab/group_vars/proxmox.local.yml"
|
||||
register: proxmox_fichiers_variables
|
||||
|
||||
- name: Charger les variables Proxmox non sensibles disponibles
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ item.stat.path }}"
|
||||
loop: "{{ proxmox_fichiers_variables.results }}"
|
||||
when:
|
||||
- item.stat.exists
|
||||
- item.stat.path is not search('vault')
|
||||
|
||||
- name: Charger les secrets Proxmox Vault
|
||||
block:
|
||||
- name: Charger proxmox.vault.yml
|
||||
ansible.builtin.include_vars:
|
||||
file: "{{ item.stat.path }}"
|
||||
loop: "{{ proxmox_fichiers_variables.results }}"
|
||||
when:
|
||||
- item.stat.exists
|
||||
- item.stat.path is search('vault')
|
||||
no_log: true
|
||||
rescue:
|
||||
- name: Expliquer l echec de chargement du Vault
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
Impossible de charger inventories/lab/group_vars/proxmox.vault.yml.
|
||||
Si le fichier est chiffre avec Ansible Vault, fournir son mot de passe
|
||||
ou definir ANSIBLE_VAULT_PASSWORD_FILE avant de relancer make creer-vm.
|
||||
|
||||
- name: Appliquer les valeurs Proxmox avec repli environnement
|
||||
ansible.builtin.set_fact:
|
||||
proxmox_api_host_effectif: "{{ proxmox_api_host | default(lookup('env', 'PROXMOX_API_HOST'), true) }}"
|
||||
proxmox_api_user_effectif: "{{ proxmox_api_user | default(lookup('env', 'PROXMOX_API_USER'), true) }}"
|
||||
proxmox_api_token_id_effectif: "{{ proxmox_api_token_id | default(lookup('env', 'PROXMOX_API_TOKEN_ID'), true) }}"
|
||||
proxmox_api_token_secret_effectif: "{{ proxmox_api_token_secret | default(lookup('env', 'PROXMOX_API_TOKEN_SECRET'), true) }}"
|
||||
proxmox_api_port_effectif: "{{ proxmox_api_port | default(lookup('env', 'PROXMOX_API_PORT'), true) }}"
|
||||
no_log: true
|
||||
|
||||
- name: Valider les parametres obligatoires Proxmox
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- proxmox_api_host_effectif | length > 0
|
||||
- proxmox_api_user_effectif | length > 0
|
||||
- proxmox_api_token_id_effectif | length > 0
|
||||
- proxmox_api_token_secret_effectif | length > 0
|
||||
- proxmox_clone_nom is defined
|
||||
- proxmox_clone_nom | length > 0
|
||||
- proxmox_clone_vmid_modele is defined
|
||||
- proxmox_clone_vmid is defined
|
||||
- proxmox_clone_noeud is defined
|
||||
- proxmox_clone_noeud | length > 0
|
||||
- proxmox_clone_ipconfig0 is defined
|
||||
- proxmox_clone_ipconfig0 | length > 0
|
||||
fail_msg: "Parametres Proxmox incomplets. Verifier proxmox.yml, proxmox.vault.yml et les variables make propres a la VM."
|
||||
|
||||
- name: Lire la cle publique SSH depuis le fichier fourni
|
||||
ansible.builtin.set_fact:
|
||||
proxmox_clone_sshkeys: "{{ lookup('ansible.builtin.file', proxmox_clone_cle_publique_fichier) }}"
|
||||
when:
|
||||
- proxmox_clone_cle_publique is not defined
|
||||
- proxmox_clone_cle_publique_fichier is defined
|
||||
- proxmox_clone_cle_publique_fichier | length > 0
|
||||
|
||||
- name: Utiliser la cle publique SSH fournie directement
|
||||
ansible.builtin.set_fact:
|
||||
proxmox_clone_sshkeys: "{{ proxmox_clone_cle_publique }}"
|
||||
when:
|
||||
- proxmox_clone_cle_publique is defined
|
||||
- proxmox_clone_cle_publique | length > 0
|
||||
|
||||
- name: Construire les serveurs DNS Cloud-Init
|
||||
ansible.builtin.set_fact:
|
||||
proxmox_clone_nameservers: >-
|
||||
{{
|
||||
(proxmox_clone_dns | default('') | split(','))
|
||||
| map('trim')
|
||||
| reject('equalto', '')
|
||||
| list
|
||||
}}
|
||||
|
||||
- name: Verifier la bibliotheque Python proxmoxer locale
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ ansible_playbook_python }} -c 'import proxmoxer'"
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
register: proxmoxer_verification
|
||||
|
||||
- name: Refuser si proxmoxer est absent
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
La bibliotheque Python proxmoxer est absente pour {{ ansible_playbook_python }}.
|
||||
Installer le paquet systeme approprie, par exemple
|
||||
sudo apt install python3-proxmoxer, ou installer proxmoxer dans
|
||||
l'environnement Python utilise par Ansible, puis relancer make creer-vm.
|
||||
when: proxmoxer_verification.rc != 0
|
||||
|
||||
- name: Cloner la VM depuis le modele
|
||||
community.general.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host_effectif }}"
|
||||
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
||||
api_user: "{{ proxmox_api_user_effectif }}"
|
||||
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
||||
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
||||
node: "{{ proxmox_clone_noeud }}"
|
||||
clone: "{{ proxmox_clone_source_nom | default('modele-debian13') }}"
|
||||
vmid: "{{ proxmox_clone_vmid_modele | int }}"
|
||||
newid: "{{ proxmox_clone_vmid | int }}"
|
||||
name: "{{ proxmox_clone_nom }}"
|
||||
storage: "{{ proxmox_clone_stockage | default(omit, true) }}"
|
||||
format: "{{ proxmox_clone_format | default(omit, true) }}"
|
||||
full: "{{ proxmox_clone_complet | default(true) | bool }}"
|
||||
timeout: "{{ proxmox_clone_timeout | default(600) | int }}"
|
||||
|
||||
- name: Ajuster le reseau de la VM clonee
|
||||
community.general.proxmox_nic:
|
||||
api_host: "{{ proxmox_api_host_effectif }}"
|
||||
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
||||
api_user: "{{ proxmox_api_user_effectif }}"
|
||||
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
||||
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
||||
vmid: "{{ proxmox_clone_vmid | int }}"
|
||||
interface: "{{ proxmox_clone_interface | default('net0') }}"
|
||||
model: virtio
|
||||
bridge: "{{ proxmox_clone_pont }}"
|
||||
tag: "{{ proxmox_clone_vlan | int if proxmox_clone_vlan is defined and proxmox_clone_vlan | string | length > 0 else omit }}"
|
||||
firewall: "{{ proxmox_clone_parefeu_interface | default(false) | bool }}"
|
||||
state: present
|
||||
when:
|
||||
- proxmox_clone_pont is defined
|
||||
- proxmox_clone_pont | length > 0
|
||||
|
||||
- name: Agrandir le disque principal du clone
|
||||
community.general.proxmox_disk:
|
||||
api_host: "{{ proxmox_api_host_effectif }}"
|
||||
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
||||
api_user: "{{ proxmox_api_user_effectif }}"
|
||||
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
||||
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
||||
vmid: "{{ proxmox_clone_vmid | int }}"
|
||||
disk: "{{ proxmox_clone_disque | default('scsi0') }}"
|
||||
size: "{{ proxmox_clone_taille_disque }}"
|
||||
state: resized
|
||||
when:
|
||||
- proxmox_clone_taille_disque is defined
|
||||
- proxmox_clone_taille_disque | length > 0
|
||||
|
||||
- name: Configurer Cloud-Init sur le clone
|
||||
community.general.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host_effectif }}"
|
||||
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
||||
api_user: "{{ proxmox_api_user_effectif }}"
|
||||
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
||||
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
||||
node: "{{ proxmox_clone_noeud }}"
|
||||
vmid: "{{ proxmox_clone_vmid | int }}"
|
||||
name: "{{ proxmox_clone_nom }}"
|
||||
update: true
|
||||
ciuser: "{{ proxmox_clone_ciuser | default(omit, true) }}"
|
||||
sshkeys: "{{ proxmox_clone_sshkeys | default(omit) }}"
|
||||
ipconfig:
|
||||
ipconfig0: "{{ proxmox_clone_ipconfig0 }}"
|
||||
nameservers: "{{ proxmox_clone_nameservers if proxmox_clone_nameservers | length > 0 else omit }}"
|
||||
searchdomains: "{{ proxmox_clone_domaines_recherche | default(omit, true) }}"
|
||||
agent: "enabled=1"
|
||||
|
||||
- name: Demarrer le clone
|
||||
community.general.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host_effectif }}"
|
||||
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
||||
api_user: "{{ proxmox_api_user_effectif }}"
|
||||
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
||||
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
||||
node: "{{ proxmox_clone_noeud }}"
|
||||
vmid: "{{ proxmox_clone_vmid | int }}"
|
||||
name: "{{ proxmox_clone_nom }}"
|
||||
state: started
|
||||
when: proxmox_clone_demarrer | default(true) | bool
|
||||
|
||||
- name: Afficher le resume du clone
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "VM creee: {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }})"
|
||||
- "Cloud-Init ipconfig0: {{ proxmox_clone_ipconfig0 }}"
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
---
|
||||
- name: Vérifier le template Debian 13 Proxmox Chezlepro
|
||||
hosts: vm_templates
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
tasks:
|
||||
- name: Afficher les partitions
|
||||
ansible.builtin.command: lsblk -f
|
||||
register: lsblk_output
|
||||
changed_when: false
|
||||
|
||||
- name: Résultat lsblk
|
||||
ansible.builtin.debug:
|
||||
var: lsblk_output.stdout_lines
|
||||
|
||||
- name: Vérifier l'absence de swap actif
|
||||
ansible.builtin.command: swapon --show
|
||||
register: swapon_output
|
||||
changed_when: false
|
||||
failed_when: swapon_output.stdout | length > 0
|
||||
|
||||
- name: Collecter les services
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Valider les services de base
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.services['ssh.service'].state == 'running'
|
||||
- ansible_facts.services['qemu-guest-agent.service'].state == 'running'
|
||||
- ansible_facts.services['chrony.service'].state == 'running'
|
||||
fail_msg: "Un service de base n'est pas actif."
|
||||
|
||||
- name: Vérifier cloud-init
|
||||
ansible.builtin.command: cloud-init --version
|
||||
register: cloud_init_version
|
||||
changed_when: false
|
||||
|
||||
- name: Afficher cloud-init
|
||||
ansible.builtin.debug:
|
||||
var: cloud_init_version.stdout
|
||||
|
||||
- name: Vérifier les erreurs critiques du boot courant
|
||||
ansible.builtin.command: journalctl -b -p err..alert --no-pager
|
||||
register: boot_errors
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Afficher les erreurs du boot courant
|
||||
ansible.builtin.debug:
|
||||
var: boot_errors.stdout_lines
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
chezlepro_apparmor_enabled: true
|
||||
apparmor_enabled: true
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@
|
|||
name: apparmor
|
||||
enabled: true
|
||||
state: started
|
||||
when: chezlepro_apparmor_enabled | bool
|
||||
when: apparmor_enabled | bool
|
||||
failed_when: false
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
chezlepro_auditd_enabled: true
|
||||
auditd_enabled: true
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@
|
|||
group: root
|
||||
mode: "0640"
|
||||
notify: Restart auditd
|
||||
when: chezlepro_auditd_enabled | bool
|
||||
when: auditd_enabled | bool
|
||||
|
||||
- name: Activer auditd
|
||||
ansible.builtin.systemd:
|
||||
name: auditd
|
||||
enabled: true
|
||||
state: started
|
||||
when: chezlepro_auditd_enabled | bool
|
||||
when: auditd_enabled | bool
|
||||
failed_when: false
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
- name: Installer chrony
|
||||
ansible.builtin.apt:
|
||||
name: chrony
|
||||
state: present
|
||||
|
||||
- name: Activer et démarrer chrony
|
||||
ansible.builtin.systemd:
|
||||
name: chrony
|
||||
enabled: true
|
||||
state: started
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
cloud_init_ssh_pwauth: true
|
||||
cloud_init_manage_etc_hosts: true
|
||||
cloud_init_disable_root: true
|
||||
cloud_init_ssh_deletekeys: true
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
- name: Installer cloud-init et cloud-guest-utils
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- cloud-init
|
||||
- cloud-guest-utils
|
||||
state: present
|
||||
|
||||
- name: Déployer la configuration cloud-init Chezlepro
|
||||
ansible.builtin.template:
|
||||
src: 99_chezlepro.cfg.j2
|
||||
dest: /etc/cloud/cloud.cfg.d/99_chezlepro.cfg
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Activer les services cloud-init Debian 13
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
loop:
|
||||
- cloud-init-local.service
|
||||
- cloud-init-main.service
|
||||
- cloud-init-network.service
|
||||
- cloud-config.service
|
||||
- cloud-final.service
|
||||
failed_when: false
|
||||
|
||||
- name: Vérifier cloud-init
|
||||
ansible.builtin.command: cloud-init --version
|
||||
register: cloud_init_version
|
||||
changed_when: false
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
# Configuration cloud-init pour template Debian 13 Chezlepro.
|
||||
|
||||
datasource_list: [ NoCloud, ConfigDrive, None ]
|
||||
|
||||
preserve_hostname: false
|
||||
manage_etc_hosts: {{ cloud_init_manage_etc_hosts | bool | lower }}
|
||||
disable_root: {{ cloud_init_disable_root | bool | lower }}
|
||||
ssh_pwauth: {{ cloud_init_ssh_pwauth | bool | lower }}
|
||||
|
||||
ssh_deletekeys: {{ cloud_init_ssh_deletekeys | bool | lower }}
|
||||
ssh_genkeytypes:
|
||||
- rsa
|
||||
- ecdsa
|
||||
- ed25519
|
||||
|
||||
growpart:
|
||||
mode: auto
|
||||
devices:
|
||||
- /
|
||||
ignore_growroot_disabled: false
|
||||
|
||||
resize_rootfs: true
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
---
|
||||
chezlepro_common_packages: []
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
- name: Mettre à jour le cache APT
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Appliquer les mises à jour disponibles
|
||||
ansible.builtin.apt:
|
||||
upgrade: full
|
||||
|
||||
- name: Installer les paquets communs
|
||||
ansible.builtin.apt:
|
||||
name: "{{ chezlepro_common_packages }}"
|
||||
state: present
|
||||
|
||||
- name: Supprimer les dépendances devenues inutiles
|
||||
ansible.builtin.apt:
|
||||
autoremove: true
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
- name: Déployer le MOTD Chezlepro
|
||||
ansible.builtin.template:
|
||||
src: motd.j2
|
||||
dest: /etc/motd
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
Système Chezlepro
|
||||
Gestion : Set-OPS / Ansible
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
- name: Installer qemu-guest-agent
|
||||
ansible.builtin.apt:
|
||||
name: qemu-guest-agent
|
||||
state: present
|
||||
|
||||
- name: Activer et démarrer qemu-guest-agent
|
||||
ansible.builtin.systemd:
|
||||
name: qemu-guest-agent
|
||||
enabled: true
|
||||
state: started
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
cloud_init_ssh_pwauth: true
|
||||
cloud_init_ssh_pwauth: false
|
||||
cloud_init_manage_etc_hosts: true
|
||||
cloud_init_disable_root: true
|
||||
cloud_init_ssh_deletekeys: true
|
||||
|
|
|
|||
|
|
@ -1,2 +1,48 @@
|
|||
---
|
||||
chezlepro_common_packages: []
|
||||
common_packages_list:
|
||||
- sudo
|
||||
- openssh-server
|
||||
- qemu-guest-agent
|
||||
- cloud-init
|
||||
- cloud-guest-utils
|
||||
- python3
|
||||
- python3-apt
|
||||
- python3-pip
|
||||
- acl
|
||||
- curl
|
||||
- wget
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- git
|
||||
- vim
|
||||
- nano
|
||||
- bash-completion
|
||||
- tmux
|
||||
- rsync
|
||||
- unzip
|
||||
- zip
|
||||
- tar
|
||||
- jq
|
||||
- htop
|
||||
- iotop
|
||||
- iftop
|
||||
- sysstat
|
||||
- lsof
|
||||
- ncdu
|
||||
- tree
|
||||
- file
|
||||
- less
|
||||
- dnsutils
|
||||
- iproute2
|
||||
- iputils-ping
|
||||
- net-tools
|
||||
- traceroute
|
||||
- mtr-tiny
|
||||
- tcpdump
|
||||
- netcat-openbsd
|
||||
- socat
|
||||
- chrony
|
||||
- logrotate
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
- needrestart
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
- name: Installer les paquets communs
|
||||
ansible.builtin.apt:
|
||||
name: "{{ chezlepro_common_packages }}"
|
||||
name: "{{ common_packages_list }}"
|
||||
state: present
|
||||
|
||||
- name: Supprimer les dépendances devenues inutiles
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
chezlepro_disable_core_dumps: true
|
||||
core_dumps_disable: true
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: chezlepro_disable_core_dumps | bool
|
||||
when: core_dumps_disable | bool
|
||||
|
||||
- name: Configurer systemd-coredump si présent
|
||||
ansible.builtin.template:
|
||||
|
|
@ -15,5 +15,5 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: chezlepro_disable_core_dumps | bool
|
||||
when: core_dumps_disable | bool
|
||||
failed_when: false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
chezlepro_fail2ban_enabled: true
|
||||
chezlepro_fail2ban_bantime: "1h"
|
||||
chezlepro_fail2ban_findtime: "10m"
|
||||
chezlepro_fail2ban_maxretry: 5
|
||||
chezlepro_ssh_port: 22
|
||||
fail2ban_ssh_enabled: true
|
||||
fail2ban_ssh_bantime: "1h"
|
||||
fail2ban_ssh_findtime: "10m"
|
||||
fail2ban_ssh_maxretry: 5
|
||||
fail2ban_ssh_port: 22
|
||||
|
|
|
|||
|
|
@ -26,4 +26,4 @@
|
|||
name: fail2ban
|
||||
enabled: true
|
||||
state: started
|
||||
when: chezlepro_fail2ban_enabled | bool
|
||||
when: fail2ban_ssh_enabled | bool
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
|
||||
[sshd]
|
||||
enabled = {{ 'true' if chezlepro_fail2ban_enabled | bool else 'false' }}
|
||||
port = {{ chezlepro_ssh_port }}
|
||||
enabled = {{ 'true' if fail2ban_ssh_enabled | bool else 'false' }}
|
||||
port = {{ fail2ban_ssh_port }}
|
||||
backend = systemd
|
||||
maxretry = {{ chezlepro_fail2ban_maxretry }}
|
||||
findtime = {{ chezlepro_fail2ban_findtime }}
|
||||
bantime = {{ chezlepro_fail2ban_bantime }}
|
||||
maxretry = {{ fail2ban_ssh_maxretry }}
|
||||
findtime = {{ fail2ban_ssh_findtime }}
|
||||
bantime = {{ fail2ban_ssh_bantime }}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,13 @@
|
|||
---
|
||||
chezlepro_hardening_packages: []
|
||||
hardening_packages_list:
|
||||
- apparmor
|
||||
- apparmor-utils
|
||||
- auditd
|
||||
- audispd-plugins
|
||||
- fail2ban
|
||||
- nftables
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
- debsums
|
||||
- needrestart
|
||||
- libpam-pwquality
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
- name: Installer les paquets de hardening
|
||||
ansible.builtin.apt:
|
||||
name: "{{ chezlepro_hardening_packages }}"
|
||||
name: "{{ hardening_packages_list }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
chezlepro_journald_system_max_use: "200M"
|
||||
chezlepro_journald_runtime_max_use: "100M"
|
||||
chezlepro_journald_max_retention_sec: "1month"
|
||||
journald_system_max_use: "200M"
|
||||
journald_runtime_max_use: "100M"
|
||||
journald_max_retention_sec: "1month"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
[Journal]
|
||||
SystemMaxUse={{ chezlepro_journald_system_max_use }}
|
||||
RuntimeMaxUse={{ chezlepro_journald_runtime_max_use }}
|
||||
MaxRetentionSec={{ chezlepro_journald_max_retention_sec }}
|
||||
SystemMaxUse={{ journald_system_max_use }}
|
||||
RuntimeMaxUse={{ journald_runtime_max_use }}
|
||||
MaxRetentionSec={{ journald_max_retention_sec }}
|
||||
Compress=yes
|
||||
Seal=yes
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
chezlepro_nftables_enabled: false
|
||||
chezlepro_firewall_allow_ssh: true
|
||||
chezlepro_firewall_allow_icmp: true
|
||||
chezlepro_firewall_input_policy: "drop"
|
||||
chezlepro_firewall_forward_policy: "drop"
|
||||
chezlepro_firewall_output_policy: "accept"
|
||||
chezlepro_ssh_port: 22
|
||||
chezlepro_firewall_extra_tcp_ports: []
|
||||
chezlepro_firewall_extra_udp_ports: []
|
||||
nftables_baseline_enabled: false
|
||||
nftables_baseline_allow_ssh: true
|
||||
nftables_baseline_allow_icmp: true
|
||||
nftables_baseline_input_policy: "drop"
|
||||
nftables_baseline_forward_policy: "drop"
|
||||
nftables_baseline_output_policy: "accept"
|
||||
nftables_baseline_ssh_port: 22
|
||||
nftables_baseline_extra_tcp_ports: []
|
||||
nftables_baseline_extra_udp_ports: []
|
||||
|
|
|
|||
|
|
@ -12,19 +12,19 @@
|
|||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload nftables
|
||||
when: chezlepro_nftables_enabled | bool
|
||||
when: nftables_baseline_enabled | bool
|
||||
|
||||
- name: Activer nftables si demandé
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
enabled: true
|
||||
state: started
|
||||
when: chezlepro_nftables_enabled | bool
|
||||
when: nftables_baseline_enabled | bool
|
||||
|
||||
- name: Désactiver nftables si demandé
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
enabled: false
|
||||
state: stopped
|
||||
when: not (chezlepro_nftables_enabled | bool)
|
||||
when: not (nftables_baseline_enabled | bool)
|
||||
failed_when: false
|
||||
|
|
|
|||
|
|
@ -5,35 +5,35 @@ flush ruleset
|
|||
|
||||
table inet chezlepro_filter {
|
||||
chain input {
|
||||
type filter hook input priority 0; policy {{ chezlepro_firewall_input_policy }};
|
||||
type filter hook input priority 0; policy {{ nftables_baseline_input_policy }};
|
||||
|
||||
iif "lo" accept
|
||||
ct state established,related accept
|
||||
ct state invalid drop
|
||||
|
||||
{% if chezlepro_firewall_allow_icmp | bool %}
|
||||
{% if nftables_baseline_allow_icmp | bool %}
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
{% endif %}
|
||||
|
||||
{% if chezlepro_firewall_allow_ssh | bool %}
|
||||
tcp dport {{ chezlepro_ssh_port }} accept
|
||||
{% if nftables_baseline_allow_ssh | bool %}
|
||||
tcp dport {{ nftables_baseline_ssh_port }} accept
|
||||
{% endif %}
|
||||
|
||||
{% for port in chezlepro_firewall_extra_tcp_ports %}
|
||||
{% for port in nftables_baseline_extra_tcp_ports %}
|
||||
tcp dport {{ port }} accept
|
||||
{% endfor %}
|
||||
|
||||
{% for port in chezlepro_firewall_extra_udp_ports %}
|
||||
{% for port in nftables_baseline_extra_udp_ports %}
|
||||
udp dport {{ port }} accept
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority 0; policy {{ chezlepro_firewall_forward_policy }};
|
||||
type filter hook forward priority 0; policy {{ nftables_baseline_forward_policy }};
|
||||
}
|
||||
|
||||
chain output {
|
||||
type filter hook output priority 0; policy {{ chezlepro_firewall_output_policy }};
|
||||
type filter hook output priority 0; policy {{ nftables_baseline_output_policy }};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
chezlepro_ssh_password_authentication: "yes"
|
||||
chezlepro_ssh_permit_root_login: "no"
|
||||
chezlepro_ssh_pubkey_authentication: "yes"
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
- name: Validate and reload ssh
|
||||
block:
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
- name: Installer openssh-server
|
||||
ansible.builtin.apt:
|
||||
name: openssh-server
|
||||
state: present
|
||||
|
||||
- name: Créer le répertoire sshd_config.d
|
||||
ansible.builtin.file:
|
||||
path: /etc/ssh/sshd_config.d
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Déployer la configuration SSH Chezlepro
|
||||
ansible.builtin.template:
|
||||
src: 10-chezlepro.conf.j2
|
||||
dest: /etc/ssh/sshd_config.d/10-chezlepro.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Validate and reload ssh
|
||||
|
||||
- name: Activer et démarrer SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
enabled: true
|
||||
state: started
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
|
||||
PermitRootLogin {{ chezlepro_ssh_permit_root_login }}
|
||||
PubkeyAuthentication {{ chezlepro_ssh_pubkey_authentication }}
|
||||
PasswordAuthentication {{ chezlepro_ssh_password_authentication }}
|
||||
KbdInteractiveAuthentication no
|
||||
X11Forwarding no
|
||||
UsePAM yes
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
---
|
||||
chezlepro_admin_user: ansible
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
- name: S'assurer que le groupe sudo existe
|
||||
ansible.builtin.group:
|
||||
name: sudo
|
||||
state: present
|
||||
|
||||
- name: S'assurer que le compte technique existe
|
||||
ansible.builtin.user:
|
||||
name: "{{ chezlepro_admin_user }}"
|
||||
shell: /bin/bash
|
||||
groups: sudo
|
||||
append: true
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Déployer sudo NOPASSWD pour le compte technique
|
||||
ansible.builtin.template:
|
||||
src: 90-ansible.j2
|
||||
dest: "/etc/sudoers.d/90-{{ chezlepro_admin_user }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
validate: "visudo -cf %s"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
{{ chezlepro_admin_user }} ALL=(ALL) NOPASSWD:ALL
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
chezlepro_ssh_password_authentication: "yes"
|
||||
chezlepro_ssh_permit_root_login: "no"
|
||||
chezlepro_ssh_pubkey_authentication: "yes"
|
||||
ssh_baseline_password_authentication: "no"
|
||||
ssh_baseline_permit_root_login: "no"
|
||||
ssh_baseline_pubkey_authentication: "yes"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
- name: Validate and reload ssh
|
||||
block:
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
listen: Validate and reload ssh
|
||||
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
listen: Validate and reload ssh
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
|
||||
PermitRootLogin {{ chezlepro_ssh_permit_root_login }}
|
||||
PubkeyAuthentication {{ chezlepro_ssh_pubkey_authentication }}
|
||||
PasswordAuthentication {{ chezlepro_ssh_password_authentication }}
|
||||
PermitRootLogin {{ ssh_baseline_permit_root_login }}
|
||||
PubkeyAuthentication {{ ssh_baseline_pubkey_authentication }}
|
||||
PasswordAuthentication {{ ssh_baseline_password_authentication }}
|
||||
AuthenticationMethods publickey
|
||||
KbdInteractiveAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
HostbasedAuthentication no
|
||||
IgnoreRhosts yes
|
||||
X11Forwarding no
|
||||
PermitUserEnvironment no
|
||||
UsePAM yes
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
chezlepro_ssh_port: 22
|
||||
chezlepro_ssh_password_authentication: "yes"
|
||||
chezlepro_ssh_permit_root_login: "no"
|
||||
chezlepro_ssh_pubkey_authentication: "yes"
|
||||
chezlepro_ssh_client_alive_interval: 300
|
||||
chezlepro_ssh_client_alive_count_max: 2
|
||||
chezlepro_ssh_max_auth_tries: 4
|
||||
chezlepro_ssh_login_grace_time: "30s"
|
||||
ssh_hardening_port: 22
|
||||
ssh_hardening_password_authentication: "no"
|
||||
ssh_hardening_permit_root_login: "no"
|
||||
ssh_hardening_pubkey_authentication: "yes"
|
||||
ssh_hardening_client_alive_interval: 300
|
||||
ssh_hardening_client_alive_count_max: 2
|
||||
ssh_hardening_max_auth_tries: 3
|
||||
ssh_hardening_login_grace_time: "20s"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
- name: Validate and reload ssh
|
||||
block:
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
- name: Valider la configuration SSH
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
listen: Validate and reload ssh
|
||||
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
- name: Recharger SSH
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: reloaded
|
||||
listen: Validate and reload ssh
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
|
||||
Port {{ chezlepro_ssh_port }}
|
||||
PermitRootLogin {{ chezlepro_ssh_permit_root_login }}
|
||||
PubkeyAuthentication {{ chezlepro_ssh_pubkey_authentication }}
|
||||
PasswordAuthentication {{ chezlepro_ssh_password_authentication }}
|
||||
Port {{ ssh_hardening_port }}
|
||||
PermitRootLogin {{ ssh_hardening_permit_root_login }}
|
||||
PubkeyAuthentication {{ ssh_hardening_pubkey_authentication }}
|
||||
PasswordAuthentication {{ ssh_hardening_password_authentication }}
|
||||
AuthenticationMethods publickey
|
||||
KbdInteractiveAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
HostbasedAuthentication no
|
||||
IgnoreRhosts yes
|
||||
X11Forwarding no
|
||||
AllowTcpForwarding no
|
||||
AllowAgentForwarding no
|
||||
ClientAliveInterval {{ chezlepro_ssh_client_alive_interval }}
|
||||
ClientAliveCountMax {{ chezlepro_ssh_client_alive_count_max }}
|
||||
MaxAuthTries {{ chezlepro_ssh_max_auth_tries }}
|
||||
LoginGraceTime {{ chezlepro_ssh_login_grace_time }}
|
||||
AllowStreamLocalForwarding no
|
||||
PermitTunnel no
|
||||
GatewayPorts no
|
||||
PermitUserEnvironment no
|
||||
PermitTTY yes
|
||||
TCPKeepAlive no
|
||||
ClientAliveInterval {{ ssh_hardening_client_alive_interval }}
|
||||
ClientAliveCountMax {{ ssh_hardening_client_alive_count_max }}
|
||||
MaxAuthTries {{ ssh_hardening_max_auth_tries }}
|
||||
LoginGraceTime {{ ssh_hardening_login_grace_time }}
|
||||
MaxSessions 2
|
||||
MaxStartups 5:30:20
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
chezlepro_admin_user: ansible
|
||||
sudo_ansible_admin_user: ansible
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
- name: S'assurer que le compte technique existe
|
||||
ansible.builtin.user:
|
||||
name: "{{ chezlepro_admin_user }}"
|
||||
name: "{{ sudo_ansible_admin_user }}"
|
||||
shell: /bin/bash
|
||||
groups: sudo
|
||||
append: true
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
- name: Déployer sudo NOPASSWD pour le compte technique
|
||||
ansible.builtin.template:
|
||||
src: 90-ansible.j2
|
||||
dest: "/etc/sudoers.d/90-{{ chezlepro_admin_user }}"
|
||||
dest: "/etc/sudoers.d/90-{{ sudo_ansible_admin_user }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
{{ chezlepro_admin_user }} ALL=(ALL) NOPASSWD:ALL
|
||||
{{ sudo_ansible_admin_user }} ALL=(ALL) NOPASSWD:ALL
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
chezlepro_sysctl_apply: true
|
||||
chezlepro_sysctl_hardening_values:
|
||||
sysctl_hardening_apply: true
|
||||
sysctl_hardening_values:
|
||||
fs.protected_hardlinks: 1
|
||||
fs.protected_symlinks: 1
|
||||
fs.protected_fifos: 2
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@
|
|||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload sysctl
|
||||
when: chezlepro_sysctl_apply | bool
|
||||
when: sysctl_hardening_apply | bool
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
|
||||
{% for key, value in chezlepro_sysctl_hardening_values | dictsort %}
|
||||
{% for key, value in sysctl_hardening_values | dictsort %}
|
||||
{{ key }} = {{ value }}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
chezlepro_disable_systemd_ssh_auto: true
|
||||
systemd_ssh_auto_disable: true
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: chezlepro_disable_systemd_ssh_auto | bool
|
||||
when: systemd_ssh_auto_disable | bool
|
||||
notify: Update grub
|
||||
|
||||
- name: Supprimer le paramètre si désactivé
|
||||
ansible.builtin.file:
|
||||
path: /etc/default/grub.d/99-chezlepro-systemd-ssh-auto.cfg
|
||||
state: absent
|
||||
when: not (chezlepro_disable_systemd_ssh_auto | bool)
|
||||
when: not (systemd_ssh_auto_disable | bool)
|
||||
notify: Update grub
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
confirm_template_cleanup: false
|
||||
chezlepro_admin_user: ansible
|
||||
template_cleanup_confirm: false
|
||||
template_cleanup_admin_user: ansible
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
- name: Refuser le nettoyage sans confirmation explicite
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- confirm_template_cleanup | bool
|
||||
fail_msg: "Nettoyage refusé. Relancer avec -e confirm_template_cleanup=true"
|
||||
- template_cleanup_confirm | bool
|
||||
fail_msg: "Nettoyage refusé. Relancer avec -e template_cleanup_confirm=true"
|
||||
|
||||
- name: Nettoyer cloud-init
|
||||
ansible.builtin.command: cloud-init clean --logs
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
state: absent
|
||||
loop:
|
||||
- /root/.bash_history
|
||||
- "/home/{{ chezlepro_admin_user }}/.bash_history"
|
||||
- "/home/{{ template_cleanup_admin_user }}/.bash_history"
|
||||
failed_when: false
|
||||
|
||||
- name: Message final
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
chezlepro_unattended_upgrades_enabled: true
|
||||
chezlepro_unattended_remove_unused_dependencies: true
|
||||
chezlepro_unattended_automatic_reboot: false
|
||||
unattended_upgrades_enabled: true
|
||||
unattended_upgrades_remove_unused_dependencies: true
|
||||
unattended_upgrades_automatic_reboot: false
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Managed by Ansible — Set-OPS
|
||||
APT::Periodic::Update-Package-Lists "{{ 1 if chezlepro_unattended_upgrades_enabled | bool else 0 }}";
|
||||
APT::Periodic::Unattended-Upgrade "{{ 1 if chezlepro_unattended_upgrades_enabled | bool else 0 }}";
|
||||
APT::Periodic::Update-Package-Lists "{{ 1 if unattended_upgrades_enabled | bool else 0 }}";
|
||||
APT::Periodic::Unattended-Upgrade "{{ 1 if unattended_upgrades_enabled | bool else 0 }}";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Unattended-Upgrade::Origins-Pattern {
|
|||
};
|
||||
|
||||
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
|
||||
Unattended-Upgrade::Remove-New-Unused-Dependencies "{{ 'true' if chezlepro_unattended_remove_unused_dependencies | bool else 'false' }}";
|
||||
Unattended-Upgrade::Remove-Unused-Dependencies "{{ 'true' if chezlepro_unattended_remove_unused_dependencies | bool else 'false' }}";
|
||||
Unattended-Upgrade::Automatic-Reboot "{{ 'true' if chezlepro_unattended_automatic_reboot | bool else 'false' }}";
|
||||
Unattended-Upgrade::Remove-New-Unused-Dependencies "{{ 'true' if unattended_upgrades_remove_unused_dependencies | bool else 'false' }}";
|
||||
Unattended-Upgrade::Remove-Unused-Dependencies "{{ 'true' if unattended_upgrades_remove_unused_dependencies | bool else 'false' }}";
|
||||
Unattended-Upgrade::Automatic-Reboot "{{ 'true' if unattended_upgrades_automatic_reboot | bool else 'false' }}";
|
||||
Unattended-Upgrade::SyslogEnable "true";
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
---
|
||||
chezlepro_disable_systemd_ssh_auto: true
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
- name: Update grub
|
||||
ansible.builtin.command: update-grub
|
||||
changed_when: true
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
- name: Déployer le paramètre GRUB pour désactiver systemd.ssh_auto
|
||||
ansible.builtin.template:
|
||||
src: 99-chezlepro-systemd-ssh-auto.cfg.j2
|
||||
dest: /etc/default/grub.d/99-chezlepro-systemd-ssh-auto.cfg
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: chezlepro_disable_systemd_ssh_auto | bool
|
||||
notify: Update grub
|
||||
|
||||
- name: Supprimer le paramètre si désactivé dans les variables
|
||||
ansible.builtin.file:
|
||||
path: /etc/default/grub.d/99-chezlepro-systemd-ssh-auto.cfg
|
||||
state: absent
|
||||
when: not (chezlepro_disable_systemd_ssh_auto | bool)
|
||||
notify: Update grub
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# Managed by Ansible — Set-OPS
|
||||
GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} systemd.ssh_auto=no"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
---
|
||||
confirm_template_cleanup: false
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
---
|
||||
- name: Refuser le nettoyage sans confirmation explicite
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- confirm_template_cleanup | bool
|
||||
fail_msg: "Nettoyage refusé. Relancer avec -e confirm_template_cleanup=true"
|
||||
|
||||
- name: Nettoyer cloud-init
|
||||
ansible.builtin.command: cloud-init clean --logs
|
||||
changed_when: true
|
||||
|
||||
- name: Nettoyer le cache APT
|
||||
ansible.builtin.apt:
|
||||
clean: true
|
||||
|
||||
- name: Supprimer les paquets devenus inutiles
|
||||
ansible.builtin.apt:
|
||||
autoremove: true
|
||||
|
||||
- name: Rotation des journaux systemd
|
||||
ansible.builtin.command: journalctl --rotate
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
|
||||
- name: Vider les anciens journaux systemd
|
||||
ansible.builtin.command: journalctl --vacuum-time=1s
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
|
||||
- name: Vider /etc/machine-id
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/machine-id
|
||||
content: ""
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0444"
|
||||
|
||||
- name: Supprimer l'ancien machine-id D-Bus
|
||||
ansible.builtin.file:
|
||||
path: /var/lib/dbus/machine-id
|
||||
state: absent
|
||||
|
||||
- name: Recréer le lien D-Bus vers /etc/machine-id
|
||||
ansible.builtin.file:
|
||||
src: /etc/machine-id
|
||||
dest: /var/lib/dbus/machine-id
|
||||
state: link
|
||||
force: true
|
||||
|
||||
- name: Message final
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Nettoyage final terminé."
|
||||
- "Éteindre la VM puis convertir en template Proxmox."
|
||||
170
scripts/config_proxmox.py
Normal file
170
scripts/config_proxmox.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Assistant de configuration Proxmox pour Set-OPS."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from getpass import getpass
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
RACINE = Path(__file__).resolve().parents[1]
|
||||
FICHIER_PROXMOX = RACINE / "inventories/lab/group_vars/proxmox.yml"
|
||||
FICHIER_VAULT = RACINE / "inventories/lab/group_vars/proxmox.vault.yml"
|
||||
|
||||
|
||||
VALEURS_DEFAUT = {
|
||||
"proxmox_api_host": "",
|
||||
"proxmox_api_user": "",
|
||||
"proxmox_api_port": "",
|
||||
"proxmox_validate_certs": False,
|
||||
"proxmox_clone_noeud": "",
|
||||
"proxmox_clone_vmid_modele": 9000,
|
||||
"proxmox_clone_source_nom": "modele-debian13",
|
||||
"proxmox_clone_stockage": "",
|
||||
"proxmox_clone_format": "",
|
||||
"proxmox_clone_complet": True,
|
||||
"proxmox_clone_timeout": 600,
|
||||
"proxmox_clone_disque": "scsi0",
|
||||
"proxmox_clone_interface": "net0",
|
||||
"proxmox_clone_pont": "vmbr0",
|
||||
"proxmox_clone_parefeu_interface": False,
|
||||
"proxmox_clone_demarrer": True,
|
||||
}
|
||||
|
||||
|
||||
LIBELLES = {
|
||||
"proxmox_api_host": "Hote API Proxmox",
|
||||
"proxmox_api_user": "Utilisateur API Proxmox",
|
||||
"proxmox_api_port": "Port API Proxmox",
|
||||
"proxmox_validate_certs": "Valider les certificats TLS",
|
||||
"proxmox_clone_noeud": "Noeud Proxmox par defaut",
|
||||
"proxmox_clone_vmid_modele": "VMID du modele Debian 13",
|
||||
"proxmox_clone_source_nom": "Nom logique du modele",
|
||||
"proxmox_clone_stockage": "Stockage Proxmox par defaut",
|
||||
"proxmox_clone_format": "Format disque par defaut",
|
||||
"proxmox_clone_complet": "Clone complet",
|
||||
"proxmox_clone_timeout": "Timeout operations Proxmox",
|
||||
"proxmox_clone_disque": "Disque principal",
|
||||
"proxmox_clone_interface": "Interface reseau",
|
||||
"proxmox_clone_pont": "Pont Proxmox",
|
||||
"proxmox_clone_parefeu_interface": "Pare-feu interface Proxmox",
|
||||
"proxmox_clone_demarrer": "Demarrer le clone apres creation",
|
||||
}
|
||||
|
||||
|
||||
def charger_yaml(path: Path) -> dict:
|
||||
if not path.exists():
|
||||
return {}
|
||||
with path.open("r", encoding="utf-8") as fichier:
|
||||
data = yaml.safe_load(fichier) or {}
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"{path} ne contient pas une table YAML.")
|
||||
return data
|
||||
|
||||
|
||||
def ecrire_yaml(path: Path, data: dict) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("w", encoding="utf-8") as fichier:
|
||||
fichier.write("---\n")
|
||||
fichier.write("# Parametres non sensibles pour les operations Proxmox.\n")
|
||||
fichier.write("# Les secrets API doivent aller dans proxmox.vault.yml.\n\n")
|
||||
yaml.safe_dump(data, fichier, default_flow_style=False, sort_keys=False, allow_unicode=True)
|
||||
|
||||
|
||||
def demander(identifiant: str, courant: object) -> object:
|
||||
libelle = LIBELLES[identifiant]
|
||||
if isinstance(courant, bool):
|
||||
defaut = "oui" if courant else "non"
|
||||
reponse = input(f"{libelle} [{defaut}] : ").strip().lower()
|
||||
if not reponse:
|
||||
return courant
|
||||
return reponse in {"o", "oui", "y", "yes", "true", "1"}
|
||||
|
||||
reponse = input(f"{libelle} [{courant}] : ").strip()
|
||||
if not reponse:
|
||||
return courant
|
||||
|
||||
if isinstance(courant, int):
|
||||
return int(reponse)
|
||||
return reponse
|
||||
|
||||
|
||||
def configurer_proxmox() -> None:
|
||||
courant = VALEURS_DEFAUT | charger_yaml(FICHIER_PROXMOX)
|
||||
nouveau = {}
|
||||
|
||||
print("Configuration Proxmox non sensible")
|
||||
print("Entrer pour conserver la valeur entre crochets.\n")
|
||||
print("Les valeurs Cloud-Init deja portees par le modele ne sont pas redemandees.\n")
|
||||
|
||||
for identifiant in VALEURS_DEFAUT:
|
||||
nouveau[identifiant] = demander(identifiant, courant.get(identifiant, VALEURS_DEFAUT[identifiant]))
|
||||
|
||||
ecrire_yaml(FICHIER_PROXMOX, nouveau)
|
||||
print(f"\nEcrit: {FICHIER_PROXMOX.relative_to(RACINE)}")
|
||||
|
||||
|
||||
def demander_oui_non(question: str, defaut: bool = False) -> bool:
|
||||
suffixe = "O/n" if defaut else "o/N"
|
||||
reponse = input(f"{question} [{suffixe}] : ").strip().lower()
|
||||
if not reponse:
|
||||
return defaut
|
||||
return reponse in {"o", "oui", "y", "yes", "true", "1"}
|
||||
|
||||
|
||||
def configurer_vault() -> None:
|
||||
if not demander_oui_non("Configurer les secrets API Proxmox maintenant"):
|
||||
return
|
||||
|
||||
if FICHIER_VAULT.exists():
|
||||
print(f"Ouverture du Vault existant: {FICHIER_VAULT.relative_to(RACINE)}")
|
||||
subprocess.run(["ansible-vault", "edit", str(FICHIER_VAULT)], check=True)
|
||||
return
|
||||
|
||||
token_id = input("Token ID Proxmox [set-ops] : ").strip() or "set-ops"
|
||||
token_secret = getpass("Token secret Proxmox : ").strip()
|
||||
if not token_secret:
|
||||
print("Secret vide: Vault non cree.")
|
||||
return
|
||||
|
||||
FICHIER_VAULT.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
fd = os.open(FICHIER_VAULT, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as fichier:
|
||||
yaml.safe_dump(
|
||||
{
|
||||
"proxmox_api_token_id": token_id,
|
||||
"proxmox_api_token_secret": token_secret,
|
||||
},
|
||||
fichier,
|
||||
default_flow_style=False,
|
||||
sort_keys=False,
|
||||
)
|
||||
subprocess.run(["ansible-vault", "encrypt", str(FICHIER_VAULT)], check=True)
|
||||
print(f"Vault cree: {FICHIER_VAULT.relative_to(RACINE)}")
|
||||
except Exception:
|
||||
if FICHIER_VAULT.exists():
|
||||
FICHIER_VAULT.unlink()
|
||||
raise
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
configurer_proxmox()
|
||||
configurer_vault()
|
||||
except KeyboardInterrupt:
|
||||
print("\nConfiguration interrompue.", file=sys.stderr)
|
||||
return 130
|
||||
except Exception as exc:
|
||||
print(f"erreur: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
191
scripts/inventory_host.py
Normal file
191
scripts/inventory_host.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Gere les hotes d'un inventaire Ansible statique Set-OPS."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def charger_inventaire(path: Path) -> dict:
|
||||
if not path.exists():
|
||||
return {"all": {"children": {}}}
|
||||
|
||||
with path.open("r", encoding="utf-8") as inventory_file:
|
||||
data = yaml.safe_load(inventory_file) or {}
|
||||
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"{path} ne contient pas une table YAML.")
|
||||
|
||||
data.setdefault("all", {})
|
||||
data["all"].setdefault("children", {})
|
||||
return data
|
||||
|
||||
|
||||
def ecrire_inventaire(path: Path, data: dict) -> None:
|
||||
with path.open("w", encoding="utf-8") as inventory_file:
|
||||
yaml.safe_dump(
|
||||
data,
|
||||
inventory_file,
|
||||
default_flow_style=False,
|
||||
sort_keys=False,
|
||||
allow_unicode=True,
|
||||
)
|
||||
|
||||
|
||||
def enfants(data: dict) -> dict:
|
||||
return data.setdefault("all", {}).setdefault("children", {})
|
||||
|
||||
|
||||
def assurer_groupe(data: dict, group: str) -> dict:
|
||||
group_data = enfants(data).setdefault(group, {})
|
||||
group_data.setdefault("hosts", {})
|
||||
return group_data
|
||||
|
||||
|
||||
def variables_hote(data: dict, host: str) -> dict:
|
||||
merged: dict = {}
|
||||
for group_data in enfants(data).values():
|
||||
hosts = group_data.get("hosts", {})
|
||||
if host in hosts and isinstance(hosts[host], dict):
|
||||
merged.update(hosts[host])
|
||||
return merged
|
||||
|
||||
|
||||
def groupes_hote(data: dict, host: str) -> list[str]:
|
||||
groups: list[str] = []
|
||||
for group, group_data in enfants(data).items():
|
||||
if host in group_data.get("hosts", {}):
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
|
||||
def retirer_hote_de_tous_les_groupes(data: dict, host: str) -> None:
|
||||
for group_data in enfants(data).values():
|
||||
group_data.get("hosts", {}).pop(host, None)
|
||||
|
||||
|
||||
def ajouter_hote(data: dict, host: str, groups: list[str], ip: str | None, ansible_user: str | None) -> None:
|
||||
vars_for_host = variables_hote(data, host)
|
||||
if ip:
|
||||
vars_for_host["ansible_host"] = ip
|
||||
if ansible_user:
|
||||
vars_for_host["ansible_user"] = ansible_user
|
||||
|
||||
for group in groups:
|
||||
assurer_groupe(data, group)["hosts"][host] = dict(vars_for_host)
|
||||
|
||||
|
||||
def definir_groupes_hote(data: dict, host: str, groups: list[str]) -> None:
|
||||
vars_for_host = variables_hote(data, host)
|
||||
if not groupes_hote(data, host):
|
||||
raise ValueError(f"Hote absent de l'inventaire: {host}")
|
||||
retirer_hote_de_tous_les_groupes(data, host)
|
||||
for group in groups:
|
||||
assurer_groupe(data, group)["hosts"][host] = dict(vars_for_host)
|
||||
|
||||
|
||||
def analyser_groupes(raw_groups: str) -> list[str]:
|
||||
groups = [group.strip() for group in raw_groups.replace(",", " ").split() if group.strip()]
|
||||
if not groups:
|
||||
raise ValueError("Au moins un groupe est requis.")
|
||||
return groups
|
||||
|
||||
|
||||
def afficher_hote(data: dict, host: str) -> None:
|
||||
groups = groupes_hote(data, host)
|
||||
if not groups:
|
||||
raise ValueError(f"Hote absent de l'inventaire: {host}")
|
||||
|
||||
yaml.safe_dump(
|
||||
{
|
||||
"hote": host,
|
||||
"groupes": groups,
|
||||
"variables": variables_hote(data, host),
|
||||
},
|
||||
sys.stdout,
|
||||
default_flow_style=False,
|
||||
sort_keys=False,
|
||||
allow_unicode=True,
|
||||
)
|
||||
|
||||
|
||||
def afficher_playbooks_hote(data: dict, host: str, playbook_dir: Path) -> None:
|
||||
groups = groupes_hote(data, host)
|
||||
if not groups:
|
||||
raise ValueError(f"Hote absent de l'inventaire: {host}")
|
||||
|
||||
missing_playbooks: list[str] = []
|
||||
playbooks: list[Path] = []
|
||||
|
||||
for group in groups:
|
||||
playbook = playbook_dir / f"{group}.yml"
|
||||
if playbook.exists():
|
||||
playbooks.append(playbook)
|
||||
else:
|
||||
missing_playbooks.append(group)
|
||||
|
||||
if missing_playbooks:
|
||||
missing = ", ".join(missing_playbooks)
|
||||
raise ValueError(f"Playbook de groupe manquant pour: {missing}")
|
||||
|
||||
for playbook in playbooks:
|
||||
print(playbook)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Gere les hotes d'un inventaire Ansible statique Set-OPS.")
|
||||
parser.add_argument("--inventaire", required=True, type=Path)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
add_parser = subparsers.add_parser("ajouter", help="Ajoute ou met a jour un hote dans un ou plusieurs groupes.")
|
||||
add_parser.add_argument("--hote", required=True)
|
||||
add_parser.add_argument("--groupes", required=True)
|
||||
add_parser.add_argument("--adresse-ip")
|
||||
add_parser.add_argument("--utilisateur-ansible")
|
||||
|
||||
groups_parser = subparsers.add_parser("definir-groupes", help="Remplace l'appartenance aux groupes pour un hote.")
|
||||
groups_parser.add_argument("--hote", required=True)
|
||||
groups_parser.add_argument("--groupes", required=True)
|
||||
|
||||
show_parser = subparsers.add_parser("afficher", help="Affiche les groupes et variables d'un hote.")
|
||||
show_parser.add_argument("--hote", required=True)
|
||||
|
||||
playbooks_parser = subparsers.add_parser("playbooks", help="Affiche les playbooks de groupes pour un hote.")
|
||||
playbooks_parser.add_argument("--hote", required=True)
|
||||
playbooks_parser.add_argument("--dossier-playbooks", required=True, type=Path)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
data = charger_inventaire(args.inventaire)
|
||||
|
||||
if args.command == "ajouter":
|
||||
ajouter_hote(
|
||||
data,
|
||||
args.hote,
|
||||
analyser_groupes(args.groupes),
|
||||
args.adresse_ip,
|
||||
args.utilisateur_ansible,
|
||||
)
|
||||
ecrire_inventaire(args.inventaire, data)
|
||||
elif args.command == "definir-groupes":
|
||||
definir_groupes_hote(data, args.hote, analyser_groupes(args.groupes))
|
||||
ecrire_inventaire(args.inventaire, data)
|
||||
elif args.command == "afficher":
|
||||
afficher_hote(data, args.hote)
|
||||
elif args.command == "playbooks":
|
||||
afficher_playbooks_hote(data, args.hote, args.dossier_playbooks)
|
||||
except Exception as exc:
|
||||
print(f"erreur: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in a new issue