Construire l'ecosysteme de services et outiller l'inventaire

Registres (source unique):
- docs/nomenclature.yml: domaines, VMID, plan d'adressage 10.1.0.0/16 segmente.
- docs/bases-donnees.yml: bases applicatives (1 appli -> 1 base -> 1 owner -> 1 DSN).

Roles de service:
- Reseau/edge/PKI/mail: nginx, step_ca, sendmail.
- Donnees/identite: postgresql (consommateur du registre BD), redis, openldap, keycloak.
- Observabilite: prometheus, loki, grafana.
- Supervision: icinga (coeur; Icinga Web 2 differe). Forge: forgejo.

Integrations clientes:
- clients_metriques, clients_journaux, clients_pki, clients_ldap, clients_smtp.

Inventaire et outillage:
- make inventaire-ui: refonte (cartes, theme sombre, onglets, vue Chaine VM->groupes->
  playbooks->roles), saisie du provisioning, auto-proposition depuis la nomenclature,
  deploiement securise (verifier/deployer, jeton anti-CSRF, verrou, mot de passe vault),
  robustesse reseau (connexions fermees, favicon).
- Makefile: cible verifier-deploiement; detection d'un group_vars de production chiffre.
- Scission serveurs_web -> serveurs_web_frontaux/dorsaux; migration de l'adressage vers
  10.1.x; retrait des hotes de test; planification des hotes; requirements.yml.

Chaque role valide en --syntax-check et ansible-lint (profil production).
Secrets references depuis Ansible Vault (jamais en clair); roles non testes live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-06-22 18:06:11 -04:00
parent 6fd0efd88d
commit 8f6de3ebe2
143 changed files with 4354 additions and 476 deletions

View file

@ -336,7 +336,7 @@ clients_pki
clients_ldap clients_ldap
clients_supervision clients_supervision
clients_metriques clients_metriques
serveurs_web serveurs_web_frontaux
serveurs_postgresql serveurs_postgresql
serveurs_prometheus serveurs_prometheus
``` ```

View file

@ -3,6 +3,38 @@
## 2026-06-22 ## 2026-06-22
### Ajouté ### Ajouté
- Rôle `serveurs_forgejo` (forge Git, Phase 5) : binaire officiel Forgejo (version épinglée + lien symbolique), utilisateur `git`, **base PostgreSQL via le registre** (4e consommateur, entrée `forgejo`), publication derrière `serveurs_nginx` (`HTTP_ADDR=127.0.0.1`, `INSTALL_LOCK`), secrets (BD + `SECRET_KEY` + `INTERNAL_TOKEN` + admin) via Vault, mailer vers `serveurs_sendmail`, compte administrateur initial créé une fois. Détails d'installation tirés de la doc officielle Forgejo. Activation de l'entrée `forgejo` dans `docs/bases-donnees.yml`. Playbook de groupe et `group_vars` production associés.
- Rôle `serveurs_icinga` (supervision active — cœur, Phase 4) : dépôt apt officiel Icinga, `icinga2` + `icingadb` + `icingadb-redis`, `icinga2 api setup` + activation de la fonctionnalité `icingadb`, **base PostgreSQL via le registre** (entrée `icingadb`, import du schéma, mot de passe partagé via Vault), config Icinga DB (BD + Redis). **Icinga Web 2 différé** à une phase dédiée. Détails de configuration Icinga DB paramétrés et signalés comme non vérifiables verbatim (pages doc partiellement indisponibles). Ajout de l'entrée `icingadb` à `docs/bases-donnees.yml`. Playbook de groupe et `group_vars` production associés.
- Rôle `serveurs_redis` (cache / files, paquet Debian) : surcharge **non destructive** de `redis.conf` via `include`, `requirepass` (Vault, car exposé sur le réseau interne), `maxmemory` + politique `allkeys-lru`, écoute localhost + IP interne. Complète le nœud données (data-01) aux côtés de PostgreSQL. Playbook de groupe et `group_vars` production associés.
- Rôle `clients_smtp` (intégration cliente relais mail) : `msmtp` + `msmtp-mta` relaient le courrier sortant (notifications, cron, alertes) vers `serveurs_sendmail`, sans daemon. Dernière intégration cliente dont le serveur central existe. Playbook de groupe et `group_vars` production associés.
- Rôle `clients_ldap` (intégration cliente identité utilisateur) : **SSSD** (NSS + PAM) vers `serveurs_openldap`, activation de `sss` dans `/etc/nsswitch.conf` (passwd/group/shadow), création automatique des répertoires personnels (`mkhomedir`), bind anonyme par défaut (bind dédié possible via Vault). Authentification des utilisateurs centralisée — complète l'identité machine de `clients_pki`. Playbook de groupe et `group_vars` production associés.
- Rôle `clients_pki` (intégration cliente PKI / identité machine) : `step-cli` (dépôt Smallstep), **confiance** dans l'AC interne (`step ca bootstrap --install` → racine dans le magasin système), **certificat d'hôte** via le provisioner, **renouvellement automatique** (unités systemd officielles `cert-renewer@.{service,timer}`, vérification toutes les 15 min). Chaque hôte obtient une identité machine vérifiable et un TLS interne réel (remplace les certificats auto-signés). Secrets (empreinte racine + mot de passe provisioner, partagé avec `serveurs_step_ca`) via Vault. Détails tirés de la doc officielle Smallstep. Playbook de groupe et `group_vars` production associés.
- Rôle `clients_journaux` (intégration cliente journaux) : **Grafana Alloy** (dépôt apt Grafana) lit le journal système (journald) et le pousse vers `serveurs_loki` ; l'utilisateur `alloy` est ajouté au groupe `systemd-journal`. Complète l'observabilité côté client (métriques + journaux). Playbook de groupe associé.
- Rôle `clients_metriques` (intégration cliente métriques) : installe `prometheus-node-exporter` (paquet Debian) sur les VM du groupe ; `serveurs_prometheus` dérive et scrape automatiquement la cible depuis l'inventaire. Première intégration cliente — ferme la boucle d'observabilité côté métriques. Playbook de groupe associé.
- Rôle `serveurs_grafana` (tableaux de bord, dépôt apt officiel Grafana) : datasources Prometheus + Loki **provisionnées automatiquement** (`/etc/grafana/provisioning/datasources/`), domaine/`root_url` et mot de passe admin (Vault) via un **drop-in systemd non destructif** (ne touche pas `grafana.ini` du paquet), inscriptions publiques désactivées, publication derrière `serveurs_nginx`. Clôt la Phase 3 observabilité. Playbook de groupe et `group_vars` production associés.
- Rôle `serveurs_loki` (agrégation de journaux, dépôt apt officiel Grafana) : configuration single-node (stockage `filesystem`, schéma TSDB v13), rétention via compactor (`retention_enabled`), écoute `:3100`. Le dépôt apt Grafana est partagé avec `serveurs_grafana`. Playbook de groupe associé.
- Rôle `serveurs_prometheus` (collecte de métriques, paquet Debian) : début de la Phase 3 observabilité. Config Prometheus avec cibles `node_exporter` **dérivées de l'inventaire** (hôtes actifs de `clients_metriques``:9100`, comme la zone DNS), jobs additionnels libres, rétention configurable via `/etc/default/prometheus`, `promtool check config` avant rechargement (handler dans le rôle). Playbook de groupe associé.
- Rôle `serveurs_keycloak` (SSO/IAM, hub d'authentification centralisée) : distribution Quarkus (version en variable, URL release officielle), **premier consommateur de bout en bout du registre de BD** (`serveurs_postgresql` crée la base/compte `keycloak`, le rôle lit la même entrée pour son DSN — mot de passe partagé via `vault_bd_keycloak`), publication derrière `serveurs_nginx` (`proxy-headers=xforwarded`, `http-enabled=true`), secrets (BD + admin bootstrap) dans un `EnvironmentFile 0640`/`no_log`, `kc.sh build` puis service systemd `start --optimized`. Activation de l'entrée `keycloak` dans `docs/bases-donnees.yml`. Détails de configuration tirés de la doc officielle Keycloak. Playbook de groupe et `group_vars` production associés.
- Rôle `serveurs_openldap` (annuaire interne) : slapd Debian configuré via debconf (installation non interactive, backend MDB), base DN dérivée du domaine (`dc=chezlepro,dc=internal`), mot de passe admin **obligatoire depuis Vault**, unités organisationnelles de base (`people`, `groups`) via `community.general.ldap_entry`. Playbook de groupe et `group_vars` production associés.
- Registre déclaratif des bases de données applicatives `docs/bases-donnees.yml` (patron « 1 appli → 1 base → 1 compte propriétaire → 1 chaîne de connexion »). Consommé côté serveur par `serveurs_postgresql` (crée base + compte propriétaire, mot de passe depuis la variable Vault nommée par `secret`) et, côté appli, pour bâtir la chaîne de connexion — secret partagé, source unique. Évite un rôle par base. Vide par défaut.
- Rôle `serveurs_step_ca` (AC interne Smallstep step-ca + provisioner ACME) : dépôt apt officiel Smallstep (clé + source deb822), installation `step-ca`/`step-cli`, utilisateur système `step`, initialisation de l'AC **une seule fois** (`step ca init` standalone + ACME, garde-fou `creates: ca.json`), mots de passe CA et provisioner **obligatoires depuis Vault**, unité systemd durcie officielle. Playbook de groupe et `group_vars` production associés. Clôt la Phase 1 des fondations. Détails d'installation tirés de la documentation officielle Smallstep (non inventés).
- Rôle `serveurs_sendmail` (relais SMTP sortant, basé sur Postfix qui fournit l'interface `sendmail`-compatible) : installation non interactive (debconf), relais limité à `mynetworks` (`10.1.0.0/16`, pas de relais ouvert), smarthost amont optionnel, TLS opportuniste (snakeoil par défaut), `postfix check` avant reload. Playbook de groupe et `group_vars` production associés.
- Rôle `serveurs_nginx` (edge) : reverse proxy et terminaison TLS, certificat auto-signé (snakeoil) par défaut avec chemins en variables pour bascule vers l'AC interne/ACME, durcissement de base (`server_tokens off`, TLS 1.2/1.3), sites de reverse proxy pilotés par variables (vides par défaut), validation `nginx -t` avant reload (handler dans le rôle). Playbook de groupe `serveurs_nginx` et `group_vars` production associés.
- Rôle `serveurs_postgresql` (premier service applicatif après PowerDNS) : PostgreSQL packagé Debian 13, écoute `127.0.0.1` + IP interne, `pg_hba` en `scram-sha-256` ouvert au réseau interne `10.1.0.0/16`, configuration via `conf.d`, provisionnement optionnel de comptes/bases par variables (mots de passe via Vault), handlers reload/restart dans le rôle. Playbook de groupe `serveurs_postgresql` et `group_vars` production associés. Ajout de `requirements.yml` (collections `community.postgresql`, `community.general`).
- Vue des liens VM → groupes → playbooks → rôles dans `make inventaire-ui` : onglet « Chaîne » par hôte (panneau détail) et vue arbre globale (bascule « Inventaire / Chaîne » en en-tête). Les rôles sont extraits des `playbooks/groupes/*.yml` et exposés dans l'API ; les groupes sans rôle sont signalés « stub ».
- Champ de mot de passe du vault Ansible dans la modale de déploiement de `make inventaire-ui` : transmis via fichier temporaire `0600` + `ANSIBLE_VAULT_PASSWORD_FILE`, supprimé après exécution, jamais journalisé. Évite tout prompt `--ask-vault-pass` bloquant en exécution non-interactive. Le flux vault de `make cloner-vm` / `creer-vm` n'est pas modifié.
- Catégorie « Modèles / essais » (VLAN 99 → `10.1.99.0/24`) dans `docs/nomenclature.yml`.
- Registre de nomenclature machine-lisible `docs/nomenclature.yml` (source unique) : domaines, catégories, plan d'adressage `10.1.0.0/16` segmenté par fonction (un /24 et un VLAN par catégorie), dérivations hostname / VMID / VLAN / IP / passerelle.
- Auto-proposition dans `make inventaire-ui` : bouton « ✨ Proposer » et auto-remplissage à la saisie du nom, qui dérivent VMID, VLAN, IP et passerelle depuis `docs/nomenclature.yml` (avec indice de zone). Migration du plan d'adressage interne vers `10.1.0.0/16` (remplace les essais `192.168.12.x`).
- Adoption du nom officiel « Set-OPS — Votre artisan numérique » : titre et en-tête de `make inventaire-ui`, et titre du `README.md`.
- Déploiement depuis `make inventaire-ui` : boutons « Vérifier » (dry-run `--check --diff`) et « Déployer » (réel) par hôte, avec console de sortie en direct. Garde-fous : jeton anti-CSRF (`X-Jeton`), verrou (une seule exécution à la fois), validation du nom d'hôte (anti-injection), refus si l'hôte n'est pas actif, déploiement bloqué tant qu'une vérification n'a pas réussi (réarmé à chaque sauvegarde), restriction à l'inventaire production. Déploiement non transactionnel (sans rollback) signalé à l'opérateur.
- Cible `make verifier-deploiement HOTE=…` : déploiement à blanc (`--check --diff`) d'un hôte selon ses groupes, réutilisée par l'interface.
- Refonte de l'interface `make inventaire-ui` : thème sombre dense, grille de cartes d'hôtes groupées par état (Actifs / Planifiés) avec badge de statut coloré, chips de filtre (Tous / Actifs / Planifiés / Bloqués), recherche, et panneau détail maître-détail à onglets (Réseau / Proxmox / Groupes) avec tuiles de chiffres-clés (VMID, mémoire, cœurs, disque, groupes), bascule d'état actif/planifié, panneau de dépendances repliable, raccourci Ctrl/Cmd+S.
- Saisie des paramètres de provisioning par hôte dans l'inventaire (variables d'hôte) : `proxmox_cidr`, `proxmox_passerelle`, `proxmox_vlan`, `proxmox_pont`, `proxmox_dns`, `proxmox_noeud`, `proxmox_stockage`, `proxmox_disque_taille`, `proxmox_memoire`, `proxmox_coeurs`, avec validation (VLAN 1-4094, CIDR 0-32, entiers positifs) et round-trip non destructif. Documentées dans `docs/nomenclature-vm.md`.
- Scission de la couche applicative web en deux groupes distincts de l'edge `serveurs_nginx` :
- `serveurs_web_frontaux` (présentation web : UI, rendu, assets) ;
- `serveurs_web_dorsaux` (application web : API, traitement) ;
- playbooks homonymes et entrée dédiée dans `docs/catalogue-services.md`.
- Ajout de `make hote-planifier` pour renseigner un hôte, son VMID et ses groupes sans création ni déploiement immédiat. - Ajout de `make hote-planifier` pour renseigner un hôte, son VMID et ses groupes sans création ni déploiement immédiat.
- Ajout de `scripts/inventory_rules.py` pour centraliser les règles partagées entre la gestion CLI et l'interface locale d'inventaire. - Ajout de `scripts/inventory_rules.py` pour centraliser les règles partagées entre la gestion CLI et l'interface locale d'inventaire.
- Ajout de `docs/dependances-groupes.yml` comme registre exploitable des dépendances causales entre groupes. - Ajout de `docs/dependances-groupes.yml` comme registre exploitable des dépendances causales entre groupes.
@ -14,6 +46,13 @@
- variables de production DNS et runbook `docs/dns-interne.md`. - variables de production DNS et runbook `docs/dns-interne.md`.
### Modifié ### Modifié
- Robustesse du serveur `make inventaire-ui` : `repondre()` avale proprement les connexions fermées par le client (`BrokenPipeError`/`ConnectionResetError`) au lieu de déverser une trace dans la console ; ajout d'un favicon (204) pour éviter le 404 répété du navigateur.
- `make deployer` et `make verifier-deploiement` détectent un `group_vars` de production chiffré (Ansible Vault) et demandent le mot de passe **une seule fois** (réutilisé pour tous les playbooks et sous-vérifications via fichier temporaire `0600`, nettoyé en fin de run). Refus propre si vault chiffré détecté en exécution **non-interactive** ; la GUI ferme `stdin` du sous-processus et fournit le mot de passe via la modale, donc elle ne bloque jamais. Ferme l'asymétrie de gestion des secrets entre terminal et GUI.
- Migration complète de l'adressage interne vers `10.1.0.0/16` : golden template `basiqueChezlepro``10.1.99.99`, et tous les exemples `192.168.x` (README, Makefile, runbooks Proxmox et template) réécrits vers le schéma `10.1.x` / `web-frontal-01`.
- Remplacement du groupe `serveurs_web` par `serveurs_web_frontaux` et `serveurs_web_dorsaux` dans les inventaires lab et production, mises à jour des exemples `README.md` et `AGENTS.md`.
- Retrait des hôtes de test `web-01` et `web-02` de l'inventaire production.
- Planification de la couche applicative web : `web-frontal-01` (95301), `web-frontal-02` (95302) dans `serveurs_web_frontaux` et `web-dorsal-01` (95401) dans `serveurs_web_dorsaux`, tous en `hotes_planifies` avec socle et durcissement.
- Mise à jour de `docs/nomenclature-vm.md` : domaines `web-frontal` / `web-dorsal`, retrait des noms de test `web-01`/`web-02`.
- Renforcement de la validation d'inventaire : - Renforcement de la validation d'inventaire :
- un hôte doit être soit planifié, soit actif, mais pas les deux ; - un hôte doit être soit planifié, soit actif, mais pas les deux ;
- un hôte actif doit avoir `ansible_host` ; - un hôte actif doit avoir `ansible_host` ;

View file

@ -84,30 +84,32 @@ aide:
@printf '%s\n' 'VM' @printf '%s\n' 'VM'
@printf '%s\n' ' Creer une VM complete:' @printf '%s\n' ' Creer une VM complete:'
@printf '%s\n' ' make creer-vm \' @printf '%s\n' ' make creer-vm \'
@printf '%s\n' ' HOTE=web-01 \' @printf '%s\n' ' HOTE=web-frontal-01 \'
@printf '%s\n' ' VMID=101 \' @printf '%s\n' ' VMID=95301 \'
@printf '%s\n' ' VLAN=12 \' @printf '%s\n' ' VLAN=15 \'
@printf '%s\n' ' ADRESSE_IP=192.168.12.101 \' @printf '%s\n' ' ADRESSE_IP=10.1.15.31 \'
@printf '%s\n' ' PASSERELLE=192.168.12.1 \' @printf '%s\n' ' PASSERELLE=10.1.15.1 \'
@printf '%s\n' ' GROUPES="serveurs_debian serveurs_durcis"' @printf '%s\n' ' GROUPES="serveurs_debian serveurs_durcis"'
@printf '%s\n' ' Cloner seulement, sans inventaire:' @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' ' make cloner-vm HOTE=web-frontal-01 VMID=95301 VLAN=15 ADRESSE_IP=10.1.15.31 PASSERELLE=10.1.15.1'
@printf '%s\n' ' Configurer Proxmox et le Vault API:' @printf '%s\n' ' Configurer Proxmox et le Vault API:'
@printf '%s\n' ' make config' @printf '%s\n' ' make config'
@printf '%s\n' '' @printf '%s\n' ''
@printf '%s\n' 'Hotes' @printf '%s\n' 'Hotes'
@printf '%s\n' ' Planifier un hote sans le deployer:' @printf '%s\n' ' Planifier un hote sans le deployer:'
@printf '%s\n' ' make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus serveurs_loki"' @printf '%s\n' ' make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus serveurs_loki"'
@printf '%s\n' ' Verifier un deploiement a blanc (dry-run):'
@printf '%s\n' ' make verifier-deploiement HOTE=web-frontal-01'
@printf '%s\n' ' Remettre un hote en conformite selon ses groupes:' @printf '%s\n' ' Remettre un hote en conformite selon ses groupes:'
@printf '%s\n' ' make deployer HOTE=web-01' @printf '%s\n' ' make deployer HOTE=web-frontal-01'
@printf '%s\n' ' Afficher un hote:' @printf '%s\n' ' Afficher un hote:'
@printf '%s\n' ' make hote-afficher HOTE=web-01' @printf '%s\n' ' make hote-afficher HOTE=web-frontal-01'
@printf '%s\n' ' Ajouter un hote existant:' @printf '%s\n' ' Ajouter un hote existant:'
@printf '%s\n' ' make hote-ajouter HOTE=web-01 VMID=99101 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"' @printf '%s\n' ' make hote-ajouter HOTE=web-frontal-01 VMID=95301 ADRESSE_IP=10.1.15.31 GROUPES="serveurs_debian serveurs_durcis"'
@printf '%s\n' ' Modifier ses groupes:' @printf '%s\n' ' Modifier ses groupes:'
@printf '%s\n' ' make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"' @printf '%s\n' ' make hote-groupes HOTE=web-frontal-01 GROUPES="serveurs_debian serveurs_durcis"'
@printf '%s\n' ' Diagnostiquer:' @printf '%s\n' ' Diagnostiquer:'
@printf '%s\n' ' make verifier-hote LIMITE=web-01' @printf '%s\n' ' make verifier-hote LIMITE=web-frontal-01'
@printf '%s\n' '' @printf '%s\n' ''
@printf '%s\n' 'Groupes' @printf '%s\n' 'Groupes'
@printf '%s\n' ' Appliquer un groupe complet:' @printf '%s\n' ' Appliquer un groupe complet:'
@ -140,8 +142,8 @@ aide:
@printf '%s\n' ' make verifier' @printf '%s\n' ' make verifier'
@printf '%s\n' '' @printf '%s\n' ''
@printf '%s\n' 'Variables frequentes' @printf '%s\n' 'Variables frequentes'
@printf '%s\n' ' HOTE=web-01 GROUPE=serveurs_debian GROUPES="serveurs_debian serveurs_durcis"' @printf '%s\n' ' HOTE=web-frontal-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' ' VMID=95301 VLAN=15 ADRESSE_IP=10.1.15.31 PASSERELLE=10.1.15.1'
@printf '%s\n' ' FICHIER_INVENTAIRE=inventories/production/hosts.yml FICHIER_DEPENDANCES=docs/dependances-groupes.yml CONFIRMER=true' @printf '%s\n' ' FICHIER_INVENTAIRE=inventories/production/hosts.yml FICHIER_DEPENDANCES=docs/dependances-groupes.yml CONFIRMER=true'
.PHONY: lint .PHONY: lint
@ -230,23 +232,35 @@ appliquer: ansible-runtime
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml" --limit '$(GROUPE):&$(GROUPE_HOTES_ACTIFS)' ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml" --limit '$(GROUPE):&$(GROUPE_HOTES_ACTIFS)'
deployer: deployer:
@if [[ -z "$(HOTE)" ]]; then \ @set -e; \
if [[ -z "$(HOTE)" ]]; then \
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \ printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
exit 2; \ exit 2; \
fi fi; \
@python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) playbooks --hote $(HOTE) --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES) > /dev/null python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) verifier-actif --hote $(HOTE); \
@python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) verifier-actif --hote $(HOTE) python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) --dependances $(FICHIER_DEPENDANCES) verifier-dependances-hote --hote $(HOTE); \
@python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) --dependances $(FICHIER_DEPENDANCES) verifier-dependances-hote --hote $(HOTE) playbooks="$$(python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) playbooks --hote $(HOTE) --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES))"; \
$(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 \ if [[ -z "$$playbooks" ]]; then \
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \ printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
exit 2; \ exit 2; \
fi; \ fi; \
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' inventories/production/group_vars 2>/dev/null | head -1 || true)"; \
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
if [[ -t 0 ]]; then \
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
vf="$$(mktemp)"; printf '%s' "$$mdp" > "$$vf"; chmod 600 "$$vf"; \
export ANSIBLE_VAULT_PASSWORD_FILE="$$vf"; \
trap 'rm -f "$$vf"' EXIT; \
else \
printf '%s\n' 'Refus: vault chiffre detecte mais aucun mot de passe (entree non interactive). Fournir ANSIBLE_VAULT_PASSWORD_FILE ou le champ vault de la GUI.'; \
exit 2; \
fi; \
fi; \
$(MAKE) _verifier-acces-hote LIMITE="$(HOTE)"; \
$(MAKE) _verifier-privileges-hote LIMITE="$(HOTE)"; \
for playbook in $$playbooks; do \ for playbook in $$playbooks; do \
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)"; \ ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)"; \
done done; \
$(MAKE) verifier-hote LIMITE="$(HOTE)" $(MAKE) verifier-hote LIMITE="$(HOTE)"
deployer-groupe: deployer-groupe:
@ -256,6 +270,36 @@ deployer-groupe:
fi fi
$(MAKE) appliquer GROUPE="$(GROUPE)" $(MAKE) appliquer GROUPE="$(GROUPE)"
.PHONY: verifier-deploiement
verifier-deploiement: ansible-runtime
@set -e; \
if [[ -z "$(HOTE)" ]]; then \
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
exit 2; \
fi; \
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) verifier-actif --hote $(HOTE); \
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) --dependances $(FICHIER_DEPENDANCES) verifier-dependances-hote --hote $(HOTE); \
playbooks="$$(python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) playbooks --hote $(HOTE) --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES))"; \
if [[ -z "$$playbooks" ]]; then \
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
exit 2; \
fi; \
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' inventories/production/group_vars 2>/dev/null | head -1 || true)"; \
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
if [[ -t 0 ]]; then \
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
vf="$$(mktemp)"; printf '%s' "$$mdp" > "$$vf"; chmod 600 "$$vf"; \
export ANSIBLE_VAULT_PASSWORD_FILE="$$vf"; \
trap 'rm -f "$$vf"' EXIT; \
else \
printf '%s\n' 'Refus: vault chiffre detecte mais aucun mot de passe (entree non interactive). Fournir ANSIBLE_VAULT_PASSWORD_FILE ou le champ vault de la GUI.'; \
exit 2; \
fi; \
fi; \
for playbook in $$playbooks; do \
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)" --check --diff; \
done
cloner-vm: ansible-runtime cloner-vm: ansible-runtime
@if [[ -z "$(HOTE)" || -z "$(VMID)" ]]; then \ @if [[ -z "$(HOTE)" || -z "$(VMID)" ]]; then \
printf '%s\n' 'Refus: relancer avec HOTE=nom VMID=id_clone.'; \ printf '%s\n' 'Refus: relancer avec HOTE=nom VMID=id_clone.'; \

