From 61e2c7ca670617a3b6e520e24f37f57631073697 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sun, 21 Jun 2026 15:13:45 -0400 Subject: [PATCH] Structurer l'exploitation Ansible Chezlepro --- .gitignore | 2 + AGENTS.md | 241 +++++++++- CHANGELOG.md | 130 ++++- Makefile | 358 ++++++++++++++ README.md | 78 ++- docs/MISE-A-JOUR-CODEX-CLAUDE.md | 32 +- docs/architecture-set-ops.md | 51 +- docs/integrations-vm.md | 43 ++ docs/modeles_vm/debian13-proxmox.md | 312 ++++++++++++ docs/procedure-template-debian13-proxmox.md | 446 ++++++++++-------- docs/vm-lifecycle.md | 194 ++++++++ docs/vm_templates/debian13-proxmox.md | 19 - inventories/lab/group_vars/all.yml | 117 ----- inventories/lab/group_vars/modeles_vm.yml | 58 +++ .../lab/group_vars/proxmox.vault.yml.example | 8 + inventories/lab/group_vars/proxmox.yml | 20 + inventories/lab/hosts.yml | 16 +- inventories/production/group_vars/all.yml | 3 +- .../production/group_vars/serveurs_debian.yml | 54 +++ inventories/production/hosts.yml | 26 +- playbooks/baseline/debian_common.yml | 10 - playbooks/groupes/serveurs_debian.yml | 22 + .../serveurs_durcis.yml} | 4 +- .../maintenance/verifier_hote_debian.yml | 38 ++ .../debian13_proxmox_nettoyer.yml} | 2 +- .../debian13_proxmox_preparer.yml} | 2 +- .../modeles_vm/debian13_proxmox_verifier.yml | 130 +++++ playbooks/proxmox/README.md | 78 ++- playbooks/proxmox/cloner_vm_debian.yml | 202 ++++++++ .../vm_templates/debian13_proxmox_verify.yml | 51 -- roles/apparmor/defaults/main.yml | 2 +- roles/apparmor/tasks/main.yml | 2 +- roles/auditd/defaults/main.yml | 2 +- roles/auditd/tasks/main.yml | 4 +- roles/base/chrony/tasks/main.yml | 11 - roles/base/cloud_init/defaults/main.yml | 5 - roles/base/cloud_init/tasks/main.yml | 32 -- .../cloud_init/templates/99_chezlepro.cfg.j2 | 23 - roles/base/common_packages/defaults/main.yml | 2 - roles/base/common_packages/tasks/main.yml | 18 - roles/base/motd/tasks/main.yml | 8 - roles/base/motd/templates/motd.j2 | 2 - roles/base/qemu_guest_agent/tasks/main.yml | 11 - roles/cloud_init/defaults/main.yml | 2 +- roles/common_packages/defaults/main.yml | 48 +- roles/common_packages/tasks/main.yml | 2 +- roles/core_dumps/defaults/main.yml | 2 +- roles/core_dumps/tasks/main.yml | 4 +- roles/fail2ban_ssh/defaults/main.yml | 10 +- roles/fail2ban_ssh/tasks/main.yml | 2 +- .../templates/sshd-chezlepro.local.j2 | 10 +- roles/hardening_packages/defaults/main.yml | 13 +- roles/hardening_packages/tasks/main.yml | 2 +- roles/journald/defaults/main.yml | 6 +- roles/journald/templates/99-chezlepro.conf.j2 | 6 +- roles/nftables_baseline/defaults/main.yml | 18 +- roles/nftables_baseline/tasks/main.yml | 6 +- .../templates/nftables.conf.j2 | 16 +- roles/security/ssh_baseline/defaults/main.yml | 4 - roles/security/ssh_baseline/handlers/main.yml | 11 - roles/security/ssh_baseline/tasks/main.yml | 28 -- .../templates/10-chezlepro.conf.j2 | 8 - roles/security/sudo_ansible/defaults/main.yml | 2 - roles/security/sudo_ansible/tasks/main.yml | 23 - .../sudo_ansible/templates/90-ansible.j2 | 2 - roles/ssh_baseline/defaults/main.yml | 6 +- roles/ssh_baseline/handlers/main.yml | 18 +- .../templates/10-chezlepro.conf.j2 | 12 +- roles/ssh_hardening/defaults/main.yml | 16 +- roles/ssh_hardening/handlers/main.yml | 18 +- .../templates/20-chezlepro-hardening.conf.j2 | 28 +- roles/sudo_ansible/defaults/main.yml | 2 +- roles/sudo_ansible/tasks/main.yml | 4 +- roles/sudo_ansible/templates/90-ansible.j2 | 2 +- roles/sysctl_hardening/defaults/main.yml | 4 +- roles/sysctl_hardening/tasks/main.yml | 2 +- .../templates/99-chezlepro-hardening.conf.j2 | 2 +- roles/systemd_ssh_auto/defaults/main.yml | 2 +- roles/systemd_ssh_auto/tasks/main.yml | 4 +- roles/template_cleanup/defaults/main.yml | 4 +- roles/template_cleanup/tasks/main.yml | 6 +- roles/unattended_upgrades/defaults/main.yml | 6 +- .../templates/20auto-upgrades.j2 | 4 +- .../templates/50unattended-upgrades.j2 | 6 +- .../systemd_ssh_auto/defaults/main.yml | 2 - .../systemd_ssh_auto/handlers/main.yml | 4 - .../systemd_ssh_auto/tasks/main.yml | 17 - .../99-chezlepro-systemd-ssh-auto.cfg.j2 | 2 - .../template_cleanup/defaults/main.yml | 2 - .../template_cleanup/tasks/main.yml | 54 --- scripts/config_proxmox.py | 170 +++++++ scripts/inventory_host.py | 191 ++++++++ 92 files changed, 2829 insertions(+), 853 deletions(-) create mode 100644 Makefile create mode 100644 docs/integrations-vm.md create mode 100644 docs/modeles_vm/debian13-proxmox.md create mode 100644 docs/vm-lifecycle.md delete mode 100644 docs/vm_templates/debian13-proxmox.md create mode 100644 inventories/lab/group_vars/modeles_vm.yml create mode 100644 inventories/lab/group_vars/proxmox.vault.yml.example create mode 100644 inventories/lab/group_vars/proxmox.yml create mode 100644 inventories/production/group_vars/serveurs_debian.yml delete mode 100644 playbooks/baseline/debian_common.yml create mode 100644 playbooks/groupes/serveurs_debian.yml rename playbooks/{hardening/debian_server_hardening.yml => groupes/serveurs_durcis.yml} (86%) create mode 100644 playbooks/maintenance/verifier_hote_debian.yml rename playbooks/{vm_templates/debian13_proxmox_cleanup.yml => modeles_vm/debian13_proxmox_nettoyer.yml} (85%) rename playbooks/{vm_templates/debian13_proxmox_prepare.yml => modeles_vm/debian13_proxmox_preparer.yml} (98%) create mode 100644 playbooks/modeles_vm/debian13_proxmox_verifier.yml create mode 100644 playbooks/proxmox/cloner_vm_debian.yml delete mode 100644 playbooks/vm_templates/debian13_proxmox_verify.yml delete mode 100644 roles/base/chrony/tasks/main.yml delete mode 100644 roles/base/cloud_init/defaults/main.yml delete mode 100644 roles/base/cloud_init/tasks/main.yml delete mode 100644 roles/base/cloud_init/templates/99_chezlepro.cfg.j2 delete mode 100644 roles/base/common_packages/defaults/main.yml delete mode 100644 roles/base/common_packages/tasks/main.yml delete mode 100644 roles/base/motd/tasks/main.yml delete mode 100644 roles/base/motd/templates/motd.j2 delete mode 100644 roles/base/qemu_guest_agent/tasks/main.yml delete mode 100644 roles/security/ssh_baseline/defaults/main.yml delete mode 100644 roles/security/ssh_baseline/handlers/main.yml delete mode 100644 roles/security/ssh_baseline/tasks/main.yml delete mode 100644 roles/security/ssh_baseline/templates/10-chezlepro.conf.j2 delete mode 100644 roles/security/sudo_ansible/defaults/main.yml delete mode 100644 roles/security/sudo_ansible/tasks/main.yml delete mode 100644 roles/security/sudo_ansible/templates/90-ansible.j2 delete mode 100644 roles/vm_template/systemd_ssh_auto/defaults/main.yml delete mode 100644 roles/vm_template/systemd_ssh_auto/handlers/main.yml delete mode 100644 roles/vm_template/systemd_ssh_auto/tasks/main.yml delete mode 100644 roles/vm_template/systemd_ssh_auto/templates/99-chezlepro-systemd-ssh-auto.cfg.j2 delete mode 100644 roles/vm_template/template_cleanup/defaults/main.yml delete mode 100644 roles/vm_template/template_cleanup/tasks/main.yml create mode 100644 scripts/config_proxmox.py create mode 100644 scripts/inventory_host.py diff --git a/.gitignore b/.gitignore index e10d28d..db052b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ *.retry .vault-pass facts_cache/ +.ansible/ *.vault +*.vault.yml *.secret *.pem *.key diff --git a/AGENTS.md b/AGENTS.md index 605ff60..407abd3 100644 --- a/AGENTS.md +++ b/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 : diff --git a/CHANGELOG.md b/CHANGELOG.md index 156d216..97f43c6 100644 --- a/CHANGELOG.md +++ b/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é. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ec34c0d --- /dev/null +++ b/Makefile @@ -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) diff --git a/README.md b/README.md index 27165c3..3c7eebd 100644 --- a/README.md +++ b/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 +``` diff --git a/docs/MISE-A-JOUR-CODEX-CLAUDE.md b/docs/MISE-A-JOUR-CODEX-CLAUDE.md index 3eab9a5..8bf73ab 100644 --- a/docs/MISE-A-JOUR-CODEX-CLAUDE.md +++ b/docs/MISE-A-JOUR-CODEX-CLAUDE.md @@ -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 : diff --git a/docs/architecture-set-ops.md b/docs/architecture-set-ops.md index 8644468..dfd5603 100644 --- a/docs/architecture-set-ops.md +++ b/docs/architecture-set-ops.md @@ -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 +``` diff --git a/docs/integrations-vm.md b/docs/integrations-vm.md new file mode 100644 index 0000000..e3ca359 --- /dev/null +++ b/docs/integrations-vm.md @@ -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. diff --git a/docs/modeles_vm/debian13-proxmox.md b/docs/modeles_vm/debian13-proxmox.md new file mode 100644 index 0000000..5df993a --- /dev/null +++ b/docs/modeles_vm/debian13-proxmox.md @@ -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 +``` diff --git a/docs/procedure-template-debian13-proxmox.md b/docs/procedure-template-debian13-proxmox.md index 52c7694..6dfe4fa 100644 --- a/docs/procedure-template-debian13-proxmox.md +++ b/docs/procedure-template-debian13-proxmox.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 : diff --git a/docs/vm-lifecycle.md b/docs/vm-lifecycle.md new file mode 100644 index 0000000..8aaf4eb --- /dev/null +++ b/docs/vm-lifecycle.md @@ -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 +``` diff --git a/docs/vm_templates/debian13-proxmox.md b/docs/vm_templates/debian13-proxmox.md deleted file mode 100644 index cdcd414..0000000 --- a/docs/vm_templates/debian13-proxmox.md +++ /dev/null @@ -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 -``` diff --git a/inventories/lab/group_vars/all.yml b/inventories/lab/group_vars/all.yml index 068b6ff..c5265ff 100644 --- a/inventories/lab/group_vars/all.yml +++ b/inventories/lab/group_vars/all.yml @@ -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 diff --git a/inventories/lab/group_vars/modeles_vm.yml b/inventories/lab/group_vars/modeles_vm.yml new file mode 100644 index 0000000..e67b06c --- /dev/null +++ b/inventories/lab/group_vars/modeles_vm.yml @@ -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" diff --git a/inventories/lab/group_vars/proxmox.vault.yml.example b/inventories/lab/group_vars/proxmox.vault.yml.example new file mode 100644 index 0000000..f148047 --- /dev/null +++ b/inventories/lab/group_vars/proxmox.vault.yml.example @@ -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: "" diff --git a/inventories/lab/group_vars/proxmox.yml b/inventories/lab/group_vars/proxmox.yml new file mode 100644 index 0000000..d90ccab --- /dev/null +++ b/inventories/lab/group_vars/proxmox.yml @@ -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 diff --git a/inventories/lab/hosts.yml b/inventories/lab/hosts.yml index 1cebd2e..3393077 100644 --- a/inventories/lab/hosts.yml +++ b/inventories/lab/hosts.yml @@ -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: {} diff --git a/inventories/production/group_vars/all.yml b/inventories/production/group_vars/all.yml index ecb535f..a8eb687 100644 --- a/inventories/production/group_vars/all.yml +++ b/inventories/production/group_vars/all.yml @@ -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. diff --git a/inventories/production/group_vars/serveurs_debian.yml b/inventories/production/group_vars/serveurs_debian.yml new file mode 100644 index 0000000..09350df --- /dev/null +++ b/inventories/production/group_vars/serveurs_debian.yml @@ -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" diff --git a/inventories/production/hosts.yml b/inventories/production/hosts.yml index 3546931..8037140 100644 --- a/inventories/production/hosts.yml +++ b/inventories/production/hosts.yml @@ -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: {} diff --git a/playbooks/baseline/debian_common.yml b/playbooks/baseline/debian_common.yml deleted file mode 100644 index 86c333c..0000000 --- a/playbooks/baseline/debian_common.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Appliquer le socle Debian commun - hosts: all - become: true - gather_facts: true - - roles: - - common_packages - - chrony - - ssh_baseline diff --git a/playbooks/groupes/serveurs_debian.yml b/playbooks/groupes/serveurs_debian.yml new file mode 100644 index 0000000..da28a7d --- /dev/null +++ b/playbooks/groupes/serveurs_debian.yml @@ -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 diff --git a/playbooks/hardening/debian_server_hardening.yml b/playbooks/groupes/serveurs_durcis.yml similarity index 86% rename from playbooks/hardening/debian_server_hardening.yml rename to playbooks/groupes/serveurs_durcis.yml index 8691fbc..4286a1e 100644 --- a/playbooks/hardening/debian_server_hardening.yml +++ b/playbooks/groupes/serveurs_durcis.yml @@ -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 diff --git a/playbooks/maintenance/verifier_hote_debian.yml b/playbooks/maintenance/verifier_hote_debian.yml new file mode 100644 index 0000000..598b866 --- /dev/null +++ b/playbooks/maintenance/verifier_hote_debian.yml @@ -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." diff --git a/playbooks/vm_templates/debian13_proxmox_cleanup.yml b/playbooks/modeles_vm/debian13_proxmox_nettoyer.yml similarity index 85% rename from playbooks/vm_templates/debian13_proxmox_cleanup.yml rename to playbooks/modeles_vm/debian13_proxmox_nettoyer.yml index 1f5c420..79c4cf1 100644 --- a/playbooks/vm_templates/debian13_proxmox_cleanup.yml +++ b/playbooks/modeles_vm/debian13_proxmox_nettoyer.yml @@ -1,6 +1,6 @@ --- - name: Nettoyage final avant conversion en template Proxmox - hosts: vm_templates + hosts: modeles_vm become: true gather_facts: true diff --git a/playbooks/vm_templates/debian13_proxmox_prepare.yml b/playbooks/modeles_vm/debian13_proxmox_preparer.yml similarity index 98% rename from playbooks/vm_templates/debian13_proxmox_prepare.yml rename to playbooks/modeles_vm/debian13_proxmox_preparer.yml index f59fbfc..aca8cfc 100644 --- a/playbooks/vm_templates/debian13_proxmox_prepare.yml +++ b/playbooks/modeles_vm/debian13_proxmox_preparer.yml @@ -1,6 +1,6 @@ --- - name: Préparer le template Debian 13 Proxmox Chezlepro - hosts: vm_templates + hosts: modeles_vm become: true gather_facts: true diff --git a/playbooks/modeles_vm/debian13_proxmox_verifier.yml b/playbooks/modeles_vm/debian13_proxmox_verifier.yml new file mode 100644 index 0000000..27f9f91 --- /dev/null +++ b/playbooks/modeles_vm/debian13_proxmox_verifier.yml @@ -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 diff --git a/playbooks/proxmox/README.md b/playbooks/proxmox/README.md index 121e423..cdd4257 100644 --- a/playbooks/proxmox/README.md +++ b/playbooks/proxmox/README.md @@ -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 +``` diff --git a/playbooks/proxmox/cloner_vm_debian.yml b/playbooks/proxmox/cloner_vm_debian.yml new file mode 100644 index 0000000..6c01579 --- /dev/null +++ b/playbooks/proxmox/cloner_vm_debian.yml @@ -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 }}" diff --git a/playbooks/vm_templates/debian13_proxmox_verify.yml b/playbooks/vm_templates/debian13_proxmox_verify.yml deleted file mode 100644 index f918b4b..0000000 --- a/playbooks/vm_templates/debian13_proxmox_verify.yml +++ /dev/null @@ -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 diff --git a/roles/apparmor/defaults/main.yml b/roles/apparmor/defaults/main.yml index 28435ea..937b073 100644 --- a/roles/apparmor/defaults/main.yml +++ b/roles/apparmor/defaults/main.yml @@ -1,2 +1,2 @@ --- -chezlepro_apparmor_enabled: true +apparmor_enabled: true diff --git a/roles/apparmor/tasks/main.yml b/roles/apparmor/tasks/main.yml index 3e0972b..a976c0e 100644 --- a/roles/apparmor/tasks/main.yml +++ b/roles/apparmor/tasks/main.yml @@ -11,5 +11,5 @@ name: apparmor enabled: true state: started - when: chezlepro_apparmor_enabled | bool + when: apparmor_enabled | bool failed_when: false diff --git a/roles/auditd/defaults/main.yml b/roles/auditd/defaults/main.yml index ba35681..7a4c631 100644 --- a/roles/auditd/defaults/main.yml +++ b/roles/auditd/defaults/main.yml @@ -1,2 +1,2 @@ --- -chezlepro_auditd_enabled: true +auditd_enabled: true diff --git a/roles/auditd/tasks/main.yml b/roles/auditd/tasks/main.yml index bc3150f..2ae21eb 100644 --- a/roles/auditd/tasks/main.yml +++ b/roles/auditd/tasks/main.yml @@ -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 diff --git a/roles/base/chrony/tasks/main.yml b/roles/base/chrony/tasks/main.yml deleted file mode 100644 index b4ec91b..0000000 --- a/roles/base/chrony/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/base/cloud_init/defaults/main.yml b/roles/base/cloud_init/defaults/main.yml deleted file mode 100644 index ca32b5c..0000000 --- a/roles/base/cloud_init/defaults/main.yml +++ /dev/null @@ -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 diff --git a/roles/base/cloud_init/tasks/main.yml b/roles/base/cloud_init/tasks/main.yml deleted file mode 100644 index 2704d87..0000000 --- a/roles/base/cloud_init/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/base/cloud_init/templates/99_chezlepro.cfg.j2 b/roles/base/cloud_init/templates/99_chezlepro.cfg.j2 deleted file mode 100644 index 1766b4d..0000000 --- a/roles/base/cloud_init/templates/99_chezlepro.cfg.j2 +++ /dev/null @@ -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 diff --git a/roles/base/common_packages/defaults/main.yml b/roles/base/common_packages/defaults/main.yml deleted file mode 100644 index 60789a8..0000000 --- a/roles/base/common_packages/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -chezlepro_common_packages: [] diff --git a/roles/base/common_packages/tasks/main.yml b/roles/base/common_packages/tasks/main.yml deleted file mode 100644 index 3251f73..0000000 --- a/roles/base/common_packages/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/base/motd/tasks/main.yml b/roles/base/motd/tasks/main.yml deleted file mode 100644 index 3814c1d..0000000 --- a/roles/base/motd/tasks/main.yml +++ /dev/null @@ -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" diff --git a/roles/base/motd/templates/motd.j2 b/roles/base/motd/templates/motd.j2 deleted file mode 100644 index 140d1ad..0000000 --- a/roles/base/motd/templates/motd.j2 +++ /dev/null @@ -1,2 +0,0 @@ -Système Chezlepro -Gestion : Set-OPS / Ansible diff --git a/roles/base/qemu_guest_agent/tasks/main.yml b/roles/base/qemu_guest_agent/tasks/main.yml deleted file mode 100644 index 6125609..0000000 --- a/roles/base/qemu_guest_agent/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/cloud_init/defaults/main.yml b/roles/cloud_init/defaults/main.yml index ca32b5c..485662e 100644 --- a/roles/cloud_init/defaults/main.yml +++ b/roles/cloud_init/defaults/main.yml @@ -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 diff --git a/roles/common_packages/defaults/main.yml b/roles/common_packages/defaults/main.yml index 60789a8..d93923e 100644 --- a/roles/common_packages/defaults/main.yml +++ b/roles/common_packages/defaults/main.yml @@ -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 diff --git a/roles/common_packages/tasks/main.yml b/roles/common_packages/tasks/main.yml index 3251f73..145e763 100644 --- a/roles/common_packages/tasks/main.yml +++ b/roles/common_packages/tasks/main.yml @@ -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 diff --git a/roles/core_dumps/defaults/main.yml b/roles/core_dumps/defaults/main.yml index 55c1b5e..8c14b43 100644 --- a/roles/core_dumps/defaults/main.yml +++ b/roles/core_dumps/defaults/main.yml @@ -1,2 +1,2 @@ --- -chezlepro_disable_core_dumps: true +core_dumps_disable: true diff --git a/roles/core_dumps/tasks/main.yml b/roles/core_dumps/tasks/main.yml index c26a10d..8692f11 100644 --- a/roles/core_dumps/tasks/main.yml +++ b/roles/core_dumps/tasks/main.yml @@ -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 diff --git a/roles/fail2ban_ssh/defaults/main.yml b/roles/fail2ban_ssh/defaults/main.yml index f68e7c1..4b2f5e9 100644 --- a/roles/fail2ban_ssh/defaults/main.yml +++ b/roles/fail2ban_ssh/defaults/main.yml @@ -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 diff --git a/roles/fail2ban_ssh/tasks/main.yml b/roles/fail2ban_ssh/tasks/main.yml index f163cd3..ef300c1 100644 --- a/roles/fail2ban_ssh/tasks/main.yml +++ b/roles/fail2ban_ssh/tasks/main.yml @@ -26,4 +26,4 @@ name: fail2ban enabled: true state: started - when: chezlepro_fail2ban_enabled | bool + when: fail2ban_ssh_enabled | bool diff --git a/roles/fail2ban_ssh/templates/sshd-chezlepro.local.j2 b/roles/fail2ban_ssh/templates/sshd-chezlepro.local.j2 index 9d6db05..d9f453c 100644 --- a/roles/fail2ban_ssh/templates/sshd-chezlepro.local.j2 +++ b/roles/fail2ban_ssh/templates/sshd-chezlepro.local.j2 @@ -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 }} diff --git a/roles/hardening_packages/defaults/main.yml b/roles/hardening_packages/defaults/main.yml index a4ed7f3..6b68d2a 100644 --- a/roles/hardening_packages/defaults/main.yml +++ b/roles/hardening_packages/defaults/main.yml @@ -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 diff --git a/roles/hardening_packages/tasks/main.yml b/roles/hardening_packages/tasks/main.yml index d85bf95..c77de62 100644 --- a/roles/hardening_packages/tasks/main.yml +++ b/roles/hardening_packages/tasks/main.yml @@ -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 diff --git a/roles/journald/defaults/main.yml b/roles/journald/defaults/main.yml index 6c0f443..9c9c205 100644 --- a/roles/journald/defaults/main.yml +++ b/roles/journald/defaults/main.yml @@ -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" diff --git a/roles/journald/templates/99-chezlepro.conf.j2 b/roles/journald/templates/99-chezlepro.conf.j2 index 96cfac5..4407115 100644 --- a/roles/journald/templates/99-chezlepro.conf.j2 +++ b/roles/journald/templates/99-chezlepro.conf.j2 @@ -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 diff --git a/roles/nftables_baseline/defaults/main.yml b/roles/nftables_baseline/defaults/main.yml index 288f360..5b23e1f 100644 --- a/roles/nftables_baseline/defaults/main.yml +++ b/roles/nftables_baseline/defaults/main.yml @@ -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: [] diff --git a/roles/nftables_baseline/tasks/main.yml b/roles/nftables_baseline/tasks/main.yml index 440e419..66de180 100644 --- a/roles/nftables_baseline/tasks/main.yml +++ b/roles/nftables_baseline/tasks/main.yml @@ -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 diff --git a/roles/nftables_baseline/templates/nftables.conf.j2 b/roles/nftables_baseline/templates/nftables.conf.j2 index 602b684..f4ff347 100644 --- a/roles/nftables_baseline/templates/nftables.conf.j2 +++ b/roles/nftables_baseline/templates/nftables.conf.j2 @@ -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 }}; } } diff --git a/roles/security/ssh_baseline/defaults/main.yml b/roles/security/ssh_baseline/defaults/main.yml deleted file mode 100644 index 13c7a1f..0000000 --- a/roles/security/ssh_baseline/defaults/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -chezlepro_ssh_password_authentication: "yes" -chezlepro_ssh_permit_root_login: "no" -chezlepro_ssh_pubkey_authentication: "yes" diff --git a/roles/security/ssh_baseline/handlers/main.yml b/roles/security/ssh_baseline/handlers/main.yml deleted file mode 100644 index d165557..0000000 --- a/roles/security/ssh_baseline/handlers/main.yml +++ /dev/null @@ -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 diff --git a/roles/security/ssh_baseline/tasks/main.yml b/roles/security/ssh_baseline/tasks/main.yml deleted file mode 100644 index daddc37..0000000 --- a/roles/security/ssh_baseline/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/security/ssh_baseline/templates/10-chezlepro.conf.j2 b/roles/security/ssh_baseline/templates/10-chezlepro.conf.j2 deleted file mode 100644 index 058168a..0000000 --- a/roles/security/ssh_baseline/templates/10-chezlepro.conf.j2 +++ /dev/null @@ -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 diff --git a/roles/security/sudo_ansible/defaults/main.yml b/roles/security/sudo_ansible/defaults/main.yml deleted file mode 100644 index d1911fe..0000000 --- a/roles/security/sudo_ansible/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -chezlepro_admin_user: ansible diff --git a/roles/security/sudo_ansible/tasks/main.yml b/roles/security/sudo_ansible/tasks/main.yml deleted file mode 100644 index 04638b6..0000000 --- a/roles/security/sudo_ansible/tasks/main.yml +++ /dev/null @@ -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" diff --git a/roles/security/sudo_ansible/templates/90-ansible.j2 b/roles/security/sudo_ansible/templates/90-ansible.j2 deleted file mode 100644 index af33be0..0000000 --- a/roles/security/sudo_ansible/templates/90-ansible.j2 +++ /dev/null @@ -1,2 +0,0 @@ -# Managed by Ansible — Set-OPS -{{ chezlepro_admin_user }} ALL=(ALL) NOPASSWD:ALL diff --git a/roles/ssh_baseline/defaults/main.yml b/roles/ssh_baseline/defaults/main.yml index 13c7a1f..214f81d 100644 --- a/roles/ssh_baseline/defaults/main.yml +++ b/roles/ssh_baseline/defaults/main.yml @@ -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" diff --git a/roles/ssh_baseline/handlers/main.yml b/roles/ssh_baseline/handlers/main.yml index d165557..5b218a4 100644 --- a/roles/ssh_baseline/handlers/main.yml +++ b/roles/ssh_baseline/handlers/main.yml @@ -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 diff --git a/roles/ssh_baseline/templates/10-chezlepro.conf.j2 b/roles/ssh_baseline/templates/10-chezlepro.conf.j2 index 058168a..623bfca 100644 --- a/roles/ssh_baseline/templates/10-chezlepro.conf.j2 +++ b/roles/ssh_baseline/templates/10-chezlepro.conf.j2 @@ -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 diff --git a/roles/ssh_hardening/defaults/main.yml b/roles/ssh_hardening/defaults/main.yml index e0d55c0..04f65c3 100644 --- a/roles/ssh_hardening/defaults/main.yml +++ b/roles/ssh_hardening/defaults/main.yml @@ -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" diff --git a/roles/ssh_hardening/handlers/main.yml b/roles/ssh_hardening/handlers/main.yml index d165557..5b218a4 100644 --- a/roles/ssh_hardening/handlers/main.yml +++ b/roles/ssh_hardening/handlers/main.yml @@ -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 diff --git a/roles/ssh_hardening/templates/20-chezlepro-hardening.conf.j2 b/roles/ssh_hardening/templates/20-chezlepro-hardening.conf.j2 index c10be9f..17ffce8 100644 --- a/roles/ssh_hardening/templates/20-chezlepro-hardening.conf.j2 +++ b/roles/ssh_hardening/templates/20-chezlepro-hardening.conf.j2 @@ -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 diff --git a/roles/sudo_ansible/defaults/main.yml b/roles/sudo_ansible/defaults/main.yml index d1911fe..3d66292 100644 --- a/roles/sudo_ansible/defaults/main.yml +++ b/roles/sudo_ansible/defaults/main.yml @@ -1,2 +1,2 @@ --- -chezlepro_admin_user: ansible +sudo_ansible_admin_user: ansible diff --git a/roles/sudo_ansible/tasks/main.yml b/roles/sudo_ansible/tasks/main.yml index 04638b6..8b49b19 100644 --- a/roles/sudo_ansible/tasks/main.yml +++ b/roles/sudo_ansible/tasks/main.yml @@ -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" diff --git a/roles/sudo_ansible/templates/90-ansible.j2 b/roles/sudo_ansible/templates/90-ansible.j2 index af33be0..e6de599 100644 --- a/roles/sudo_ansible/templates/90-ansible.j2 +++ b/roles/sudo_ansible/templates/90-ansible.j2 @@ -1,2 +1,2 @@ # Managed by Ansible — Set-OPS -{{ chezlepro_admin_user }} ALL=(ALL) NOPASSWD:ALL +{{ sudo_ansible_admin_user }} ALL=(ALL) NOPASSWD:ALL diff --git a/roles/sysctl_hardening/defaults/main.yml b/roles/sysctl_hardening/defaults/main.yml index 8275411..149884f 100644 --- a/roles/sysctl_hardening/defaults/main.yml +++ b/roles/sysctl_hardening/defaults/main.yml @@ -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 diff --git a/roles/sysctl_hardening/tasks/main.yml b/roles/sysctl_hardening/tasks/main.yml index 9db7c46..189e62f 100644 --- a/roles/sysctl_hardening/tasks/main.yml +++ b/roles/sysctl_hardening/tasks/main.yml @@ -7,4 +7,4 @@ group: root mode: "0644" notify: Reload sysctl - when: chezlepro_sysctl_apply | bool + when: sysctl_hardening_apply | bool diff --git a/roles/sysctl_hardening/templates/99-chezlepro-hardening.conf.j2 b/roles/sysctl_hardening/templates/99-chezlepro-hardening.conf.j2 index 2dc837e..fda055e 100644 --- a/roles/sysctl_hardening/templates/99-chezlepro-hardening.conf.j2 +++ b/roles/sysctl_hardening/templates/99-chezlepro-hardening.conf.j2 @@ -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 %} diff --git a/roles/systemd_ssh_auto/defaults/main.yml b/roles/systemd_ssh_auto/defaults/main.yml index 91ed923..94aae0f 100644 --- a/roles/systemd_ssh_auto/defaults/main.yml +++ b/roles/systemd_ssh_auto/defaults/main.yml @@ -1,2 +1,2 @@ --- -chezlepro_disable_systemd_ssh_auto: true +systemd_ssh_auto_disable: true diff --git a/roles/systemd_ssh_auto/tasks/main.yml b/roles/systemd_ssh_auto/tasks/main.yml index 180de1b..e6311fa 100644 --- a/roles/systemd_ssh_auto/tasks/main.yml +++ b/roles/systemd_ssh_auto/tasks/main.yml @@ -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 diff --git a/roles/template_cleanup/defaults/main.yml b/roles/template_cleanup/defaults/main.yml index f98f4b9..cae974c 100644 --- a/roles/template_cleanup/defaults/main.yml +++ b/roles/template_cleanup/defaults/main.yml @@ -1,3 +1,3 @@ --- -confirm_template_cleanup: false -chezlepro_admin_user: ansible +template_cleanup_confirm: false +template_cleanup_admin_user: ansible diff --git a/roles/template_cleanup/tasks/main.yml b/roles/template_cleanup/tasks/main.yml index 935709b..06cf3d7 100644 --- a/roles/template_cleanup/tasks/main.yml +++ b/roles/template_cleanup/tasks/main.yml @@ -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 diff --git a/roles/unattended_upgrades/defaults/main.yml b/roles/unattended_upgrades/defaults/main.yml index c03d4c0..2aee998 100644 --- a/roles/unattended_upgrades/defaults/main.yml +++ b/roles/unattended_upgrades/defaults/main.yml @@ -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 diff --git a/roles/unattended_upgrades/templates/20auto-upgrades.j2 b/roles/unattended_upgrades/templates/20auto-upgrades.j2 index 4688423..573019e 100644 --- a/roles/unattended_upgrades/templates/20auto-upgrades.j2 +++ b/roles/unattended_upgrades/templates/20auto-upgrades.j2 @@ -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"; diff --git a/roles/unattended_upgrades/templates/50unattended-upgrades.j2 b/roles/unattended_upgrades/templates/50unattended-upgrades.j2 index 9f8f9bb..c13d48e 100644 --- a/roles/unattended_upgrades/templates/50unattended-upgrades.j2 +++ b/roles/unattended_upgrades/templates/50unattended-upgrades.j2 @@ -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"; diff --git a/roles/vm_template/systemd_ssh_auto/defaults/main.yml b/roles/vm_template/systemd_ssh_auto/defaults/main.yml deleted file mode 100644 index 91ed923..0000000 --- a/roles/vm_template/systemd_ssh_auto/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -chezlepro_disable_systemd_ssh_auto: true diff --git a/roles/vm_template/systemd_ssh_auto/handlers/main.yml b/roles/vm_template/systemd_ssh_auto/handlers/main.yml deleted file mode 100644 index 1567104..0000000 --- a/roles/vm_template/systemd_ssh_auto/handlers/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -- name: Update grub - ansible.builtin.command: update-grub - changed_when: true diff --git a/roles/vm_template/systemd_ssh_auto/tasks/main.yml b/roles/vm_template/systemd_ssh_auto/tasks/main.yml deleted file mode 100644 index 7f2d2b2..0000000 --- a/roles/vm_template/systemd_ssh_auto/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/vm_template/systemd_ssh_auto/templates/99-chezlepro-systemd-ssh-auto.cfg.j2 b/roles/vm_template/systemd_ssh_auto/templates/99-chezlepro-systemd-ssh-auto.cfg.j2 deleted file mode 100644 index afbac27..0000000 --- a/roles/vm_template/systemd_ssh_auto/templates/99-chezlepro-systemd-ssh-auto.cfg.j2 +++ /dev/null @@ -1,2 +0,0 @@ -# Managed by Ansible — Set-OPS -GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} systemd.ssh_auto=no" diff --git a/roles/vm_template/template_cleanup/defaults/main.yml b/roles/vm_template/template_cleanup/defaults/main.yml deleted file mode 100644 index e912a80..0000000 --- a/roles/vm_template/template_cleanup/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -confirm_template_cleanup: false diff --git a/roles/vm_template/template_cleanup/tasks/main.yml b/roles/vm_template/template_cleanup/tasks/main.yml deleted file mode 100644 index 5264d53..0000000 --- a/roles/vm_template/template_cleanup/tasks/main.yml +++ /dev/null @@ -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." diff --git a/scripts/config_proxmox.py b/scripts/config_proxmox.py new file mode 100644 index 0000000..c9f436c --- /dev/null +++ b/scripts/config_proxmox.py @@ -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()) diff --git a/scripts/inventory_host.py b/scripts/inventory_host.py new file mode 100644 index 0000000..00ea508 --- /dev/null +++ b/scripts/inventory_host.py @@ -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())