diff --git a/.gitignore b/.gitignore index c371f4a..7a807cf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ collections/ build/ *.retry .vault_pass +inventory/host_vars/*/dhcp.yml diff --git a/Makefile b/Makefile index c76a01a..9217cd2 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,9 @@ INV ?= inventory/hosts.yml .PHONY: deps deploy vms base apps waf client tests destroy creds -deps: ## Collections Ansible +deps: ## Collections Ansible + client Python de l'API Proxmox ansible-galaxy collection install -r requirements.yml -p ./collections + python3 -m pip install --user proxmoxer requests deploy: ## Déploiement complet ansible-playbook -i $(INV) playbooks/site.yml vms: diff --git a/README.md b/README.md index f8d01bb..c6b8a91 100644 --- a/README.md +++ b/README.md @@ -33,25 +33,70 @@ Le domaine par défaut est `bwpoc.internal` (TLD réservé par l'ICANN pour l'us ## Prérequis -- Un nœud Proxmox VE 8 joignable en SSH **root** depuis le poste Ansible, avec accès Internet (image Debian, images Docker, listes noires communautaires). -- 3 IP libres sur le réseau des VM (1 en mode tout-en-un). Le playbook vérifie par ping qu'elles sont libres avant de créer les VM. -- Sur le poste Ansible : `ansible-core` 2.16 ou plus, une paire de clés SSH. +- Un nœud Proxmox VE 8 ou 9 (ici `lancelot`, 172.16.16.7) avec accès Internet (image Debian, images Docker, listes noires communautaires). +- Un accès SSH **root** au nœud, utilisé **une seule fois** pour créer le modèle de VM et le pool `bwpoc`. Tout le reste passe par l'API avec un compte dédié (voir plus bas). +- Un réseau de VM avec DHCP : ici le VLAN 200 "LAB", qui fournit IP, passerelle et DNS. Le poste Ansible doit pouvoir joindre ce VLAN en SSH. +- Sur le poste Ansible : `ansible-core` 2.17 ou plus (exigé par `community.proxmox`), `proxmoxer` et `requests` en Python, une paire de clés SSH. ```bash git clone git@gitea.vlab.bzh:vLab-BZH/bunkerwebpoc.git && cd bunkerwebpoc -make deps # installe community.docker et ansible.posix dans ./collections +make deps # collections community.docker et community.proxmox, puis proxmoxer ``` ## Configuration Tout ce qui dépend de votre infra est marqué `A ADAPTER` : -1. [`inventory/hosts.yml`](inventory/hosts.yml) : IP du nœud Proxmox, IP et VMID des 3 VM, CPU, RAM, disque. -2. [`inventory/group_vars/all.yml`](inventory/group_vars/all.yml) : domaine, passerelle, DNS, stockage Proxmox, pont, VLAN, clé SSH publique. +1. [`inventory/hosts.yml`](inventory/hosts.yml) : nœud Proxmox, VMID des 3 VM, CPU, RAM, disque. Pas d'IP : elles viennent du DHCP. +2. [`inventory/group_vars/all.yml`](inventory/group_vars/all.yml) : domaine, nœud, stockage, pont et VLAN, compte API, clé SSH publique. 3. [`inventory/group_vars/bunkerweb.yml`](inventory/group_vars/bunkerweb.yml) : les réglages BunkerWeb, globaux et par site. C'est le fichier à modifier pour jouer avec les fonctionnalités. Les mots de passe (MariaDB, Redis, admin de l'UI) sont générés au premier lancement dans `credentials/` côté poste Ansible (ignoré par git). Pour les mettre sous `ansible-vault`, remplacez les `lookup('password')` de `bunkerweb.yml` par vos variables chiffrées. +## Compte Proxmox dédié + +Le clonage et le pilotage des VM passent par l'API avec l'utilisateur `bwpoc@pve` et un jeton, limité à un pool `bwpoc`. Seule la création du modèle (import d'une image disque par chemin, réservé à `root@pam`) se fait en SSH root, une fois. + +Privilèges nécessaires, regroupés dans un rôle `BWPoC` : + +| Privilège | Pour quoi faire | +|---|---| +| `VM.Allocate`, `VM.Clone` | créer les clones dans le pool, les supprimer | +| `VM.Config.CPU`, `VM.Config.Memory`, `VM.Config.Disk`, `VM.Config.Network`, `VM.Config.Cloudinit`, `VM.Config.Options`, `VM.Config.HWType`, `VM.Config.CDROM` | régler CPU, RAM, disque, cloud-init, démarrage auto, tags | +| `VM.PowerMgmt` | démarrer et arrêter les VM | +| `VM.Audit`, `Pool.Audit` | lire l'état des VM et du pool | +| `VM.GuestAgent.Audit` (PVE 9) ou `VM.Monitor` (PVE 8) | lire l'IP DHCP par l'agent QEMU | + +Et les ACL, au plus juste : + +| Chemin | Rôle | Pourquoi | +|---|---|---| +| `/pool/bwpoc` | `BWPoC` | les VM du PoC et le modèle 9100, placé dans le pool | +| `/storage/local-lvm` | `PVEDatastoreUser` | allouer les disques des clones | +| `/sdn/zones/localnetwork/vmbr0/200` | `PVESDNUser` | brancher les VM sur le VLAN 200 (et seulement lui) | +| `/nodes/lancelot` | `PVEAuditor` | lecture du nœud par le module Ansible | + +En root sur `lancelot` (sur PVE 8, remplacer `VM.GuestAgent.Audit` par `VM.Monitor`) : + +```bash +pveum pool add bwpoc --comment "PoC BunkerWeb" +pveum role add BWPoC --privs "VM.Allocate,VM.Clone,VM.Audit,VM.PowerMgmt,VM.Config.CPU,VM.Config.Memory,VM.Config.Disk,VM.Config.Network,VM.Config.Cloudinit,VM.Config.Options,VM.Config.HWType,VM.Config.CDROM,VM.GuestAgent.Audit,Pool.Audit" +pveum user add bwpoc@pve --comment "PoC BunkerWeb (Ansible)" +pveum user token add bwpoc@pve ansible --privsep 0 # affiche le secret une seule fois +pveum acl modify /pool/bwpoc --users bwpoc@pve --roles BWPoC +pveum acl modify /storage/local-lvm --users bwpoc@pve --roles PVEDatastoreUser +pveum acl modify /sdn/zones/localnetwork/vmbr0/200 --users bwpoc@pve --roles PVESDNUser +pveum acl modify /nodes/lancelot --users bwpoc@pve --roles PVEAuditor +``` + +Si le réseau "LAB" est un VNet SDN plutôt qu'un tag sur `vmbr0`, l'ACL réseau devient `/sdn/zones//LAB` et, dans `all.yml`, `pve_bridge: LAB` avec `pve_vlan_tag: ""`. + +Le secret du jeton reste hors du dépôt, dans l'environnement : + +```bash +export PVE_TOKEN_SECRET='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +``` + ## Déploiement ```bash @@ -60,7 +105,7 @@ make deploy # = ansible-playbook playbooks/site.yml | Étape | Playbook | Ce qu'elle fait | |---|---|---| -| 1 | `00-proxmox.yml` | télécharge l'image Debian 13 genericcloud, crée le modèle cloud-init (VMID 9100), clone les VM, injecte IP et clé SSH, agrandit les disques, démarre | +| 1 | `00-proxmox.yml` | en root (une fois) : télécharge l'image Debian 13 genericcloud, y ajoute l'agent QEMU, crée le modèle cloud-init (VMID 9100) et le pool `bwpoc`. Puis par l'API avec `bwpoc@pve` : clone les VM dans le pool, cloud-init en DHCP + clé SSH, agrandit les disques, démarre, lit l'IP obtenue par l'agent QEMU et l'écrit dans `inventory/host_vars//dhcp.yml` | | 2 | `10-base.yml` | paquets de base, agent QEMU, résolution locale des noms du PoC vers le WAF, Docker CE depuis le dépôt officiel | | 3 | `20-apps.yml` | stack des applis, filtrage `DOCKER-USER` (mode split) | | 4 | `30-bunkerweb.yml` | stack BunkerWeb, attente qu'`api` réponde au travers du WAF, écriture de `credentials/ACCES.txt` | @@ -76,12 +121,18 @@ ansible-playbook playbooks/render.yml # écrit build/split/... ou build/single Un exemple de rendu (secrets remplacés par `CHANGEME`) est versionné dans [`examples/split/`](examples/split/) (`bunkerweb.compose.yml`, `apps.compose.yml`, `bwpoc-firewall.sh`). +### Adresses IP et DHCP + +Les VM démarrent sur le VLAN 200 "LAB" en DHCP. Le modèle embarque l'agent QEMU : le playbook lit l'IP de chaque VM par l'API Proxmox, l'enregistre dans `inventory/host_vars//dhcp.yml` (ignoré par git) et recharge l'inventaire. Les playbooks suivants, et vos relances (`make waf`...), utilisent ces fichiers. + +Comme l'IP du WAF sert au filtrage `DOCKER-USER` et aux `/etc/hosts`, **réservez les baux DHCP** des trois VM (adresses MAC visibles dans Proxmox). Si une IP change quand même, relancez `make vms base apps waf client` : les fichiers `dhcp.yml` et les règles sont régénérés. + ### Accéder aux sites depuis votre poste Les VM du PoC résolvent déjà les noms (bloc dans `/etc/hosts`). Sur votre poste, ajoutez la ligne suivante à `/etc/hosts`, ou déclarez les enregistrements dans votre DNS de labo : ``` -192.168.200.61 www.bwpoc.internal shop.bwpoc.internal api.bwpoc.internal bwui.bwpoc.internal + www.bwpoc.internal shop.bwpoc.internal api.bwpoc.internal bwui.bwpoc.internal ``` Puis ouvrez `https://bwui.bwpoc.internal/` (certificat auto-signé à accepter). Identifiants : `make creds`. @@ -115,7 +166,7 @@ Depuis `bw-client` (ou n'importe quel poste avec `curl` et `jq`, le script utili ```bash make tests # depuis le poste Ansible -ssh debian@192.168.200.63 bwpoc-tests # ou directement sur le client +ssh debian@ bwpoc-tests # ou directement sur le client ``` Le script enchaîne une vingtaine de contrôles et affiche `OK`, `KO` ou `INFO` : @@ -143,7 +194,7 @@ Le script enchaîne une vingtaine de contrôles et affiche `OK`, `KO` ou `INFO` Les derniers tests **bannissent volontairement votre IP** du site `api` pendant 5 minutes (`bw_ban_time`). Pour lever le ban : menu *Bans* de l'UI, ou sur `bw-waf` : ```bash -docker exec bw-scheduler bwcli unban 192.168.200.63 +docker exec bw-scheduler bwcli unban ``` ### Avec et sans WAF @@ -152,12 +203,12 @@ Pour mesurer l'apport du WAF, autorisez le client à joindre les applis en direc ```yaml # inventory/group_vars/all.yml -apps_direct_access_sources: [192.168.200.63] +apps_direct_access_sources: [""] ``` ```bash make apps -ssh debian@192.168.200.63 bwpoc-tests --direct +ssh debian@ bwpoc-tests --direct ``` La SQLi de Juice Shop renvoie alors la liste complète des produits (200) au lieu d'un 403, et le `PUT` passe. @@ -175,7 +226,7 @@ La SQLi de Juice Shop renvoie alors la liste complète des produits (200) au lie ```bash make waf # réapplique la conf BunkerWeb après modification de bunkerweb.yml -make destroy # supprime les VM (confirmation demandée), modèle conservé +make destroy # supprime les VM en root sur le nœud (confirmation demandée), modèle conservé ansible-playbook playbooks/99-destroy.yml -e destroy_template=true # supprime aussi le modèle ``` @@ -194,8 +245,8 @@ Sur `bw-waf`, la stack vit dans `/opt/bwpoc/bunkerweb` (`docker compose ps`, `lo inventory/ hosts.yml (3 VM), single.yml (tout-en-un), group_vars/ playbooks/ 00 à 50 + site.yml, render.yml, 99-destroy.yml roles/ - proxmox_template/ modèle Debian 13 cloud-init via qm - proxmox_vm/ clonage, cloud-init, démarrage + proxmox_template/ modèle Debian 13 cloud-init + agent QEMU, pool (root, une fois) + proxmox_vm/ clonage par l'API (bwpoc@pve), DHCP, lecture des IP common/ paquets, agent QEMU, /etc/hosts docker/ Docker CE + compose (dépôt officiel) apps/ compose des applis, vitrine, filtrage DOCKER-USER diff --git a/docs/architecture.excalidraw b/docs/architecture.excalidraw index ba51fed..0c0830e 100644 --- a/docs/architecture.excalidraw +++ b/docs/architecture.excalidraw @@ -22,12 +22,12 @@ "frameId": null, "index": "a0", "roundness": null, - "seed": 1536219028, + "seed": 1863763712, "version": 2, - "versionNonce": 1272381996, + "versionNonce": 1229261056, "isDeleted": false, "boundElements": null, - "updated": 1793095752318, + "updated": 1793096641458, "link": null, "locked": false, "text": "PoC BunkerWeb 1.6.17 sur Proxmox", @@ -59,12 +59,12 @@ "frameId": null, "index": "a1", "roundness": null, - "seed": 70851500, + "seed": 1205676288, "version": 2, - "versionNonce": 1118996116, + "versionNonce": 539756288, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "docker-compose sur 3 VM Debian 13, déployé par Ansible - le WAF est le seul chemin vers les applis", @@ -98,12 +98,12 @@ "roundness": { "type": 3 }, - "seed": 1278788884, + "seed": 245753600, "version": 2, - "versionNonce": 1552800940, + "versionNonce": 1304160512, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -112,7 +112,7 @@ "type": "text", "x": 320, "y": 132, - "width": 259, + "width": 454, "height": 32.5, "angle": 0, "strokeColor": "#1e1e1e", @@ -126,21 +126,21 @@ "frameId": null, "index": "a3", "roundness": null, - "seed": 1148241452, + "seed": 72354048, "version": 2, - "versionNonce": 1632408596, + "versionNonce": 1796417280, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, - "text": "Proxmox VE - pve01", + "text": "Proxmox VE - lancelot (172.16.16.7)", "fontSize": 26, "fontFamily": 5, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "Proxmox VE - pve01", + "originalText": "Proxmox VE - lancelot (172.16.16.7)", "autoResize": true, "lineHeight": 1.25 }, @@ -165,17 +165,17 @@ "roundness": { "type": 3 }, - "seed": 210174612, + "seed": 793839360, "version": 2, - "versionNonce": 377841452, + "versionNonce": 1616300288, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "-wLdJuCSSU3isLGRUYsjf" + "id": "Af2mwOlXmrzqbdbEEWZFs" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -200,12 +200,12 @@ "roundness": { "type": 3 }, - "seed": 625942700, + "seed": 1869015296, "version": 2, - "versionNonce": 206107028, + "versionNonce": 202786560, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -214,7 +214,7 @@ "type": "text", "x": 348, "y": 272, - "width": 160, + "width": 100, "height": 27.5, "angle": 0, "strokeColor": "#1e1e1e", @@ -228,21 +228,21 @@ "frameId": null, "index": "a6", "roundness": null, - "seed": 1932627988, + "seed": 1301817088, "version": 2, - "versionNonce": 1848091052, + "versionNonce": 479570176, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, - "text": "bw-client - .63", + "text": "bw-client", "fontSize": 22, "fontFamily": 5, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "bw-client - .63", + "originalText": "bw-client", "autoResize": true, "lineHeight": 1.25 }, @@ -267,17 +267,17 @@ "roundness": { "type": 3 }, - "seed": 1048455980, + "seed": 346147072, "version": 2, - "versionNonce": 584949524, + "versionNonce": 1605723904, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "eHqZIridF0wrU6HRLPRVo" + "id": "SlU8Q87Uz7NKL4ZnYbYpJ" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -302,17 +302,17 @@ "roundness": { "type": 3 }, - "seed": 271478164, + "seed": 1442531072, "version": 2, - "versionNonce": 983469100, + "versionNonce": 771262720, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "ud-DFX9hc28YXTLCu3ycp" + "id": "39LnfMslwfj0lwpj1N9OR" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -335,12 +335,12 @@ "frameId": null, "index": "a9", "roundness": null, - "seed": 577234348, + "seed": 260090112, "version": 2, - "versionNonce": 763403412, + "versionNonce": 846235392, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -381,12 +381,12 @@ "roundness": { "type": 3 }, - "seed": 78879508, + "seed": 620390144, "version": 2, - "versionNonce": 1574184620, + "versionNonce": 1342138624, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -395,7 +395,7 @@ "type": "text", "x": 718, "y": 192, - "width": 137, + "width": 81, "height": 27.5, "angle": 0, "strokeColor": "#1e1e1e", @@ -409,21 +409,21 @@ "frameId": null, "index": "aB", "roundness": null, - "seed": 104222764, + "seed": 193169664, "version": 2, - "versionNonce": 904391188, + "versionNonce": 1086826240, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, - "text": "bw-waf - .61", + "text": "bw-waf", "fontSize": 22, "fontFamily": 5, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "bw-waf - .61", + "originalText": "bw-waf", "autoResize": true, "lineHeight": 1.25 }, @@ -448,17 +448,17 @@ "roundness": { "type": 3 }, - "seed": 1530337428, + "seed": 118851328, "version": 2, - "versionNonce": 1819439404, + "versionNonce": 1311394048, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "yTLo2Vng9jiScCVL5wf8x" + "id": "JKbfDPIiEI4fA4EEXe6hE" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -483,17 +483,17 @@ "roundness": { "type": 3 }, - "seed": 1775463084, + "seed": 1143630080, "version": 2, - "versionNonce": 520718228, + "versionNonce": 926598912, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "hguasmJb74910iJe48rlW" + "id": "hRBEDMGyTSx5wa_AhCgbB" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -518,17 +518,17 @@ "roundness": { "type": 3 }, - "seed": 1601103380, + "seed": 952936192, "version": 2, - "versionNonce": 1440967596, + "versionNonce": 66660608, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "WIA-EPcOdhCM-V6poW90H" + "id": "rJHn1YADsJuVx_y8MZxC6" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -553,17 +553,17 @@ "roundness": { "type": 3 }, - "seed": 1072248108, + "seed": 83183872, "version": 2, - "versionNonce": 2098951444, + "versionNonce": 843704064, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "TymlawtgjzMOi0T6nTv_x" + "id": "-hg8jbuzcfJlp8F3G3gQ1" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -588,17 +588,17 @@ "roundness": { "type": 3 }, - "seed": 1985020820, + "seed": 1721747200, "version": 2, - "versionNonce": 206640684, + "versionNonce": 1558972672, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "3UXlHuNY841IZz4g0ZNcQ" + "id": "ww8fu0t2s4n99PHCH9kvq" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -621,12 +621,12 @@ "frameId": null, "index": "aH", "roundness": null, - "seed": 367112108, + "seed": 694429952, "version": 2, - "versionNonce": 1838396052, + "versionNonce": 1047856896, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "bw-universe 10.20.30.0/24 (API)", @@ -658,12 +658,12 @@ "frameId": null, "index": "aI", "roundness": null, - "seed": 1980985620, + "seed": 755951360, "version": 2, - "versionNonce": 749039788, + "versionNonce": 1417865472, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "réseau bw-db", @@ -695,12 +695,12 @@ "frameId": null, "index": "aJ", "roundness": null, - "seed": 1004984876, + "seed": 485951744, "version": 2, - "versionNonce": 1864908820, + "versionNonce": 1480337152, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -739,12 +739,12 @@ "frameId": null, "index": "aK", "roundness": null, - "seed": 2116824724, + "seed": 412747520, "version": 2, - "versionNonce": 646609708, + "versionNonce": 1983760640, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "API", @@ -776,12 +776,12 @@ "frameId": null, "index": "aL", "roundness": null, - "seed": 1826796716, + "seed": 1529735424, "version": 2, - "versionNonce": 969874836, + "versionNonce": 1813989120, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -820,12 +820,12 @@ "frameId": null, "index": "aM", "roundness": null, - "seed": 1330723860, + "seed": 633415424, "version": 2, - "versionNonce": 1684682156, + "versionNonce": 1570547968, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "proxy bwui", @@ -857,12 +857,12 @@ "frameId": null, "index": "aN", "roundness": null, - "seed": 1988050732, + "seed": 1871235328, "version": 2, - "versionNonce": 381569812, + "versionNonce": 1453221632, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -901,12 +901,12 @@ "frameId": null, "index": "aO", "roundness": null, - "seed": 595106196, + "seed": 1090799360, "version": 2, - "versionNonce": 1936629804, + "versionNonce": 908036352, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -945,12 +945,12 @@ "frameId": null, "index": "aP", "roundness": null, - "seed": 1629911468, + "seed": 1971824896, "version": 2, - "versionNonce": 946587796, + "versionNonce": 1681491712, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -989,12 +989,12 @@ "frameId": null, "index": "aQ", "roundness": null, - "seed": 98060052, + "seed": 1189308160, "version": 2, - "versionNonce": 729922220, + "versionNonce": 994470144, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1035,12 +1035,12 @@ "roundness": { "type": 3 }, - "seed": 402809900, + "seed": 413829376, "version": 2, - "versionNonce": 311268884, + "versionNonce": 1366337280, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1049,7 +1049,7 @@ "type": "text", "x": 1308, "y": 192, - "width": 153, + "width": 91, "height": 27.5, "angle": 0, "strokeColor": "#1e1e1e", @@ -1063,21 +1063,21 @@ "frameId": null, "index": "aS", "roundness": null, - "seed": 719733908, + "seed": 64915200, "version": 2, - "versionNonce": 1464698156, + "versionNonce": 949045504, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, - "text": "bw-apps - .62", + "text": "bw-apps", "fontSize": 22, "fontFamily": 5, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "bw-apps - .62", + "originalText": "bw-apps", "autoResize": true, "lineHeight": 1.25 }, @@ -1102,17 +1102,17 @@ "roundness": { "type": 3 }, - "seed": 285015724, + "seed": 342976768, "version": 2, - "versionNonce": 840545172, + "versionNonce": 1254344448, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "vZ2I-5RJHooP7DQTsH2ul" + "id": "JdfU-Amrtmtu_Cmtep0GB" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1137,17 +1137,17 @@ "roundness": { "type": 3 }, - "seed": 1213764116, + "seed": 880125696, "version": 2, - "versionNonce": 1547435948, + "versionNonce": 159394048, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "_QqJdXGiIbXNZuILuYvGY" + "id": "uufjvSSLzIWWPXKmgaLq0" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1172,17 +1172,17 @@ "roundness": { "type": 3 }, - "seed": 1958758700, + "seed": 878463232, "version": 2, - "versionNonce": 356917524, + "versionNonce": 1823663872, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "_rNV8XuTeLBAhBJAUkdOm" + "id": "32wxfqeHWtENze0lGW9mT" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1207,17 +1207,17 @@ "roundness": { "type": 3 }, - "seed": 1831153556, + "seed": 86558464, "version": 2, - "versionNonce": 1651976748, + "versionNonce": 429066496, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "qqyyGfg_SvBJDhoUUdnEh" + "id": "SBymGOxZkAbdhWzv8Xwm5" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1240,12 +1240,12 @@ "frameId": null, "index": "aX", "roundness": null, - "seed": 1186349996, + "seed": 1407920384, "version": 2, - "versionNonce": 59301524, + "versionNonce": 1136527104, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1284,12 +1284,12 @@ "frameId": null, "index": "aY", "roundness": null, - "seed": 1501699348, + "seed": 309847808, "version": 2, - "versionNonce": 2095645868, + "versionNonce": 1682565376, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1328,12 +1328,12 @@ "frameId": null, "index": "aZ", "roundness": null, - "seed": 218689068, + "seed": 1587415296, "version": 2, - "versionNonce": 1704455188, + "versionNonce": 1281697536, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1372,12 +1372,12 @@ "frameId": null, "index": "aa", "roundness": null, - "seed": 1457871508, + "seed": 1759708928, "version": 2, - "versionNonce": 1362857772, + "versionNonce": 1965345024, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1416,12 +1416,12 @@ "frameId": null, "index": "ab", "roundness": null, - "seed": 2023901356, + "seed": 1341450496, "version": 2, - "versionNonce": 493439380, + "versionNonce": 1932019456, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "HTTP 8081-8083\ndepuis bw-waf uniquement", @@ -1453,12 +1453,12 @@ "frameId": null, "index": "ac", "roundness": null, - "seed": 268757012, + "seed": 82454272, "version": 2, - "versionNonce": 1071172012, + "versionNonce": 1738778880, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1497,12 +1497,12 @@ "frameId": null, "index": "ad", "roundness": null, - "seed": 221008684, + "seed": 862963968, "version": 2, - "versionNonce": 1580398356, + "versionNonce": 344418048, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "attaques HTTPS\nvia /etc/hosts", @@ -1536,17 +1536,17 @@ "roundness": { "type": 3 }, - "seed": 1758905748, + "seed": 1393379072, "version": 2, - "versionNonce": 199930924, + "versionNonce": 1732675840, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "bfJhe4UvG8FsofCpAoNkZ" + "id": "-ffQ0SRQ7j9UXYsPDMI0R" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1569,12 +1569,12 @@ "frameId": null, "index": "af", "roundness": null, - "seed": 1225854380, + "seed": 613329152, "version": 2, - "versionNonce": 74118292, + "versionNonce": 2097317632, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1613,12 +1613,12 @@ "frameId": null, "index": "ag", "roundness": null, - "seed": 1452339988, + "seed": 801924864, "version": 2, - "versionNonce": 56315564, + "versionNonce": 797796608, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "HTTPS www / shop / api / bwui", @@ -1652,17 +1652,17 @@ "roundness": { "type": 3 }, - "seed": 1299871788, + "seed": 1322354944, "version": 2, - "versionNonce": 1841775124, + "versionNonce": 1763288832, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "HyBQHnKMlKXlGiehADIur" + "id": "D3yKdgArmT-__EgZgqWXC" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1685,12 +1685,12 @@ "frameId": null, "index": "ai", "roundness": null, - "seed": 933851284, + "seed": 1739032320, "version": 2, - "versionNonce": 651467052, + "versionNonce": 200820992, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "points": [ @@ -1716,7 +1716,7 @@ "x": 40, "y": 712, "width": 221, - "height": 37.5, + "height": 56.25, "angle": 0, "strokeColor": "#0ca678", "backgroundColor": "transparent", @@ -1729,21 +1729,21 @@ "frameId": null, "index": "aj", "roundness": null, - "seed": 106074796, + "seed": 1840802048, "version": 2, - "versionNonce": 1363009428, + "versionNonce": 88917760, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, - "text": "SSH root : qm create / clone\nSSH debian : Docker, compose", + "text": "SSH root : modèle (une fois)\nAPI bwpoc@pve : clonage, IP\nSSH debian : Docker, compose", "fontSize": 15, "fontFamily": 5, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "SSH root : qm create / clone\nSSH debian : Docker, compose", + "originalText": "SSH root : modèle (une fois)\nAPI bwpoc@pve : clonage, IP\nSSH debian : Docker, compose", "autoResize": true, "lineHeight": 1.25 }, @@ -1766,12 +1766,12 @@ "frameId": null, "index": "ak", "roundness": null, - "seed": 735460884, + "seed": 924755712, "version": 2, - "versionNonce": 1371575212, + "versionNonce": 1476864256, "isDeleted": false, "boundElements": null, - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false, "text": "Sites protégés (*.bwpoc.internal) et fonctionnalités testées", @@ -1805,17 +1805,17 @@ "roundness": { "type": 3 }, - "seed": 1380146476, + "seed": 1287866624, "version": 2, - "versionNonce": 386190612, + "versionNonce": 1847322368, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "XxyPlQj-5QnCZ1Zmf_BXE" + "id": "CCZyvcrjO1arjaijkmfNF" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1840,17 +1840,17 @@ "roundness": { "type": 3 }, - "seed": 1812814740, + "seed": 1253164800, "version": 2, - "versionNonce": 1648967212, + "versionNonce": 2134509824, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "4MHW2FcpXx1t6mSqSXVTO" + "id": "RRS3-uw9WDpkIgzXNKgn9" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1875,17 +1875,17 @@ "roundness": { "type": 3 }, - "seed": 716625836, + "seed": 1198663936, "version": 2, - "versionNonce": 814877332, + "versionNonce": 805766912, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "e5UYkx6aghzm2w0I3MXB-" + "id": "MatkLklYBQClZvh6fqOCK" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, @@ -1910,26 +1910,26 @@ "roundness": { "type": 3 }, - "seed": 579127572, + "seed": 1054926592, "version": 2, - "versionNonce": 1633196204, + "versionNonce": 2098260224, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "kcDtjZHiLETGgDHdTT3HH" + "id": "-uL4gJq4pHtE5x6NdqntE" } ], - "updated": 1793095752319, + "updated": 1793096641458, "link": null, "locked": false }, { - "id": "-wLdJuCSSU3isLGRUYsjf", + "id": "Af2mwOlXmrzqbdbEEWZFs", "type": "text", - "x": 861.5, + "x": 783, "y": 810.75, - "width": 237, + "width": 394, "height": 22.5, "angle": 0, "strokeColor": "#1e1e1e", @@ -1943,26 +1943,26 @@ "frameId": null, "index": "ap", "roundness": null, - "seed": 1889245204, + "seed": 1200498432, "version": 3, - "versionNonce": 1644755732, + "versionNonce": 2017617664, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, - "text": "vmbr0 - 192.168.200.0/24", + "text": "VLAN 200 LAB - DHCP (IP, passerelle, DNS)", "fontSize": 18, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", "containerId": "bus", - "originalText": "vmbr0 - 192.168.200.0/24", + "originalText": "VLAN 200 LAB - DHCP (IP, passerelle, DNS)", "autoResize": true, "lineHeight": 1.25 }, { - "id": "eHqZIridF0wrU6HRLPRVo", + "id": "SlU8Q87Uz7NKL4ZnYbYpJ", "type": "text", "x": 386, "y": 336.75, @@ -1980,12 +1980,12 @@ "frameId": null, "index": "aq", "roundness": null, - "seed": 1662481812, + "seed": 556877568, "version": 3, - "versionNonce": 1533472812, + "versionNonce": 1891338496, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "curl · sqlmap · nmap · ab", @@ -1999,7 +1999,7 @@ "lineHeight": 1.25 }, { - "id": "ud-DFX9hc28YXTLCu3ycp", + "id": "39LnfMslwfj0lwpj1N9OR", "type": "text", "x": 373.5, "y": 417.5, @@ -2017,12 +2017,12 @@ "frameId": null, "index": "ar", "roundness": null, - "seed": 624665364, + "seed": 426796800, "version": 3, - "versionNonce": 652245140, + "versionNonce": 828212992, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bwpoc-tests\n20 contrôles automatisés", @@ -2036,7 +2036,7 @@ "lineHeight": 1.25 }, { - "id": "yTLo2Vng9jiScCVL5wf8x", + "id": "JKbfDPIiEI4fA4EEXe6hE", "type": "text", "x": 796, "y": 251.25, @@ -2054,12 +2054,12 @@ "frameId": null, "index": "as", "roundness": null, - "seed": 1031676052, + "seed": 2093713152, "version": 3, - "versionNonce": 267389612, + "versionNonce": 1113745664, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bunkerweb\nnginx + ModSecurity + OWASP CRS 4\n:80 / :443 (TCP + UDP HTTP/3)", @@ -2073,7 +2073,7 @@ "lineHeight": 1.25 }, { - "id": "hguasmJb74910iJe48rlW", + "id": "hRBEDMGyTSx5wa_AhCgbB", "type": "text", "x": 775, "y": 437.5, @@ -2091,12 +2091,12 @@ "frameId": null, "index": "at", "roundness": null, - "seed": 1939140116, + "seed": 130197248, "version": 3, - "versionNonce": 787236372, + "versionNonce": 1511302912, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bw-scheduler\ngénère la conf", @@ -2110,7 +2110,7 @@ "lineHeight": 1.25 }, { - "id": "WIA-EPcOdhCM-V6poW90H", + "id": "rJHn1YADsJuVx_y8MZxC6", "type": "text", "x": 1017.5, "y": 437.5, @@ -2128,12 +2128,12 @@ "frameId": null, "index": "au", "roundness": null, - "seed": 678824852, + "seed": 1725286144, "version": 3, - "versionNonce": 963761452, + "versionNonce": 2083342592, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bw-ui :7000\ninterface web", @@ -2147,7 +2147,7 @@ "lineHeight": 1.25 }, { - "id": "TymlawtgjzMOi0T6nTv_x", + "id": "-hg8jbuzcfJlp8F3G3gQ1", "type": "text", "x": 788, "y": 617.5, @@ -2165,12 +2165,12 @@ "frameId": null, "index": "av", "roundness": null, - "seed": 777484564, + "seed": 914679552, "version": 3, - "versionNonce": 790782868, + "versionNonce": 518506240, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bw-db\nMariaDB 11", @@ -2184,7 +2184,7 @@ "lineHeight": 1.25 }, { - "id": "3UXlHuNY841IZz4g0ZNcQ", + "id": "ww8fu0t2s4n99PHCH9kvq", "type": "text", "x": 1003, "y": 617.5, @@ -2202,12 +2202,12 @@ "frameId": null, "index": "aw", "roundness": null, - "seed": 548353684, + "seed": 55576320, "version": 3, - "versionNonce": 357978028, + "versionNonce": 2040277248, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bw-redis\nbans et sessions", @@ -2221,7 +2221,7 @@ "lineHeight": 1.25 }, { - "id": "vZ2I-5RJHooP7DQTsH2ul", + "id": "JdfU-Amrtmtu_Cmtep0GB", "type": "text", "x": 1391, "y": 257.5, @@ -2239,12 +2239,12 @@ "frameId": null, "index": "ax", "roundness": null, - "seed": 1009992724, + "seed": 1236739840, "version": 3, - "versionNonce": 1303957780, + "versionNonce": 475457280, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "poc-www - nginx\nvitrine :8081", @@ -2258,7 +2258,7 @@ "lineHeight": 1.25 }, { - "id": "_QqJdXGiIbXNZuILuYvGY", + "id": "uufjvSSLzIWWPXKmgaLq0", "type": "text", "x": 1366, "y": 357.5, @@ -2276,12 +2276,12 @@ "frameId": null, "index": "ay", "roundness": null, - "seed": 920232340, + "seed": 1983530752, "version": 3, - "versionNonce": 600275500, + "versionNonce": 640616704, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "poc-shop - Juice Shop\n:8082", @@ -2295,7 +2295,7 @@ "lineHeight": 1.25 }, { - "id": "_rNV8XuTeLBAhBJAUkdOm", + "id": "32wxfqeHWtENze0lGW9mT", "type": "text", "x": 1374.5, "y": 457.5, @@ -2313,12 +2313,12 @@ "frameId": null, "index": "az", "roundness": null, - "seed": 400051988, + "seed": 1700357888, "version": 3, - "versionNonce": 2049281684, + "versionNonce": 1591871232, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "poc-api - go-httpbin\n:8083", @@ -2332,7 +2332,7 @@ "lineHeight": 1.25 }, { - "id": "qqyyGfg_SvBJDhoUUdnEh", + "id": "SBymGOxZkAbdhWzv8Xwm5", "type": "text", "x": 1357.5, "y": 617.5, @@ -2350,12 +2350,12 @@ "frameId": null, "index": "b00", "roundness": null, - "seed": 262491284, + "seed": 1670678272, "version": 3, - "versionNonce": 1336890540, + "versionNonce": 2103830784, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "iptables DOCKER-USER\nseul bw-waf passe", @@ -2369,11 +2369,11 @@ "lineHeight": 1.25 }, { - "id": "bfJhe4UvG8FsofCpAoNkZ", + "id": "-ffQ0SRQ7j9UXYsPDMI0R", "type": "text", - "x": 73.5, + "x": 65.5, "y": 172.5, - "width": 153, + "width": 169, "height": 45, "angle": 0, "strokeColor": "#1e1e1e", @@ -2387,26 +2387,26 @@ "frameId": null, "index": "b01", "roundness": null, - "seed": 1341561364, + "seed": 762029824, "version": 3, - "versionNonce": 1159633940, + "versionNonce": 1661544192, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, - "text": "Navigateur admin\nhosts vers .61", + "text": "Navigateur admin\nhosts vers bw-waf", "fontSize": 18, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", "containerId": "admin", - "originalText": "Navigateur admin\nhosts vers .61", + "originalText": "Navigateur admin\nhosts vers bw-waf", "autoResize": true, "lineHeight": 1.25 }, { - "id": "HyBQHnKMlKXlGiehADIur", + "id": "D3yKdgArmT-__EgZgqWXC", "type": "text", "x": 65.5, "y": 627.5, @@ -2424,12 +2424,12 @@ "frameId": null, "index": "b02", "roundness": null, - "seed": 1673672596, + "seed": 1868482304, "version": 3, - "versionNonce": 381148972, + "versionNonce": 180406528, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "Contrôleur Ansible\nplaybooks 00 à 50", @@ -2443,7 +2443,7 @@ "lineHeight": 1.25 }, { - "id": "XxyPlQj-5QnCZ1Zmf_BXE", + "id": "CCZyvcrjO1arjaijkmfNF", "type": "text", "x": 344.5, "y": 955, @@ -2461,12 +2461,12 @@ "frameId": null, "index": "b03", "roundness": null, - "seed": 121513236, + "seed": 1173219072, "version": 3, - "versionNonce": 949614996, + "versionNonce": 357320448, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "www - vitrine\nantibot JS, en-têtes, HTTPS", @@ -2480,7 +2480,7 @@ "lineHeight": 1.25 }, { - "id": "4MHW2FcpXx1t6mSqSXVTO", + "id": "RRS3-uw9WDpkIgzXNKgn9", "type": "text", "x": 694, "y": 955, @@ -2498,12 +2498,12 @@ "frameId": null, "index": "b04", "roundness": null, - "seed": 2145411732, + "seed": 1033438976, "version": 3, - "versionNonce": 827005356, + "versionNonce": 1774168320, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "shop - Juice Shop\nWAF SQLi / XSS / traversée", @@ -2517,7 +2517,7 @@ "lineHeight": 1.25 }, { - "id": "e5UYkx6aghzm2w0I3MXB-", + "id": "MatkLklYBQClZvh6fqOCK", "type": "text", "x": 1032.5, "y": 955, @@ -2535,12 +2535,12 @@ "frameId": null, "index": "b05", "roundness": null, - "seed": 1952378900, + "seed": 1390421760, "version": 3, - "versionNonce": 802006804, + "versionNonce": 1378576128, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "api - go-httpbin\ndébit, méthodes, bad behavior", @@ -2554,7 +2554,7 @@ "lineHeight": 1.25 }, { - "id": "kcDtjZHiLETGgDHdTT3HH", + "id": "-uL4gJq4pHtE5x6NdqntE", "type": "text", "x": 1401.5, "y": 955, @@ -2572,12 +2572,12 @@ "frameId": null, "index": "b06", "roundness": null, - "seed": 1152372116, + "seed": 1917011712, "version": 3, - "versionNonce": 982713388, + "versionNonce": 1172474112, "isDeleted": false, "boundElements": null, - "updated": 1793095752325, + "updated": 1793096641464, "link": null, "locked": false, "text": "bwui - UI BunkerWeb\nrapports, bans, réglages", diff --git a/docs/architecture.png b/docs/architecture.png index 1ba2c47..c97de78 100644 Binary files a/docs/architecture.png and b/docs/architecture.png differ diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml index 99ef749..a75092b 100644 --- a/inventory/group_vars/all.yml +++ b/inventory/group_vars/all.yml @@ -9,16 +9,20 @@ # ".internal" est réservé par l'ICANN pour l'usage privé : pas de collision possible. poc_domain: bwpoc.internal # A ADAPTER si vous avez une zone DNS de labo -# --- Réseau des VM (cloud-init) -------------------------------------- -poc_net_prefix: 24 # A ADAPTER -poc_net_gateway: 192.168.200.1 # A ADAPTER -poc_net_dns: [192.168.200.1] # A ADAPTER -poc_net_searchdomain: "{{ poc_domain }}" - # --- Proxmox ---------------------------------------------------------- +pve_node: lancelot pve_storage: local-lvm # A ADAPTER : stockage des disques VM +# Réseau "LAB", VLAN 200, adressage par DHCP (passerelle et DNS fournis). +# Si LAB est un VNet SDN : pve_bridge: LAB et pve_vlan_tag: "" pve_bridge: vmbr0 # A ADAPTER -pve_vlan_tag: "" # vide = pas de VLAN, sinon ex. "200" +pve_vlan_tag: "200" +pve_pool: bwpoc # pool délégué au compte dédié + +# Compte dédié pour le clonage par l'API (voir README, "Compte Proxmox dédié") +pve_api_user: bwpoc@pve +pve_api_token_id: ansible +pve_api_token_secret: "{{ lookup('ansible.builtin.env', 'PVE_TOKEN_SECRET') }}" +pve_validate_certs: false # certificat auto-signé du nœud pve_template_vmid: 9100 pve_template_name: debian13-cloud pve_image_url: https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2 diff --git a/inventory/hosts.yml b/inventory/hosts.yml index 67ad841..24054ed 100644 --- a/inventory/hosts.yml +++ b/inventory/hosts.yml @@ -1,13 +1,15 @@ --- # Inventaire du PoC BunkerWeb. +# Les VM prennent leur IP par DHCP (VLAN 200 "LAB") : 00-proxmox.yml la lit +# via l'agent QEMU et l'écrit dans inventory/host_vars//dhcp.yml. # Mode "split" (défaut) : 3 VM distinctes (WAF, applis, client). # Mode "single" : voir inventory/single.yml (tout sur une seule VM). all: children: proxmox: hosts: - pve01: - ansible_host: 192.168.200.10 # A ADAPTER : nœud Proxmox qui héberge les VM + lancelot: + ansible_host: 172.16.16.7 ansible_user: root poc_vms: vars: @@ -17,7 +19,6 @@ all: bunkerweb: hosts: bw-waf: - ansible_host: 192.168.200.61 # A ADAPTER vm_id: 9101 vm_cores: 2 vm_memory: 4096 @@ -25,7 +26,6 @@ all: apps: hosts: bw-apps: - ansible_host: 192.168.200.62 # A ADAPTER vm_id: 9102 vm_cores: 2 vm_memory: 4096 @@ -33,7 +33,6 @@ all: client: hosts: bw-client: - ansible_host: 192.168.200.63 # A ADAPTER vm_id: 9103 vm_cores: 1 vm_memory: 1024 diff --git a/inventory/single.yml b/inventory/single.yml index 9eef7f3..5f3fe0b 100644 --- a/inventory/single.yml +++ b/inventory/single.yml @@ -5,8 +5,8 @@ all: children: proxmox: hosts: - pve01: - ansible_host: 192.168.200.10 # A ADAPTER + lancelot: + ansible_host: 172.16.16.7 ansible_user: root poc_vms: vars: @@ -16,7 +16,6 @@ all: bunkerweb: hosts: bw-poc: - ansible_host: 192.168.200.61 # A ADAPTER vm_id: 9101 vm_cores: 4 vm_memory: 6144 diff --git a/playbooks/00-proxmox.yml b/playbooks/00-proxmox.yml index 895b71f..159cbd8 100644 --- a/playbooks/00-proxmox.yml +++ b/playbooks/00-proxmox.yml @@ -1,9 +1,27 @@ --- -# Crée le modèle Debian 13 cloud-init puis clone les VM du PoC. -# A sauter si vous fournissez vos propres VM (Debian 12/13 avec accès SSH). -- name: Provisionner les VM sur Proxmox +# 1) Modèle Debian 13 cloud-init : une seule fois, en SSH root sur le nœud +# (l'import d'une image disque par chemin absolu est réservé à root@pam). +- name: Modèle cloud-init et pool sur le nœud Proxmox hosts: proxmox gather_facts: false roles: - proxmox_template + +# 2) Clonage des VM par l'API, avec le compte dédié bwpoc@pve (jeton API). +# A sauter si vous fournissez vos propres VM (Debian 12/13 avec accès SSH). +- name: Clonage des VM par l'API Proxmox + hosts: proxmox + connection: local + gather_facts: false + vars: + ansible_become: false + ansible_python_interpreter: "{{ ansible_playbook_python }}" + module_defaults: + group/community.proxmox.proxmox: + api_host: "{{ ansible_host }}" + api_user: "{{ pve_api_user }}" + api_token_id: "{{ pve_api_token_id }}" + api_token_secret: "{{ pve_api_token_secret }}" + validate_certs: "{{ pve_validate_certs }}" + roles: - proxmox_vm diff --git a/playbooks/99-destroy.yml b/playbooks/99-destroy.yml index c189d96..03ff49e 100644 --- a/playbooks/99-destroy.yml +++ b/playbooks/99-destroy.yml @@ -31,3 +31,11 @@ register: tpl_destroy changed_when: "'destroyed' in tpl_destroy.stdout" when: destroy_template | default(false) | bool + + - name: Oublier les IP DHCP mémorisées + ansible.builtin.file: + path: "{{ inventory_dir }}/host_vars/{{ item }}/dhcp.yml" + state: absent + loop: "{{ groups['poc_vms'] | unique }}" + delegate_to: localhost + become: false diff --git a/requirements.yml b/requirements.yml index 0418557..35b7d81 100644 --- a/requirements.yml +++ b/requirements.yml @@ -2,4 +2,5 @@ collections: - name: community.docker version: ">=3.10.0" - - name: ansible.posix + - name: community.proxmox + version: ">=1.3.0" diff --git a/roles/proxmox_template/tasks/main.yml b/roles/proxmox_template/tasks/main.yml index 63d6401..04e2037 100644 --- a/roles/proxmox_template/tasks/main.yml +++ b/roles/proxmox_template/tasks/main.yml @@ -6,6 +6,13 @@ changed_when: false failed_when: false +- name: Créer le pool {{ pve_pool }} (délégation au compte dédié) + ansible.builtin.shell: | + pveum pool list --output-format json | grep -q '"poolid":"{{ pve_pool }}"' && exit 0 + pveum pool add {{ pve_pool }} --comment "PoC BunkerWeb" && echo created + register: pool_add + changed_when: "'created' in pool_add.stdout" + - name: Créer le modèle when: tpl_status.rc != 0 block: @@ -14,6 +21,20 @@ url: "{{ pve_image_url }}" dest: "{{ pve_image_path }}" mode: "0644" + force: true # image vierge à chaque création du modèle + + - name: Installer libguestfs-tools (personnalisation de l'image) + ansible.builtin.apt: + name: libguestfs-tools + state: present + update_cache: true + + - name: Ajouter l'agent QEMU à l'image (indispensable pour lire l'IP DHCP) + ansible.builtin.command: >- + virt-customize -a {{ pve_image_path }} + --install qemu-guest-agent + --truncate /etc/machine-id + changed_when: true - name: Créer la VM modèle ansible.builtin.command: >- @@ -43,3 +64,10 @@ - name: Convertir en modèle ansible.builtin.command: qm template {{ pve_template_vmid }} changed_when: true + +- name: Placer le modèle dans le pool (le compte dédié peut le cloner) + ansible.builtin.shell: | + pvesh get /pools/{{ pve_pool }} --output-format json | grep -q '"vmid":{{ pve_template_vmid }}[,}]' && exit 0 + pveum pool modify {{ pve_pool }} --vms {{ pve_template_vmid }} && echo added + register: pool_tpl + changed_when: "'added' in pool_tpl.stdout" diff --git a/roles/proxmox_vm/tasks/main.yml b/roles/proxmox_vm/tasks/main.yml index da52a33..03c75d1 100644 --- a/roles/proxmox_vm/tasks/main.yml +++ b/roles/proxmox_vm/tasks/main.yml @@ -1,13 +1,24 @@ --- -# Clone et configure les VM du PoC à partir du modèle (exécuté sur le nœud Proxmox). -- name: Déposer la clé SSH publique pour cloud-init - ansible.builtin.copy: - content: "{{ lookup('ansible.builtin.file', poc_ssh_pubkey_file) }}\n" - dest: /root/bwpoc_authorized_keys - mode: "0600" +# Clone et configure les VM du PoC par l'API Proxmox (compte dédié, voir README). +# Exécuté sur le contrôleur Ansible : connection local dans le playbook. +- name: Contrôler la présence du jeton API + ansible.builtin.assert: + that: pve_api_token_secret | length > 0 + fail_msg: "Exportez PVE_TOKEN_SECRET (secret du jeton {{ pve_api_user }}!{{ pve_api_token_id }})" + quiet: true + +- name: Créer le répertoire des host_vars par VM + ansible.builtin.file: + path: "{{ inventory_dir }}/host_vars/{{ item }}" + state: directory + mode: "0755" + loop: "{{ groups['poc_vms'] | unique }}" - name: Créer chaque VM ansible.builtin.include_tasks: vm.yml loop: "{{ groups['poc_vms'] | unique }}" loop_control: loop_var: vm_name + +- name: Recharger l'inventaire avec les IP DHCP + ansible.builtin.meta: refresh_inventory diff --git a/roles/proxmox_vm/tasks/vm.yml b/roles/proxmox_vm/tasks/vm.yml index b40c0bb..486cdfa 100644 --- a/roles/proxmox_vm/tasks/vm.yml +++ b/roles/proxmox_vm/tasks/vm.yml @@ -4,53 +4,86 @@ vm: "{{ hostvars[vm_name] }}" - name: "{{ vm_name }} : la VM existe-t-elle ?" - ansible.builtin.command: qm status {{ vm.vm_id }} - register: vm_status - changed_when: false - failed_when: false + community.proxmox.proxmox_vm_info: + vmid: "{{ vm.vm_id }}" + node: "{{ pve_node }}" + config: none + register: vm_info - name: "{{ vm_name }} : création" - when: vm_status.rc != 0 + when: vm_info.proxmox_vms | length == 0 block: - - name: "{{ vm_name }} : l'IP {{ vm.ansible_host }} doit être libre" - ansible.builtin.command: ping -c 2 -W 1 {{ vm.ansible_host }} - register: ip_ping - changed_when: false - failed_when: ip_ping.rc == 0 - - - name: "{{ vm_name }} : clonage du modèle" - ansible.builtin.command: >- - qm clone {{ pve_template_vmid }} {{ vm.vm_id }} - --name {{ vm_name }} --full 1 --storage {{ pve_storage }} - changed_when: true - - - name: "{{ vm_name }} : ressources et cloud-init" - ansible.builtin.command: >- - qm set {{ vm.vm_id }} - --cores {{ vm.vm_cores }} --memory {{ vm.vm_memory }} - --onboot 1 --tags bwpoc - --ciuser {{ poc_vm_user }} - --sshkeys /root/bwpoc_authorized_keys - --ipconfig0 ip={{ vm.ansible_host }}/{{ poc_net_prefix }},gw={{ poc_net_gateway }} - --nameserver "{{ poc_net_dns | join(' ') }}" - --searchdomain {{ poc_net_searchdomain }} - --ciupgrade 0 - changed_when: true + - name: "{{ vm_name }} : clonage du modèle {{ pve_template_vmid }}" + community.proxmox.proxmox_kvm: + node: "{{ pve_node }}" + vmid: "{{ pve_template_vmid }}" + clone: "{{ pve_template_name }}" + newid: "{{ vm.vm_id }}" + name: "{{ vm_name }}" + full: true + storage: "{{ pve_storage }}" + pool: "{{ pve_pool }}" + timeout: 600 - name: "{{ vm_name }} : agrandissement du disque à {{ vm.vm_disk_gb }} Go" - ansible.builtin.command: qm resize {{ vm.vm_id }} scsi0 {{ vm.vm_disk_gb }}G - changed_when: true + community.proxmox.proxmox_disk: + vmid: "{{ vm.vm_id }}" + disk: scsi0 + state: resized + size: "{{ vm.vm_disk_gb }}G" + +- name: "{{ vm_name }} : ressources et cloud-init" + community.proxmox.proxmox_kvm: + node: "{{ pve_node }}" + vmid: "{{ vm.vm_id }}" + update: true + cores: "{{ vm.vm_cores }}" + memory: "{{ vm.vm_memory }}" + onboot: true + tags: [bwpoc] + ciuser: "{{ poc_vm_user }}" + sshkeys: "{{ lookup('ansible.builtin.file', poc_ssh_pubkey_file) }}" + ipconfig: + ipconfig0: "ip=dhcp" # passerelle et DNS fournis par le DHCP du VLAN LAB + ciupgrade: false - name: "{{ vm_name }} : démarrage" - ansible.builtin.command: qm start {{ vm.vm_id }} - register: vm_start - changed_when: vm_start.rc == 0 - failed_when: vm_start.rc != 0 and 'already running' not in vm_start.stderr + community.proxmox.proxmox_kvm: + node: "{{ pve_node }}" + vmid: "{{ vm.vm_id }}" + state: started + timeout: 120 -- name: "{{ vm_name }} : attente du SSH" +- name: "{{ vm_name }} : adresse DHCP remontée par l'agent QEMU" + community.proxmox.proxmox_vm_info: + node: "{{ pve_node }}" + vmid: "{{ vm.vm_id }}" + config: current + network: true + register: vm_net + until: >- + (vm_net.proxmox_vms[0].network | default([]) | rejectattr('name', 'equalto', 'lo') + | map(attribute='ip-addresses') | flatten | selectattr('ip-address-type', 'equalto', 'ipv4') | list | length) > 0 + retries: 40 + delay: 10 + +- name: "{{ vm_name }} : mémoriser l'IP" + ansible.builtin.set_fact: + vm_ip: >- + {{ vm_net.proxmox_vms[0].network | rejectattr('name', 'equalto', 'lo') + | map(attribute='ip-addresses') | flatten | selectattr('ip-address-type', 'equalto', 'ipv4') + | map(attribute='ip-address') | first }} + +- name: "{{ vm_name }} : écrire l'IP dans inventory/host_vars (relu par les playbooks suivants)" + ansible.builtin.copy: + dest: "{{ inventory_dir }}/host_vars/{{ vm_name }}/dhcp.yml" + mode: "0644" + content: | + # Généré par 00-proxmox.yml : IP obtenue par DHCP (VMID {{ vm.vm_id }}) + ansible_host: {{ vm_ip }} + +- name: "{{ vm_name }} : attente du SSH sur {{ vm_ip }}" ansible.builtin.wait_for: - host: "{{ vm.ansible_host }}" + host: "{{ vm_ip }}" port: 22 timeout: 300 - delegate_to: localhost - become: false diff --git a/tests/bwpoc-tests.sh b/tests/bwpoc-tests.sh index 9c6459d..8702a41 100755 --- a/tests/bwpoc-tests.sh +++ b/tests/bwpoc-tests.sh @@ -21,9 +21,9 @@ SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" # shellcheck source=/dev/null [ -f "$SCRIPT_DIR/bwpoc-tests.env" ] && . "$SCRIPT_DIR/bwpoc-tests.env" -WAF_IP="${WAF_IP:-192.168.200.61}" +WAF_IP="${WAF_IP:?WAF_IP non défini (bwpoc-tests.env ou environnement)}" DOMAIN="${DOMAIN:-bwpoc.internal}" -APPS_IP="${APPS_IP:-192.168.200.62}" +APPS_IP="${APPS_IP:-}" PORT_SHOP="${PORT_SHOP:-8082}" PORT_API="${PORT_API:-8083}"