View file

@ -1,4 +1,4 @@
# Set-OPS # Set-OPS — Votre artisan numérique
Dépôt Ansible central pour construire, configurer, maintenir et documenter les systèmes de Chezlepro Inc. Dépôt Ansible central pour construire, configurer, maintenir et documenter les systèmes de Chezlepro Inc.
@ -57,7 +57,7 @@ Inspecter les inventaires :
```bash ```bash
make inventaire make inventaire
make hote-afficher HOTE=web-01 make hote-afficher HOTE=web-frontal-01
``` ```
Ouvrir l'interface locale de gestion d'inventaire : Ouvrir l'interface locale de gestion d'inventaire :
@ -75,15 +75,15 @@ make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_dur
Ajouter une VM à l'inventaire production et l'associer aux groupes qui détermineront sa configuration : Ajouter une VM à l'inventaire production et l'associer aux groupes qui détermineront sa configuration :
```bash ```bash
make hote-ajouter HOTE=web-01 VMID=99101 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis" make hote-ajouter HOTE=web-frontal-01 VMID=95301 ADRESSE_IP=10.1.15.31 GROUPES="serveurs_debian serveurs_durcis"
make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis" make hote-groupes HOTE=web-frontal-01 GROUPES="serveurs_debian serveurs_durcis"
``` ```
Chaque groupe opérationnel doit avoir son playbook homonyme dans `playbooks/groupes/`. 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. 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.
Les groupes opérationnels avec des hôtes, comme `serveurs_web` ou `clients_dns`, sont validés contre `playbooks/groupes/` par les commandes d'inventaire. Les groupes opérationnels avec des hôtes, comme `serveurs_web_frontaux` ou `clients_dns`, sont validés contre `playbooks/groupes/` par les commandes d'inventaire.
Le catalogue des services et intégrations prévus est dans `docs/catalogue-services.md`. Le catalogue des services et intégrations prévus est dans `docs/catalogue-services.md`.
@ -97,8 +97,8 @@ Créer un clone depuis le modèle Debian 13 via l'API Proxmox :
```bash ```bash
make config 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 creer-vm HOTE=web-frontal-01 VMID=95301 VLAN=15 ADRESSE_IP=10.1.15.31 PASSERELLE=10.1.15.1 GROUPES="serveurs_debian serveurs_durcis"
make deployer HOTE=web-01 make deployer HOTE=web-frontal-01
``` ```
Les valeurs Cloud-Init communes déjà présentes dans le modèle Proxmox sont héritées par les clones. Les valeurs Cloud-Init communes déjà présentes dans le modèle Proxmox sont héritées par les clones.
@ -119,7 +119,7 @@ make nettoyer-modele CONFIRMER=true
Déployer ou remettre en conformité une VM Debian clonée depuis le template : Déployer ou remettre en conformité une VM Debian clonée depuis le template :
```bash ```bash
make deployer HOTE=web-01 make deployer HOTE=web-frontal-01
``` ```
Déployer ou remettre en conformité un groupe : Déployer ou remettre en conformité un groupe :

46
docs/bases-donnees.yml Normal file
View file

@ -0,0 +1,46 @@
---
# Registre des bases de donnees applicatives Set-OPS.
#
# Patron : 1 appli -> 1 base nommee -> 1 compte proprietaire (db_owner) -> 1 chaine de connexion.
#
# Deux consommateurs du MEME registre :
# - serveurs_postgresql : cree chaque base + son compte proprietaire
# (mot de passe = la variable Vault nommee par le champ "secret").
# - le role applicatif : construit sa chaine de connexion vers cette base.
#
# Le mot de passe est PARTAGE : il vit une seule fois dans le Vault (variable nommee
# par "secret"), lu par le serveur (pour creer le compte) et par l'appli (pour se
# connecter). Source unique, jamais de desynchronisation.
#
# Convention de chaine de connexion cote appli :
# host=<IP du serveur BD> port=5432 dbname=<base> user=<proprietaire>
# password={{ lookup('vars', <secret>) }} sslmode=require
# L'IP du serveur BD se derive de la nomenclature (ex. data-01 -> 10.1.13.11).
#
# Prerequis : la dependance "appli requiert serveurs_postgresql actif" est declaree
# dans docs/dependances-groupes.yml (la base existe avant que l'appli s'y connecte).
#
# Ajouter une appli = une entree ici + son secret dans le Vault. Aucun nouveau role.
bases_donnees:
keycloak:
base: keycloak
proprietaire: keycloak
serveur: serveurs_postgresql
secret: vault_bd_keycloak
icingadb:
base: icingadb
proprietaire: icingadb
serveur: serveurs_postgresql
secret: vault_bd_icingadb
# icingaweb : a ajouter avec la phase Icinga Web 2 (2e base de l'ecosysteme Icinga).
forgejo:
base: forgejo
proprietaire: forgejo
serveur: serveurs_postgresql
secret: vault_bd_forgejo
# nextcloud:
# base: nextcloud
# proprietaire: nextcloud
# serveur: serveurs_postgresql
# secret: vault_bd_nextcloud

View file

@ -34,6 +34,25 @@ Un service central peut partager un hôte avec d'autres services du même domain
| Nextcloud | `serveurs_nextcloud` | `playbooks/groupes/serveurs_nextcloud.yml` | `nextcloud` | | Nextcloud | `serveurs_nextcloud` | `playbooks/groupes/serveurs_nextcloud.yml` | `nextcloud` |
| Collabora | `serveurs_collabora` | `playbooks/groupes/serveurs_collabora.yml` | `collabora` | | Collabora | `serveurs_collabora` | `playbooks/groupes/serveurs_collabora.yml` | `collabora` |
## Couche applicative web
La couche applicative web est distincte de l'edge `serveurs_nginx` :
- `serveurs_nginx` est l'edge : mandataire inverse, terminaison TLS, WAF ; il reçoit le trafic externe et le route.
- `serveurs_web_frontaux` est la couche présentation web (UI, rendu, assets), servie *derrière* l'edge.
- `serveurs_web_dorsaux` est la couche application web (API, traitement), consommée par les frontaux.
Le « web » de ces deux groupes est implicite par leur position derrière `serveurs_nginx`. Le jour où un service dorsal n'est pas web (worker batch, file, démon), créer un groupe dédié plutôt que d'élargir `serveurs_web_dorsaux`.
| Couche | Groupe | Playbook | Rôle futur |
| --- | --- | --- | --- |
| Présentation web (frontends) | `serveurs_web_frontaux` | `playbooks/groupes/serveurs_web_frontaux.yml` | `web_frontaux` |
| Application web (backends) | `serveurs_web_dorsaux` | `playbooks/groupes/serveurs_web_dorsaux.yml` | `web_dorsaux` |
Hôtes planifiés : `web-frontal-01` et `web-frontal-02` dans `serveurs_web_frontaux` ; `web-dorsal-01` dans `serveurs_web_dorsaux`. Aucun n'est encore actif (à créer puis activer).
Dépendance d'intégration prévue : `serveurs_web_frontaux` devra publier via `serveurs_nginx` (règle « tout service exposé en HTTP(S) passe par l'edge »). Cette dépendance n'est pas encore déclarée dans `docs/dependances-groupes.yml` (les deux groupes sont vides) ; elle sera ajoutée avec le rôle `web_frontaux`, lorsque des frontaux actifs devront publier via l'edge.
## Hôtes planifiés ## Hôtes planifiés
| Hôte | Services | | Hôte | Services |

View file

@ -39,8 +39,8 @@ ansible_host defini
Exemple : Exemple :
```text ```text
web-01 ansible_host=192.168.12.101 web-frontal-01 ansible_host=10.1.15.31
-> web-01.chezlepro.internal A 192.168.12.101 -> web-frontal-01.chezlepro.internal A 10.1.15.31
``` ```
Les enregistrements additionnels explicites sont dans `serveurs_powerdns_records`. Les enregistrements additionnels explicites sont dans `serveurs_powerdns_records`.

View file

@ -61,7 +61,7 @@ VM destinée à devenir un template, pas un serveur de production
Vérifications utiles depuis le poste Ansible : Vérifications utiles depuis le poste Ansible :
```bash ```bash
ssh ansible@192.168.12.99 ssh ansible@10.1.99.99
ansible -i inventories/lab/hosts.yml modeles_vm -m ping ansible -i inventories/lab/hosts.yml modeles_vm -m ping
ansible -i inventories/lab/hosts.yml modeles_vm -m setup ansible -i inventories/lab/hosts.yml modeles_vm -m setup
``` ```

View file

@ -25,9 +25,19 @@ obs-01
mon-01 mon-01
forge-01 forge-01
collab-01 collab-01
web-frontal-01
web-dorsal-01
``` ```
Les noms courts historiques `web-01` et `web-02` restent valides pour les deux premières VM déjà créées. La couche applicative web suit le même format. Le tier est porté par le domaine, au singulier puisqu'il nomme une instance :
```text
web-frontal-01 présentation web (UI, rendu, assets), groupe serveurs_web_frontaux
web-frontal-02
web-dorsal-01 application web (API, traitement), groupe serveurs_web_dorsaux
```
Les anciens noms de test `web-01` et `web-02` sont retirés. Ils ne doivent pas être réutilisés comme noms de production : préférer `web-frontal-NN` et `web-dorsal-NN`.
## Regroupements recommandés ## Regroupements recommandés
@ -43,6 +53,8 @@ Les noms courts historiques `web-01` et `web-02` restent valides pour les deux p
| `mon-01` | `serveurs_icinga` | | `mon-01` | `serveurs_icinga` |
| `forge-01` | `serveurs_forgejo` | | `forge-01` | `serveurs_forgejo` |
| `collab-01` | `serveurs_nextcloud`, `serveurs_collabora` | | `collab-01` | `serveurs_nextcloud`, `serveurs_collabora` |
| `web-frontal-01`, `web-frontal-02` | `serveurs_web_frontaux` |
| `web-dorsal-01` | `serveurs_web_dorsaux` |
Les groupes restent fins et composables. La cohabitation se fait en associant plusieurs groupes au même hôte. Les groupes restent fins et composables. La cohabitation se fait en associant plusieurs groupes au même hôte.
@ -57,6 +69,56 @@ Les groupes restent fins et composables. La cohabitation se fait en associant pl
| `95xxx` | applications internes | | `95xxx` | applications internes |
| `99xxx` | modèles, essais initiaux ou exceptions documentées | | `99xxx` | modèles, essais initiaux ou exceptions documentées |
## Plan d'adressage interne
Réseau interne unique : `10.1.0.0/16`. Segmentation par fonction, un `/24` et un VLAN par catégorie, **3ᵉ octet = VLAN** (L2 alignée sur L3).
| Catégorie | VLAN | Sous-réseau | Passerelle |
| --- | --- | --- | --- |
| 1 — Fondations / infra | 11 | `10.1.11.0/24` | `10.1.11.1` |
| 2 — Identité | 12 | `10.1.12.0/24` | `10.1.12.1` |
| 3 — Données | 13 | `10.1.13.0/24` | `10.1.13.1` |
| 4 — Observabilité | 14 | `10.1.14.0/24` | `10.1.14.1` |
| 5 — Applications | 15 | `10.1.15.0/24` | `10.1.15.1` |
Adresse d'hôte (4ᵉ octet) : **`service × 10 + NN`**. `.1` = passerelle ; `.2``.9` réservés. Exemple : `web-dorsal-01` (catégorie 5, service 4, NN 01) → `10.1.15.41`.
Tout se dérive du domaine de l'hôte, et la source unique machine-lisible est **`docs/nomenclature.yml`** :
```text
hostname = <domaine>-<NN>
VMID = 9 · catégorie · service · NN
VLAN = catégorie.vlan
IP = 10.1.<vlan>.(service × 10 + NN)
```
`make inventaire-ui` lit ce registre et **propose** automatiquement VMID, VLAN, IP et passerelle quand on nomme un hôte. La sécurité entre zones se fera par règles inter-zones (nftables / edge), pas par l'adressage.
Contrainte : `NN` de 01 à 09 par domaine (l'octet hôte reste dans le bloc du service). Au-delà, ouvrir un nouveau domaine/service dans `docs/nomenclature.yml`.
La segmentation `10.1.0.0/16` remplace l'ancienne plage d'essais `192.168.12.x`.
## Variables de provisioning d'hôte
L'inventaire est la source de vérité du provisioning d'une VM. Ces variables d'hôte sont saisissables via `make inventaire-ui` (ou à la main) et décrivent l'instanciation attendue :
| Variable | Sens | Type |
| --- | --- | --- |
| `ansible_host` | adresse IP | str |
| `proxmox_cidr` | masque réseau en bits (0-32) | int |
| `proxmox_passerelle` | passerelle | str |
| `proxmox_vlan` | tag VLAN (1-4094) | int |
| `proxmox_pont` | pont réseau Proxmox (ex. `vmbr0`) | str |
| `proxmox_dns` | serveurs DNS Cloud-Init (séparés par virgule) | str |
| `proxmox_vmid` | identifiant VM Proxmox | int |
| `proxmox_noeud` | nœud Proxmox cible | str |
| `proxmox_stockage` | stockage du disque | str |
| `proxmox_disque_taille` | taille disque (ex. `32G`) | str |
| `proxmox_memoire` | mémoire en Mo | int |
| `proxmox_coeurs` | nombre de cœurs | int |
Ces valeurs sont renseignées dans l'inventaire ; leur consommation automatique par `make creer-vm` (au lieu des arguments en ligne de commande) est l'étape d'intégration suivante. Une valeur vide n'est pas écrite, pour garder l'inventaire propre.
## Hôtes planifiés ## Hôtes planifiés
Les hôtes prévus mais non encore déployés sont placés dans `hotes_planifies`. Les hôtes prévus mais non encore déployés sont placés dans `hotes_planifies`.

42
docs/nomenclature.yml Normal file
View file

@ -0,0 +1,42 @@
---
# Nomenclature Set-OPS : source unique pour hostnames, VMID, VLAN et adressage.
#
# Derivations (tout part du domaine de l'hote) :
# hostname = <domaine>-<NN> ex. web-frontal-03
# VMID = 9 . categorie . service . NN ex. 9 5 3 03 -> 95303
# VLAN = categorie.vlan ex. 15
# IP = <prefixe_sous_reseau>.(service*10 + NN) ex. 10.1.15.33
# passerelle / CIDR : voir la categorie
#
# Contrainte : NN va de 01 a 09 par domaine (l'octet hote = service*10 + NN
# reste dans le bloc du service). Au-dela, ouvrir un nouveau domaine ou service.
supernet: "10.1.0.0/16"
cidr_hote: 24
reservations:
passerelle: 1 # .1 = passerelle de chaque /24
reserve_min: 2 # .2 a .9 reserves (reseau, HA, futur)
reserve_max: 9
categories:
1: { libelle: "Fondations / infra", vlan: 11, sous_reseau: "10.1.11.0/24", passerelle: "10.1.11.1" }
2: { libelle: "Identite", vlan: 12, sous_reseau: "10.1.12.0/24", passerelle: "10.1.12.1" }
3: { libelle: "Donnees", vlan: 13, sous_reseau: "10.1.13.0/24", passerelle: "10.1.13.1" }
4: { libelle: "Observabilite", vlan: 14, sous_reseau: "10.1.14.0/24", passerelle: "10.1.14.1" }
5: { libelle: "Applications", vlan: 15, sous_reseau: "10.1.15.0/24", passerelle: "10.1.15.1" }
9: { libelle: "Modeles / essais", vlan: 99, sous_reseau: "10.1.99.0/24", passerelle: "10.1.99.1" }
domaines:
infra-pki: { categorie: 1, service: 1 }
infra-edge: { categorie: 1, service: 2 }
infra-mail: { categorie: 1, service: 3 }
infra-dns: { categorie: 1, service: 4 }
idm: { categorie: 2, service: 1 }
data: { categorie: 3, service: 1 }
obs: { categorie: 4, service: 1 }
mon: { categorie: 4, service: 4 }
forge: { categorie: 5, service: 1 }
collab: { categorie: 5, service: 2 }
web-frontal: { categorie: 5, service: 3 }
web-dorsal: { categorie: 5, service: 4 }

View file

@ -495,8 +495,8 @@ Exemple IP statique temporaire pour joindre la VM de modèle :
```bash ```bash
qm set 9000 --ciuser ansible qm set 9000 --ciuser ansible
qm set 9000 --sshkeys ~/.ssh/id_ed25519.pub qm set 9000 --sshkeys ~/.ssh/id_ed25519.pub
qm set 9000 --ipconfig0 ip=192.168.11.150/24,gw=192.168.11.1 qm set 9000 --ipconfig0 ip=10.1.99.99/24,gw=10.1.99.1
qm set 9000 --nameserver 192.168.11.1 qm set 9000 --nameserver 10.1.99.1
``` ```
Pour les clones, ces valeurs seront remplacées par l'identité réelle du serveur. Pour les clones, ces valeurs seront remplacées par l'identité réelle du serveur.
@ -761,11 +761,11 @@ Exemple :
```bash ```bash
make creer-vm \ make creer-vm \
HOTE=web-01 \ HOTE=web-frontal-01 \
VMID=101 \ VMID=95301 \
VLAN=11 \ VLAN=11 \
ADRESSE_IP=192.168.11.101 \ ADRESSE_IP=10.1.15.31 \
PASSERELLE=192.168.11.1 \ PASSERELLE=10.1.15.1 \
GROUPES="serveurs_debian serveurs_durcis" GROUPES="serveurs_debian serveurs_durcis"
``` ```
@ -774,13 +774,13 @@ Le VLAN est volontairement saisi au moment de créer chaque VM. Il ne fait pas p
Après le premier démarrage, tester l'accès : Après le premier démarrage, tester l'accès :
```bash ```bash
ssh ansible@192.168.11.101 ssh ansible@10.1.15.31
``` ```
Ensuite appliquer la conformité Ansible : Ensuite appliquer la conformité Ansible :
```bash ```bash
make deployer HOTE=web-01 make deployer HOTE=web-frontal-01
``` ```
--- ---

View file

@ -114,8 +114,8 @@ serveurs_durcis -> playbooks/groupes/serveurs_durcis.yml
L'appartenance aux groupes devient la déclaration d'intention : L'appartenance aux groupes devient la déclaration d'intention :
```text ```text
web-01 dans serveurs_debian -> applique le socle Debian web-frontal-01 dans serveurs_debian -> applique le socle Debian
web-01 dans serveurs_durcis -> applique le durcissement commun web-frontal-01 dans serveurs_durcis -> applique le durcissement commun
``` ```
Un groupe sans playbook ne doit pas être assigné à une VM de production. Un groupe sans playbook ne doit pas être assigné à une VM de production.
@ -145,8 +145,8 @@ L'accès SSH par mot de passe est désactivé dès le socle. L'activation de nft
Pour une VM déjà clonée et démarrée : Pour une VM déjà clonée et démarrée :
```bash ```bash
make hote-ajouter HOTE=web-01 VMID=99101 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis" make hote-ajouter HOTE=web-frontal-01 VMID=95301 ADRESSE_IP=10.1.15.31 GROUPES="serveurs_debian serveurs_durcis"
make deployer HOTE=web-01 make deployer HOTE=web-frontal-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. 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.

View file

@ -3,7 +3,7 @@ all:
modeles_vm: modeles_vm:
hosts: hosts:
basiqueChezlepro: basiqueChezlepro:
ansible_host: 192.168.12.99 ansible_host: 10.1.99.99
ansible_user: ansible ansible_user: ansible
ansible_become: true ansible_become: true
@ -13,7 +13,10 @@ all:
serveurs_durcis: serveurs_durcis:
hosts: {} hosts: {}
serveurs_web: serveurs_web_frontaux:
hosts: {}
serveurs_web_dorsaux:
hosts: {} hosts: {}
hotes_proxmox: hotes_proxmox:

View file

@ -0,0 +1,8 @@
---
# Conformite identite utilisateur cliente (hotes authentifiant via OpenLDAP).
clients_ldap_uri: "ldap://idm-01.chezlepro.internal"
clients_ldap_base_dn: "dc=chezlepro,dc=internal"
# Bind anonyme par defaut. Pour un compte de lecture dedie, definir ici
# clients_ldap_bind_dn et clients_ldap_bind_password ("{{ vault_ldap_sssd }}" via Vault).

View file

@ -0,0 +1,11 @@
---
# Conformite PKI cliente (tout hote devant une identite machine).
clients_pki_ca_url: "https://infra-pki-01.chezlepro.internal:8443"
clients_pki_provisioner: "admin@chezlepro.internal"
# Secrets OBLIGATOIRES, a fournir via Ansible Vault avant tout deploiement reel :
# clients_pki_ca_fingerprint: "{{ vault_step_ca_fingerprint }}"
# clients_pki_provisioner_password: "{{ vault_step_ca_provisioner_password }}"
# Le provisioner password est PARTAGE avec serveurs_step_ca : le placer dans un
# vault partage (ex. group_vars/all/) lisible par les deux groupes.

View file

@ -0,0 +1,5 @@
---
# Conformite relais mail client (hotes relayant vers serveurs_sendmail).
clients_smtp_relais: "infra-mail-01.chezlepro.internal"
clients_smtp_port: 25

View file

@ -0,0 +1,9 @@
---
# Conformite Forgejo (forge-01, VLAN 15).
serveurs_forgejo_hostname: "forge.chezlepro.internal"
serveurs_forgejo_db_host: "10.1.13.11" # data-01
# Secrets OBLIGATOIRES via Ansible Vault (vault partage pour vault_bd_forgejo) :
# serveurs_forgejo_secret_key, serveurs_forgejo_internal_token,
# serveurs_forgejo_admin_password, vault_bd_forgejo

View file

@ -0,0 +1,8 @@
---
# Conformite Grafana (obs-01, VLAN 14).
serveurs_grafana_hostname: "grafana.chezlepro.internal"
# Secret OBLIGATOIRE, a fournir via Ansible Vault avant tout deploiement reel :
# serveurs_grafana_admin_password: "{{ vault_grafana_admin }}"
# Convertir en dossier group_vars/serveurs_grafana/ avec main.yml + vault.yml (chiffre).

View file

@ -0,0 +1,8 @@
---
# Conformite supervision Icinga (mon-01, VLAN 14).
serveurs_icinga_db_host: "10.1.13.11" # data-01 (serveurs_postgresql)
# Secret BD OBLIGATOIRE, a fournir via Ansible Vault (partage avec serveurs_postgresql) :
# vault_bd_icingadb: "..."
# A placer dans un vault partage (ex. group_vars/all/) lisible par les deux groupes.

View file

@ -0,0 +1,12 @@
---
# Conformite Keycloak (idm-01, VLAN 12).
serveurs_keycloak_hostname: "keycloak.chezlepro.internal"
serveurs_keycloak_db_host: "10.1.13.11" # data-01 (serveurs_postgresql)
# Secrets OBLIGATOIRES, a fournir via Ansible Vault avant tout deploiement reel :
# - serveurs_keycloak_admin_password: "{{ vault_keycloak_admin }}" (admin bootstrap)
# - vault_bd_keycloak: "..." (mot de passe BD, partage avec serveurs_postgresql)
# Convertir en dossier group_vars/serveurs_keycloak/ avec main.yml (clair) + vault.yml (chiffre).
# Le secret BD (vault_bd_keycloak) doit etre lisible aussi par serveurs_postgresql
# (placer dans un vault partage, ex. group_vars/all/).

View file

@ -0,0 +1,9 @@
---
# Conformite nginx / edge (infra-edge-01, VLAN 11).
# Sites de reverse proxy : a declarer ici quand un service web doit etre publie.
# L'amont pointe vers l'IP interne et le port du service (cf. nomenclature).
serveurs_nginx_sites: []
# - nom: forge
# domaine: forge.chezlepro.internal
# amont: "http://10.1.15.11:3000"

View file

@ -0,0 +1,11 @@
---
# Conformite annuaire OpenLDAP (idm-01, VLAN 12).
serveurs_openldap_domaine: "chezlepro.internal"
serveurs_openldap_organisation: "Chezlepro Inc"
# Secret OBLIGATOIRE, a fournir via Ansible Vault avant tout deploiement reel.
# Convertir en dossier group_vars/serveurs_openldap/ avec :
# main.yml : serveurs_openldap_admin_password: "{{ vault_openldap_admin }}"
# vault.yml : vault_openldap_admin: "..." (chiffre ansible-vault)
# Tant qu'il est absent, le role refuse de s'executer (assertion explicite).

View file

@ -0,0 +1,10 @@
---
# Conformite PostgreSQL (zone donnees, VLAN 13).
serveurs_postgresql_reseaux_autorises:
- "10.1.0.0/16"
# Comptes et bases applicatifs : a declarer ici quand un service en a besoin,
# avec les mots de passe references depuis Ansible Vault (jamais en clair).
serveurs_postgresql_comptes: []
serveurs_postgresql_bases: []

View file

@ -0,0 +1,8 @@
---
# Conformite Redis (data-01, VLAN 13).
serveurs_redis_maxmemory: "256mb"
# Secret OBLIGATOIRE, a fournir via Ansible Vault avant tout deploiement reel :
# serveurs_redis_password: "{{ vault_redis }}"
# Convertir en dossier group_vars/serveurs_redis/ avec main.yml + vault.yml (chiffre).

View file

@ -0,0 +1,11 @@
---
# Conformite relais SMTP (infra-mail-01, VLAN 11).
serveurs_sendmail_domaine: "chezlepro.internal"
serveurs_sendmail_reseaux_autorises:
- "127.0.0.0/8"
- "[::1]/128"
- "10.1.0.0/16"
# Smarthost amont : vide = remise directe. A renseigner si relais externe requis.
serveurs_sendmail_smarthost: ""

View file

@ -0,0 +1,16 @@
---
# Conformite AC interne step-ca (infra-pki-01, VLAN 11).
serveurs_step_ca_nom: "Chezlepro Internal CA"
serveurs_step_ca_adresse: ":8443"
serveurs_step_ca_acme: true
# Secrets OBLIGATOIRES, a fournir via Ansible Vault avant tout deploiement reel.
# Convertir ce fichier en dossier group_vars/serveurs_step_ca/ avec :
# main.yml (en clair) :
# serveurs_step_ca_password: "{{ vault_step_ca_password }}"
# serveurs_step_ca_provisioner_password: "{{ vault_step_ca_provisioner_password }}"
# vault.yml (chiffre ansible-vault) :
# vault_step_ca_password: "..."
# vault_step_ca_provisioner_password: "..."
# Tant qu'ils sont absents, le role refuse de s'executer (assertion explicite).

View file

@ -3,139 +3,251 @@ all:
modeles_vm: modeles_vm:
hosts: {} hosts: {}
hotes_actifs: hotes_actifs:
hosts: hosts: {}
web-01:
ansible_host: 192.168.12.101
ansible_user: ansible
proxmox_vmid: 99101
web-02:
ansible_host: 192.168.12.102
ansible_user: ansible
proxmox_vmid: 99102
hotes_planifies: hotes_planifies:
hosts: hosts:
infra-pki-01:
proxmox_vmid: 91101
infra-edge-01:
proxmox_vmid: 91201
infra-mail-01:
proxmox_vmid: 91301
infra-dns-01:
proxmox_vmid: 91401
idm-01:
proxmox_vmid: 92101
data-01:
proxmox_vmid: 93101
obs-01:
proxmox_vmid: 94101
mon-01:
proxmox_vmid: 94401
forge-01:
proxmox_vmid: 95101
collab-01: collab-01:
ansible_host: 10.1.15.21
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.15.1
proxmox_vlan: 15
proxmox_vmid: 95201 proxmox_vmid: 95201
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
data-01:
ansible_host: 10.1.13.11
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.13.1
proxmox_vlan: 13
proxmox_vmid: 93101
proxmox_noeud: asgard
proxmox_stockage: TrueNAS
proxmox_disque_taille: 160G
proxmox_memoire: 2048
proxmox_coeurs: 4
forge-01:
ansible_host: 10.1.15.11
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.15.1
proxmox_vlan: 15
proxmox_vmid: 95101
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
idm-01:
ansible_host: 10.1.12.11
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.12.1
proxmox_vlan: 12
proxmox_vmid: 92101
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
infra-dns-01:
ansible_host: 10.1.11.41
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.11.1
proxmox_vlan: 11
proxmox_vmid: 91401
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
infra-edge-01:
ansible_host: 10.1.11.21
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.11.1
proxmox_vlan: 11
proxmox_vmid: 91201
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
infra-mail-01:
ansible_host: 10.1.11.31
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.11.1
proxmox_vlan: 11
proxmox_vmid: 91301
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
infra-pki-01:
ansible_host: 10.1.11.11
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.11.1
proxmox_vlan: 11
proxmox_vmid: 91101
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
mon-01:
ansible_host: 10.1.14.41
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.14.1
proxmox_vlan: 14
proxmox_vmid: 94401
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
obs-01:
ansible_host: 10.1.14.11
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.14.1
proxmox_vlan: 14
proxmox_vmid: 94101
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 4
web-dorsal-01:
ansible_host: 10.1.15.41
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.15.1
proxmox_vlan: 15
proxmox_vmid: 95401
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
web-frontal-01:
ansible_host: 10.1.15.31
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.15.1
proxmox_vlan: 15
proxmox_vmid: 95301
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
web-frontal-02:
ansible_host: 10.1.15.32
ansible_user: ansible
proxmox_cidr: 24
proxmox_passerelle: 10.1.15.1
proxmox_vlan: 15
proxmox_vmid: 95302
proxmox_stockage: TrueNAS
proxmox_disque_taille: 16G
proxmox_memoire: 2048
proxmox_coeurs: 2
clients_dns:
hosts:
collab-01: {}
web-frontal-01: {}
clients_journaux:
hosts: {}
clients_ldap:
hosts: {}
clients_metriques:
hosts: {}
clients_pki:
hosts: {}
clients_smtp:
hosts: {}
clients_supervision:
hosts:
web-frontal-01: {}
serveurs_collabora:
hosts:
collab-01: {}
serveurs_debian: serveurs_debian:
hosts: hosts:
web-01: collab-01: {}
ansible_host: 192.168.12.101 data-01: {}
ansible_user: ansible forge-01: {}
web-02: idm-01: {}
ansible_host: 192.168.12.102 infra-dns-01: {}
ansible_user: ansible
infra-pki-01: {}
infra-edge-01: {} infra-edge-01: {}
infra-mail-01: {} infra-mail-01: {}
infra-dns-01: {} infra-pki-01: {}
idm-01: {}
data-01: {}
obs-01: {}
mon-01: {} mon-01: {}
forge-01: {} obs-01: {}
collab-01: {} web-dorsal-01: {}
web-frontal-01: {}
web-frontal-02: {}
serveurs_durcis: serveurs_durcis:
hosts: hosts:
web-01: collab-01: {}
ansible_host: 192.168.12.101 data-01: {}
ansible_user: ansible forge-01: {}
web-02: idm-01: {}
ansible_host: 192.168.12.102 infra-dns-01: {}
ansible_user: ansible
infra-pki-01: {}
infra-edge-01: {} infra-edge-01: {}
infra-mail-01: {} infra-mail-01: {}
infra-dns-01: {} infra-pki-01: {}
idm-01: {}
data-01: {}
obs-01: {}
mon-01: {} mon-01: {}
forge-01: {} obs-01: {}
collab-01: {} web-dorsal-01: {}
serveurs_web: web-frontal-01: {}
web-frontal-02: {}
serveurs_forgejo:
hosts: hosts:
web-01: forge-01: {}
ansible_host: 192.168.12.101 serveurs_grafana:
ansible_user: ansible hosts:
web-02: obs-01: {}
ansible_host: 192.168.12.102 serveurs_icinga:
ansible_user: ansible hosts:
mon-01: {}
serveurs_keycloak: serveurs_keycloak:
hosts: hosts:
idm-01: {} idm-01: {}
serveurs_loki:
hosts:
obs-01: {}
serveurs_nextcloud:
hosts:
collab-01: {}
serveurs_nginx:
hosts:
infra-edge-01: {}
serveurs_openldap: serveurs_openldap:
hosts: hosts:
idm-01: {} idm-01: {}
serveurs_postgresql: serveurs_postgresql:
hosts: hosts:
data-01: {} data-01: {}
serveurs_loki: serveurs_powerdns:
hosts: hosts:
obs-01: {} infra-dns-01: {}
serveurs_icinga:
hosts:
mon-01: {}
serveurs_prometheus: serveurs_prometheus:
hosts: hosts:
obs-01: {} obs-01: {}
serveurs_grafana: serveurs_redis:
hosts: hosts:
obs-01: {} data-01: {}
serveurs_forgejo:
hosts:
forge-01: {}
serveurs_sendmail: serveurs_sendmail:
hosts: hosts:
infra-mail-01: {} infra-mail-01: {}
serveurs_step_ca: serveurs_step_ca:
hosts: hosts:
infra-pki-01: {} infra-pki-01: {}
serveurs_powerdns: serveurs_web_dorsaux:
hosts: hosts:
infra-dns-01: {} web-dorsal-01: {}
serveurs_redis: serveurs_web_frontaux:
hosts: hosts:
data-01: {} web-frontal-01: {}
serveurs_nginx: web-frontal-02: {}
hosts:
infra-edge-01: {}
serveurs_nextcloud:
hosts:
collab-01: {}
serveurs_collabora:
hosts:
collab-01: {}
clients_dns:
hosts: {}
clients_pki:
hosts: {}
clients_ldap:
hosts: {}
clients_supervision:
hosts: {}
clients_metriques:
hosts: {}
clients_journaux:
hosts: {}
clients_smtp:
hosts: {}
hotes_proxmox: hotes_proxmox:
hosts: {} hosts: {}

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que l'intégration cliente journaux reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe clients_journaux." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- clients_journaux

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que l'intégration cliente LDAP reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe clients_ldap." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- clients_ldap

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que l'intégration cliente métriques reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe clients_metriques." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- clients_metriques

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que l'intégration cliente PKI reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe clients_pki." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- clients_pki

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que l'intégration cliente SMTP reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe clients_smtp." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- clients_smtp

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Forgejo reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_forgejo." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_forgejo

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Grafana reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_grafana." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_grafana

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que la plateforme Icinga reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_icinga." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_icinga

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Keycloak reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_keycloak." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_keycloak

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Loki reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_loki." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_loki

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service NGINX reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_nginx." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_nginx

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service OpenLDAP reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_openldap." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_openldap

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service PostgreSQL reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_postgresql." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_postgresql

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Prometheus reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_prometheus." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_prometheus

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Redis reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_redis." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_redis

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service Sendmail reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_sendmail." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_sendmail

View file

@ -4,7 +4,12 @@
become: true become: true
gather_facts: true gather_facts: true
tasks: pre_tasks:
- name: Indiquer que le service step-ca reste à définir - name: Vérifier que la cible est Debian
ansible.builtin.debug: ansible.builtin.assert:
msg: "Aucun rôle n'est encore associé au groupe serveurs_step_ca." that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveurs_step_ca

View file

@ -1,6 +1,6 @@
--- ---
- name: Appliquer le groupe serveurs_web - name: Appliquer le groupe serveurs_web_dorsaux
hosts: serveurs_web hosts: serveurs_web_dorsaux
become: true become: true
gather_facts: true gather_facts: true
@ -12,6 +12,6 @@
fail_msg: "Ce playbook est prévu pour Debian." fail_msg: "Ce playbook est prévu pour Debian."
tasks: tasks:
- name: Indiquer que le rôle web reste à définir - name: Indiquer que le rôle dorsal web reste à définir
ansible.builtin.debug: ansible.builtin.debug:
msg: "Aucun rôle web n'est encore associé au groupe serveurs_web." msg: "Aucun rôle n'est encore associé au groupe serveurs_web_dorsaux (couche application web : API, traitement)."

View file

@ -0,0 +1,17 @@
---
- name: Appliquer le groupe serveurs_web_frontaux
hosts: serveurs_web_frontaux
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."
tasks:
- name: Indiquer que le rôle frontal web reste à définir
ansible.builtin.debug:
msg: "Aucun rôle n'est encore associé au groupe serveurs_web_frontaux (couche présentation web, derrière serveurs_nginx)."

View file

@ -54,11 +54,11 @@ Créer un clone Debian depuis le modèle :
```bash ```bash
make creer-vm \ make creer-vm \
HOTE=web-01 \ HOTE=web-frontal-01 \
VMID=101 \ VMID=95301 \
VLAN=12 \ VLAN=15 \
ADRESSE_IP=192.168.12.101 \ ADRESSE_IP=10.1.15.31 \
PASSERELLE=192.168.12.1 \ PASSERELLE=10.1.15.1 \
GROUPES="serveurs_debian serveurs_durcis" GROUPES="serveurs_debian serveurs_durcis"
``` ```
@ -67,7 +67,7 @@ make creer-vm \
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 : 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 ```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" make creer-vm HOTE=web-frontal-01 VMID=95301 VLAN=15 ADRESSE_IP=10.1.15.31 PASSERELLE=10.1.15.1 TAILLE_DISQUE=64G GROUPES="serveurs_debian serveurs_durcis"
``` ```
Proxmox ne permet pas de réduire un disque existant. Proxmox ne permet pas de réduire un disque existant.
@ -75,5 +75,5 @@ 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 : `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 ```bash
make deployer HOTE=web-01 make deployer HOTE=web-frontal-01
``` ```

6
requirements.yml Normal file
View file

@ -0,0 +1,6 @@
---
# Collections Ansible requises par Set-OPS.
# Installer : ansible-galaxy collection install -r requirements.yml
collections:
- name: community.postgresql
- name: community.general

View file

@ -0,0 +1,27 @@
# clients_journaux
Intégration cliente **journaux** : expédie le journal système (journald) de la VM
vers `serveurs_loki`, via **Grafana Alloy**.
## Rôle
- Ajoute le **dépôt apt Grafana** et installe `alloy`.
- Ajoute l'utilisateur `alloy` au groupe `systemd-journal` (lecture du journal).
- Déploie `/etc/alloy/config.alloy` : `loki.source.journal``loki.write` vers Loki.
## Boucle
VM dans `clients_journaux` → Alloy lit journald → pousse vers Loki (obs-01) →
visible dans Grafana (datasource Loki).
## Variables
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `clients_journaux_loki_url` | `http://10.1.14.11:3100/loki/api/v1/push` | Endpoint Loki (obs-01) |
## Notes / limites
- La **syntaxe de configuration Alloy évolue** entre versions ; la config fournie
cible un Alloy récent (`loki.source.journal`, `loki.write`). Ajustement mineur
possible selon la version installée.
- Étiquettes par défaut : `job=systemd-journal`, `host=<inventory_hostname>`.
## Prérequis
- Dépendance `clients_journaux requiert serveurs_loki actif` (déjà dans `docs/dependances-groupes.yml`).

View file

@ -0,0 +1,15 @@
---
clients_journaux_paquets:
- alloy
clients_journaux_service: "alloy"
clients_journaux_utilisateur: "alloy"
clients_journaux_config: "/etc/alloy/config.alloy"
# Point de collecte Loki (obs-01, serveurs_loki, VLAN 14).
clients_journaux_loki_url: "http://10.1.14.11:3100/loki/api/v1/push"
# Depot apt officiel Grafana (partage avec serveurs_loki / serveurs_grafana).
clients_journaux_depot_cle_url: "https://apt.grafana.com/gpg-full.key"
clients_journaux_depot_cle_fichier: "/etc/apt/keyrings/grafana.asc"
clients_journaux_depot_source: "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main"

View file

@ -0,0 +1,6 @@
---
- name: Redemarrer alloy
ansible.builtin.systemd:
name: "{{ clients_journaux_service }}"
state: restarted
listen: Redemarrer alloy

View file

@ -0,0 +1,50 @@
---
- name: Assurer le repertoire des trousseaux apt
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
- name: Telecharger la cle de signature Grafana
ansible.builtin.get_url:
url: "{{ clients_journaux_depot_cle_url }}"
dest: "{{ clients_journaux_depot_cle_fichier }}"
owner: root
group: root
mode: "0644"
- name: Ajouter le depot apt Grafana
ansible.builtin.apt_repository:
repo: "{{ clients_journaux_depot_source }}"
filename: grafana
state: present
- name: Installer Grafana Alloy
ansible.builtin.apt:
name: "{{ clients_journaux_paquets }}"
state: present
update_cache: true
- name: Autoriser Alloy a lire le journal systeme
ansible.builtin.user:
name: "{{ clients_journaux_utilisateur }}"
groups: systemd-journal
append: true
notify: Redemarrer alloy
- name: Deployer la configuration Alloy
ansible.builtin.template:
src: config.alloy.j2
dest: "{{ clients_journaux_config }}"
owner: root
group: "{{ clients_journaux_utilisateur }}"
mode: "0640"
notify: Redemarrer alloy
- name: Activer et demarrer Alloy
ansible.builtin.systemd:
name: "{{ clients_journaux_service }}"
enabled: true
state: started

View file

@ -0,0 +1,16 @@
// Gere par Set-OPS (role clients_journaux). Ne pas editer a la main.
loki.write "loki" {
endpoint {
url = "{{ clients_journaux_loki_url }}"
}
}
loki.source.journal "journal" {
forward_to = [loki.write.loki.receiver]
max_age = "12h"
labels = {
job = "systemd-journal",
host = "{{ inventory_hostname }}",
}
}

View file

@ -0,0 +1,35 @@
# clients_ldap
Intégration cliente **identité utilisateur** : l'hôte authentifie les utilisateurs
via l'annuaire central `serveurs_openldap`, grâce à **SSSD** (NSS + PAM).
## Rôle
- Installe `sssd`, `sssd-ldap`, `libnss-sss`, `libpam-sss`.
- Déploie `/etc/sssd/sssd.conf` (`0600`) pointant vers OpenLDAP (`id_provider`/`auth_provider = ldap`).
- Active `sss` dans `/etc/nsswitch.conf` (passwd, group, shadow).
- Active la **création automatique du répertoire personnel** (`pam-auth-update --enable mkhomedir`).
- `libpam-sss` active automatiquement le module PAM `sss`.
## Effet
Après ce rôle, les comptes de l'annuaire Chezlepro peuvent se connecter à l'hôte
(SSH, console) et leur home est créé à la première connexion. Couplé à `clients_pki`
(identité machine), l'écosystème a une authentification **machines + utilisateurs** centralisée.
## Variables principales
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `clients_ldap_uri` | `ldap://idm-01.chezlepro.internal` | URI de l'annuaire |
| `clients_ldap_base_dn` | `dc=chezlepro,dc=internal` | Base de recherche |
| `clients_ldap_bind_dn` | `""` (anonyme) | Compte de lecture (optionnel, mdp via Vault) |
| `clients_ldap_tls_reqcert` | `never` | Validation TLS (→ `demand` avec LDAPS) |
## Notes / limites
- **Bind anonyme** par défaut : suppose une lecture publique de l'annuaire. Pour un
bind dédié, définir `clients_ldap_bind_dn` + `clients_ldap_bind_password` (Vault) et
créer le compte de service dans l'annuaire.
- **LDAP en clair** sur le réseau interne pour l'instant ; basculer en **LDAPS** quand
OpenLDAP exposera un certificat (step_ca), avec `clients_ldap_tls_reqcert: demand`.
- Les entrées utilisateurs/groupes (POSIX) doivent exister dans l'annuaire (gestion d'identité).
## Prérequis
- Dépendance `clients_ldap requiert serveurs_openldap actif` (déjà dans `docs/dependances-groupes.yml`).

View file

@ -0,0 +1,24 @@
---
clients_ldap_paquets:
- sssd
- sssd-ldap
- libnss-sss
- libpam-sss
- ldap-utils
clients_ldap_service: "sssd"
clients_ldap_domaine: "chezlepro"
clients_ldap_uri: "ldap://idm-01.chezlepro.internal"
clients_ldap_base_dn: "dc=chezlepro,dc=internal"
# Bind de lecture. Vide => bind anonyme (lecture publique de l'annuaire).
clients_ldap_bind_dn: ""
clients_ldap_bind_password: "" # {{ vault_ldap_sssd }} si un bind DN est defini
# TLS : "never" tant que OpenLDAP n'expose pas LDAPS ; passer a "demand"
# avec LDAPS + confiance step_ca (clients_pki).
clients_ldap_tls_reqcert: "never"
# Creation automatique du repertoire personnel a la premiere connexion.
clients_ldap_mkhomedir: true

View file

@ -0,0 +1,6 @@
---
- name: Redemarrer sssd
ansible.builtin.systemd:
name: "{{ clients_ldap_service }}"
state: restarted
listen: Redemarrer sssd

View file

@ -0,0 +1,40 @@
---
- name: Installer SSSD et les modules LDAP
ansible.builtin.apt:
name: "{{ clients_ldap_paquets }}"
state: present
update_cache: true
cache_valid_time: 3600
- name: Deployer la configuration SSSD
ansible.builtin.template:
src: sssd.conf.j2
dest: /etc/sssd/sssd.conf
owner: root
group: root
mode: "0600"
no_log: true
notify: Redemarrer sssd
- name: Activer SSS dans nsswitch
ansible.builtin.lineinfile:
path: /etc/nsswitch.conf
regexp: '^({{ item }}:\s+)(?!.*\bsss\b)(.*)$'
line: '\1\2 sss'
backrefs: true
loop:
- passwd
- group
- shadow
- name: Activer la creation automatique des repertoires personnels
ansible.builtin.command:
cmd: pam-auth-update --enable mkhomedir
changed_when: false
when: clients_ldap_mkhomedir | bool
- name: Activer et demarrer SSSD
ansible.builtin.systemd:
name: "{{ clients_ldap_service }}"
enabled: true
state: started

View file

@ -0,0 +1,18 @@
# Gere par Set-OPS (role clients_ldap). Ne pas editer a la main.
[sssd]
config_file_version = 2
services = nss, pam
domains = {{ clients_ldap_domaine }}
[domain/{{ clients_ldap_domaine }}]
id_provider = ldap
auth_provider = ldap
ldap_uri = {{ clients_ldap_uri }}
ldap_search_base = {{ clients_ldap_base_dn }}
{% if clients_ldap_bind_dn | length > 0 %}
ldap_default_bind_dn = {{ clients_ldap_bind_dn }}
ldap_default_authtok = {{ clients_ldap_bind_password }}
{% endif %}
ldap_tls_reqcert = {{ clients_ldap_tls_reqcert }}
cache_credentials = true
enumerate = false

View file

@ -0,0 +1,26 @@
# clients_metriques
Intégration cliente **métriques** : installe `node_exporter` sur la VM pour qu'elle
soit collectée par `serveurs_prometheus`.
## Rôle
- Installe `prometheus-node-exporter` (paquet Debian).
- Configure l'adresse d'écoute (`:9100` par défaut).
- Active et démarre le service.
## Boucle complète
1. On ajoute une VM au groupe `clients_metriques` et on l'active.
2. Ce rôle y installe `node_exporter` (`:9100`).
3. `serveurs_prometheus` **dérive automatiquement** la cible depuis l'inventaire
(hôtes actifs de `clients_metriques`) et la scrape.
Aucun edit manuel côté Prometheus.
## Variables
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `clients_metriques_adresse_ecoute` | `:9100` | `--web.listen-address` |
## Prérequis
- Dépendance `clients_metriques requiert serveurs_prometheus actif` (déjà dans `docs/dependances-groupes.yml`).
- Accès du serveur Prometheus au port `9100` (segmentation réseau / nftables).

View file

@ -0,0 +1,9 @@
---
clients_metriques_paquets:
- prometheus-node-exporter
clients_metriques_service: "prometheus-node-exporter"
# Adresse d'ecoute de node_exporter. Par defaut toutes les interfaces ; la
# segmentation reseau (VLAN / nftables) restreint l'acces au serveur Prometheus.
clients_metriques_adresse_ecoute: ":9100"

View file

@ -0,0 +1,6 @@
---
- name: Redemarrer node_exporter
ansible.builtin.systemd:
name: "{{ clients_metriques_service }}"
state: restarted
listen: Redemarrer node_exporter

View file

@ -0,0 +1,22 @@
---
- name: Installer node_exporter
ansible.builtin.apt:
name: "{{ clients_metriques_paquets }}"
state: present
update_cache: true
cache_valid_time: 3600
- name: Definir les arguments de node_exporter (adresse d'ecoute)
ansible.builtin.template:
src: default-node-exporter.j2
dest: /etc/default/prometheus-node-exporter
owner: root
group: root
mode: "0644"
notify: Redemarrer node_exporter
- name: Activer et demarrer node_exporter
ansible.builtin.systemd:
name: "{{ clients_metriques_service }}"
enabled: true
state: started

View file

@ -0,0 +1,2 @@
# Gere par Set-OPS (role clients_metriques).
ARGS="--web.listen-address={{ clients_metriques_adresse_ecoute }}"

View file

@ -0,0 +1,43 @@
# clients_pki
Intégration cliente **PKI / identité machine** : chaque hôte fait confiance à l'AC
interne (`serveurs_step_ca`), obtient **son propre certificat** et le renouvelle
automatiquement.
## Rôle
- Installe `step-cli` (dépôt apt Smallstep).
- **Confiance** : `step ca bootstrap --install` → installe la racine de l'AC dans le
magasin de confiance système (l'hôte fait confiance au TLS interne réel).
- **Certificat d'hôte** : `step ca certificate <fqdn>` via le provisioner.
- **Renouvellement automatique** : unités systemd officielles `cert-renewer@.{service,timer}`
(vérifie/renouvelle toutes les 15 min, recharge le service consommateur s'il existe).
## « Chaque hôte authentifiable »
Après ce rôle, l'hôte possède une **identité machine** vérifiable : un certificat émis
par l'AC Chezlepro, renouvelé sans intervention. Les services peuvent l'utiliser pour
du TLS / mTLS interne.
## Secrets requis (Vault)
```yaml
clients_pki_ca_fingerprint: "{{ vault_step_ca_fingerprint }}" # empreinte racine de l'AC
clients_pki_provisioner_password: "{{ vault_step_ca_provisioner_password }}" # partage avec serveurs_step_ca
```
L'empreinte racine s'obtient sur l'AC (`step certificate fingerprint <root_ca.crt>`).
## Variables principales
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `clients_pki_ca_url` | `https://infra-pki-01.chezlepro.internal:8443` | URL de l'AC |
| `clients_pki_provisioner` | `admin@chezlepro.internal` | Provisioner émetteur |
| `clients_pki_nom_cert` | FQDN de l'hôte | Nom du certificat |
## Notes / sécurité
- Émission via le provisioner **JWK** (mot de passe partagé) : tout hôte avec ce secret
peut émettre des certificats. Acceptable en interne ; pour durcir, basculer vers ACME
ou des jetons à usage unique par hôte (phase ultérieure).
- Pour qu'un service (nginx, keycloak…) recharge automatiquement après renouvellement,
nommer son certificat d'après le service (instance `cert-renewer@<service>`).
## Prérequis
- Dépendance `clients_pki requiert serveurs_step_ca actif` (déjà dans `docs/dependances-groupes.yml`).
- Réseau vers l'AC (`:8443`).

View file

@ -0,0 +1,24 @@
---
clients_pki_paquets:
- step-cli
clients_pki_steppath: "/etc/step"
# AC interne (serveurs_step_ca / infra-pki-01).
clients_pki_ca_url: "https://infra-pki-01.chezlepro.internal:8443"
clients_pki_provisioner: "admin@chezlepro.internal"
# Identite de l'hote.
clients_pki_nom_cert: "{{ ansible_fqdn | default(ansible_hostname) }}"
clients_pki_cert: "{{ clients_pki_steppath }}/certs/{{ clients_pki_nom_cert }}.crt"
clients_pki_cle: "{{ clients_pki_steppath }}/certs/{{ clients_pki_nom_cert }}.key"
# Secrets OBLIGATOIRES (Ansible Vault).
clients_pki_ca_fingerprint: "" # {{ vault_step_ca_fingerprint }} (empreinte racine de l'AC)
clients_pki_provisioner_password: "" # {{ vault_step_ca_provisioner_password }} (partage avec serveurs_step_ca)
# Depot apt officiel Smallstep (partage avec serveurs_step_ca).
clients_pki_depot_cle_url: "https://packages.smallstep.com/keys/apt/repo-signing-key.gpg"
clients_pki_depot_cle_fichier: "/etc/apt/keyrings/smallstep.asc"
clients_pki_depot_uri: "https://packages.smallstep.com/stable/debian"
clients_pki_depot_suite: "debs"

View file

@ -0,0 +1,5 @@
---
- name: Recharger systemd
ansible.builtin.systemd:
daemon_reload: true
listen: Recharger systemd

View file

@ -0,0 +1,104 @@
---
- name: Exiger les secrets PKI (Vault)
ansible.builtin.assert:
that:
- clients_pki_ca_fingerprint | length > 0
- clients_pki_provisioner_password | length > 0
fail_msg: >-
clients_pki_ca_fingerprint et clients_pki_provisioner_password
sont requis (a fournir via Ansible Vault).
- name: Assurer le repertoire des trousseaux apt
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
- name: Telecharger la cle de signature Smallstep
ansible.builtin.get_url:
url: "{{ clients_pki_depot_cle_url }}"
dest: "{{ clients_pki_depot_cle_fichier }}"
owner: root
group: root
mode: "0644"
- name: Ajouter le depot apt Smallstep
ansible.builtin.template:
src: smallstep.sources.j2
dest: /etc/apt/sources.list.d/smallstep.sources
owner: root
group: root
mode: "0644"
- name: Installer step-cli
ansible.builtin.apt:
name: "{{ clients_pki_paquets }}"
state: present
update_cache: true
- name: Creer le repertoire STEPPATH des certificats
ansible.builtin.file:
path: "{{ clients_pki_steppath }}/certs"
state: directory
owner: root
group: root
mode: "0755"
- name: Deployer le mot de passe du provisioner
ansible.builtin.copy:
content: "{{ clients_pki_provisioner_password }}"
dest: "{{ clients_pki_steppath }}/provisioner.pass"
owner: root
group: root
mode: "0600"
no_log: true
- name: Etablir la confiance dans l'AC interne (bootstrap + installation racine)
ansible.builtin.command:
cmd: >-
step ca bootstrap
--ca-url {{ clients_pki_ca_url }}
--fingerprint {{ clients_pki_ca_fingerprint }}
--install --force
creates: "{{ clients_pki_steppath }}/certs/root_ca.crt"
environment:
STEPPATH: "{{ clients_pki_steppath }}"
- name: Obtenir le certificat d'hote
ansible.builtin.command:
cmd: >-
step ca certificate {{ clients_pki_nom_cert }}
{{ clients_pki_cert }} {{ clients_pki_cle }}
--provisioner {{ clients_pki_provisioner }}
--provisioner-password-file {{ clients_pki_steppath }}/provisioner.pass
--force
creates: "{{ clients_pki_cert }}"
environment:
STEPPATH: "{{ clients_pki_steppath }}"
- name: Deployer l'unite systemd de renouvellement
ansible.builtin.template:
src: cert-renewer@.service.j2
dest: /etc/systemd/system/cert-renewer@.service
owner: root
group: root
mode: "0644"
notify: Recharger systemd
- name: Deployer le minuteur de renouvellement
ansible.builtin.template:
src: cert-renewer@.timer.j2
dest: /etc/systemd/system/cert-renewer@.timer
owner: root
group: root
mode: "0644"
notify: Recharger systemd
- name: Activer le renouvellement automatique du certificat d'hote
ansible.builtin.systemd:
name: "cert-renewer@{{ clients_pki_nom_cert }}.timer"
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,20 @@
# Gere par Set-OPS (role clients_pki). Renouvellement de certificat (Smallstep).
[Unit]
Description=Certificate renewer for %I
After=network-online.target
Wants=network-online.target
Documentation=https://smallstep.com/docs/step-ca/renewal
StartLimitIntervalSec=0
[Service]
Type=oneshot
User=root
Environment=STEPPATH={{ clients_pki_steppath }}
Environment=CERT_LOCATION={{ clients_pki_steppath }}/certs/%i.crt
Environment=KEY_LOCATION={{ clients_pki_steppath }}/certs/%i.key
ExecCondition=/usr/bin/step certificate needs-renewal ${CERT_LOCATION}
ExecStart=/usr/bin/step ca renew --force ${CERT_LOCATION} ${KEY_LOCATION}
ExecStartPost=/usr/bin/env sh -c "! systemctl --quiet is-active %i.service || systemctl try-reload-or-restart %i"
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,12 @@
# Gere par Set-OPS (role clients_pki).
[Unit]
Description=Timer for certificate renewal of %I
Documentation=https://smallstep.com/docs/step-ca/renewal
[Timer]
Persistent=true
OnCalendar=*:1/15
RandomizedDelaySec=5m
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,5 @@
Types: deb
URIs: {{ clients_pki_depot_uri }}
Suites: {{ clients_pki_depot_suite }}
Components: main
Signed-By: {{ clients_pki_depot_cle_fichier }}

View file

@ -0,0 +1,29 @@
# clients_smtp
Intégration cliente **relais mail** : l'hôte relaie son courrier sortant (notifications,
cron, alertes) vers `serveurs_sendmail`, via **msmtp**.
## Rôle
- Installe `msmtp` et `msmtp-mta` (`msmtp-mta` fournit `/usr/sbin/sendmail`, donc le
courrier système — cron, etc. — passe par le relais).
- Déploie `/etc/msmtprc` pointant vers le relais central (port 25, sans auth : le relais
fait confiance au réseau interne).
## Effet
Tout courrier émis localement (`mail`, cron, scripts) est relayé vers `serveurs_sendmail`
(infra-mail-01), qui assure la remise. Léger, sans daemon.
## Variables
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `clients_smtp_relais` | `infra-mail-01.chezlepro.internal` | MTA relais |
| `clients_smtp_port` | `25` | Port du relais |
| `clients_smtp_from` | `<hostname>@chezlepro.internal` | Expéditeur par défaut |
| `clients_smtp_tls` | `false` | TLS (→ `true` avec certificats step_ca) |
## Notes / limites
- Pas d'authentification SMTP : repose sur la confiance réseau (`mynetworks` du relais).
- TLS interne désactivé pour l'instant ; activable avec la confiance step_ca (`clients_pki`).
## Prérequis
- Dépendance `clients_smtp requiert serveurs_sendmail actif` (déjà dans `docs/dependances-groupes.yml`).

View file

@ -0,0 +1,12 @@
---
# Relais mail local leger (msmtp) vers serveurs_sendmail.
clients_smtp_paquets:
- msmtp
- msmtp-mta # fournit /usr/sbin/sendmail
clients_smtp_relais: "infra-mail-01.chezlepro.internal"
clients_smtp_port: 25
clients_smtp_from: "{{ ansible_hostname }}@chezlepro.internal"
# TLS : off sur le reseau interne pour l'instant ; on avec certificats step_ca.
clients_smtp_tls: false

View file

@ -0,0 +1,15 @@
---
- name: Installer msmtp (relais mail local)
ansible.builtin.apt:
name: "{{ clients_smtp_paquets }}"
state: present
update_cache: true
cache_valid_time: 3600
- name: Deployer la configuration msmtp
ansible.builtin.template:
src: msmtprc.j2
dest: /etc/msmtprc
owner: root
group: root
mode: "0644"

View file

@ -0,0 +1,9 @@
# Gere par Set-OPS (role clients_smtp). Ne pas editer a la main.
defaults
tls {{ 'on' if clients_smtp_tls | bool else 'off' }}
syslog on
account default
host {{ clients_smtp_relais }}
port {{ clients_smtp_port }}
from {{ clients_smtp_from }}

View file

@ -0,0 +1,34 @@
# serveurs_forgejo
Forge Git **Forgejo** (binaire officiel) — dépôts Git, CI, revue de code.
## Rôle
- Installe le binaire Forgejo (version épinglée, lien symbolique courant), utilisateur `git`.
- Crée l'arborescence (`/var/lib/forgejo`, `/etc/forgejo`).
- **Base PostgreSQL via le registre** (`docs/bases-donnees.yml`, entrée `forgejo`).
- `app.ini` : serveur derrière nginx (`HTTP_ADDR=127.0.0.1`, `ROOT_URL` https), DB, secrets, mailer vers `serveurs_sendmail`, inscription désactivée, `INSTALL_LOCK` (pas d'assistant web).
- Service systemd + création du **compte administrateur** initial (une fois).
## Secrets requis (Vault)
```yaml
serveurs_forgejo_secret_key: "{{ vault_forgejo_secret_key }}" # forgejo generate secret SECRET_KEY
serveurs_forgejo_internal_token: "{{ vault_forgejo_internal_token }}" # forgejo generate secret INTERNAL_TOKEN
serveurs_forgejo_admin_password: "{{ vault_forgejo_admin }}"
vault_bd_forgejo: "..." # mot de passe BD (partage avec serveurs_postgresql)
```
## Variables principales
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `serveurs_forgejo_version` | `10.0.0` | Version (à vérifier/bumper) |
| `serveurs_forgejo_hostname` | `forge.chezlepro.internal` | Domaine (via nginx) |
| `serveurs_forgejo_db_host` | `10.1.13.11` | data-01 |
## Notes / limites
- **Version** à confirmer sur forgejo.org/download ; l'URL suit le motif officiel des releases.
- Le **site nginx** publiant Forgejo se déclare côté `serveurs_nginx`.
- Mailer en SMTP simple vers le relais interne (sans TLS pour l'instant).
- Non testé live (forge-01 planifié).
## Prérequis
- Dépendances `serveurs_postgresql`, `serveurs_nginx`, `serveurs_sendmail` (cf. `docs/dependances-groupes.yml`).

View file

@ -0,0 +1,30 @@
---
# Version Forgejo : verifier/ajuster sur https://forgejo.org/download/
serveurs_forgejo_version: "10.0.0"
serveurs_forgejo_url: >-
https://codeberg.org/forgejo/forgejo/releases/download/v{{ serveurs_forgejo_version }}/forgejo-{{ serveurs_forgejo_version }}-linux-amd64
serveurs_forgejo_binaire: "/usr/local/bin/forgejo"
serveurs_forgejo_utilisateur: "git"
serveurs_forgejo_data: "/var/lib/forgejo"
serveurs_forgejo_config_dir: "/etc/forgejo"
serveurs_forgejo_config: "/etc/forgejo/app.ini"
serveurs_forgejo_service: "forgejo"
# Publication (derriere serveurs_nginx, TLS a l'edge).
serveurs_forgejo_hostname: "forge.chezlepro.internal"
serveurs_forgejo_http_port: 3000
# Base de donnees (registre docs/bases-donnees.yml).
serveurs_forgejo_db_cle: "forgejo"
serveurs_forgejo_db_host: "10.1.13.11" # data-01
# Relais courriel.
serveurs_forgejo_smtp: "infra-mail-01.chezlepro.internal"
# Secrets OBLIGATOIRES (Ansible Vault).
serveurs_forgejo_secret_key: "" # {{ vault_forgejo_secret_key }}
serveurs_forgejo_internal_token: "" # {{ vault_forgejo_internal_token }}
serveurs_forgejo_admin_user: "forgejo-admin"
serveurs_forgejo_admin_email: "admin@chezlepro.internal"
serveurs_forgejo_admin_password: "" # {{ vault_forgejo_admin }}

View file

@ -0,0 +1,7 @@
---
- name: Redemarrer forgejo
ansible.builtin.systemd:
name: "{{ serveurs_forgejo_service }}"
state: restarted
daemon_reload: true
listen: Redemarrer forgejo

View file

@ -0,0 +1,115 @@
---
- name: Exiger les secrets Forgejo (Vault)
ansible.builtin.assert:
that:
- serveurs_forgejo_secret_key | length > 0
- serveurs_forgejo_internal_token | length > 0
- serveurs_forgejo_admin_password | length > 0
fail_msg: "secret_key, internal_token et admin_password sont requis (Ansible Vault)."
- name: Charger le registre des bases applicatives
ansible.builtin.include_vars:
file: "{{ role_path }}/../../docs/bases-donnees.yml"
- name: Resoudre l'entree de base Forgejo
ansible.builtin.set_fact:
serveurs_forgejo_entree: "{{ (bases_donnees | default({}))[serveurs_forgejo_db_cle] | default({}) }}"
- name: Exiger l'entree de registre et son secret
ansible.builtin.assert:
that:
- serveurs_forgejo_entree.base is defined
- serveurs_forgejo_entree.secret is defined
fail_msg: "Entree '{{ serveurs_forgejo_db_cle }}' absente de docs/bases-donnees.yml."
- name: Resoudre le mot de passe de base (Vault)
ansible.builtin.set_fact:
serveurs_forgejo_db_password: "{{ lookup('vars', serveurs_forgejo_entree.secret) }}"
no_log: true
- name: Creer l'utilisateur systeme git
ansible.builtin.user:
name: "{{ serveurs_forgejo_utilisateur }}"
system: true
shell: /bin/bash
home: "/home/{{ serveurs_forgejo_utilisateur }}"
create_home: true
comment: "Git Version Control"
- name: Telecharger le binaire Forgejo
ansible.builtin.get_url:
url: "{{ serveurs_forgejo_url }}"
dest: "/usr/local/bin/forgejo-{{ serveurs_forgejo_version }}"
owner: root
group: root
mode: "0755"
- name: Activer la version courante du binaire
ansible.builtin.file:
src: "/usr/local/bin/forgejo-{{ serveurs_forgejo_version }}"
dest: "{{ serveurs_forgejo_binaire }}"
state: link
notify: Redemarrer forgejo
- name: Creer les repertoires de donnees
ansible.builtin.file:
path: "{{ serveurs_forgejo_data }}/{{ item }}"
state: directory
owner: "{{ serveurs_forgejo_utilisateur }}"
group: "{{ serveurs_forgejo_utilisateur }}"
mode: "0750"
loop:
- ""
- custom
- data
- log
- name: Creer le repertoire de configuration
ansible.builtin.file:
path: "{{ serveurs_forgejo_config_dir }}"
state: directory
owner: root
group: "{{ serveurs_forgejo_utilisateur }}"
mode: "0770"
- name: Deployer app.ini
ansible.builtin.template:
src: app.ini.j2
dest: "{{ serveurs_forgejo_config }}"
owner: root
group: "{{ serveurs_forgejo_utilisateur }}"
mode: "0640"
no_log: true
notify: Redemarrer forgejo
- name: Deployer l'unite systemd forgejo
ansible.builtin.template:
src: forgejo.service.j2
dest: /etc/systemd/system/forgejo.service
owner: root
group: root
mode: "0644"
notify: Redemarrer forgejo
- name: Activer et demarrer Forgejo
ansible.builtin.systemd:
name: "{{ serveurs_forgejo_service }}"
enabled: true
state: started
daemon_reload: true
- name: Creer le compte administrateur (une fois)
ansible.builtin.shell:
cmd: >-
{{ serveurs_forgejo_binaire }} admin user create
--admin --username {{ serveurs_forgejo_admin_user }}
--email {{ serveurs_forgejo_admin_email }}
--password '{{ serveurs_forgejo_admin_password }}'
--config {{ serveurs_forgejo_config }}
&& touch {{ serveurs_forgejo_data }}/.admin-created
creates: "{{ serveurs_forgejo_data }}/.admin-created"
become: true
become_user: "{{ serveurs_forgejo_utilisateur }}"
environment:
GITEA_WORK_DIR: "{{ serveurs_forgejo_data }}"
no_log: true

View file

@ -0,0 +1,34 @@
; Gere par Set-OPS (role serveurs_forgejo). Ne pas editer a la main.
APP_NAME = Forgejo Chezlepro
RUN_USER = {{ serveurs_forgejo_utilisateur }}
RUN_MODE = prod
WORK_PATH = {{ serveurs_forgejo_data }}
[server]
DOMAIN = {{ serveurs_forgejo_hostname }}
ROOT_URL = https://{{ serveurs_forgejo_hostname }}/
HTTP_ADDR = 127.0.0.1
HTTP_PORT = {{ serveurs_forgejo_http_port }}
SSH_DOMAIN = {{ serveurs_forgejo_hostname }}
[database]
DB_TYPE = postgres
HOST = {{ serveurs_forgejo_db_host }}:5432
NAME = {{ serveurs_forgejo_entree.base }}
USER = {{ serveurs_forgejo_entree.proprietaire }}
PASSWD = {{ serveurs_forgejo_db_password }}
[security]
INSTALL_LOCK = true
SECRET_KEY = {{ serveurs_forgejo_secret_key }}
INTERNAL_TOKEN = {{ serveurs_forgejo_internal_token }}
[service]
DISABLE_REGISTRATION = true
[mailer]
ENABLED = true
PROTOCOL = smtp
SMTP_ADDR = {{ serveurs_forgejo_smtp }}
SMTP_PORT = 25
FROM = forge@chezlepro.internal

View file

@ -0,0 +1,20 @@
[Unit]
Description=Forgejo (forge Git)
Documentation=https://forgejo.org/docs/
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={{ serveurs_forgejo_utilisateur }}
Group={{ serveurs_forgejo_utilisateur }}
WorkingDirectory={{ serveurs_forgejo_data }}
ExecStart={{ serveurs_forgejo_binaire }} web --config {{ serveurs_forgejo_config }}
Restart=always
RestartSec=5
Environment=USER={{ serveurs_forgejo_utilisateur }}
Environment=HOME=/home/{{ serveurs_forgejo_utilisateur }}
Environment=GITEA_WORK_DIR={{ serveurs_forgejo_data }}
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,33 @@
# serveurs_grafana
Tableaux de bord **Grafana** (dépôt apt officiel Grafana). Interface d'observabilité
au-dessus de Prometheus (métriques) et Loki (journaux).
## Rôle
- Ajoute le **dépôt apt Grafana** et installe `grafana`.
- **Provisionne les datasources** Prometheus + Loki (`/etc/grafana/provisioning/datasources/`) — Grafana les câble au démarrage.
- Domaine, `root_url` et **mot de passe admin (Vault)** via un **drop-in systemd** (non destructif, ne touche pas `grafana.ini` du paquet).
- Inscriptions publiques désactivées.
## Publication
- Derrière `serveurs_nginx` (TLS à l'edge) ; `GF_SERVER_ROOT_URL=https://grafana.chezlepro.internal/`.
- Le site nginx se déclare côté `serveurs_nginx`.
## Secret requis (Vault)
```yaml
serveurs_grafana_admin_password: "{{ vault_grafana_admin }}"
```
## Variables principales
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `serveurs_grafana_hostname` | `grafana.chezlepro.internal` | Domaine |
| `serveurs_grafana_prometheus_url` | `http://localhost:9090` | Datasource Prometheus |
| `serveurs_grafana_loki_url` | `http://localhost:3100` | Datasource Loki |
## Notes
- Prometheus, Loki et Grafana sont co-localisés sur `obs-01` → datasources en `localhost`.
- Dépendance `serveurs_grafana requiert serveurs_prometheus + serveurs_loki actifs` (déjà dans `docs/dependances-groupes.yml`).
## Hors périmètre
- Dashboards provisionnés (à ajouter ensuite), alerting Grafana, authentification OIDC via Keycloak (intégration ultérieure).

View file

@ -0,0 +1,22 @@
---
serveurs_grafana_paquets:
- grafana
serveurs_grafana_service: "grafana-server"
serveurs_grafana_port: 3000
# Publication (derriere serveurs_nginx, TLS termine a l'edge).
serveurs_grafana_hostname: "grafana.chezlepro.internal"
serveurs_grafana_root_url: "https://{{ serveurs_grafana_hostname }}/"
# Datasources (Prometheus + Loki, co-localises sur obs-01).
serveurs_grafana_prometheus_url: "http://localhost:9090"
serveurs_grafana_loki_url: "http://localhost:3100"
# Secret OBLIGATOIRE (Ansible Vault).
serveurs_grafana_admin_password: "" # {{ vault_grafana_admin }}
# Depot apt officiel Grafana (partage avec serveurs_loki).
serveurs_grafana_depot_cle_url: "https://apt.grafana.com/gpg-full.key"
serveurs_grafana_depot_cle_fichier: "/etc/apt/keyrings/grafana.asc"
serveurs_grafana_depot_source: "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main"

View file

@ -0,0 +1,7 @@
---
- name: Redemarrer grafana
ansible.builtin.systemd:
name: "{{ serveurs_grafana_service }}"
state: restarted
daemon_reload: true
listen: Redemarrer grafana

View file

@ -0,0 +1,68 @@
---
- name: Exiger le mot de passe admin Grafana (Vault)
ansible.builtin.assert:
that:
- serveurs_grafana_admin_password | length > 0
fail_msg: "serveurs_grafana_admin_password est requis (Ansible Vault)."
- name: Assurer le repertoire des trousseaux apt
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
- name: Telecharger la cle de signature Grafana
ansible.builtin.get_url:
url: "{{ serveurs_grafana_depot_cle_url }}"
dest: "{{ serveurs_grafana_depot_cle_fichier }}"
owner: root
group: root
mode: "0644"
- name: Ajouter le depot apt Grafana
ansible.builtin.apt_repository:
repo: "{{ serveurs_grafana_depot_source }}"
filename: grafana
state: present
- name: Installer Grafana
ansible.builtin.apt:
name: "{{ serveurs_grafana_paquets }}"
state: present
update_cache: true
- name: Provisionner les datasources (Prometheus + Loki)
ansible.builtin.template:
src: datasources.yaml.j2
dest: /etc/grafana/provisioning/datasources/chezlepro.yaml
owner: root
group: grafana
mode: "0640"
notify: Redemarrer grafana
- name: Assurer le repertoire de drop-in systemd
ansible.builtin.file:
path: /etc/systemd/system/grafana-server.service.d
state: directory
owner: root
group: root
mode: "0755"
- name: Deployer la configuration d'environnement (domaine + secret)
ansible.builtin.template:
src: chezlepro.conf.j2
dest: /etc/systemd/system/grafana-server.service.d/chezlepro.conf
owner: root
group: root
mode: "0600"
no_log: true
notify: Redemarrer grafana
- name: Activer et demarrer Grafana
ansible.builtin.systemd:
name: "{{ serveurs_grafana_service }}"
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,5 @@
[Service]
Environment=GF_SERVER_DOMAIN={{ serveurs_grafana_hostname }}
Environment=GF_SERVER_ROOT_URL={{ serveurs_grafana_root_url }}
Environment=GF_SECURITY_ADMIN_PASSWORD={{ serveurs_grafana_admin_password }}
Environment=GF_USERS_ALLOW_SIGN_UP=false

View file

@ -0,0 +1,13 @@
# Gere par Set-OPS (role serveurs_grafana). Ne pas editer a la main.
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: {{ serveurs_grafana_prometheus_url }}
isDefault: true
- name: Loki
type: loki
access: proxy
url: {{ serveurs_grafana_loki_url }}

View file

@ -0,0 +1,33 @@
# serveurs_icinga
Supervision active **Icinga** — cœur de surveillance. Portée actuelle : **Icinga 2 +
Icinga DB** (avec son Redis dédié et sa base PostgreSQL). **Icinga Web 2 est différé**
à une phase dédiée.
## Rôle (cœur)
- Ajoute le **dépôt apt officiel Icinga** (trousseau + source signée) et installe
`icinga2`, `icingadb`, `icingadb-redis`.
- `icinga2 api setup` + activation de la fonctionnalité `icingadb`.
- **Base PostgreSQL via le registre** (`docs/bases-donnees.yml`, entrée `icingadb`) :
PostgreSQL crée la base/compte, ce rôle importe le **schéma** et écrit
`/etc/icingadb/config.yml` (BD + Redis).
## Base de données
Entrée registre `icingadb` (PostgreSQL sur data-01). Mot de passe partagé via Vault
(`vault_bd_icingadb`), comme Keycloak. Dépendance `serveurs_icinga requiert
serveurs_postgresql actif` (déjà dans `docs/dependances-groupes.yml`).
## Différé (phase Icinga Web 2)
- `icingaweb2` + sa **2e base** (`icingaweb`) + PHP-FPM + vhost nginx + assistant de
configuration (jeton de setup). À faire proprement avec sa doc verbatim.
## Limites / caveats honnêtes
- Les pages détaillées Icinga DB/Web ont renvoyé des 404 ; la **config Icinga DB**
(`config.yml`, chemin du schéma `/usr/share/icingadb/schema/pgsql/schema.sql`, port
Redis `6380`) suit la **structure documentée standard** mais **n'a pas pu être
vérifiée verbatim** — à confirmer/ajuster selon la version installée (variables prévues).
- Non testé live (mon-01 planifié).
- L'import de schéma s'exécute une seule fois (marqueur `/etc/icingadb/.schema-imported`).
## Prérequis
- `serveurs_postgresql` actif (base `icingadb` créée), accès réseau à data-01:5432.

View file

@ -0,0 +1,28 @@
---
serveurs_icinga_paquets:
- icinga2
- icingadb
- icingadb-redis
- postgresql-client # pour importer le schema vers la BD distante
serveurs_icinga_service_icinga2: "icinga2"
serveurs_icinga_service_icingadb: "icingadb"
serveurs_icinga_service_redis: "icingadb-redis"
# Depot apt officiel Icinga (paquet trousseau + source signee).
serveurs_icinga_keyring_url: >-
https://packages.icinga.com/icinga-archive-keyring_latest+debian{{ ansible_distribution_major_version }}.deb
serveurs_icinga_depot_source: >-
deb [signed-by=/usr/share/keyrings/icinga-archive-keyring.gpg]
https://packages.icinga.com/debian icinga-{{ ansible_distribution_release }} main
# Base Icinga DB : entree du registre docs/bases-donnees.yml.
serveurs_icinga_db_cle: "icingadb"
serveurs_icinga_db_host: "10.1.13.11" # data-01 (serveurs_postgresql)
# Redis dedie a Icinga DB (paquet icingadb-redis).
serveurs_icinga_redis_host: "localhost"
serveurs_icinga_redis_port: 6380
serveurs_icinga_config: "/etc/icingadb/config.yml"
serveurs_icinga_schema: "/usr/share/icingadb/schema/pgsql/schema.sql"

View file

@ -0,0 +1,12 @@
---
- name: Redemarrer icinga2
ansible.builtin.systemd:
name: "{{ serveurs_icinga_service_icinga2 }}"
state: restarted
listen: Redemarrer icinga2
- name: Redemarrer icingadb
ansible.builtin.systemd:
name: "{{ serveurs_icinga_service_icingadb }}"
state: restarted
listen: Redemarrer icingadb

View file

@ -0,0 +1,90 @@
---
- name: Telecharger le trousseau de cles Icinga
ansible.builtin.get_url:
url: "{{ serveurs_icinga_keyring_url }}"
dest: "/tmp/icinga-archive-keyring.deb"
mode: "0644"
- name: Installer le trousseau de cles Icinga
ansible.builtin.apt:
deb: "/tmp/icinga-archive-keyring.deb"
state: present
- name: Ajouter le depot apt Icinga
ansible.builtin.apt_repository:
repo: "{{ serveurs_icinga_depot_source }}"
filename: icinga
state: present
- name: Installer Icinga 2, Icinga DB et Redis dedie
ansible.builtin.apt:
name: "{{ serveurs_icinga_paquets }}"
state: present
update_cache: true
- name: Charger le registre des bases applicatives
ansible.builtin.include_vars:
file: "{{ role_path }}/../../docs/bases-donnees.yml"
- name: Resoudre l'entree de base Icinga DB
ansible.builtin.set_fact:
serveurs_icinga_entree: "{{ (bases_donnees | default({}))[serveurs_icinga_db_cle] | default({}) }}"
- name: Exiger l'entree de registre et son secret
ansible.builtin.assert:
that:
- serveurs_icinga_entree.base is defined
- serveurs_icinga_entree.secret is defined
fail_msg: "Entree '{{ serveurs_icinga_db_cle }}' absente de docs/bases-donnees.yml."
- name: Resoudre le mot de passe de base (Vault)
ansible.builtin.set_fact:
serveurs_icinga_db_password: "{{ lookup('vars', serveurs_icinga_entree.secret) }}"
no_log: true
- name: Configurer l'API Icinga 2
ansible.builtin.command:
cmd: icinga2 api setup
creates: /etc/icinga2/features-enabled/api.conf
notify: Redemarrer icinga2
- name: Activer la fonctionnalite icingadb dans Icinga 2
ansible.builtin.command:
cmd: icinga2 feature enable icingadb
creates: /etc/icinga2/features-enabled/icingadb.conf
notify: Redemarrer icinga2
- name: Activer et demarrer le Redis Icinga DB
ansible.builtin.systemd:
name: "{{ serveurs_icinga_service_redis }}"
enabled: true
state: started
- name: Importer le schema Icinga DB dans PostgreSQL (une fois)
ansible.builtin.shell:
cmd: >-
PGPASSWORD='{{ serveurs_icinga_db_password }}'
psql -h {{ serveurs_icinga_db_host }} -U {{ serveurs_icinga_entree.proprietaire }}
-d {{ serveurs_icinga_entree.base }} -f {{ serveurs_icinga_schema }}
&& touch /etc/icingadb/.schema-imported
creates: /etc/icingadb/.schema-imported
no_log: true
- name: Deployer la configuration Icinga DB
ansible.builtin.template:
src: config.yml.j2
dest: "{{ serveurs_icinga_config }}"
owner: root
group: icingadb
mode: "0640"
no_log: true
notify: Redemarrer icingadb
- name: Activer et demarrer Icinga 2 et Icinga DB
ansible.builtin.systemd:
name: "{{ item }}"
enabled: true
state: started
loop:
- "{{ serveurs_icinga_service_icinga2 }}"
- "{{ serveurs_icinga_service_icingadb }}"

View file

@ -0,0 +1,11 @@
# Gere par Set-OPS (role serveurs_icinga). Ne pas editer a la main.
database:
type: pgsql
host: {{ serveurs_icinga_db_host }}
database: {{ serveurs_icinga_entree.base }}
user: {{ serveurs_icinga_entree.proprietaire }}
password: {{ serveurs_icinga_db_password }}
redis:
host: {{ serveurs_icinga_redis_host }}
port: {{ serveurs_icinga_redis_port }}

View file

@ -0,0 +1,42 @@
# serveurs_keycloak
SSO / IAM **Keycloak** (distribution Quarkus) — hub d'authentification centralisée
(OIDC/SAML) pour les services Chezlepro.
## Rôle
- Installe un JRE, télécharge et extrait la distribution Keycloak dans `/opt/keycloak`.
- Configure la **base PostgreSQL via le registre** `docs/bases-donnees.yml` (entrée `keycloak`).
- Publie derrière `serveurs_nginx` (TLS à l'edge) : `hostname`, `proxy-headers=xforwarded`, `http-enabled=true`.
- Secrets (mot de passe BD + admin bootstrap) dans `/etc/keycloak/keycloak.env` (`0640`, `no_log`).
- `kc.sh build --db=postgres` (une fois par version) puis service systemd `start --optimized`.
## Premier consommateur du registre de BD
L'entrée `keycloak` de `docs/bases-donnees.yml` fait que :
1. `serveurs_postgresql` crée la base `keycloak` + le compte propriétaire `keycloak`.
2. Ce rôle lit **la même entrée** pour bâtir sa connexion (`db-url-database`, `db-username`, mot de passe = `vault_bd_keycloak`).
Mot de passe partagé, source unique. Dépendance `serveurs_keycloak requiert serveurs_postgresql actif` déjà dans `docs/dependances-groupes.yml`.
## Secrets requis (Vault)
```yaml
serveurs_keycloak_admin_password: "{{ vault_keycloak_admin }}" # admin bootstrap
vault_bd_keycloak: "..." # mot de passe BD (partage avec postgres)
```
## Variables principales
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `serveurs_keycloak_version` | `26.0.7` | Version (à vérifier/bumper sur keycloak.org/downloads) |
| `serveurs_keycloak_hostname` | `keycloak.chezlepro.internal` | Hostname public (via nginx) |
| `serveurs_keycloak_db_host` | `10.1.13.11` | IP de data-01 (serveurs_postgresql) |
| `serveurs_keycloak_admin_user` | `admin` | Admin bootstrap |
## Notes / limites
- **Version** : à confirmer/bumper ; l'URL suit le motif officiel des releases GitHub.
- L'unité systemd est dérivée des conventions Keycloak (la doc officielle ne fournit pas d'unité) — sandboxing volontairement modéré pour ne pas casser le démarrage.
- Le **site nginx** publiant Keycloak se déclare côté `serveurs_nginx` (`serveurs_nginx_sites`), pas ici.
- Certificats : TLS terminé à l'edge ; aucun certificat sur Keycloak.
## Prérequis
- Réseau sortant pour télécharger la distribution.
- Base PostgreSQL atteignable (dépendance `serveurs_postgresql`).

View file

@ -0,0 +1,21 @@
---
# Distribution Keycloak (Quarkus). Verifier/ajuster la version sur keycloak.org/downloads.
serveurs_keycloak_version: "26.0.7"
serveurs_keycloak_url: "https://github.com/keycloak/keycloak/releases/download/{{ serveurs_keycloak_version }}/keycloak-{{ serveurs_keycloak_version }}.tar.gz"
serveurs_keycloak_java_paquet: "default-jre-headless"
serveurs_keycloak_home: "/opt/keycloak"
serveurs_keycloak_utilisateur: "keycloak"
serveurs_keycloak_service: "keycloak"
# Publication (derriere serveurs_nginx, TLS termine a l'edge).
serveurs_keycloak_hostname: "keycloak.chezlepro.internal"
# Base de donnees : entree du registre docs/bases-donnees.yml.
serveurs_keycloak_db_cle: "keycloak"
# IP interne du serveur PostgreSQL (data-01, cf. nomenclature).
serveurs_keycloak_db_host: "10.1.13.11"
# Secrets (Ansible Vault) — obligatoires.
serveurs_keycloak_admin_user: "admin"
serveurs_keycloak_admin_password: "" # {{ vault_keycloak_admin }}

View file

@ -0,0 +1,7 @@
---
- name: Redemarrer keycloak
ansible.builtin.systemd:
name: "{{ serveurs_keycloak_service }}"
state: restarted
daemon_reload: true
listen: Redemarrer keycloak

View file

@ -0,0 +1,119 @@
---
- name: Exiger le mot de passe admin Keycloak (Vault)
ansible.builtin.assert:
that:
- serveurs_keycloak_admin_password | length > 0
fail_msg: "serveurs_keycloak_admin_password est requis (Ansible Vault)."
- name: Charger le registre des bases applicatives
ansible.builtin.include_vars:
file: "{{ role_path }}/../../docs/bases-donnees.yml"
- name: Resoudre l'entree de base de donnees Keycloak
ansible.builtin.set_fact:
serveurs_keycloak_entree: "{{ (bases_donnees | default({}))[serveurs_keycloak_db_cle] | default({}) }}"
- name: Exiger une entree de registre et son secret
ansible.builtin.assert:
that:
- serveurs_keycloak_entree.base is defined
- serveurs_keycloak_entree.proprietaire is defined
- serveurs_keycloak_entree.secret is defined
fail_msg: "Entree '{{ serveurs_keycloak_db_cle }}' absente de docs/bases-donnees.yml."
- name: Resoudre le mot de passe de base de donnees (Vault)
ansible.builtin.set_fact:
serveurs_keycloak_db_password: "{{ lookup('vars', serveurs_keycloak_entree.secret) }}"
no_log: true
- name: Installer le JRE
ansible.builtin.apt:
name: "{{ serveurs_keycloak_java_paquet }}"
state: present
update_cache: true
cache_valid_time: 3600
- name: Creer l'utilisateur systeme keycloak
ansible.builtin.user:
name: "{{ serveurs_keycloak_utilisateur }}"
system: true
home: "{{ serveurs_keycloak_home }}"
create_home: false
shell: /usr/sbin/nologin
- name: Creer le repertoire d'installation
ansible.builtin.file:
path: "{{ serveurs_keycloak_home }}"
state: directory
owner: "{{ serveurs_keycloak_utilisateur }}"
group: "{{ serveurs_keycloak_utilisateur }}"
mode: "0750"
- name: Telecharger la distribution Keycloak
ansible.builtin.get_url:
url: "{{ serveurs_keycloak_url }}"
dest: "/tmp/keycloak-{{ serveurs_keycloak_version }}.tar.gz"
mode: "0644"
- name: Extraire Keycloak
ansible.builtin.unarchive:
src: "/tmp/keycloak-{{ serveurs_keycloak_version }}.tar.gz"
dest: "{{ serveurs_keycloak_home }}"
remote_src: true
extra_opts: ["--strip-components=1"]
owner: "{{ serveurs_keycloak_utilisateur }}"
group: "{{ serveurs_keycloak_utilisateur }}"
creates: "{{ serveurs_keycloak_home }}/bin/kc.sh"
- name: Deployer keycloak.conf (config non sensible)
ansible.builtin.template:
src: keycloak.conf.j2
dest: "{{ serveurs_keycloak_home }}/conf/keycloak.conf"
owner: "{{ serveurs_keycloak_utilisateur }}"
group: "{{ serveurs_keycloak_utilisateur }}"
mode: "0640"
notify: Redemarrer keycloak
- name: Creer le repertoire de configuration des secrets
ansible.builtin.file:
path: /etc/keycloak
state: directory
owner: root
group: "{{ serveurs_keycloak_utilisateur }}"
mode: "0750"
- name: Deployer le fichier d'environnement (secrets)
ansible.builtin.template:
src: keycloak.env.j2
dest: /etc/keycloak/keycloak.env
owner: root
group: "{{ serveurs_keycloak_utilisateur }}"
mode: "0640"
no_log: true
notify: Redemarrer keycloak
- name: Construire Keycloak (une fois par version)
ansible.builtin.shell:
cmd: >-
{{ serveurs_keycloak_home }}/bin/kc.sh build --db=postgres
&& touch {{ serveurs_keycloak_home }}/.kc-built-{{ serveurs_keycloak_version }}
creates: "{{ serveurs_keycloak_home }}/.kc-built-{{ serveurs_keycloak_version }}"
become: true
become_user: "{{ serveurs_keycloak_utilisateur }}"
notify: Redemarrer keycloak
- name: Deployer l'unite systemd keycloak
ansible.builtin.template:
src: keycloak.service.j2
dest: /etc/systemd/system/keycloak.service
owner: root
group: root
mode: "0644"
notify: Redemarrer keycloak
- name: Activer et demarrer keycloak
ansible.builtin.systemd:
name: "{{ serveurs_keycloak_service }}"
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,10 @@
# Gere par Set-OPS (role serveurs_keycloak). Ne pas editer a la main.
db=postgres
db-url-host={{ serveurs_keycloak_db_host }}
db-url-database={{ serveurs_keycloak_entree.base }}
db-username={{ serveurs_keycloak_entree.proprietaire }}
# Derriere serveurs_nginx (TLS termine a l'edge).
hostname={{ serveurs_keycloak_hostname }}
proxy-headers=xforwarded
http-enabled=true

View file

@ -0,0 +1,4 @@
# Gere par Set-OPS (role serveurs_keycloak). Secrets — ne pas editer a la main.
KC_DB_PASSWORD={{ serveurs_keycloak_db_password }}
KC_BOOTSTRAP_ADMIN_USERNAME={{ serveurs_keycloak_admin_user }}
KC_BOOTSTRAP_ADMIN_PASSWORD={{ serveurs_keycloak_admin_password }}

View file

@ -0,0 +1,24 @@
[Unit]
Description=Keycloak Identity and Access Management
Documentation=https://www.keycloak.org/server/configuration
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={{ serveurs_keycloak_utilisateur }}
Group={{ serveurs_keycloak_utilisateur }}
EnvironmentFile=/etc/keycloak/keycloak.env
WorkingDirectory={{ serveurs_keycloak_home }}
ExecStart={{ serveurs_keycloak_home }}/bin/kc.sh start --optimized
Restart=on-failure
RestartSec=5
TimeoutStartSec=120
LimitNOFILE=102642
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,30 @@
# serveurs_loki
Agrégation de journaux **Loki** (dépôt apt officiel Grafana), plateforme d'observabilité.
## Rôle
- Ajoute le **dépôt apt Grafana** (clé + source) et installe `loki`.
- Déploie une configuration **single-node** (stockage `filesystem`, schéma TSDB v13).
- Rétention via le compactor (`retention_enabled`, `retention_period`).
- Écoute sur `:3100`.
## Variables principales
| Variable | Défaut | Rôle |
| --- | --- | --- |
| `serveurs_loki_port` | `3100` | Port HTTP |
| `serveurs_loki_chemin` | `/var/lib/loki` | Stockage local |
| `serveurs_loki_retention` | `744h` (31 j) | Rétention des journaux |
## Intégration
- Côté VM : l'expédition des journaux (promtail / Grafana Alloy) sera gérée par le
futur rôle `clients_journaux`.
- `serveurs_grafana` consommera Loki comme datasource.
## Notes / limites
- Le format de configuration Loki **évolue entre versions** ; la config fournie cible
un Loki récent (TSDB v13, single-node). Un ajustement mineur peut être requis selon
la version installée par le dépôt.
- Le chemin du fichier de config (`/etc/loki/config.yml`) suit le paquet Debian Grafana.
## Hors périmètre
- Mode distribué/microservices, stockage objet (S3), alerting Loki.

View file

@ -0,0 +1,14 @@
---
serveurs_loki_paquets:
- loki
serveurs_loki_service: "loki"
serveurs_loki_config: "/etc/loki/config.yml"
serveurs_loki_port: 3100
serveurs_loki_chemin: "/var/lib/loki"
serveurs_loki_retention: "744h" # 31 jours
# Depot apt officiel Grafana (partage avec serveurs_grafana).
serveurs_loki_depot_cle_url: "https://apt.grafana.com/gpg-full.key"
serveurs_loki_depot_cle_fichier: "/etc/apt/keyrings/grafana.asc"
serveurs_loki_depot_source: "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main"

Some files were not shown because too many files have changed in this diff Show more